Files
lawnchair/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
Adam Cohen c77bc45fa5 Fix issues with drag and drop from All Apps in non-default grids
=> Also fix the widget tray in these non-default grids, and the external shortcut addition flow. The layout was broken as it was scaling with the icon size. Instead keep it fixed which looks much better, similar to how we maintain all apps.
=> This also fixes a small jump when dragging shortcuts external to Launcher.

issue 154169001

Change-Id: Iad1e3859dd6fedccce9b5c6633e64426a4630c31
2020-05-08 16:00:08 -07:00

228 lines
7.1 KiB
Java

/*
* Copyright (C) 2018 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.widget;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import com.android.launcher3.Reorderable;
import com.android.launcher3.dragndrop.DraggableView;
import java.util.ArrayList;
/**
* Extension of AppWidgetHostView with support for controlled keyboard navigation.
*/
public abstract class NavigableAppWidgetHostView extends AppWidgetHostView
implements DraggableView, Reorderable {
/**
* The scaleX and scaleY value such that the widget fits within its cellspans, scaleX = scaleY.
*/
private float mScaleToFit = 1f;
/**
* The translation values to center the widget within its cellspans.
*/
private final PointF mTranslationForCentering = new PointF(0, 0);
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
private float mScaleForReorderBounce = 1f;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
public NavigableAppWidgetHostView(Context context) {
super(context);
}
@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 (shouldAllowDirectClick()) {
focusableChildren.get(0).performClick();
mChildrenFocused = false;
return true;
}
// continue;
}
default:
focusableChildren.get(0).requestFocus();
return true;
}
}
}
return super.onKeyUp(keyCode, event);
}
/**
* For a widget with only a single interactive element, return true if whole widget should act
* as a single interactive element, and clicking 'enter' should activate the child element
* directly. Otherwise clicking 'enter' will only move the focus inside the widget.
*/
protected abstract boolean shouldAllowDirectClick();
@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(mChildrenFocused && 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);
}
public View getView() {
return this;
}
private void updateTranslation() {
super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+ mTranslationForCentering.x);
super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
+ mTranslationForCentering.y);
}
public void setTranslationForCentering(float x, float y) {
mTranslationForCentering.set(x, y);
updateTranslation();
}
public void setReorderBounceOffset(float x, float y) {
mTranslationForReorderBounce.set(x, y);
updateTranslation();
}
public void getReorderBounceOffset(PointF offset) {
offset.set(mTranslationForReorderBounce);
}
@Override
public void setReorderPreviewOffset(float x, float y) {
mTranslationForReorderPreview.set(x, y);
updateTranslation();
}
@Override
public void getReorderPreviewOffset(PointF offset) {
offset.set(mTranslationForReorderPreview);
}
private void updateScale() {
super.setScaleX(mScaleToFit * mScaleForReorderBounce);
super.setScaleY(mScaleToFit * mScaleForReorderBounce);
}
public void setReorderBounceScale(float scale) {
mScaleForReorderBounce = scale;
updateScale();
}
public float getReorderBounceScale() {
return mScaleForReorderBounce;
}
public void setScaleToFit(float scale) {
mScaleToFit = scale;
updateScale();
}
public float getScaleToFit() {
return mScaleToFit;
}
@Override
public int getViewType() {
return DRAGGABLE_WIDGET;
}
@Override
public void getWorkspaceVisualDragBounds(Rect bounds) {
int width = (int) (getMeasuredWidth() * mScaleToFit);
int height = (int) (getMeasuredHeight() * mScaleToFit);
bounds.set(0, 0 , width, height);
}
}