fix shortcut handling

    Bugs: 3400078
    	  3270434
	  3368141
	  3381411

    Fixed controller to check for menu & ctrl keys for shortcuts
    Changed focus handling in url bar
    Keyboard focus transitions are not 100% working yet, but it's possible
    to navigate to all the elements

Change-Id: I514d85a37dae2589e3ce1ecb18c7fc0bf4e715f3
diff --git a/res/layout/url_bar.xml b/res/layout/url_bar.xml
index e925a58..2998677 100644
--- a/res/layout/url_bar.xml
+++ b/res/layout/url_bar.xml
@@ -72,7 +72,6 @@
                 android:visibility="gone" />
             <com.android.browser.UrlInputView
                 android:id="@+id/url_focused"
-                android:focusable="true"
                 android:layout_width="0dip"
                 android:layout_weight="1.0"
                 android:layout_height="match_parent"
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 68b58f7..75b5acf 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -35,4 +35,5 @@
     <color name="bookmarkListFaviconBackground">#23ffffff</color>
     <color name="urlTextColor">#0E774A</color>
     <color name="tabFaviconBackground">#FF555555</color>
+    <color name="tabFocusHighlight">#FF00FAF0</color>
 </resources>
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index 025482b..9bce3cd 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -163,6 +163,8 @@
     public void onConfigurationChanged(Configuration config) {
     }
 
+    public abstract void editUrl(boolean clearInput);
+
     // key handling
 
     @Override
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index ddd5aab..8c38e59 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -276,5 +276,4 @@
         mController.onActivityResult(requestCode, resultCode, intent);
     }
 
-
 }
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 80da85f..6393ac8 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -1026,9 +1026,7 @@
     // callback from phone title bar
     public void editUrl() {
         if (mOptionsMenuOpen) mActivity.closeOptionsMenu();
-        String url = (getCurrentTopWebView() == null) ? null : getCurrentTopWebView().getUrl();
-        startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
-                null, false);
+        mUi.editUrl(false);
     }
 
     public void startVoiceSearch() {
@@ -2412,6 +2410,12 @@
         startSearch(result, false, bundle, false);
     }
 
