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