2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.launcher3;
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;
31 import com.android.launcher3.DragLayer.TouchCompleteListener;
33 import java.util.ArrayList;
38 public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
40 LayoutInflater mInflater;
42 private CheckLongPressHelper mLongPressHelper;
43 private StylusEventHelper mStylusEventHelper;
44 private Context mContext;
45 private int mPreviousOrientation;
46 private DragLayer mDragLayer;
50 private boolean mChildrenFocused;
52 public LauncherAppWidgetHostView(Context 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());
63 protected View getErrorView() {
64 return mInflater.inflate(R.layout.appwidget_error, this, false);
67 public void updateLastInflationOrientation() {
68 mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
72 public void updateAppWidget(RemoteViews remoteViews) {
73 // Store the orientation in which the widget was inflated
74 updateLastInflationOrientation();
75 super.updateAppWidget(remoteViews);
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) {
87 public boolean onInterceptTouchEvent(MotionEvent ev) {
88 // Just in case the previous long press hasn't been cleared, we make sure to start fresh
90 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
91 mLongPressHelper.cancelLongPress();
94 // Consume any touch events for ourselves after longpress is triggered
95 if (mLongPressHelper.hasPerformedLongPress()) {
96 mLongPressHelper.cancelLongPress();
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();
106 switch (ev.getAction()) {
107 case MotionEvent.ACTION_DOWN: {
108 if (!mStylusEventHelper.inStylusButtonPressed()) {
109 mLongPressHelper.postCheckForLongPress();
111 mDragLayer.setTouchCompleteListener(this);
115 case MotionEvent.ACTION_UP:
116 case MotionEvent.ACTION_CANCEL:
117 mLongPressHelper.cancelLongPress();
119 case MotionEvent.ACTION_MOVE:
120 if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
121 mLongPressHelper.cancelLongPress();
126 // Otherwise continue letting touch events fall through to children
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();
138 case MotionEvent.ACTION_MOVE:
139 if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
140 mLongPressHelper.cancelLongPress();
148 protected void onAttachedToWindow() {
149 super.onAttachedToWindow();
150 mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
154 public void cancelLongPress() {
155 super.cancelLongPress();
156 mLongPressHelper.cancelLongPress();
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");
169 public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
170 return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
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();
183 public int getDescendantFocusability() {
184 return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
185 : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
189 public boolean dispatchKeyEvent(KeyEvent event) {
190 if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
191 && event.getAction() == KeyEvent.ACTION_UP) {
192 mChildrenFocused = false;
196 return super.dispatchKeyEvent(event);
200 public boolean onKeyDown(int keyCode, KeyEvent event) {
201 if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
202 event.startTracking();
205 return super.onKeyDown(keyCode, event);
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) {
218 mChildrenFocused = false;
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;
232 focusableChildren.get(0).requestFocus();
237 return super.onKeyUp(keyCode, event);
241 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
243 mChildrenFocused = false;
245 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
249 public void requestChildFocus(View child, View focused) {
250 super.requestChildFocus(child, focused);
251 dispatchChildFocus(focused != null);
255 public void clearChildFocus(View child) {
256 super.clearChildFocus(child);
257 dispatchChildFocus(false);
261 public boolean dispatchUnhandledMove(View focused, int direction) {
262 return mChildrenFocused;
265 private void dispatchChildFocus(boolean focused) {
266 if (getOnFocusChangeListener() != null) {
267 getOnFocusChangeListener().onFocusChange(this, focused || isFocused());
272 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
274 super.onLayout(changed, left, top, right, bottom);
275 } catch (final RuntimeException e) {
276 post(new Runnable() {
279 // Update the widget with 0 Layout id, to reset the view to error view.
280 updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));