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

Commit cd7431d9 authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

Merge "Add system shortcuts when long pressing recent icon" into ub-launcher3-master

parents f62255e3 2fae2a0e
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     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.
-->
<com.android.quickstep.TaskMenuView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/bg_popup_item_width"
    android:layout_height="wrap_content"
    android:visibility="invisible"
    android:elevation="@dimen/deep_shortcuts_elevation"
    android:orientation="vertical"
    android:background="?attr/popupColorPrimary"
    android:divider="@drawable/all_apps_divider"
    android:showDividers="middle"
    android:animateLayoutChanges="true">
        <TextView
            android:id="@+id/task_icon_and_name"
            android:layout_width="match_parent"
            android:layout_height="112dp"
            android:textSize="14sp"
            android:paddingTop="18dp"
            android:drawablePadding="8dp"
            android:gravity="center_horizontal"/>
</com.android.quickstep.TaskMenuView>
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

    <dimen name="task_thumbnail_top_margin">24dp</dimen>
    <dimen name="task_thumbnail_icon_size">48dp</dimen>
    <dimen name="task_menu_background_radius">12dp</dimen>

    <dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
    <dimen name="quickstep_fling_min_velocity">250dp</dimen>
+223 −0
Original line number Diff line number Diff line
/*
 * 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.quickstep;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Outline;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.TextView;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.systemui.shared.recents.model.Task;

/**
 * Contains options for a recent task when long-pressing its icon.
 */
public class TaskMenuView extends AbstractFloatingView {

    private static final Rect sTempRect = new Rect();

    /** Note that these will be shown in order from top to bottom, if available for the task. */
    private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[] {
            new TaskSystemShortcut.Widgets(),
            new TaskSystemShortcut.AppInfo(),
            new TaskSystemShortcut.Install()
    };

    private static final long OPEN_CLOSE_DURATION = 220;

    private Launcher mLauncher;
    private TextView mTaskIconAndName;
    private AnimatorSet mOpenCloseAnimator;

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

    public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mLauncher = Launcher.getLauncher(context);
        setClipToOutline(true);
        setOutlineProvider(new ViewOutlineProvider() {
            @Override
            public void getOutline(View view, Outline outline) {
                float r = getResources().getDimensionPixelSize(R.dimen.task_menu_background_radius);
                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), r);
            }
        });
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTaskIconAndName = findViewById(R.id.task_icon_and_name);
    }

    @Override
    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            DragLayer dl = mLauncher.getDragLayer();
            if (!dl.isEventOverView(this, ev)) {
                // TODO: log this once we have a new container type for it?
                close(true);
                return true;
            }
        }
        return false;
    }

    @Override
    protected void handleClose(boolean animate) {
        if (animate) {
            animateClose();
        } else {
            closeComplete();
        }
    }

    @Override
    public void logActionCommand(int command) {
        // TODO
    }

    @Override
    protected boolean isOfType(int type) {
        return (type & TYPE_TASK_MENU) != 0;
    }

    public static boolean showForTask(TaskView taskView) {
        Launcher launcher = Launcher.getLauncher(taskView.getContext());
        final TaskMenuView taskMenuView = (TaskMenuView) launcher.getLayoutInflater().inflate(
                        R.layout.task_menu, launcher.getDragLayer(), false);
        return taskMenuView.populateAndShowForTask(taskView);
    }

    private boolean populateAndShowForTask(TaskView taskView) {
        if (isAttachedToWindow()) {
            return false;
        }
        mLauncher.getDragLayer().addView(this);
        addMenuOptions(taskView.getTask());
        orientAroundTaskView(taskView);
        post(this::animateOpen);
        return true;
    }

    private void addMenuOptions(Task task) {
        Drawable icon = task.icon.getConstantState().newDrawable();
        int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
        icon.setBounds(0, 0, iconSize, iconSize);
        mTaskIconAndName.setCompoundDrawables(null, icon, null, null);
        mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, task));

        LayoutInflater inflater = mLauncher.getLayoutInflater();
        for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
            OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, task);
            if (onClickListener != null) {
                DeepShortcutView menuOptionView = (DeepShortcutView) inflater.inflate(
                        R.layout.system_shortcut, this, false);
                menuOptionView.getIconView().setBackgroundResource(menuOption.iconResId);
                menuOptionView.getBubbleText().setText(menuOption.labelResId);
                menuOptionView.setOnClickListener(onClickListener);
                addView(menuOptionView);
            }
        }
    }

    private void orientAroundTaskView(TaskView taskView) {
        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
        mLauncher.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
        Rect insets = mLauncher.getDragLayer().getInsets();
        setX(sTempRect.left + (sTempRect.width() - getMeasuredWidth()) / 2 - insets.left);
        setY(sTempRect.top - mTaskIconAndName.getPaddingTop() - insets.top);
    }

    private void animateOpen() {
        animateOpenOrClosed(false);
        mIsOpen = true;
    }

    private void animateClose() {
        animateOpenOrClosed(true);
    }

    private void animateOpenOrClosed(boolean closing) {
        if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
            return;
        }
        mOpenCloseAnimator = LauncherAnimUtils.createAnimatorSet();
        mOpenCloseAnimator.play(createOpenCloseOutlineProvider()
                .createRevealAnimator(this, closing));
        mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                setVisibility(VISIBLE);
            }

            @Override
            public void onAnimationSuccess(Animator animator) {
                if (closing) {
                    closeComplete();
                }
            }
        });
        mOpenCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
        mOpenCloseAnimator.setDuration(OPEN_CLOSE_DURATION);
        mOpenCloseAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        mOpenCloseAnimator.start();
    }

    private void closeComplete() {
        mIsOpen = false;
        mLauncher.getDragLayer().removeView(this);
    }

    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
        int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
        float fromRadius = iconSize / 2;
        float toRadius = getResources().getDimensionPixelSize(
                R.dimen.task_menu_background_radius);
        Point iconCenter = new Point(getWidth() / 2, mTaskIconAndName.getPaddingTop() + iconSize / 2);
        Rect fromRect = new Rect(iconCenter.x, iconCenter.y, iconCenter.x, iconCenter.y);
        Rect toRect = new Rect(0, 0, getWidth(), getHeight());
        return new RoundedRectRevealOutlineProvider(fromRadius, toRadius, fromRect, toRect) {
            @Override
            public boolean shouldRemoveElevationDuringAnimation() {
                return true;
            }
        };
    }
}
+92 −0
Original line number Diff line number Diff line
/*
 * 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.quickstep;

import android.content.ComponentName;
import android.content.Intent;
import android.os.UserHandle;
import android.view.View;

import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.InstantAppResolver;
import com.android.systemui.shared.recents.model.Task;

/**
 * Represents a system shortcut that can be shown for a recent task.
 */
