diff options
author | Jin Cao <jinyan@google.com> | 2014-08-05 14:03:59 -0700 |
---|---|---|
committer | Jin Cao <jinyan@google.com> | 2014-08-08 17:57:43 -0700 |
commit | a7404589b03ac9dd0d07b3f7d0a1ec92ac9acb62 (patch) | |
tree | 0d8e9613760c7ffff1a6ca24b910671a231f38cb /src | |
parent | 094986e3c824d705909af10464954532c377adc3 (diff) | |
download | android_packages_apps_UnifiedEmail-a7404589b03ac9dd0d07b3f7d0a1ec92ac9acb62.tar.gz android_packages_apps_UnifiedEmail-a7404589b03ac9dd0d07b3f7d0a1ec92ac9acb62.tar.bz2 android_packages_apps_UnifiedEmail-a7404589b03ac9dd0d07b3f7d0a1ec92ac9acb62.zip |
[KBNav CV] basic support for CV keyboard nav
Support basic navigation via keyboard in CV.
b/16636060
Change-Id: I66dbcd8015d722244b57c4e24579d0d854d3ee74
Diffstat (limited to 'src')
6 files changed, 137 insertions, 21 deletions
diff --git a/src/com/android/mail/browse/ConversationContainer.java b/src/com/android/mail/browse/ConversationContainer.java index a5e82b177..f99bd3731 100644 --- a/src/com/android/mail/browse/ConversationContainer.java +++ b/src/com/android/mail/browse/ConversationContainer.java @@ -29,7 +29,6 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.webkit.WebView; -import android.widget.Adapter; import android.widget.ListView; import android.widget.ScrollView; diff --git a/src/com/android/mail/browse/ConversationOverlayItem.java b/src/com/android/mail/browse/ConversationOverlayItem.java index e52e9d662..08f2cdfba 100644 --- a/src/com/android/mail/browse/ConversationOverlayItem.java +++ b/src/com/android/mail/browse/ConversationOverlayItem.java @@ -42,6 +42,7 @@ public abstract class ConversationOverlayItem { * @see Adapter#getItemViewType(int) */ public abstract @ConversationViewType int getType(); + /** * Inflate and perform one-time initialization on a view for later binding. */ @@ -55,6 +56,7 @@ public abstract class ConversationOverlayItem { * know they can cut certain corners that do not affect a view's height) */ public abstract void bindView(View v, boolean measureOnly); + /** * Returns true if this overlay view is meant to be positioned right on top of the overlay * below. This special positioning allows {@link ConversationContainer} to stack overlays @@ -63,6 +65,10 @@ public abstract class ConversationOverlayItem { */ public abstract boolean isContiguous(); + public View.OnKeyListener getOnKeyListener() { + return null; + } + /** * Returns true if this overlay view is in its expanded state. */ @@ -181,4 +187,15 @@ public abstract class ConversationOverlayItem { public void rebindView(View view) { // DO NOTHING } + + public void registerOnKeyListeners(View... views) { + final View.OnKeyListener listener = getOnKeyListener(); + if (listener != null) { + for (View v : views) { + if (v != null) { + v.setOnKeyListener(listener); + } + } + } + } } diff --git a/src/com/android/mail/browse/ConversationViewAdapter.java b/src/com/android/mail/browse/ConversationViewAdapter.java index 0ae489694..bda63c136 100644 --- a/src/com/android/mail/browse/ConversationViewAdapter.java +++ b/src/com/android/mail/browse/ConversationViewAdapter.java @@ -110,6 +110,8 @@ public class ConversationViewAdapter extends BaseAdapter { private final BidiFormatter mBidiFormatter; + private final View.OnKeyListener mOnKeyListener; + public class ConversationHeaderItem extends ConversationOverlayItem { public final Conversation mConversation; @@ -124,19 +126,22 @@ public class ConversationViewAdapter extends BaseAdapter { @Override public View createView(Context context, LayoutInflater inflater, ViewGroup parent) { - final ConversationViewHeader headerView = (ConversationViewHeader) inflater.inflate( + final ConversationViewHeader v = (ConversationViewHeader) inflater.inflate( R.layout.conversation_view_header, parent, false); - headerView.setCallbacks( + v.setCallbacks( mConversationCallbacks, mAccountController, mConversationUpdater); - headerView.bind(this); - headerView.setSubject(mConversation.subject); + v.bind(this); + v.setSubject(mConversation.subject); if (mAccountController.getAccount().supportsCapability( UIProvider.AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV)) { - headerView.setFolders(mConversation); + v.setFolders(mConversation); } - headerView.setStarred(mConversation.starred); + v.setStarred(mConversation.starred); + + // Register the onkey listener for all relevant views + registerOnKeyListeners(v, v.findViewById(R.id.subject_and_folder_view)); - return headerView; + return v; } @Override @@ -150,6 +155,11 @@ public class ConversationViewAdapter extends BaseAdapter { return true; } + @Override + public View.OnKeyListener getOnKeyListener() { + return mOnKeyListener; + } + public ConversationViewAdapter getAdapter() { return ConversationViewAdapter.this; } @@ -169,11 +179,16 @@ public class ConversationViewAdapter extends BaseAdapter { @Override public View createView(Context context, LayoutInflater inflater, ViewGroup parent) { - final ConversationFooterView view = (ConversationFooterView) + final ConversationFooterView v = (ConversationFooterView) inflater.inflate(R.layout.conversation_footer, parent, false); - view.setAccountController(mAccountController); - view.setConversationFooterCallbacks(mConversationFooterCallbacks); - return view; + v.setAccountController(mAccountController); + v.setConversationFooterCallbacks(mConversationFooterCallbacks); + + // Register the onkey listener for all relevant views + registerOnKeyListeners(v, v.findViewById(R.id.reply_button), + v.findViewById(R.id.reply_all_button), v.findViewById(R.id.forward_button)); + + return v; } @Override @@ -191,6 +206,11 @@ public class ConversationViewAdapter extends BaseAdapter { return true; } + @Override + public View.OnKeyListener getOnKeyListener() { + return mOnKeyListener; + } + public MessageHeaderItem getLastMessageHeaderItem() { return mLastMessageHeaderItem; } @@ -248,6 +268,12 @@ public class ConversationViewAdapter extends BaseAdapter { v.setCallbacks(mAdapter.mMessageCallbacks); v.setContactInfoSource(mAdapter.mContactInfoSource); v.setVeiledMatcher(mAdapter.mMatcher); + + // Register the onkey listener for all relevant views + registerOnKeyListeners(v, v.findViewById(R.id.upper_header), + v.findViewById(R.id.hide_details), v.findViewById(R.id.edit_draft), + v.findViewById(R.id.reply), v.findViewById(R.id.reply_all), + v.findViewById(R.id.overflow), v.findViewById(R.id.send_date)); return v; } @@ -269,6 +295,11 @@ public class ConversationViewAdapter extends BaseAdapter { } @Override + public View.OnKeyListener getOnKeyListener() { + return mAdapter.getOnKeyListener(); + } + + @Override public boolean isExpanded() { return mExpanded; } @@ -367,6 +398,9 @@ public class ConversationViewAdapter extends BaseAdapter { R.layout.conversation_message_footer, parent, false); v.initialize(mAdapter.mLoaderManager, mAdapter.mFragmentManager, mAdapter.mAccountController, mAdapter.mFooterCallbacks); + + // Register the onkey listener for all relevant views + registerOnKeyListeners(v, v.findViewById(R.id.view_entire_message_prompt)); return v; } @@ -382,6 +416,11 @@ public class ConversationViewAdapter extends BaseAdapter { } @Override + public View.OnKeyListener getOnKeyListener() { + return mAdapter.getOnKeyListener(); + } + + @Override public boolean isExpanded() { return mHeaderItem.isExpanded(); } @@ -427,10 +466,14 @@ public class ConversationViewAdapter extends BaseAdapter { @Override public View createView(Context context, LayoutInflater inflater, ViewGroup parent) { - final SuperCollapsedBlock scb = (SuperCollapsedBlock) inflater.inflate( + final SuperCollapsedBlock v = (SuperCollapsedBlock) inflater.inflate( R.layout.super_collapsed_block, parent, false); - scb.initialize(mSuperCollapsedListener); - return scb; + v.initialize(mSuperCollapsedListener); + v.setOnKeyListener(mOnKeyListener); + + // Register the onkey listener for all relevant views + registerOnKeyListeners(v); + return v; } @Override @@ -445,6 +488,11 @@ public class ConversationViewAdapter extends BaseAdapter { } @Override + public View.OnKeyListener getOnKeyListener() { + return mOnKeyListener; + } + + @Override public boolean isExpanded() { return false; } @@ -479,7 +527,8 @@ public class ConversationViewAdapter extends BaseAdapter { OnClickListener scbListener, Map<String, Address> addressCache, FormattedDateBuilder dateBuilder, - BidiFormatter bidiFormatter) { + BidiFormatter bidiFormatter, + View.OnKeyListener onKeyListener) { mContext = controllableActivity.getActivityContext(); mDateBuilder = dateBuilder; mAccountController = accountController; @@ -499,6 +548,7 @@ public class ConversationViewAdapter extends BaseAdapter { mMatcher = controllableActivity.getAccountController().getVeiledAddressMatcher(); mBidiFormatter = bidiFormatter; + mOnKeyListener = onKeyListener; } @Override @@ -665,4 +715,8 @@ public class ConversationViewAdapter extends BaseAdapter { public BidiFormatter getBidiFormatter() { return mBidiFormatter; } + + public View.OnKeyListener getOnKeyListener() { + return mOnKeyListener; + } } diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java index f5a0cdad7..803574ffc 100644 --- a/src/com/android/mail/ui/ConversationViewFragment.java +++ b/src/com/android/mail/ui/ConversationViewFragment.java @@ -30,6 +30,7 @@ import android.os.SystemClock; import android.support.v4.text.BidiFormatter; import android.support.v4.util.ArrayMap; import android.text.TextUtils; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnLayoutChangeListener; @@ -97,7 +98,7 @@ import java.util.Set; public class ConversationViewFragment extends AbstractConversationViewFragment implements SuperCollapsedBlock.OnClickListener, OnLayoutChangeListener, MessageHeaderView.MessageHeaderViewCallbacks, MessageFooterView.MessageFooterCallbacks, - WebViewContextMenu.Callbacks, ConversationFooterCallbacks { + WebViewContextMenu.Callbacks, ConversationFooterCallbacks, View.OnKeyListener { private static final String LOG_TAG = LogTag.getLogTag(); public static final String LAYOUT_TAG = "ConvLayout"; @@ -130,6 +131,8 @@ public class ConversationViewFragment extends AbstractConversationViewFragment i protected ConversationWebView mWebView; + private ViewGroup mTopmostOverlay; + private ConversationViewProgressController mProgressController; private Button mNewMessageBar; @@ -276,7 +279,7 @@ public class ConversationViewFragment extends AbstractConversationViewFragment i mAdapter = new ConversationViewAdapter(mActivity, this, getLoaderManager(), this, this, getContactInfoSource(), this, this, - getListController(), this, mAddressCache, dateBuilder, mBidiFormatter); + getListController(), this, mAddressCache, dateBuilder, mBidiFormatter, this); mConversationContainer.setOverlayAdapter(mAdapter); // set up snap header (the adapter usually does this with the other ones) @@ -354,9 +357,10 @@ public class ConversationViewFragment extends AbstractConversationViewFragment i .findViewById(R.id.conversation_container); mConversationContainer.setAccountController(this); - final ViewGroup topmostOverlay = + mTopmostOverlay = (ViewGroup) mConversationContainer.findViewById(R.id.conversation_topmost_overlay); - inflateSnapHeader(topmostOverlay, inflater); + mTopmostOverlay.setOnKeyListener(this); + inflateSnapHeader(mTopmostOverlay, inflater); mConversationContainer.setupSnapHeader(); setupNewMessageBar(); @@ -1115,6 +1119,26 @@ public class ConversationViewFragment extends AbstractConversationViewFragment i return getMessageCursor().getMessageForId(Long.parseLong(messageId)); } + @Override + public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { + // Only care about enter and esc + View currFocus = mActivity.getWindow().getCurrentFocus(); + if (keyCode == KeyEvent.KEYCODE_BACK && currFocus != null && + currFocus.getId() != R.id.conversation_topmost_overlay) { + if (keyEvent.getAction() == KeyEvent.ACTION_UP) { + mTopmostOverlay.requestFocus(); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_ENTER && (currFocus == null || + currFocus.getId() == R.id.conversation_topmost_overlay)) { + if (keyEvent.getAction() == KeyEvent.ACTION_UP) { + mConversationContainer.findViewById(R.id.upper_header).requestFocus(); + } + return true; + } + return false; + } + public class ConversationWebViewClient extends AbstractConversationWebViewClient { public ConversationWebViewClient(Account account) { super(account); diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java index 781efa3d4..c37137257 100644 --- a/src/com/android/mail/ui/TwoPaneController.java +++ b/src/com/android/mail/ui/TwoPaneController.java @@ -42,7 +42,7 @@ import com.android.mail.utils.Utils; * abounds. */ public final class TwoPaneController extends AbstractActivityController implements - ConversationViewFrame.DownEventListener { + ConversationViewFrame.DownEventListener, TwoPaneLayout.TwoPaneLayoutListener { private static final String SAVED_MISCELLANEOUS_VIEW = "saved-miscellaneous-view"; private static final String SAVED_MISCELLANEOUS_VIEW_TRANSACTION_ID = @@ -153,6 +153,7 @@ public final class TwoPaneController extends AbstractActivityController implemen return false; } mLayout.setController(this, Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction())); + mLayout.setLayoutListener(this); mActivity.getWindow().setBackgroundDrawable(null); mIsTabletLandscape = !mActivity.getResources().getBoolean(R.bool.list_collapsible); @@ -590,4 +591,12 @@ public final class TwoPaneController extends AbstractActivityController implemen } return false; } + + @Override + public void onListViewLayout(boolean isOnScreen) { + ConversationListFragment clf = getConversationListFragment(); + if (clf != null && clf.getListView() != null) { + clf.getListView().setFocusable(isOnScreen); + } + } } diff --git a/src/com/android/mail/ui/TwoPaneLayout.java b/src/com/android/mail/ui/TwoPaneLayout.java index 5af59ccff..5f29664a7 100644 --- a/src/com/android/mail/ui/TwoPaneLayout.java +++ b/src/com/android/mail/ui/TwoPaneLayout.java @@ -96,6 +96,8 @@ final class TwoPaneLayout extends FrameLayout implements ModeChangeListener { private View mFoldersView; private View mListView; + private TwoPaneLayoutListener mLayoutListener; + public static final int MISCELLANEOUS_VIEW_ID = R.id.miscellaneous_pane; private final Runnable mTransitionCompleteRunnable = new Runnable() { @@ -171,6 +173,10 @@ final class TwoPaneLayout extends FrameLayout implements ModeChangeListener { super.onLayout(changed, l, t, r, b); } + public void setLayoutListener(TwoPaneLayoutListener listener) { + mLayoutListener = listener; + } + /** * Sizes up the three sliding panes. This method will ensure that the LayoutParams of the panes * have the correct widths set for the current overall size and view mode. @@ -238,6 +244,10 @@ final class TwoPaneLayout extends FrameLayout implements ModeChangeListener { } animatePanes(foldersX, listX, convX); + // For keyboard navigation, let's disable focus on the list if it's not visible. + if (mLayoutListener != null) { + mLayoutListener.onListViewLayout(listX >= 0); + } mPositionedMode = mCurrentMode; } @@ -456,4 +466,7 @@ final class TwoPaneLayout extends FrameLayout implements ModeChangeListener { return !mListCollapsible; } + public interface TwoPaneLayoutListener { + public void onListViewLayout(boolean isOnScreen); + } } |