OSDN Git Service

Preventing a Runtime widget crash from crashing the Launcher
[android-x86/packages-apps-Launcher3.git] / src / com / android / launcher3 / LauncherAppWidgetHostView.java
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
62     @Override
63     protected View getErrorView() {
64         return mInflater.inflate(R.layout.appwidget_error, this, false);
65     }
66
67     public void updateLastInflationOrientation() {
68         mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
69     }
70
71     @Override
72     public void updateAppWidget(RemoteViews remoteViews) {
73         // Store the orientation in which the widget was inflated
74         updateLastInflationOrientation();
75         super.updateAppWidget(remoteViews);
76     }
77
78     public boolean isReinflateRequired() {
79         // Re-inflate is required if the orientation has changed since last inflated.
80         int orientation = mContext.getResources().getConfiguration().orientation;
81         if (mPreviousOrientation != orientation) {
82            return true;
83        }
84        return false;
85     }
86
87     public boolean onInterceptTouchEvent(MotionEvent ev) {
88         // Just in case the previous long press hasn't been cleared, we make sure to start fresh
89         // on touch down.
90         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
91             mLongPressHelper.cancelLongPress();
92         }
93
94         // Consume any touch events for ourselves after longpress is triggered
95         if (mLongPressHelper.hasPerformedLongPress()) {
96             mLongPressHelper.cancelLongPress();
97             return true;
98         }
99
100         // Watch for longpress or stylus button press events at this level to
101         // make sure users can always pick up this widget
102         if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
103             mLongPressHelper.cancelLongPress();
104             return true;
105         }
106         switch (ev.getAction()) {
107             case MotionEvent.ACTION_DOWN: {
108                 if (!mStylusEventHelper.inStylusButtonPressed()) {
109                     mLongPressHelper.postCheckForLongPress();
110                 }
111                 mDragLayer.setTouchCompleteListener(this);
112                 break;
113             }
114
115             case MotionEvent.ACTION_UP:
116             case MotionEvent.ACTION_CANCEL:
117                 mLongPressHelper.cancelLongPress();
118                 break;
119             case MotionEvent.ACTION_MOVE:
120                 if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
121                     mLongPressHelper.cancelLongPress();
122                 }
123                 break;
124         }
125
126         // Otherwise continue letting touch events fall through to children
127         return false;
128     }
129
130     public boolean onTouchEvent(MotionEvent ev) {
131         // If the widget does not handle touch, then cancel
132         // long press when we release the touch
133         switch (ev.getAction()) {
134             case MotionEvent.ACTION_UP:
135             case MotionEvent.ACTION_CANCEL:
136                 mLongPressHelper.cancelLongPress();
137                 break;
138             case MotionEvent.ACTION_MOVE:
139                 if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
140                     mLongPressHelper.cancelLongPress();
141                 }
142                 break;
143         }
144         return false;
145     }
146
147     @Override
148     protected void onAttachedToWindow() {
149         super.onAttachedToWindow();
150         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
151     }
152
153     @Override
154     public void cancelLongPress() {
155         super.cancelLongPress();
156         mLongPressHelper.cancelLongPress();
157     }
158
159     @Override
160     public AppWidgetProviderInfo getAppWidgetInfo() {
161         AppWidgetProviderInfo info = super.getAppWidgetInfo();
162         if (info != null && !(info instanceof LauncherAppWidgetProviderInfo)) {
163             throw new IllegalStateException("Launcher widget must have"
164                     + " LauncherAppWidgetProviderInfo");
165         }
166         return info;
167     }
168
169     public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
170         return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
171     }
172
173     @Override
174     public void onTouchComplete() {
175         if (!mLongPressHelper.hasPerformedLongPress()) {
176             // If a long press has been performed, we don't want to clear the record of that since
177             // we still may be receiving a touch up which we want to intercept
178             mLongPressHelper.cancelLongPress();
179         }
180     }
181
182     @Override
183     public int getDescendantFocusability() {
184         return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
185                 : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
186     }
187
188     @Override
189     public boolean dispatchKeyEvent(KeyEvent event) {
190         if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
191                 && event.getAction() == KeyEvent.ACTION_UP) {
192             mChildrenFocused = false;
193             requestFocus();
194             return true;
195         }
196         return super.dispatchKeyEvent(event);
197     }
198
199     @Override
200     public boolean onKeyDown(int keyCode, KeyEvent event) {
201         if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
202             event.startTracking();
203             return true;
204         }
205         return super.onKeyDown(keyCode, event);
206     }
207
208     @Override
209     public boolean onKeyUp(int keyCode, KeyEvent event) {
210         if (event.isTracking()) {
211             if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
212                 mChildrenFocused = true;
213                 ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
214                 focusableChildren.remove(this);
215                 int childrenCount = focusableChildren.size();
216                 switch (childrenCount) {
217                     case 0:
218                         mChildrenFocused = false;
219                         break;
220                     case 1: {
221                         if (getTag() instanceof ItemInfo) {
222                             ItemInfo item = (ItemInfo) getTag();
223                             if (item.spanX == 1 && item.spanY == 1) {
224                                 focusableChildren.get(0).performClick();
225                                 mChildrenFocused = false;
226                                 return true;
227                             }
228                         }
229                         // continue;
230                     }
231                     default:
232                         focusableChildren.get(0).requestFocus();
233                         return true;
234                 }
235             }
236         }
237         return super.onKeyUp(keyCode, event);
238     }
239
240     @Override
241     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
242         if (gainFocus) {
243             mChildrenFocused = false;
244         }
245         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
246     }
247
248     @Override
249     public void requestChildFocus(View child, View focused) {
250         super.requestChildFocus(child, focused);
251         dispatchChildFocus(focused != null);
252     }
253
254     @Override
255     public void clearChildFocus(View child) {
256         super.clearChildFocus(child);
257         dispatchChildFocus(false);
258     }
259
260     @Override
261     public boolean dispatchUnhandledMove(View focused, int direction) {
262         return mChildrenFocused;
263     }
264
265     private void dispatchChildFocus(boolean focused) {
266         if (getOnFocusChangeListener() != null) {
267             getOnFocusChangeListener().onFocusChange(this, focused || isFocused());
268         }
269     }
270
271     @Override
272     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
273         try {
274             super.onLayout(changed, left, top, right, bottom);
275         } catch (final RuntimeException e) {
276             post(new Runnable() {
277                 @Override
278                 public void run() {
279                     // Update the widget with 0 Layout id, to reset the view to error view.
280                     updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
281                 }
282             });
283         }
284     }
285 }