public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {

    protected T mSystemShortcut;

    protected TaskSystemShortcut(T systemShortcut) {
        super(systemShortcut.iconResId, systemShortcut.labelResId);
        mSystemShortcut = systemShortcut;
    }

    @Override
    public View.OnClickListener getOnClickListener(Launcher launcher, ItemInfo itemInfo) {
        return null;
    }

    public View.OnClickListener getOnClickListener(final Launcher launcher, final Task task) {
        ShortcutInfo dummyInfo = new ShortcutInfo();
        dummyInfo.intent = new Intent();
        ComponentName component = task.getTopComponent();
        dummyInfo.intent.setComponent(component);
        dummyInfo.user = UserHandle.getUserHandleForUid(task.key.userId);
        dummyInfo.title = TaskUtils.getTitle(launcher, task);

        return getOnClickListenerForTask(launcher, task, dummyInfo);
    }

    protected View.OnClickListener getOnClickListenerForTask(final Launcher launcher,
            final Task task, final ItemInfo dummyInfo) {
        return mSystemShortcut.getOnClickListener(launcher, dummyInfo);
    }


    public static class Widgets extends TaskSystemShortcut<SystemShortcut.Widgets> {
        public Widgets() {
            super(new SystemShortcut.Widgets());
        }
    }

    public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
        public AppInfo() {
            super(new SystemShortcut.AppInfo());
        }
    }

    public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
        public Install() {
            super(new SystemShortcut.Install());
        }

        @Override
        protected View.OnClickListener getOnClickListenerForTask(Launcher launcher, Task task,
                ItemInfo itemInfo) {
            if (InstantAppResolver.newInstance(launcher).isInstantApp(launcher,
                        task.getTopComponent().getPackageName())) {
                return mSystemShortcut.createOnClickListener(launcher, itemInfo);
            }
            return null;
        }
    }
}
+42 −0
Original line number Diff line number Diff line
/*
 * 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.quickstep;

import android.content.pm.PackageManager;
import android.util.Log;

import com.android.launcher3.Launcher;
import com.android.systemui.shared.recents.model.Task;

/**
 * Contains helpful methods for retrieving data from {@link Task}s.
 * TODO: remove this once we switch to getting the icon and label from IconCache.
 */
public class TaskUtils {
    private static final String TAG = "TaskUtils";

    public static CharSequence getTitle(Launcher launcher, Task task) {
        PackageManager pm = launcher.getPackageManager();
        try {
            return pm.getPackageInfo(task.getTopComponent().getPackageName(), 0)
                    .applicationInfo.loadLabel(pm);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Failed to get title for task " + task, e);
        }
        return "";
    }
}
Loading