LauncherAppWidgetHostView.java revision 0c4e37223f93fea2627e69c0b007d6c22ddf8461
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.DragLayer.TouchCompleteListener; 32 33import java.util.ArrayList; 34 35/** 36 * {@inheritDoc} 37 */ 38public 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