blob: 570607ec7e113a5de8d771212a85c5681c87b477 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Rect;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
import java.util.ArrayList;
/**
* {@inheritDoc}
*/
public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
LayoutInflater mInflater;
private CheckLongPressHelper mLongPressHelper;
private StylusEventHelper mStylusEventHelper;
private Context mContext;
@ViewDebug.ExportedProperty(category = "launcher")
private int mPreviousOrientation;
private DragLayer mDragLayer;
private float mSlop;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
public LauncherAppWidgetHostView(Context context) {
super(context);
mContext = context;
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDragLayer = ((Launcher) context).getDragLayer();
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
}
@Override
protected View getErrorView() {
return mInflater.inflate(R.layout.appwidget_error, this, false);
}
public void updateLastInflationOrientation() {
mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
}
@Override
public void updateAppWidget(RemoteViews remoteViews) {
// Store the orientation in which the widget was inflated
updateLastInflationOrientation();
super.updateAppWidget(remoteViews);
}
public boolean isReinflateRequired() {
// Re-inflate is required if the orientation has changed since last inflated.
int orientation = mContext.getResources().getConfiguration().orientation;
if (mPreviousOrientation != orientation) {
return true;
}
return false;
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Just in case the previous long press hasn't been cleared, we make sure to start fresh
// on touch down.
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mLongPressHelper.cancelLongPress();
}
// Consume any touch events for ourselves after longpress is triggered
if (mLongPressHelper.hasPerformedLongPress()) {
mLongPressHelper.cancelLongPress();
return true;
}
// Watch for longpress or stylus button press events at this level to
// make sure users can always pick up this widget
if (mStylusEventHelper.onMotionEvent(ev)) {
mLongPressHelper.cancelLongPress();
return true;
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (!mStylusEventHelper.inStylusButtonPressed()) {
mLongPressHelper.postCheckForLongPress();
}
mDragLayer.setTouchCompleteListener(this);
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mLongPressHelper.cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
mLongPressHelper.cancelLongPress();
}
break;
}
// Otherwise continue letting touch events fall through to children
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
// If the widget does not handle touch, then cancel
// long press when we release the touch
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mLongPressHelper.cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
mLongPressHelper.cancelLongPress();
}
break;
}
return false;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
public void cancelLongPress() {
super.cancelLongPress();
mLongPressHelper.cancelLongPress();
}
@Override
public AppWidgetProviderInfo getAppWidgetInfo() {
AppWidgetProviderInfo info = super.getAppWidgetInfo();
if (info != null && !(info instanceof LauncherAppWidgetProviderInfo)) {
throw new IllegalStateException("Launcher widget must have"
+ " LauncherAppWidgetProviderInfo");
}
return info;
}
public LauncherAppWidgetProviderInfo getLauncherAppWidgetProviderInfo() {
return (LauncherAppWidgetProviderInfo) getAppWidgetInfo();
}
@Override
public void onTouchComplete() {
if (!mLongPressHelper.hasPerformedLongPress()) {
// If a long press has been performed, we don't want to clear the record of that since
// we still may be receiving a touch up which we want to intercept
mLongPressHelper.cancelLongPress();
}
}
@Override
public int getDescendantFocusability() {
return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
: ViewGroup.FOCUS_BLOCK_DESCENDANTS;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
&& event.getAction() == KeyEvent.ACTION_UP) {
mChildrenFocused = false;
requestFocus();
return true;
}
return super.dispatchKeyEvent(event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
event.startTracking();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (event.isTracking()) {
if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
mChildrenFocused = true;
ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
focusableChildren.remove(this);
int childrenCount = focusableChildren.size();
switch (childrenCount) {
case 0:
mChildrenFocused = false;
break;
case 1: {
if (getTag() instanceof ItemInfo) {
ItemInfo item = (ItemInfo) getTag();
if (item.spanX == 1 && item.spanY == 1) {
focusableChildren.get(0).performClick();
mChildrenFocused = false;
return true;
}
}
// continue;
}
default:
focusableChildren.get(0).requestFocus();
return true;
}
}
}
return super.onKeyUp(keyCode, event);
}
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
if (gainFocus) {
mChildrenFocused = false;
dispatchChildFocus(false);
}
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
}
@Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, focused);
dispatchChildFocus(focused != null);
if (focused != null) {
focused.setFocusableInTouchMode(false);
}
}
@Override
public void clearChildFocus(View child) {
super.clearChildFocus(child);
dispatchChildFocus(false);
}
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
return mChildrenFocused;
}
private void dispatchChildFocus(boolean childIsFocused) {
// The host view's background changes when selected, to indicate the focus is inside.
setSelected(childIsFocused);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try {
super.onLayout(changed, left, top, right, bottom);
} catch (final RuntimeException e) {
post(new Runnable() {
@Override
public void run() {
// Update the widget with 0 Layout id, to reset the view to error view.
updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
}
});
}
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(getClass().getName());
}
}