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

Commit 3aed7aff authored by James Cook's avatar James Cook Committed by Android (Google) Code Review
Browse files

Merge "Allow more transient icons in app shelf"

parents 2e9c889d fa28d939
Loading
Loading
Loading
Loading
+5 −69
Original line number Diff line number Diff line
@@ -91,42 +91,10 @@
                    android:src="@drawable/nav_app_divider"
                    />
    
                <!-- TODO: Build the list of icons dynamically. -->
                <com.android.systemui.statusbar.phone.NavigationBarRecents
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    >
                    <ImageView android:id="@+id/recent0"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_marginLeft="8dp"
                        android:layout_marginRight="8dp"
                        android:padding="6dp"
                        android:layout_gravity="center"
                        android:scaleType="centerInside"
                        android:visibility="invisible"
                        />
                    <ImageView android:id="@+id/recent1"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_marginLeft="8dp"
                        android:layout_marginRight="8dp"
                        android:padding="6dp"
                        android:layout_gravity="center"
                        android:scaleType="centerInside"
                        android:visibility="invisible"
                        />
                    <ImageView android:id="@+id/recent2"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_marginLeft="8dp"
                        android:layout_marginRight="8dp"
                        android:padding="6dp"
                        android:layout_gravity="center"
                        android:scaleType="centerInside"
                        android:visibility="invisible"
                    />
                </com.android.systemui.statusbar.phone.NavigationBarRecents>
            </LinearLayout>

            <FrameLayout
@@ -287,42 +255,10 @@
                    android:src="@drawable/nav_app_divider"
                    />
    
                <!-- TODO: Build the list of icons dynamically. -->
            <com.android.systemui.statusbar.phone.NavigationBarRecents
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                    >
                    <ImageView android:id="@+id/recent0"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_marginLeft="8dp"
                        android:layout_marginRight="8dp"
                        android:padding="6dp"
                        android:layout_gravity="center"
                        android:scaleType="centerInside"
                        android:visibility="invisible"
                        />
                    <ImageView android:id="@+id/recent1"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_marginLeft="8dp"
                        android:layout_marginRight="8dp"
                        android:padding="6dp"
                        android:layout_gravity="center"
                        android:scaleType="centerInside"
                        android:visibility="invisible"
                        />
                    <ImageView android:id="@+id/recent2"
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_marginLeft="8dp"
                        android:layout_marginRight="8dp"
                        android:padding="6dp"
                        android:layout_gravity="center"
                        android:scaleType="centerInside"
                        android:visibility="invisible"
                />
                </com.android.systemui.statusbar.phone.NavigationBarRecents>
            </LinearLayout>

            <FrameLayout
+19 −11
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -111,19 +111,27 @@ class NavigationBarApps extends LinearLayout {
        return button;
    }

    // Not shared with NavigationBarRecents because the data model is specific to pinned apps.
    private class AppLongClickListener implements View.OnLongClickListener {
        @Override
        public boolean onLongClick(View v) {
            mDragView = v;
            ImageView icon = (ImageView) v;
            ComponentName activityName = mAppsModel.getApp(indexOfChild(v));
            startAppDrag(icon, activityName);
            return true;
        }
    }

    /** Helper function to start dragging an app icon (either pinned or recent). */
    static void startAppDrag(ImageView icon, ComponentName activityName) {
        // The drag data is an Intent to launch the activity.
        Intent mainIntent = Intent.makeMainActivity(activityName);
        ClipData dragData = ClipData.newIntent("", mainIntent);
        // Use the ImageView to create the shadow.
            View.DragShadowBuilder shadow = new AppIconDragShadowBuilder((ImageView) v);
            v.startDrag(dragData, shadow, null /* myLocalState */, 0 /* flags */);
            return true;
        }
        View.DragShadowBuilder shadow = new AppIconDragShadowBuilder(icon);
        // Use a global drag because the icon might be dragged into the launcher.
        icon.startDrag(dragData, shadow, null /* myLocalState */, View.DRAG_FLAG_GLOBAL);
    }

    @Override
