Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d775d35 authored by Andrew Cole's avatar Andrew Cole Committed by Android (Google) Code Review
Browse files

Merge "Unit testing ButtonDropTarget" into udc-dev

parents 7c49cb3a 44898c3d
Loading
Loading
Loading
Loading
+22 −13
Original line number Diff line number Diff line
@@ -18,8 +18,6 @@ package com.android.launcher3;

import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import static com.android.launcher3.LauncherState.NORMAL;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -34,12 +32,15 @@ import android.view.accessibility.AccessibilityEvent;
import android.widget.PopupWindow;
import android.widget.TextView;

import androidx.annotation.VisibleForTesting;

import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;

/**
 * Implements a DropTarget.
@@ -59,8 +60,8 @@ public abstract class ButtonDropTarget extends TextView

    private final Rect mTempRect = new Rect();

    protected final Launcher mLauncher;

    protected final ActivityContext mActivityContext;
    protected final DropTargetHandler mDropTargetHandler;
    protected DropTargetBar mDropTargetBar;

    /** Whether this drop target is active for the current drag */
@@ -83,13 +84,17 @@ public abstract class ButtonDropTarget extends TextView
    private PopupWindow mToolTip;
    private int mToolTipLocation;

    public ButtonDropTarget(Context context) {
        this(context, null, 0);
    }
    public ButtonDropTarget(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ButtonDropTarget(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mLauncher = Launcher.getLauncher(context);
        mActivityContext = ActivityContext.lookupContext(context);
        mDropTargetHandler = mActivityContext.getDropTargetHandler();

        Resources resources = getResources();
        mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
@@ -210,7 +215,8 @@ public abstract class ButtonDropTarget extends TextView
    @Override
    public boolean isDropEnabled() {
        return mActive && (mAccessibleDrag ||
                mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold);
                mActivityContext.getDragController().getDistanceDragged()
                        >= mDragDistanceThreshold);
    }

    @Override
@@ -229,7 +235,8 @@ public abstract class ButtonDropTarget extends TextView
            // FlingAnimation handles the animation and then calls completeDrop().
            return;
        }
        final DragLayer dragLayer = mLauncher.getDragLayer();

        final DragLayer dragLayer = mDropTargetHandler.getDragLayer();
        final DragView dragView = d.dragView;
        final Rect to = getIconRect(d);
        final float scale = (float) to.width() / dragView.getMeasuredWidth();
@@ -240,9 +247,10 @@ public abstract class ButtonDropTarget extends TextView
        Runnable onAnimationEndRunnable = () -> {
            completeDrop(d);
            mDropTargetBar.onDragEnd();
            mLauncher.getStateManager().goToState(NORMAL);
            mDropTargetHandler.onDropAnimationComplete();
        };


        dragLayer.animateView(d.dragView, to, scale, 0.1f, 0.1f,
                DRAG_VIEW_DROP_DURATION,
                Interpolators.DEACCEL_2, onAnimationEndRunnable,
@@ -261,10 +269,10 @@ public abstract class ButtonDropTarget extends TextView
    @Override
    public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
        super.getHitRect(outRect);
        outRect.bottom += mLauncher.getDeviceProfile().dropTargetDragPaddingPx;
        outRect.bottom += mActivityContext.getDeviceProfile().dropTargetDragPaddingPx;

        sTempCords[0] = sTempCords[1] = 0;
        mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords);
        mActivityContext.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords);
        outRect.offsetTo(sTempCords[0], sTempCords[1]);
    }

@@ -273,7 +281,7 @@ public abstract class ButtonDropTarget extends TextView
        int viewHeight = dragObject.dragView.getMeasuredHeight();
        int drawableWidth = mDrawable.getIntrinsicWidth();
        int drawableHeight = mDrawable.getIntrinsicHeight();
        DragLayer dragLayer = mLauncher.getDragLayer();
        DragLayer dragLayer = mDropTargetHandler.getDragLayer();

        // Find the rect to animate to (the view is center aligned)
        Rect to = new Rect();
