Support keyboard navigation for RTL
In two-pane landscape mode support our custom navigation
for RTL mode (left/right reversed).
Change-Id: I8d3afe1362fc935f8b6140ce37bcfd6a9ba88bb2
diff --git a/res/layout/folder_list.xml b/res/layout/folder_list.xml
index 3632f9c..239dea7 100644
--- a/res/layout/folder_list.xml
+++ b/res/layout/folder_list.xml
@@ -24,8 +24,8 @@
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:nextFocusRight="@+id/conversation_list_view"
- android:fadingEdge="none" />
+ android:fadingEdge="none"
+ style="@style/DrawerNavigationStyle" />
<com.android.mail.ui.MiniDrawerView
android:id="@+id/mini_drawer"
diff --git a/res/values-ldrtl/styles-ldrtl.xml b/res/values-ldrtl/styles-ldrtl.xml
index 54df227..99a1b53 100644
--- a/res/values-ldrtl/styles-ldrtl.xml
+++ b/res/values-ldrtl/styles-ldrtl.xml
@@ -63,6 +63,10 @@
<item name="android:layout_marginEnd">@dimen/toast_bar_margin</item>
</style>
+ <style name="DrawerNavigationStyle">
+ <item name="android:nextFocusLeft">@+id/conversation_list_view</item>
+ </style>
+
<style name="FolderListItemStartStyle">
<item name="android:layout_marginStart">@dimen/folder_list_item_text_start_margin</item>
</style>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e0c7b59..2bbb1ef 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -590,10 +590,13 @@
<item name="android:layout_marginRight">@dimen/conversation_border_margin_side</item>
</style>
- <style name="MiniDrawerItemStyleBase">
+ <style name="DrawerNavigationStyle">
+ <item name="android:nextFocusRight">@+id/conversation_list_view</item>
+ </style>
+
+ <style name="MiniDrawerItemStyleBase" parent="@style/DrawerNavigationStyle">
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:focusable">true</item>
- <item name="android:nextFocusRight">@+id/conversation_list_view</item>
<item name="android:scaleType">fitCenter</item>
</style>
diff --git a/src/com/android/mail/ui/ConversationListFragment.java b/src/com/android/mail/ui/ConversationListFragment.java
index dba18a8..57c947c 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -61,6 +61,7 @@
import com.android.mail.ui.SwipeableListView.ListItemsRemovedListener;
import com.android.mail.ui.SwipeableListView.SwipeListener;
import com.android.mail.ui.ViewMode.ModeChangeListener;
+import com.android.mail.utils.KeyboardUtils;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
import com.android.mail.utils.Utils;
@@ -155,10 +156,11 @@
// True if NO DATA has returned, false if we either partially or fully loaded the data
private boolean mInitialCursorLoading;
- private @IdRes int mNextFocusLeftId;
+ private @IdRes int mNextFocusStartId;
// Tracks if a onKey event was initiated from the listview (received ACTION_DOWN before
// ACTION_UP). If not, the listview only receives ACTION_UP.
private boolean mKeyInitiatedFromList;
+ private boolean mIsRtl;
/** Duration, in milliseconds, of the CAB mode (peek icon) animation. */
private static long sSelectionModeAnimationDuration = -1;
@@ -467,13 +469,12 @@
mListView.setOnKeyListener(this);
mListView.setOnItemClickListener(this);
+ mIsRtl = Utils.isCurrentLocaleRtl();
// For tablets, the default left focus is the mini-drawer
- if (mTabletDevice) {
- mNextFocusLeftId = R.id.current_account_avatar;
+ if (mTabletDevice && mNextFocusStartId == 0) {
+ mNextFocusStartId = R.id.current_account_avatar;
}
- if (mNextFocusLeftId != 0) {
- mListView.setNextFocusLeftId(mNextFocusLeftId);
- }
+ setNextFocusStartOnList();
// enable animateOnLayout (equivalent of setLayoutTransition) only for >=JB (b/14302062)
if (Utils.isRunningJellybeanOrLater()) {
@@ -639,7 +640,7 @@
if (view instanceof SwipeableListView) {
SwipeableListView list = (SwipeableListView) view;
// Don't need to handle ENTER because it's auto-handled as a "click".
- if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ if (KeyboardUtils.isKeycodeDirectionEnd(keyCode, mIsRtl)) {
if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
if (mKeyInitiatedFromList) {
onListItemSelected(list.getSelectedView(), list.getSelectedItemPosition());
@@ -649,14 +650,14 @@
mKeyInitiatedFromList = true;
}
return true;
- } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
- if (keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- final int position = list.getSelectedItemPosition();
- final Object item = getAnimatedAdapter().getItem(position);
- if (item != null && item instanceof ConversationCursor) {
- final Conversation conv = ((ConversationCursor) item).getConversation();
- mCallbacks.onConversationFocused(conv);
- }
+ } else if ((keyCode == KeyEvent.KEYCODE_DPAD_UP ||
+ keyCode == KeyEvent.KEYCODE_DPAD_DOWN) &&
+ keyEvent.getAction() == KeyEvent.ACTION_UP) {
+ final int position = list.getSelectedItemPosition();
+ final Object item = getAnimatedAdapter().getItem(position);
+ if (item != null && item instanceof ConversationCursor) {
+ final Conversation conv = ((ConversationCursor) item).getConversation();
+ mCallbacks.onConversationFocused(conv);
}
}
}
@@ -1225,10 +1226,18 @@
return mListView;
}
- public void setNextFocusLeftId(@IdRes int id) {
- mNextFocusLeftId = id;
- if (mListView != null) {
- mListView.setNextFocusLeftId(mNextFocusLeftId);
+ public void setNextFocusStartId(@IdRes int id) {
+ mNextFocusStartId = id;
+ setNextFocusStartOnList();
+ }
+
+ private void setNextFocusStartOnList() {
+ if (mListView != null && mNextFocusStartId != 0) {
+ // Since we manually handle right navigation from the list, let's just always set both
+ // the default left and right navigation to the left id so that whenever the framework
+ // handles one of these directions, it will go to the left side regardless of RTL.
+ mListView.setNextFocusLeftId(mNextFocusStartId);
+ mListView.setNextFocusRightId(mNextFocusStartId);
}
}
}
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index 6c23274..1f173f2 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -82,6 +82,7 @@
import com.android.mail.providers.UIProvider;
import com.android.mail.ui.ConversationViewState.ExpansionState;
import com.android.mail.utils.ConversationViewUtils;
+import com.android.mail.utils.KeyboardUtils;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
import com.android.mail.utils.Utils;
@@ -141,6 +142,7 @@
private View mOriginalKeyedView;
private int mMaxScreenHeight;
private int mTopOfVisibleScreen;
+ private boolean mIsRtl;
protected ConversationContainer mConversationContainer;
@@ -440,6 +442,8 @@
mViewsCreated = true;
mWebViewLoadedData = false;
+ mIsRtl = Utils.isCurrentLocaleRtl();
+
return rootView;
}
@@ -1178,8 +1182,8 @@
if (mOriginalKeyedView != null) {
final int id = mOriginalKeyedView.getId();
final boolean isActionUp = keyEvent.getAction() == KeyEvent.ACTION_UP;
- final boolean isLeft = keyCode == KeyEvent.KEYCODE_DPAD_LEFT;
- final boolean isRight = keyCode == KeyEvent.KEYCODE_DPAD_RIGHT;
+ final boolean isStart = KeyboardUtils.isKeycodeDirectionStart(keyCode, mIsRtl);
+ final boolean isEnd = KeyboardUtils.isKeycodeDirectionEnd(keyCode, mIsRtl);
final boolean isUp = keyCode == KeyEvent.KEYCODE_DPAD_UP;
final boolean isDown = keyCode == KeyEvent.KEYCODE_DPAD_DOWN;
@@ -1187,14 +1191,14 @@
// We manually check if the view+direction combination should shift focus away from the
// conversation view to the thread list in two-pane landscape mode.
final boolean isTwoPaneLand = mNavigationController.isTwoPaneLandscape();
- final boolean navigateAway = shouldNavigateAway(id, isLeft, isTwoPaneLand);
+ final boolean navigateAway = shouldNavigateAway(id, isStart, isTwoPaneLand);
if (mNavigationController.onInterceptKeyFromCV(keyCode, keyEvent, navigateAway)) {
return true;
}
// If controller didn't handle the event, check directional interception.
- if ((isLeft || isRight) && shouldInterceptLeftRightEvents(
- id, isLeft, isRight, isTwoPaneLand)) {
+ if ((isStart || isEnd) && shouldInterceptLeftRightEvents(
+ id, isStart, isEnd, isTwoPaneLand)) {
return true;
} else if (isUp || isDown) {
// We don't do anything on up/down for overlay
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 3238c02..5cd0348 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -119,7 +119,7 @@
TAG_CONVERSATION_LIST);
fragmentTransaction.commitAllowingStateLoss();
// Set default navigation here once the ConversationListFragment is created.
- conversationListFragment.setNextFocusLeftId(
+ conversationListFragment.setNextFocusStartId(
getClfNextFocusLeftId(getFolderListFragment().isMinimized()));
}
@@ -252,7 +252,7 @@
final ConversationListFragment clf = getConversationListFragment();
if (clf != null) {
- clf.setNextFocusLeftId(getClfNextFocusLeftId(flf.isMinimized()));
+ clf.setNextFocusStartId(getClfNextFocusLeftId(flf.isMinimized()));
final SwipeableListView list = clf.getListView();
if (list != null) {
diff --git a/src/com/android/mail/utils/KeyboardUtils.java b/src/com/android/mail/utils/KeyboardUtils.java
new file mode 100644
index 0000000..7bfd61c
--- /dev/null
+++ b/src/com/android/mail/utils/KeyboardUtils.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2014, Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.utils;
+
+import android.view.KeyEvent;
+
+/**
+ * Utility class for keyboard navigation.
+ */
+public class KeyboardUtils {
+ /**
+ * Determine if the provided keycode is toward the start direction of the normal layout.
+ * This is used for keyboard navigation since the layouts are flipped for RTL.
+ */
+ public static boolean isKeycodeDirectionStart(int keyCode, boolean isRtl) {
+ return (!isRtl && keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ||
+ (isRtl && keyCode == KeyEvent.KEYCODE_DPAD_RIGHT);
+ }
+
+ /**
+ * Determine if the provided keycode is toward the end direction of the normal layout.
+ * This is used for keyboard navigation since the layouts are flipped for RTL.
+ */
+ public static boolean isKeycodeDirectionEnd(int keyCode, boolean isRtl) {
+ return (isRtl && keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ||
+ (!isRtl && keyCode == KeyEvent.KEYCODE_DPAD_RIGHT);
+ }
+}
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 662a270..05f3665 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -35,6 +35,8 @@
import android.os.Build;
import android.os.Bundle;
import android.provider.Browser;
+import android.support.v4.text.TextUtilsCompat;
+import android.support.v4.view.ViewCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
@@ -63,7 +65,6 @@
import com.android.mail.providers.UIProvider;
import com.android.mail.providers.UIProvider.EditSettingsExtras;
import com.android.mail.ui.HelpActivity;
-import com.android.mail.ui.ViewMode;
import com.google.android.mail.common.html.parser.HtmlDocument;
import com.google.android.mail.common.html.parser.HtmlParser;
import com.google.android.mail.common.html.parser.HtmlTree;
@@ -1157,4 +1158,9 @@
final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
return (networkInfo != null) && networkInfo.isConnected();
}
+
+ public static boolean isCurrentLocaleRtl() {
+ return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) ==
+ ViewCompat.LAYOUT_DIRECTION_RTL;
+ }
}