+    @Override
+    public void startSearch(String url) {
+        startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
+                null, false);
+    }
+
     private void startSearch(String initialQuery, boolean selectInitialQuery,
             Bundle appSearchData, boolean globalSearch) {
         if (appSearchData == null) {
@@ -2445,14 +2449,13 @@
 
         // Even if MENU is already held down, we need to call to super to open
         // the IME on long press.
-        if (!noModifiers && KeyEvent.KEYCODE_MENU == keyCode) {
+        if (!noModifiers
+                && ((KeyEvent.KEYCODE_MENU == keyCode)
+                        || (KeyEvent.KEYCODE_CTRL_LEFT == keyCode)
+                        || (KeyEvent.KEYCODE_CTRL_RIGHT == keyCode))) {
             mMenuIsDown = true;
             return false;
         }
-        // The default key mode is DEFAULT_KEYS_SEARCH_LOCAL. As the MENU is
-        // still down, we don't want to trigger the search. Pretend to consume
-        // the key and do nothing.
-        if (mMenuIsDown) return true;
 
         WebView webView = getCurrentTopWebView();
         if (webView == null) return false;
@@ -2525,10 +2528,12 @@
 //          case KeyEvent.KEYCODE_O:    // in Chrome: open file
 //          case KeyEvent.KEYCODE_P:    // in Chrome: print page
 //          case KeyEvent.KEYCODE_Q:    // unused
-//            case KeyEvent.KEYCODE_R:
+//          case KeyEvent.KEYCODE_R:
 //          case KeyEvent.KEYCODE_S:    // in Chrome: saves page
             case KeyEvent.KEYCODE_T:
-                if (ctrl) {
+                // we can't use the ctrl/shift flags, they check for
+                // exclusive use of a modifier
+                if (event.isCtrlPressed()) {
                     if (event.isShiftPressed()) {
                         openIncognitoTab();
                     } else {
@@ -2544,8 +2549,8 @@
 //          case KeyEvent.KEYCODE_Y:    // unused
 //          case KeyEvent.KEYCODE_Z:    // unused
         }
-        // if we get here, it is a regular key and webview is not null
-        return mUi.dispatchKey(keyCode, event);
+        // it is a regular key and webview is not null
+         return mUi.dispatchKey(keyCode, event);
     }
 
     boolean onKeyUp(int keyCode, KeyEvent event) {
diff --git a/src/com/android/browser/PhoneUi.java b/src/com/android/browser/PhoneUi.java
index a7b7834..f1939e4 100644
--- a/src/com/android/browser/PhoneUi.java
+++ b/src/com/android/browser/PhoneUi.java
@@ -99,6 +99,12 @@
     }
 
     @Override
+    public void editUrl(boolean clearInput) {
+        String url = getActiveTab().getUrl();
+        mUiController.startSearch(url);
+    }
+
+    @Override
     public boolean onBackKey() {
         if (mActiveTabsPage != null) {
             // if tab page is showing, hide it
diff --git a/src/com/android/browser/TabBar.java b/src/com/android/browser/TabBar.java
index c00e1f5..990d68f 100644
--- a/src/com/android/browser/TabBar.java
+++ b/src/com/android/browser/TabBar.java
@@ -85,6 +85,7 @@
 
     private final Paint mActiveShaderPaint = new Paint();
     private final Paint mInactiveShaderPaint = new Paint();
+    private final Paint mFocusPaint = new Paint();
     private final Matrix mActiveMatrix = new Matrix();
     private final Matrix mInactiveMatrix = new Matrix();
 
@@ -131,6 +132,9 @@
         mInactiveShaderPaint.setStyle(Paint.Style.FILL);
         mInactiveShaderPaint.setAntiAlias(true);
 
+        mFocusPaint.setStyle(Paint.Style.STROKE);
+        mFocusPaint.setAntiAlias(true);
+        mFocusPaint.setColor(res.getColor(R.color.tabFocusHighlight));
     }
 
     void setUseQuickControls(boolean useQuickControls) {
@@ -399,6 +403,8 @@
             lp.width = selected ? mTabWidthSelected : mTabWidthUnselected;
             lp.height =  LayoutParams.MATCH_PARENT;
             setLayoutParams(lp);
+            setFocusable(!selected);
+            postInvalidate();
         }
 
         void setDisplayTitle(String title) {
@@ -479,6 +485,9 @@
             matrix.setTranslate(-left, 0.0f);
             (mSelected ? mActiveShader : mInactiveShader).setLocalMatrix(matrix);
             canvas.drawPath(clipPath, paint);
+            if (isFocused()) {
+                canvas.drawPath(clipPath, mFocusPaint);
+            }
         }
 
         private void setTabPath(Path path, int l, int t, int r, int b) {
diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java
index 2aa5bb9..3931526 100644
--- a/src/com/android/browser/TitleBarXLarge.java
+++ b/src/com/android/browser/TitleBarXLarge.java
@@ -26,6 +26,8 @@
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -137,7 +139,7 @@
         mUrlInput.setOnFocusChangeListener(this);
         mUrlInput.setSelectAllOnFocus(true);
         mUrlInput.addTextChangedListener(this);
-        setEditMode(false);
+        setFocusState(false);
     }
 
     void updateNavigationState(Tab tab) {
@@ -190,9 +192,22 @@
 
     @Override
     public void onFocusChange(View view, boolean hasFocus) {
-        setEditMode(hasFocus);
-        mUrlContainer.setBackgroundDrawable(hasFocus
-                ? mFocusDrawable : mUnfocusDrawable);
+        // if losing focus and not in touch mode, leave as is
+        if (hasFocus || view.isInTouchMode() || mUrlInput.needsUpdate()) {
+            setFocusState(hasFocus);
+            mUrlContainer.setBackgroundDrawable(hasFocus
+                    ? mFocusDrawable : mUnfocusDrawable);
+        }
+        if (hasFocus) {
+            mUrlInput.forceIme();
+            if (mInVoiceMode) {
+                mUrlInput.forceFilter();
+            }
+        } else if (!mUrlInput.needsUpdate()) {
+            mUrlInput.dismissDropDown();
+            mUrlInput.hideIME();
+        }
+        mUrlInput.clearNeedsUpdate();
     }
 
     public void setCurrentUrlIsBookmark(boolean isBookmark) {
@@ -203,7 +218,7 @@
      * called from the Ui when the user wants to edit
      * @param clearInput clear the input field
      */
-    void onEditUrl(boolean clearInput) {
+    void startEditingUrl(boolean clearInput) {
         // editing takes preference of progress
         mContainer.setVisibility(View.VISIBLE);
         if (mUseQuickControls) {
@@ -267,8 +282,8 @@
         }
     }
 
-    private void setEditMode(boolean edit) {
-        if (edit) {
+    private void setFocusState(boolean focus) {
+        if (focus) {
             mUrlInput.setDropDownWidth(mUrlContainer.getWidth());
             mUrlInput.setDropDownHorizontalOffset(-mUrlInput.getLeft());
             mSearchButton.setVisibility(View.GONE);
@@ -390,4 +405,31 @@
         mUrlInput.setIncognitoMode(incognito);
     }
 
+    @Override
+    public View focusSearch(View focused, int dir) {
+        if (FOCUS_DOWN == dir && hasFocus()) {
+            return getCurrentWebView();
+        }
+        return super.focusSearch(focused, dir);
+    }
+
+    @Override
+    public boolean dispatchKeyEventPreIme(KeyEvent evt) {
+        if (evt.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            // catch back key in order to do slightly more cleanup than usual
+            mUrlInput.clearFocus();
+            return true;
+        }
+        return super.dispatchKeyEventPreIme(evt);
+    }
+
+    private WebView getCurrentWebView() {
+        Tab t = mUi.getActiveTab();
+        if (t != null) {
+            return t.getWebView();
+        } else {
+            return null;
+        }
+    }
+
 }
diff --git a/src/com/android/browser/UI.java b/src/com/android/browser/UI.java
index 8de2b19..34dcaee 100644
--- a/src/com/android/browser/UI.java
+++ b/src/com/android/browser/UI.java
@@ -118,6 +118,8 @@
 
     void showMaxTabsWarning();
 
+    void editUrl(boolean clearInput);
+
     boolean dispatchKey(int code, KeyEvent event);
 
 }
diff --git a/src/com/android/browser/UiController.java b/src/com/android/browser/UiController.java
index a16b44b..6075d36 100644
--- a/src/com/android/browser/UiController.java
+++ b/src/com/android/browser/UiController.java
@@ -53,6 +53,8 @@
 
     void bookmarksOrHistoryPicker(boolean openHistory);
 
+    void startSearch(String url);
+
     void startVoiceSearch();
 
     void showVoiceSearchResults(String title);
diff --git a/src/com/android/browser/UrlInputView.java b/src/com/android/browser/UrlInputView.java
index 1f15b32..2ec2111 100644
--- a/src/com/android/browser/UrlInputView.java
+++ b/src/com/android/browser/UrlInputView.java
@@ -29,7 +29,6 @@
 import android.util.Patterns;
 import android.view.KeyEvent;
 import android.view.View;
-import android.view.View.OnFocusChangeListener;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
@@ -44,7 +43,7 @@
  * handling suggestions
  */
 public class UrlInputView extends AutoCompleteTextView
-        implements OnFocusChangeListener, OnEditorActionListener,
+        implements OnEditorActionListener,
         CompletionListener, OnItemClickListener {
 
 
@@ -55,12 +54,11 @@
     private UrlInputListener   mListener;
     private InputMethodManager mInputManager;
     private SuggestionsAdapter mAdapter;
-    private OnFocusChangeListener mWrappedFocusListener;
     private View mContainer;
     private boolean mLandscape;
-    private boolean mInVoiceMode;
     private boolean mIncognitoMode;
     private int mVOffset;
+    private boolean mNeedsUpdate;
 
     public UrlInputView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -80,7 +78,6 @@
     private void init(Context ctx) {
         mInputManager = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
         setOnEditorActionListener(this);
-        super.setOnFocusChangeListener(this);
         mAdapter = new SuggestionsAdapter(ctx, this);
         setAdapter(mAdapter);
         setSelectAllOnFocus(true);
@@ -88,6 +85,21 @@
         setThreshold(1);
         setOnItemClickListener(this);
         mVOffset = 0;
+        mNeedsUpdate = false;
+    }
+
+    /**
+     * check if focus change requires a title bar update
+     */
+    boolean needsUpdate() {
+        return mNeedsUpdate;
+    }
+
+    /**
+     * clear the focus change needs title bar update flag
+     */
+    void clearNeedsUpdate() {
+        mNeedsUpdate = false;
     }
 
     void setController(UiController controller) {
@@ -107,9 +119,12 @@
         mContainer = container;
     }
 
+    public void setUrlInputListener(UrlInputListener listener) {
+        mListener = listener;
+    }
+
     void setVoiceResults(List<String> voiceResults) {
         mAdapter.setVoiceResults(voiceResults);
-        mInVoiceMode = (voiceResults != null);
     }
 
     @Override
@@ -148,43 +163,28 @@
     }
 
     @Override
-    public void setOnFocusChangeListener(OnFocusChangeListener focusListener) {
-        mWrappedFocusListener = focusListener;
-    }
-
-    @Override
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
         finishInput(getText().toString(), null, TYPED);
         return true;
     }
 
-    @Override
-    public void onFocusChange(View v, boolean hasFocus) {
-        if (mWrappedFocusListener != null) {
-            mWrappedFocusListener.onFocusChange(v, hasFocus);
-        }
-        if (hasFocus) {
-            forceIme();
-            if (mInVoiceMode) {
-                performFiltering(getText().toString(), 0);
-                showDropDown();
-            }
-        } else {
-            finishInput(null, null, null);
-        }
+    void forceFilter() {
+        performFiltering(getText().toString(), 0);
+        showDropDown();
     }
 
-    public void setUrlInputListener(UrlInputListener listener) {
-        mListener = listener;
-    }
-
-    public void forceIme() {
+    void forceIme() {
         mInputManager.focusIn(this);
         mInputManager.showSoftInput(this, 0);
     }
 
+    void hideIME() {
+        mInputManager.hideSoftInputFromWindow(getWindowToken(), 0);
+    }
+
     private void finishInput(String url, String extra, String source) {
-        this.dismissDropDown();
+        mNeedsUpdate = true;
+        dismissDropDown();
         mInputManager.hideSoftInputFromWindow(getWindowToken(), 0);
         if (TextUtils.isEmpty(url)) {
             mListener.onDismiss();
@@ -230,17 +230,6 @@
     }
 
     @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent evt) {
-        if ((evt.getAction() == KeyEvent.ACTION_DOWN)
-                && (keyCode == KeyEvent.KEYCODE_BACK)) {
-            // catch back key in order to do slightly more cleanup than usual
-            clearFocus();
-            return true;
-        }
-        return super.onKeyPreIme(keyCode, evt);
-    }
-
-    @Override
     public void onItemClick(
             AdapterView<?> parent, View view, int position, long id) {
         SuggestItem item = mAdapter.getItem(position);
@@ -263,4 +252,13 @@
         mAdapter.setIncognitoMode(mIncognitoMode);
     }
 
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent evt) {
+        if (keyCode == KeyEvent.KEYCODE_ESCAPE && !isInTouchMode()) {
+            finishInput(null, null, null);
+            return true;
+        }
+        return super.onKeyDown(keyCode, evt);
+    }
+
 }
diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java
index 88df015..f361e57 100644
--- a/src/com/android/browser/XLargeUi.java
+++ b/src/com/android/browser/XLargeUi.java
@@ -260,18 +260,19 @@
         return 0;
     }
 
-    void editUrl(boolean clearInput) {
+    @Override
+    public void editUrl(boolean clearInput) {
         if (mUiController.isInCustomActionMode()) {
             mUiController.endActionMode();
         }
         showTitleBar();
-        mTitleBar.onEditUrl(clearInput);
+        mTitleBar.startEditingUrl(clearInput);
     }
 
     void showTitleBarAndEdit() {
         mTitleBar.setShowProgressOnly(false);
         showTitleBar();
-        mTitleBar.onEditUrl(false);
+        mTitleBar.startEditingUrl(false);
     }
 
     void stopEditingUrl() {
@@ -387,21 +388,32 @@
     @Override
     public boolean dispatchKey(int code, KeyEvent event) {
         WebView web = getActiveTab().getWebView();
-        switch (code) {
-            case KeyEvent.KEYCODE_TAB:
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                if ((web != null) && web.hasFocus()) {
-                    editUrl(true);
-                    return true;
-                }
-        }
-        boolean ctrl = event.hasModifiers(KeyEvent.META_CTRL_ON);
-        if (!ctrl && event.isPrintingKey() && !mTitleBar.isEditingUrl()) {
-            editUrl(true);
-            return mContentView.dispatchKeyEvent(event);
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+
+            switch (code) {
+                case KeyEvent.KEYCODE_TAB:
+                case KeyEvent.KEYCODE_DPAD_UP:
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                    if ((web != null) && web.hasFocus() && !mTitleBar.hasFocus()) {
+                        editUrl(false);
+                        return true;
+                    }
+            }
+            boolean ctrl = event.hasModifiers(KeyEvent.META_CTRL_ON);
+            if (!ctrl && isTypingKey(event) && !mTitleBar.isEditingUrl()) {
+                editUrl(true);
+                return mContentView.dispatchKeyEvent(event);
+            }
         }
         return false;
     }
 
+    private boolean isTypingKey(KeyEvent evt) {
+        return evt.getUnicodeChar() > 0;
+    }
+
+    TabBar getTabBar() {
+        return mTabBar;
+    }
+
 }