@@ -314,7 +322,7 @@ public abstract class ButtonDropTarget extends TextView

    @Override
    public void onClick(View v) {
        mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
        mDropTargetHandler.onClick(this);
    }

    public void setTextVisible(boolean isVisible) {
@@ -407,7 +415,8 @@ public abstract class ButtonDropTarget extends TextView
    /**
     * Returns if the text will be clipped vertically within the provided availableHeight.
     */
    private boolean isTextClippedVertically(int availableHeight) {
    @VisibleForTesting
    protected boolean isTextClippedVertically(int availableHeight) {
        availableHeight -= getPaddingTop() + getPaddingBottom();
        if (availableHeight <= 0) {
            return true;
+8 −29
Original line number Diff line number Diff line
@@ -18,24 +18,19 @@ package com.android.launcher3;

import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNDO;

import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;

import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.views.Snackbar;

public class DeleteDropTarget extends ButtonDropTarget {

@@ -43,6 +38,10 @@ public class DeleteDropTarget extends ButtonDropTarget {

    private StatsLogManager.LauncherEvent mLauncherEvent;

    public DeleteDropTarget(Context context) {
        this(context, null, 0);
    }

    public DeleteDropTarget(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
@@ -120,7 +119,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
    @Override
    public void onDrop(DragObject d, DragOptions options) {
        if (canRemove(d.dragInfo)) {
            mLauncher.getModelWriter().prepareToUndoDelete();
            mDropTargetHandler.prepareToUndoDelete();
        }
        super.onDrop(d, options);
        mStatsLogManager.logger().withInstanceId(d.logInstanceId)
@@ -131,26 +130,8 @@ public class DeleteDropTarget extends ButtonDropTarget {
    public void completeDrop(DragObject d) {
        ItemInfo item = d.dragInfo;
        if (canRemove(item)) {
            ItemInfo pageItem = item;
            if (item.container <= 0) {
                View v = mLauncher.getWorkspace().getHomescreenIconByItemId(item.container);
                if (v != null) {
                    pageItem = (ItemInfo) v.getTag();
                }
            }
            IntSet pageIds = pageItem.container == Favorites.CONTAINER_DESKTOP
                    ? IntSet.wrap(pageItem.screenId)
                    : mLauncher.getWorkspace().getCurrentPageScreenIds();

            onAccessibilityDrop(null, item);
            ModelWriter modelWriter = mLauncher.getModelWriter();
            Runnable onUndoClicked = () -> {
                mLauncher.setPagesToBindSynchronously(pageIds);
                modelWriter.abortDelete();
                mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO);
            };
            Snackbar.show(mLauncher, R.string.item_removed, R.string.undo,
                    modelWriter::commitDelete, onUndoClicked);
            mDropTargetHandler.onDeleteComplete(item);
        }
    }

@@ -162,9 +143,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
        // Remove the item from launcher and the db, we can ignore the containerInfo in this call
        // because we already remove the drag view from the folder (if the drag originated from
        // a folder) in Folder.beginDrag()
        mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop");
        mLauncher.getWorkspace().stripEmptyScreens();
        mLauncher.getDragLayer()
                .announceForAccessibility(getContext().getString(R.string.item_removed));
        CharSequence announcement = getContext().getString(R.string.item_removed);
        mDropTargetHandler.onAccessibilityDelete(view, item, announcement);
    }
}
+119 −0
Original line number Diff line number Diff line
package com.android.launcher3

import android.content.ComponentName
import android.view.View
import com.android.launcher3.DropTarget.DragObject
import com.android.launcher3.SecondaryDropTarget.DeferredOnComplete
import com.android.launcher3.dragndrop.DragLayer
import com.android.launcher3.logging.StatsLogManager.LauncherEvent
import com.android.launcher3.model.ModelWriter
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.util.IntSet
import com.android.launcher3.util.PendingRequestArgs
import com.android.launcher3.views.Snackbar

/**
 * Handler class for drop target actions that require modifying or interacting with launcher.
 *
 * This class is created by Launcher and provided the instance of launcher when created, which
 * allows us to decouple drop target controllers from Launcher to enable easier testing.
 */
class DropTargetHandler(launcher: Launcher) {
    val mLauncher: Launcher = launcher

    val modelWriter: ModelWriter = mLauncher.modelWriter

    fun onDropAnimationComplete() {
        mLauncher.stateManager.goToState(LauncherState.NORMAL)
    }

    fun onSecondaryTargetCompleteDrop(target: ComponentName?, d: DragObject) {
        when (val dragSource = d.dragSource) {
            is DeferredOnComplete -> {
                val deferred: DeferredOnComplete = dragSource
                if (d.dragSource is SecondaryDropTarget.DeferredOnComplete) {
                    target?.let {
                        deferred.mPackageName = it.packageName
                        mLauncher.addOnResumeCallback { deferred.onLauncherResume() }
                    }
                        ?: deferred.sendFailure()
                }
            }
        }
    }

    fun reconfigureWidget(widgetId: Int, info: ItemInfo) {
        mLauncher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(widgetId, null, info))
        mLauncher.appWidgetHolder.startConfigActivity(
            mLauncher,
            widgetId,
            Launcher.REQUEST_RECONFIGURE_APPWIDGET
        )
    }

    fun dismissPrediction(
        announcement: CharSequence,
        onActionClicked: Runnable,
        onDismiss: Runnable?
    ) {
        mLauncher.dragLayer.announceForAccessibility(announcement)
        Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, onDismiss, onActionClicked)
    }

    fun getViewUnderDrag(info: ItemInfo): View? {
        return if (
            info is LauncherAppWidgetInfo &&
                info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                mLauncher.workspace.dragInfo != null
        ) {
            mLauncher.workspace.dragInfo.cell
        } else null
    }

    fun prepareToUndoDelete() {
        mLauncher.modelWriter.prepareToUndoDelete()
    }

    fun onDeleteComplete(item: ItemInfo) {
        var pageItem: ItemInfo = item
        if (item.container <= 0) {
            val v = mLauncher.workspace.getHomescreenIconByItemId(item.container)
            v?.let { pageItem = v.tag as ItemInfo }
        }
        val pageIds =
            if (pageItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP)
                IntSet.wrap(pageItem.screenId)
            else mLauncher.workspace.currentPageScreenIds
        val onUndoClicked = Runnable {
            mLauncher.setPagesToBindSynchronously(pageIds)
            modelWriter.abortDelete()
            mLauncher.statsLogManager.logger().log(LauncherEvent.LAUNCHER_UNDO)
        }

        Snackbar.show(
            mLauncher,
            R.string.item_removed,
            R.string.undo,
            modelWriter::commitDelete,
            onUndoClicked
        )
    }

    fun onAccessibilityDelete(view: View?, item: ItemInfo, announcement: CharSequence) {
        // Remove the item from launcher and the db, we can ignore the containerInfo in this call
        // because we already remove the drag view from the folder (if the drag originated from
        // a folder) in Folder.beginDrag()
        mLauncher.removeItem(view, item, true /* deleteFromDb */, "removed by accessibility drop")
        mLauncher.workspace.stripEmptyScreens()
        mLauncher.dragLayer.announceForAccessibility(announcement)
    }

    fun getDragLayer(): DragLayer {
        return mLauncher.dragLayer
    }

    fun onClick(buttonDropTarget: ButtonDropTarget) {
        mLauncher.accessibilityDelegate.handleAccessibleDrop(buttonDropTarget, null, null)
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -1770,6 +1770,11 @@ public class Launcher extends StatefulActivity<LauncherState>
        return mDragController;
    }

    @Override
    public DropTargetHandler getDropTargetHandler() {
        return new DropTargetHandler(this);
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        if (requestCode != -1) {
+16 −33
Original line number Diff line number Diff line
@@ -3,8 +3,6 @@ package com.android.launcher3;
import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;

import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.INVALID;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
@@ -45,10 +43,7 @@ import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.views.Snackbar;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;

import java.net.URISyntaxException;
@@ -204,7 +199,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
            user = item.user;
        }
        if (intent != null) {
            LauncherActivityInfo info = mLauncher.getSystemService(LauncherApps.class)
            LauncherActivityInfo info = getContext().getSystemService(LauncherApps.class)
                    .resolveActivity(intent, user);
            if (info != null
                    && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -239,23 +234,11 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
    public void completeDrop(final DragObject d) {
        ComponentName target = performDropAction(getViewUnderDrag(d.dragInfo), d.dragInfo,
                d.logInstanceId);
        if (d.dragSource instanceof DeferredOnComplete) {
            DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
            if (target != null) {
                deferred.mPackageName = target.getPackageName();
                mLauncher.addOnResumeCallback(deferred::onLauncherResume);
            } else {
                deferred.sendFailure();
            }
        }
        mDropTargetHandler.onSecondaryTargetCompleteDrop(target, d);
    }

    private View getViewUnderDrag(ItemInfo info) {
        if (info instanceof LauncherAppWidgetInfo && info.container == CONTAINER_DESKTOP &&
                mLauncher.getWorkspace().getDragInfo() != null) {
            return mLauncher.getWorkspace().getDragInfo().cell;
        }
        return null;
        return mDropTargetHandler.getViewUnderDrag(info);
    }

    /**
@@ -286,18 +269,15 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
        if (mCurrentAccessibilityAction == RECONFIGURE) {
            int widgetId = getReconfigurableWidgetId(view);
            if (widgetId != INVALID_APPWIDGET_ID) {
                mLauncher.setWaitingForResult(
                        PendingRequestArgs.forWidgetInfo(widgetId, null, info));
                mLauncher.getAppWidgetHolder().startConfigActivity(mLauncher, widgetId,
                        REQUEST_RECONFIGURE_APPWIDGET);
                mDropTargetHandler.reconfigureWidget(widgetId, info);
            }
            return null;
        }
        if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
            if (FeatureFlags.ENABLE_DISMISS_PREDICTION_UNDO.get()) {
                mLauncher.getDragLayer()
                        .announceForAccessibility(getContext().getString(R.string.item_removed));
                Snackbar.show(mLauncher, R.string.item_removed, R.string.undo, () -> { }, () -> {
                CharSequence announcement = getContext().getString(R.string.item_removed);
                mDropTargetHandler
                        .dismissPrediction(announcement, () -> {}, () -> {
                    mStatsLogManager.logger()
                            .withInstanceId(instanceId)
                            .withItemInfo(info)
@@ -306,20 +286,23 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
            }
            return null;
        }
        // else: mCurrentAccessibilityAction == UNINSTALL

        ComponentName cn = getUninstallTarget(info);
        if (cn == null) {
            // System applications cannot be installed. For now, show a toast explaining that.
            // We may give them the option of disabling apps this way.
            Toast.makeText(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
            Toast.makeText(
                    getContext(),
                    R.string.uninstall_system_app_text,
                    Toast.LENGTH_SHORT
                ).show();
            return null;
        }
        try {
            Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0)
            Intent i = Intent.parseUri(getContext().getString(R.string.delete_package_intent), 0)
                    .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
                    .putExtra(Intent.EXTRA_USER, info.user);
            mLauncher.startActivity(i);
            getContext().startActivity(i);
            FileLog.d(TAG, "start uninstall activity " + cn.getPackageName());
            return cn;
        } catch (URISyntaxException e) {
@@ -339,12 +322,12 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
     * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until
     * {@link #onLauncherResume}
     */
    private class DeferredOnComplete implements DragSource {
    protected class DeferredOnComplete implements DragSource {

        private final DragSource mOriginal;
        private final Context mContext;

        private String mPackageName;
        protected String mPackageName;
        private DragObject mDragObject;

        public DeferredOnComplete(DragSource original, Context context) {
Loading