LauncherAppWidgetHostView.java revision b23bb416d0ad2d21eab495afb3dc0237c8d81d45
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
17package com.android.launcher3;
18
19import android.appwidget.AppWidgetHostView;
20import android.appwidget.AppWidgetProviderInfo;
21import android.content.Context;
22import android.graphics.Rect;
23import android.view.KeyEvent;
24import android.view.LayoutInflater;
25import android.view.MotionEvent;
26import android.view.View;
27import android.view.ViewConfiguration;
28import android.view.ViewGroup;
29import android.widget.RemoteViews;
30
31import com.android.launcher3.dragndrop.DragLayer;
32import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
33
34import java.util.ArrayList;
35
36/**
37 * {@inheritDoc}
38 */
39public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
40
41    LayoutInflater mInflater;
42
43    private CheckLongPressHelper mLongPressHelper;
44    private StylusEventHelper mStylusEventHelper;
45    private Context mContext;
46    private int mPreviousOrientation;
47    private DragLayer mDragLayer;
48
49    private float mSlop;
50
51    private boolean mChildrenFocused;
52
53    public LauncherAppWidgetHostView(Context context) {
54        super(context);
55        mContext = context;
56        mLongPressHelper = new CheckLongPressHelper(this);
57        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
58        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
59        mDragLayer = ((Launcher) context).getDragLayer();
60        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
61    }
62
63    @Override
64    protected View getErrorView() {
65        return mInflater.inflate(R.layout.appwidget_error, this, false);
66    }
67
68    public void updateLastInflationOrientation() {
69        mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
70    }
71
72    @Override
73    public void updateAppWidget(RemoteViews remoteViews) {
74        // Store the orientation in which the widget was inflated
75        updateLastInflationOrientation();
76        super.updateAppWidget(remoteViews);
77    }
78
79    public boolean isReinflateRequired() {
80        // Re-inflate is required if the orientation has changed since last inflated.
81        int orientation = mContext.getResources().getConfiguration().orientation;
82        if (mPreviousOrientation != orientation) {
83           return true;
84       }
85       return false;
86    }
87
88    public boolean onInterceptTouchEvent(MotionEvent ev) {
89        // Just in case the previous long press hasn't been cleared, we make sure to start fresh
90        // on touch down.
91        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
92            mLongPressHelper.cancelLongPress();
93        }
94
95        // Consume any touch events for ourselves after longpress is triggered
96        if (mLongPressHelper.hasPerformedLongPress()) {
97            mLongPressHelper.cancelLongPress();
98            return true;
99        }
100
101        // Watch for longpress or stylus button press events at this level to
102        // make sure users can always pick up this widget
103        if (mStylusEventHelper.onMotionEvent(ev)) {
104            mLongPressHelper.cancelLongPress();
105            return true;
106        }
107        switch (ev.getAction()) {
108            case MotionEvent.ACTION_DOWN: {
109                if (!mStylusEventHelper.inStylusButtonPressed()) {
110                    mLongPressHelper.postCheckForLongPress();
111                }
112                mDragLayer.setTouchCompleteListener(this);
113                break;
114            }
115
116            case MotionEvent.ACTION_UP:
117            case MotionEvent.ACTION_CANCEL:
118                mLongPressHelper.cancelLongPress();
119                break;
120            case MotionEvent.ACTION_MOVE:
121                if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
122                    mLongPressHelper.cancelLongPress();
123                }
124                break;
125        }
126
127        // Otherwise continue letting touch events fall through to children
128        return false;
129    }
130
131    public boolean onTouchEvent(MotionEvent ev) {
132        // If the widget does not handle touch, then cancel
133        // long press when we release the touch
134        switch (ev.getAction()) {
135            case MotionEvent.ACTION_UP:
136            case MotionEvent.ACTION_CANCEL:
137                mLongPressHelper.cancelLongPress();
138                break;
139            case MotionEvent.ACTION_MOVE:
140                if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
141                    mLongPressHelper.cancelLongPress();
142                }
143                break;
144        }
145        return false;
146    }
147
148    @Override
149    protected void onAttachedToWindow() {
150        super.onAttachedToWindow();
151        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
152    }
153
154    @Override
155    public void cancelLongPress() {
156        super.cancelLongPress();
157        mLongPressHelper.cancelLongPress();
158    }
159
160    @Override
161    public AppWidgetProviderInfo getAppWidgetInfo() {
162        AppWidgetProviderInfo info = super.getAppWidgetInfo();
163        if (info != null && !(info instanceof LauncherAppWidgetProviderInfo)) {
164            throw new IllegalStateException("Launcher widget must have"
165                    + " LauncherAppWidgetProviderInfo");
166        }
167        return info;
168    }
169
170    public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
171        return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
172    }
173
174    @Override
175    public void onTouchComplete() {
176        if (!mLongPressHelper.hasPerformedLongPress()) {
177            // If a long press has been performed, we don't want to clear the record of that since
178            // we still may be receiving a touch up which we want to intercept
179            mLongPressHelper.cancelLongPress();
180        }
181    }
182
183    @Override
184    public int getDescendantFocusability() {
185        return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
186                : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
187    }
188
189    @Override
190    public boolean dispatchKeyEvent(KeyEvent event) {
191        if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
192                && event.getAction() == KeyEvent.ACTION_UP) {
193            mChildrenFocused = false;
194            requestFocus();
195            return true;
196        }
197        return super.dispatchKeyEvent(event);
198    }
199
200    @Override
201    public boolean onKeyDown(int keyCode, KeyEvent event) {
202        if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
203            event.startTracking();
204            return true;
205        }
206        return super.onKeyDown(keyCode, event);
207    }
208
209    @Override
210    public boolean onKeyUp(int keyCode, KeyEvent event) {
211        if (event.isTracking()) {
212            if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
213                mChildrenFocused = true;
214                ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
215                focusableChildren.remove(this);
216                int childrenCount = focusableChildren.size();
217                switch (childrenCount) {
218                    case 0:
219                        mChildrenFocused = false;
220                        break;
221                    case 1: {
222                        if (getTag() instanceof ItemInfo) {
223                            ItemInfo item = (ItemInfo) getTag();
224                            if (item.spanX == 1 && item.spanY == 1) {
225                                focusableChildren.get(0).performClick();
226                                mChildrenFocused = false;
227                                return true;
228                            }
229                        }
230                        // continue;
231                    }
232                    default:
233                        focusableChildren.get(0).requestFocus();
234                        return true;
235                }
236            }
237        }
238        return super.onKeyUp(keyCode, event);
239    }
240
241    @Override
242    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
243        if (gainFocus) {
244            mChildrenFocused = false;
245        }
246        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
247    }
248}
249