Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3;
     18 
     19 import android.appwidget.AppWidgetHostView;
     20 import android.appwidget.AppWidgetProviderInfo;
     21 import android.content.Context;
     22 import android.graphics.Rect;
     23 import android.view.KeyEvent;
     24 import android.view.LayoutInflater;
     25 import android.view.MotionEvent;
     26 import android.view.View;
     27 import android.view.ViewConfiguration;
     28 import android.view.ViewGroup;
     29 import android.widget.RemoteViews;
     30 
     31 import com.android.launcher3.DragLayer.TouchCompleteListener;
     32 
     33 import java.util.ArrayList;
     34 
     35 /**
     36  * {@inheritDoc}
     37  */
     38 public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
     39 
     40     LayoutInflater mInflater;
     41 
     42     private CheckLongPressHelper mLongPressHelper;
     43     private StylusEventHelper mStylusEventHelper;
     44     private Context mContext;
     45     private int mPreviousOrientation;
     46     private DragLayer mDragLayer;
     47 
     48     private float mSlop;
     49 
     50     private boolean mChildrenFocused;
     51 
     52     public LauncherAppWidgetHostView(Context context) {
     53         super(context);
     54         mContext = context;
     55         mLongPressHelper = new CheckLongPressHelper(this);
     56         mStylusEventHelper = new StylusEventHelper(this);
     57         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     58         mDragLayer = ((Launcher) context).getDragLayer();
     59         setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     60 
     61         setBackgroundResource(R.drawable.widget_internal_focus_bg);
     62     }
     63 
     64     @Override
     65     protected View getErrorView() {
     66         return mInflater.inflate(R.layout.appwidget_error, this, false);
     67     }
     68 
     69     public void updateLastInflationOrientation() {
     70         mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
     71     }
     72 
     73     @Override
     74     public void updateAppWidget(RemoteViews remoteViews) {
     75         // Store the orientation in which the widget was inflated
     76         updateLastInflationOrientation();
     77         super.updateAppWidget(remoteViews);
     78     }
     79 
     80     public boolean isReinflateRequired() {
     81         // Re-inflate is required if the orientation has changed since last inflated.
     82         int orientation = mContext.getResources().getConfiguration().orientation;
     83         if (mPreviousOrientation != orientation) {
     84            return true;
     85        }
     86        return false;
     87     }
     88 
     89     public boolean onInterceptTouchEvent(MotionEvent ev) {
     90         // Just in case the previous long press hasn't been cleared, we make sure to start fresh
     91         // on touch down.
     92         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
     93             mLongPressHelper.cancelLongPress();
     94         }
     95 
     96         // Consume any touch events for ourselves after longpress is triggered
     97         if (mLongPressHelper.hasPerformedLongPress()) {
     98             mLongPressHelper.cancelLongPress();
     99             return true;
    100         }
    101 
    102         // Watch for longpress or stylus button press events at this level to
    103         // make sure users can always pick up this widget
    104         if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
    105             mLongPressHelper.cancelLongPress();
    106             return true;
    107         }
    108         switch (ev.getAction()) {
    109             case MotionEvent.ACTION_DOWN: {
    110                 if (!mStylusEventHelper.inStylusButtonPressed()) {
    111                     mLongPressHelper.postCheckForLongPress();
    112                 }
    113                 mDragLayer.setTouchCompleteListener(this);
    114                 break;
    115             }
    116 
    117             case MotionEvent.ACTION_UP:
    118             case MotionEvent.ACTION_CANCEL:
    119                 mLongPressHelper.cancelLongPress();
    120                 break;
    121             case MotionEvent.ACTION_MOVE:
    122                 if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
    123                     mLongPressHelper.cancelLongPress();
    124                 }
    125                 break;
    126         }
    127 
    128         // Otherwise continue letting touch events fall through to children
    129         return false;
    130     }
    131 
    132     public boolean onTouchEvent(MotionEvent ev) {
    133         // If the widget does not handle touch, then cancel
    134         // long press when we release the touch
    135         switch (ev.getAction()) {
    136             case MotionEvent.ACTION_UP:
    137             case MotionEvent.ACTION_CANCEL:
    138                 mLongPressHelper.cancelLongPress();
    139                 break;
    140             case MotionEvent.ACTION_MOVE:
    141                 if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
    142                     mLongPressHelper.cancelLongPress();
    143                 }
    144                 break;
    145         }
    146         return false;
    147     }
    148 
    149     @Override
    150     protected void onAttachedToWindow() {
    151         super.onAttachedToWindow();
    152         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    153     }
    154 
    155     @Override
    156     public void cancelLongPress() {
    157         super.cancelLongPress();
    158         mLongPressHelper.cancelLongPress();
    159     }
    160 
    161     @Override
    162     public AppWidgetProviderInfo getAppWidgetInfo() {
    163         AppWidgetProviderInfo info = super.getAppWidgetInfo();
    164         if (info != null && !(info instanceof LauncherAppWidgetProviderInfo)) {
    165             throw new IllegalStateException("Launcher widget must have"
    166                     + " LauncherAppWidgetProviderInfo");
    167         }
    168         return info;
    169     }
    170 
    171     public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
    172         return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
    173     }
    174 
    175     @Override
    176     public void onTouchComplete() {
    177         if (!mLongPressHelper.hasPerformedLongPress()) {
    178             // If a long press has been performed, we don't want to clear the record of that since
    179             // we still may be receiving a touch up which we want to intercept
    180             mLongPressHelper.cancelLongPress();
    181         }
    182     }
    183 
    184     @Override
    185     public int getDescendantFocusability() {
    186         return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
    187                 : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
    188     }
    189 
    190     @Override
    191     public boolean dispatchKeyEvent(KeyEvent event) {
    192         if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
    193                 && event.getAction() == KeyEvent.ACTION_UP) {
    194             mChildrenFocused = false;
    195             requestFocus();
    196             return true;
    197         }
    198         return super.dispatchKeyEvent(event);
    199     }
    200 
    201     @Override
    202     public boolean onKeyDown(int keyCode, KeyEvent event) {
    203         if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
    204             event.startTracking();
    205             return true;
    206         }
    207         return super.onKeyDown(keyCode, event);
    208     }
    209 
    210     @Override
    211     public boolean onKeyUp(int keyCode, KeyEvent event) {
    212         if (event.isTracking()) {
    213             if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
    214                 mChildrenFocused = true;
    215                 ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
    216                 focusableChildren.remove(this);
    217                 int childrenCount = focusableChildren.size();
    218                 switch (childrenCount) {
    219                     case 0:
    220                         mChildrenFocused = false;
    221                         break;
    222                     case 1: {
    223                         if (getTag() instanceof ItemInfo) {
    224                             ItemInfo item = (ItemInfo) getTag();
    225                             if (item.spanX == 1 && item.spanY == 1) {
    226                                 focusableChildren.get(0).performClick();
    227                                 mChildrenFocused = false;
    228                                 return true;
    229                             }
    230                         }
    231                         // continue;
    232                     }
    233                     default:
    234                         focusableChildren.get(0).requestFocus();
    235                         return true;
    236                 }
    237             }
    238         }
    239         return super.onKeyUp(keyCode, event);
    240     }
    241 
    242     @Override
    243     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
    244         if (gainFocus) {
    245             mChildrenFocused = false;
    246             dispatchChildFocus(false);
    247         }
    248         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
    249     }
    250 
    251     @Override
    252     public void requestChildFocus(View child, View focused) {
    253         super.requestChildFocus(child, focused);
    254         dispatchChildFocus(mChildrenFocused && focused != null);
    255         if (focused != null) {
    256             focused.setFocusableInTouchMode(false);
    257         }
    258     }
    259 
    260     @Override
    261     public void clearChildFocus(View child) {
    262         super.clearChildFocus(child);
    263         dispatchChildFocus(false);
    264     }
    265 
    266     @Override
    267     public boolean dispatchUnhandledMove(View focused, int direction) {
    268         return mChildrenFocused;
    269     }
    270 
    271     private void dispatchChildFocus(boolean childIsFocused) {
    272         // The host view's background changes when selected, to indicate the focus is inside.
    273         setSelected(childIsFocused);
    274     }
    275 
    276     @Override
    277     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    278         try {
    279             super.onLayout(changed, left, top, right, bottom);
    280         } catch (final RuntimeException e) {
    281             post(new Runnable() {
    282                 @Override
    283                 public void run() {
    284                     // Update the widget with 0 Layout id, to reset the view to error view.
    285                     updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
    286                 }
    287             });
    288         }
    289     }
    290 }
    291