Allowing widgets to be focusable

> Pressing 'enter' enters the focus inside the widget.
> If the widget has only one focusable, it directly performs click
> Pressing 'esc' brings the focus back to the parent host.

Bug: 25199581
Change-Id: Ia7fd5d929f5945d61173abd349660a5efbe74afc
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d258141..8ba8f3e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1622,18 +1622,28 @@
                 // The AppWidgetHostView has already been inflated and instantiated
                 launcherInfo.hostView = hostView;
             }
-            launcherInfo.hostView.setTag(launcherInfo);
             launcherInfo.hostView.setVisibility(View.VISIBLE);
-            launcherInfo.notifyWidgetSizeChanged(this);
-
-            mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, info.cellX,
-                    info.cellY, launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
-
-            addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
+            addAppWidgetToWorkspace(launcherInfo, appWidgetInfo, isWorkspaceLocked());
         }
         resetAddInfo();
     }
 
+    private void addAppWidgetToWorkspace(LauncherAppWidgetInfo item,
+            LauncherAppWidgetProviderInfo appWidgetInfo, boolean insert) {
+        item.hostView.setTag(item);
+        item.onBindAppWidget(this);
+
+        item.hostView.setFocusable(true);
+        item.hostView.setOnFocusChangeListener(mFocusHandler);
+
+        mWorkspace.addInScreen(item.hostView, item.container, item.screenId,
+                item.cellX, item.cellY, item.spanX, item.spanY, insert);
+
+        if (!item.isCustomWidget()) {
+            addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
+        }
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -4123,15 +4133,7 @@
             item.hostView.setOnClickListener(this);
         }
 
-        item.hostView.setTag(item);
-        item.onBindAppWidget(this);
-
-        workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
-                item.cellY, item.spanX, item.spanY, false);
-        if (!item.isCustomWidget()) {
-            addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
-        }
-
+        addAppWidgetToWorkspace(item, appWidgetInfo, false);
         workspace.requestLayout();
 
         if (DEBUG_WIDGETS) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index cf461a5..34c2943 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -19,6 +19,8 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.graphics.Rect;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -28,6 +30,8 @@
 
 import com.android.launcher3.DragLayer.TouchCompleteListener;
 
+import java.util.ArrayList;
+
 /**
  * {@inheritDoc}
  */
@@ -43,6 +47,8 @@
 
     private float mSlop;
 
+    private boolean mChildrenFocused;
+
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mContext = context;
@@ -175,6 +181,67 @@
 
     @Override
     public int getDescendantFocusability() {
-        return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+        return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
+                : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
+                && event.getAction() == KeyEvent.ACTION_UP) {
+            mChildrenFocused = false;
+            requestFocus();
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+            event.startTracking();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (event.isTracking()) {
+            if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+                mChildrenFocused = true;
+                ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
+                focusableChildren.remove(this);
+                int childrenCount = focusableChildren.size();
+                switch (childrenCount) {
+                    case 0:
+                        mChildrenFocused = false;
+                        break;
+                    case 1: {
+                        if (getTag() instanceof ItemInfo) {
+                            ItemInfo item = (ItemInfo) getTag();
+                            if (item.spanX == 1 && item.spanY == 1) {
+                                focusableChildren.get(0).performClick();
+                                mChildrenFocused = false;
+                                return true;
+                            }
+                        }
+                        // continue;
+                    }
+                    default:
+                        focusableChildren.get(0).requestFocus();
+                        return true;
+                }
+            }
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        if (gainFocus) {
+            mChildrenFocused = false;
+        }
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
     }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 882f7e2..55edf45 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -123,17 +123,11 @@
      */
     void onBindAppWidget(Launcher launcher) {
         if (!mHasNotifiedInitialWidgetSizeChanged) {
-            notifyWidgetSizeChanged(launcher);
+            AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
+            mHasNotifiedInitialWidgetSizeChanged = true;
         }
     }
 
-    /**
-     * Trigger an update callback to the widget to notify it that its size has changed.
-     */
-    void notifyWidgetSizeChanged(Launcher launcher) {
-        AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
-        mHasNotifiedInitialWidgetSizeChanged = true;
-    }
 
     @Override
     public String toString() {