@@ -161,7 +169,7 @@ class NavigationBarApps extends LinearLayout {
     * an app shortcut and will be accepted for a drop.
     */
    private boolean onDragStarted(DragEvent event) {
        if (DEBUG) Log.d(TAG, "onDragStarted");
        if (DEBUG) Slog.d(TAG, "onDragStarted");

        // Ensure that an app shortcut is being dragged.
        if (!canAcceptDrag(event)) {
@@ -194,7 +202,7 @@ class NavigationBarApps extends LinearLayout {
     * needs to use LinearLayout/ViewGroup methods.
     */
    private void onDragEnteredIcon(View target) {
        if (DEBUG) Log.d(TAG, "onDragEntered " + indexOfChild(target));
        if (DEBUG) Slog.d(TAG, "onDragEntered " + indexOfChild(target));

        // If the drag didn't start from an existing icon, add an invisible placeholder to create
        // empty space for the user to drag into.
@@ -227,7 +235,7 @@ class NavigationBarApps extends LinearLayout {
    }

    private boolean onDrop(DragEvent event) {
        if (DEBUG) Log.d(TAG, "onDrop");
        if (DEBUG) Slog.d(TAG, "onDrop");

        int dragViewIndex = indexOfChild(mDragView);
        if (mAppsModel.getApp(dragViewIndex) == null) {
@@ -285,7 +293,7 @@ class NavigationBarApps extends LinearLayout {

    /** Cleans up at the end of the drag. */
    private boolean onDragEnded() {
        if (DEBUG) Log.d(TAG, "onDragEnded");
        if (DEBUG) Slog.d(TAG, "onDragEnded");

        if (mDragView != null) {
            // The icon wasn't dropped into the app list. Remove the placeholder.
+85 −49
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -28,7 +27,10 @@ import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -36,6 +38,7 @@ import android.widget.LinearLayout;
import com.android.systemui.R;

import java.util.List;
import java.util.Set;

/**
 * Recent task icons appearing in the navigation bar. Touching an icon brings the activity to the
@@ -43,21 +46,31 @@ import java.util.List;
 * icons.
 */
class NavigationBarRecents extends LinearLayout {
    private final static boolean DEBUG = false;
    private final static String TAG = "NavigationBarRecents";

    private final static int[] RECENT_APP_BUTTON_IDS = { R.id.recent0, R.id.recent1, R.id.recent2 };
    // Maximum number of icons to show.
    // TODO: Implement an overflow UI so the shelf can display an unlimited number of recents.
    private final static int MAX_RECENTS = 10;

    private final ActivityManager mActivityManager;
    private final PackageManager mPackageManager;
    private final LayoutInflater mLayoutInflater;
    private final TaskStackListenerImpl mTaskStackListener;
    // Recent tasks being displayed in the shelf.
    private final Set<ComponentName> mCurrentTasks = new ArraySet<ComponentName>(MAX_RECENTS);

    public NavigationBarRecents(Context context, AttributeSet attrs) {
        super(context, attrs);
        mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        mPackageManager = getContext().getPackageManager();
        mLayoutInflater = LayoutInflater.from(context);

        // Listen for task stack changes and refresh when they happen. Update notifications happen
        // on an IPC thread, so use Handler to handle the message on the main thread.
        // TODO: This has too much latency. It only adds the icon when app launch is completed
        // and the launch animation is done playing. This class should add the icon immediately
        // when the launch starts.
        Handler handler = new Handler();
        mTaskStackListener = new TaskStackListenerImpl(handler);
        IActivityManager iam = ActivityManagerNative.getDefault();
@@ -68,54 +81,86 @@ class NavigationBarRecents extends LinearLayout {
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        // Set up the buttons.
        for (int i = 0; i < RECENT_APP_BUTTON_IDS.length; i++) {
            ImageView button = getRecentAppButton(i);
            button.setOnLongClickListener(AppLongClickListener.getInstance());
        }

        // TODO: When is the right time to do the initial update?
        updateRecentApps();
    }

    private ImageView getRecentAppButton(int index) {
        return (ImageView) findViewById(RECENT_APP_BUTTON_IDS[index]);
    }

    private void updateRecentApps() {
        // TODO: Should this be getRunningTasks?
        List<ActivityManager.RecentTaskInfo> tasks = mActivityManager.getRecentTasksForUser(
                RECENT_APP_BUTTON_IDS.length,
        // TODO: Query other UserHandles?
        List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasksForUser(
                MAX_RECENTS,
                ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
                ActivityManager.RECENT_IGNORE_UNAVAILABLE |
                ActivityManager.RECENT_INCLUDE_PROFILES |
                ActivityManager.RECENT_WITH_EXCLUDED,
                UserHandle.USER_CURRENT);
        // Show the recent icons with the oldest on the left.
        int buttonIndex = 0;
        int taskIndex = tasks.size() - 1;
        while (taskIndex >= 0 && buttonIndex < RECENT_APP_BUTTON_IDS.length) {
            updateRecentAppButton(getRecentAppButton(buttonIndex), tasks.get(taskIndex));
            taskIndex--;
            buttonIndex++;
        if (DEBUG) Slog.d(TAG, "Got recents " + recentTasks.size());
        removeMissingRecents(recentTasks);
        addNewRecents(recentTasks);
    }

    // Remove any icons that disappeared from recents.
    private void removeMissingRecents(List<ActivityManager.RecentTaskInfo> recentTasks) {
        // Extract the component names.
        Set<ComponentName> recentComponents = new ArraySet<ComponentName>(recentTasks.size());
        for (ActivityManager.RecentTaskInfo task : recentTasks) {
            ComponentName component = task.baseIntent.getComponent();
            if (component == null) {  // It's unclear if this can happen in practice.
                continue;
            }
            recentComponents.add(component);
        }
        // Hide the unused buttons.
        while (buttonIndex < RECENT_APP_BUTTON_IDS.length) {
            hideButton(getRecentAppButton(buttonIndex));
            buttonIndex++;

        // Start with a copy of the currently displayed tasks.
        Set<ComponentName> removed = new ArraySet<ComponentName>(mCurrentTasks);
        // Remove all the entries that still exist in recents.
        removed.removeAll(recentComponents);
        // The remaining entries no longer exist in recents, so remove their icons.
        for (ComponentName task : removed) {
            removeIcon(task);
        }
    }

    private void updateRecentAppButton(ImageView button, ActivityManager.RecentTaskInfo task) {
        ComponentName component = task.baseIntent.getComponent();
        if (component == null) {
            hideButton(button);
    // Removes the icon for a task.
    private void removeIcon(ComponentName task) {
        for (int i = 0; i < getChildCount(); i++) {
            ComponentName childTask = (ComponentName) getChildAt(i).getTag();
            if (childTask.equals(task)) {
                if (DEBUG) Slog.d(TAG, "removing missing " + task);
                removeViewAt(i);
                mCurrentTasks.remove(task);
                return;
            }
        }
    }

    // Adds new tasks at the end of the icon list.
    private void addNewRecents(List<ActivityManager.RecentTaskInfo> recentTasks) {
        for (ActivityManager.RecentTaskInfo task : recentTasks) {
            // Don't overflow the list.
            if (getChildCount() >= MAX_RECENTS) {
                return;
            }
            ComponentName component = task.baseIntent.getComponent();
            if (component == null) {  // It's unclear if this can happen in practice.
                continue;
            }
            // Don't add tasks that are already being shown.
            if (mCurrentTasks.contains(component)) {
                continue;
            }
            addRecentAppButton(task);
        }
    }

    // Adds an icon at the end of the list to represent an activity for a given component.
    private void addRecentAppButton(ActivityManager.RecentTaskInfo task) {
        // Add this task to the currently-shown set.
        ComponentName component = task.baseIntent.getComponent();
        mCurrentTasks.add(component);
        if (DEBUG) Slog.d(TAG, "adding " + component);

        ImageView button = (ImageView) mLayoutInflater.inflate(
                R.layout.navigation_bar_app_item, this, false /* attachToRoot */);
        button.setOnLongClickListener(AppLongClickListener.getInstance());
        addView(button);

        // Use the View's tag to store metadata for drag and drop.
        button.setTag(component);
@@ -134,11 +179,6 @@ class NavigationBarRecents extends LinearLayout {
        });
    }

    private void hideButton(ImageView button) {
        button.setImageDrawable(null);
        button.setVisibility(View.GONE);
    }

    /**
     * A listener that updates the app buttons whenever the recents task stack changes.
     * NOTE: This is not the right way to do this.
@@ -173,13 +213,9 @@ class NavigationBarRecents extends LinearLayout {

        @Override
        public boolean onLongClick(View v) {
            ImageView icon = (ImageView) v;
            ComponentName activityName = (ComponentName) v.getTag();
            // The drag data is an Intent to launch the activity.
            Intent mainIntent = Intent.makeMainActivity(activityName);
            ClipData dragData = ClipData.newIntent("", mainIntent);
            // Use the ImageView to create the shadow.
            View.DragShadowBuilder shadow = new AppIconDragShadowBuilder((ImageView) v);
            v.startDrag(dragData, shadow, null /* myLocalState */, 0 /* flags */);
            NavigationBarApps.startAppDrag(icon, activityName);
            return true;
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -391,7 +391,7 @@ public final class ActivityManagerService extends ActivityManagerNative
    static final int LAST_PREBOOT_DELIVERED_FILE_VERSION = 10000;
    // Delay in notifying task stack change listeners (in millis)
    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 1000;
    static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
    // Necessary ApplicationInfo flags to mark an app as persistent
    private static final int PERSISTENT_MASK =