Merge "Drag from appDrawer & pin onto secondary display homescreen" into tm-qpr-dev am: ddbe35cc87

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/19915466

Change-Id: Ifa46e8a0331d53ae35cc3d4f3cfbaf0e1b39a574
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Brian Isganitis
2022-10-20 18:36:51 +00:00
committed by Automerger Merge Worker
6 changed files with 461 additions and 8 deletions
@@ -299,6 +299,10 @@ public final class FeatureFlags {
public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
"ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar.");
public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(
"SECONDARY_DRAG_N_DROP_TO_PIN", false,
"Enable dragging and dropping to pin apps within secondary display");
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
@@ -168,15 +168,18 @@ public class PinnedAppsAdapter extends BaseAdapter implements OnSharedPreference
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
private void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
/**
* Pins or unpins apps from home screen
*/
public void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
ComponentKey key = new ComponentKey(info.getTargetComponent(), info.user);
if (op.apply(key)) {
createFilteredAppsList();
Set<ComponentKey> copy = new HashSet<>(mPinnedApps);
Executors.MODEL_EXECUTOR.submit(() ->
mPrefs.edit().putStringSet(PINNED_APPS_KEY,
copy.stream().map(this::encode).collect(Collectors.toSet()))
.apply());
copy.stream().map(this::encode).collect(Collectors.toSet()))
.apply());
}
}
@@ -210,6 +213,13 @@ public class PinnedAppsAdapter extends BaseAdapter implements OnSharedPreference
mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user)));
}
/**
* Pins app to home screen
*/
public void addPinnedApp(ItemInfo info) {
update(info, mPinnedApps::add);
}
private class PinUnPinShortcut extends SystemShortcut<SecondaryDisplayLauncher> {
private final boolean mIsPinned;
@@ -18,6 +18,9 @@ package com.android.launcher3.secondarydisplay;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
@@ -26,6 +29,9 @@ import android.view.inputmethod.InputMethodManager;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
@@ -33,6 +39,11 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.AppInfo;
@@ -52,11 +63,11 @@ import java.util.HashMap;
* Launcher activity for secondary displays
*/
public class SecondaryDisplayLauncher extends BaseDraggingActivity
implements BgDataModel.Callbacks {
implements BgDataModel.Callbacks, DragController.DragListener {
private LauncherModel mModel;
private BaseDragLayer mDragLayer;
private SecondaryDragController mDragController;
private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView;
private View mAppsButton;
@@ -69,10 +80,13 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
private boolean mBindingItems = false;
private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
private final int[] mTempXY = new int[2];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mModel = LauncherAppState.getInstance(this).getModel();
mDragController = new SecondaryDragController(this);
mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
if (getWindow().getDecorView().isAttachedToWindow()) {
@@ -86,6 +100,12 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
initUi();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
this.getDragController().removeDragListener(this);
}
private void initUi() {
if (mDragLayer != null) {
return;
@@ -106,12 +126,19 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
mAppsView = findViewById(R.id.apps_view);
mAppsButton = findViewById(R.id.all_apps_button);
mDragController.addDragListener(this);
mPopupDataProvider = new PopupDataProvider(
mAppsView.getAppsStore()::updateNotificationDots);
mModel.addCallbacksAndLoad(this);
}
@Override
protected void onPause() {
super.onPause();
mDragController.cancelDrag();
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -129,12 +156,21 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
showAppDrawer(false);
}
public DragController getDragController() {
return mDragController;
}
@Override
public void onBackPressed() {
if (finishAutoCancelActionMode()) {
return;
}
if (mDragController.isDragging()) {
mDragController.cancelDrag();
return;
}
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -202,7 +238,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
float closeR = Themes.getDialogCornerRadius(this);
float startR = mAppsButton.getWidth() / 2f;
float[] buttonPos = new float[] { startR, startR};
float[] buttonPos = new float[]{startR, startR};
mDragLayer.getDescendantCoordRelativeToSelf(mAppsButton, buttonPos);
mDragLayer.mapCoordInSelfToDescendant(mAppsView, buttonPos);
final Animator animator = ViewAnimationUtils.createCircularReveal(mAppsView,
@@ -236,6 +272,7 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
@Override
public void startBinding() {
mBindingItems = true;
mDragController.cancelDrag();
}
@Override
@@ -308,4 +345,101 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
startActivitySafely(v, intent, item);
}
}
/**
* Core functionality for beginning a drag operation for an item that will be dropped within
* the secondary display grid home screen
*/
public void beginDragShared(View child, DragSource source, DragOptions options) {
Object dragObject = child.getTag();
if (!(dragObject instanceof ItemInfo)) {
String msg = "Drag started with a view that has no tag set. This "
+ "will cause a crash (issue 11627249) down the line. "
+ "View: " + child + " tag: " + child.getTag();
throw new IllegalStateException(msg);
}
beginDragShared(child, source, (ItemInfo) dragObject,
new DragPreviewProvider(child), options);
}
private void beginDragShared(View child, DragSource source,
ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions options) {
float iconScale = 1f;
if (child instanceof BubbleTextView) {
FastBitmapDrawable icon = ((BubbleTextView) child).getIcon();
if (icon != null) {
iconScale = icon.getAnimatedScale();
}
}
// clear pressed state if necessary
child.clearFocus();
child.setPressed(false);
if (child instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) child;
icon.clearPressedBackground();
}
DraggableView draggableView = null;
if (child instanceof DraggableView) {
draggableView = (DraggableView) child;
}
final View contentView = previewProvider.getContentView();
final float scale;
// The draggable drawable follows the touch point around on the screen
final Drawable drawable;
if (contentView == null) {
drawable = previewProvider.createDrawable();
scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
} else {
drawable = null;
scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
}
int halfPadding = previewProvider.previewPadding / 2;
int dragLayerX = mTempXY[0];
int dragLayerY = mTempXY[1];
Point dragVisualizeOffset = null;
Rect dragRect = new Rect();
if (draggableView != null) {
draggableView.getSourceVisualDragBounds(dragRect);
dragLayerY += dragRect.top;
dragVisualizeOffset = new Point(-halfPadding, halfPadding);
}
if (contentView != null) {
mDragController.startDrag(
contentView,
draggableView,
dragLayerX,
dragLayerY,
source,
dragObject,
dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
options);
} else {
mDragController.startDrag(
drawable,
draggableView,
dragLayerX,
dragLayerY,
source,
dragObject,
dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
options);
}
}
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
@Override
public void onDragEnd() { }
}
@@ -0,0 +1,194 @@
/*
* Copyright (C) 2022 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.secondarydisplay;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragDriver;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.testing.shared.TestProtocol;
/**
* Drag controller for Secondary Launcher activity
*/
public class SecondaryDragController extends DragController<SecondaryDisplayLauncher> {
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
public SecondaryDragController(SecondaryDisplayLauncher secondaryLauncher) {
super(secondaryLauncher);
}
@Override
protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
float dragViewScaleOnDrop, DragOptions options) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DROP_TARGET, "5");
}
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
}
mActivity.hideKeyboard();
mOptions = options;
if (mOptions.simulatedDndStartPoint != null) {
mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
}
final int registrationX = mMotionDown.x - dragLayerX;
final int registrationY = mMotionDown.y - dragLayerY;
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
mLastDropTarget = null;
mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
mDragObject.originalView = originalView;
mIsInPreDrag = mOptions.preDragCondition != null
&& !mOptions.preDragCondition.shouldStartDrag(0);
final Resources res = mActivity.getResources();
final float scaleDps = mIsInPreDrag
? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
final DragView dragView = mDragObject.dragView = drawable != null
? new SecondaryDragView(
mActivity,
drawable,
registrationX,
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
scaleDps)
: new SecondaryDragView(
mActivity,
view,
view.getMeasuredWidth(),
view.getMeasuredHeight(),
registrationX,
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
scaleDps);
dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
mDragDriver = DragDriver.create(this, mOptions, ev -> {
});
if (!mOptions.isAccessibleDrag) {
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
}
mDragObject.dragSource = source;
mDragObject.dragInfo = dragInfo;
mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
if (dragOffset != null) {
dragView.setDragVisualizeOffset(new Point(dragOffset));
}
if (dragRegion != null) {
dragView.setDragRegion(new Rect(dragRegion));
}
mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
dragView.show(mLastTouch.x, mLastTouch.y);
mDistanceSinceScroll = 0;
if (!mIsInPreDrag) {
callOnDragStart();
} else if (mOptions.preDragCondition != null) {
mOptions.preDragCondition.onPreDragStart(mDragObject);
}
handleMoveEvent(mLastTouch.x, mLastTouch.y);
return dragView;
}
@Override
protected void exitDrag() { }
@Override
protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
DropTarget target = new DropTarget() {
@Override
public boolean isDropEnabled() {
return true;
}
@Override
public void onDrop(DragObject dragObject, DragOptions options) {
((SecondaryDragLayer) mActivity.getDragLayer()).getPinnedAppsAdapter().addPinnedApp(
dragObject.dragInfo);
dragObject.dragView.remove();
}
@Override
public void onDragEnter(DragObject dragObject) {
if (getDistanceDragged() > mActivity.getResources().getDimensionPixelSize(
R.dimen.drag_distanceThreshold)) {
mActivity.showAppDrawer(false);
AbstractFloatingView.closeAllOpenViews(mActivity);
}
}
@Override
public void onDragOver(DragObject dragObject) { }
@Override
public void onDragExit(DragObject dragObject) { }
@Override
public boolean acceptDrop(DragObject dragObject) {
return true;
}
@Override
public void prepareAccessibilityDrop() { }
@Override
public void getHitRectRelativeToDragLayer(Rect outRect) { }
};
return target;
}
}
@@ -29,8 +29,12 @@ import android.widget.GridView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DropTarget;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
@@ -59,7 +63,8 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher>
@Override
public void recreateControllers() {
mControllers = new TouchController[] {new CloseAllAppsTouchController()};
mControllers = new TouchController[]{new CloseAllAppsTouchController(),
mActivity.getDragController()};
}
/**
@@ -166,6 +171,10 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher>
}
}
public PinnedAppsAdapter getPinnedAppsAdapter() {
return mPinnedAppsAdapter;
}
private boolean onIconLongClicked(View v) {
if (!(v instanceof BubbleTextView)) {
return false;
@@ -183,6 +192,7 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher>
if (popupDataProvider == null) {
return false;
}
final PopupContainerWithArrow container =
(PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
R.layout.popup_container, mActivity.getDragLayer(), false);
@@ -192,7 +202,42 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher>
Collections.emptyList(),
Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v),
APP_INFO.getShortcut(mActivity, item, v)));
v.getParent().requestDisallowInterceptTouchEvent(true);
container.requestFocus();
if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
return true;
}
DragOptions options = new DragOptions();
DeviceProfile grid = mActivity.getDeviceProfile();
options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
options.preDragCondition = container.createPreDragCondition(false);
if (options.preDragCondition == null) {
options.preDragCondition = new DragOptions.PreDragCondition() {
private DragView<SecondaryDisplayLauncher> mDragView;
@Override
public boolean shouldStartDrag(double distanceDragged) {
return mDragView != null && mDragView.isAnimationFinished();
}
@Override
public void onPreDragStart(DropTarget.DragObject dragObject) {
mDragView = dragObject.dragView;
if (!shouldStartDrag(0)) {
mDragView.setOnAnimationEndCallback(() -> {
mActivity.beginDragShared(v, mActivity.getAppsView(), options);
});
}
}
@Override
public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
mDragView = null;
}
};
}
mActivity.beginDragShared(v, mActivity.getAppsView(), options);
return true;
}
}
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2022 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.secondarydisplay;
import android.graphics.drawable.Drawable;
import android.view.View;
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragView;
/**
* A DragView drawn/used by the Secondary Launcher activity.
*/
public class SecondaryDragView extends DragView<SecondaryDisplayLauncher> {
public SecondaryDragView(SecondaryDisplayLauncher launcher,
Drawable drawable,
int registrationX, int registrationY, float initialScale, float scaleOnDrop,
float finalScaleDps) {
super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
finalScaleDps);
}
public SecondaryDragView(SecondaryDisplayLauncher launcher, View content, int width, int height,
int registrationX, int registrationY, float initialScale, float scaleOnDrop,
float finalScaleDps) {
super(launcher, content, width, height, registrationX, registrationY, initialScale,
scaleOnDrop, finalScaleDps);
}
@Override
public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
Runnable onAnimationEnd = () -> {
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
mActivity.getDragLayer().removeView(this);
};
duration = Math.max(duration,
getResources().getInteger(R.integer.config_dropAnimMinDuration));
animate()
.translationX(toTouchX - mRegistrationX)
.translationY(toTouchY - mRegistrationY)
.scaleX(mScaleOnDrop)
.scaleY(mScaleOnDrop)
.withEndAction(onAnimationEnd)
.setDuration(duration)
.start();
}
}