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

Commit a1f96b90 authored by Hyunyoung Song's avatar Hyunyoung Song Committed by Android (Google) Code Review
Browse files

Merge "Using StatsLog for notifyingAppPredictor"

parents a6f9cc02 35376a35
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ message Application {
// Legacy shortcuts and shortcuts handled by ShortcutManager
message Shortcut {
  optional string shortcut_name = 1;
  optional string shortcut_id = 2;
}

// AppWidgets handled by AppWidgetManager
+144 −88
Original line number Diff line number Diff line
@@ -16,6 +16,12 @@
package com.android.launcher3.appprediction;

import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;

import android.annotation.TargetApi;
@@ -31,29 +37,38 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.AnyThread;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;

import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.FolderContainer;
import com.android.launcher3.logger.LauncherAtom.HotseatContainer;
import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
import com.android.launcher3.logging.StatsLogManager.EventEnum;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.systemui.plugins.AppLaunchEventsPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.launcher3.pm.UserCache;
import com.android.quickstep.logging.StatsLogCompatManager;
import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Predicate;

/**
 * Subclass of app tracker which publishes the data to the prediction engine and gets back results.
 */
@TargetApi(Build.VERSION_CODES.Q)
public class PredictionAppTracker extends AppLaunchTracker
        implements PluginListener<AppLaunchEventsPlugin> {
public class PredictionAppTracker extends AppLaunchTracker implements StatsLogConsumer {

    private static final String TAG = "PredictionAppTracker";
    private static final boolean DBG = false;
@@ -65,7 +80,6 @@ public class PredictionAppTracker extends AppLaunchTracker

    protected final Context mContext;
    private final Handler mMessageHandler;
    private final List<AppLaunchEventsPlugin> mAppLaunchEventsPluginsList;

    // Accessed only on worker thread
    private AppPredictor mHomeAppPredictor;
@@ -76,10 +90,6 @@ public class PredictionAppTracker extends AppLaunchTracker
        InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);

        mMessageHandler.sendEmptyMessage(MSG_INIT);

        mAppLaunchEventsPluginsList = new ArrayList<>();
        PluginManagerWrapper.INSTANCE.get(context)
                .addPluginListener(this, AppLaunchEventsPlugin.class, true);
    }

    @UiThread
@@ -96,6 +106,7 @@ public class PredictionAppTracker extends AppLaunchTracker
            mHomeAppPredictor.destroy();
            mHomeAppPredictor = null;
        }
        StatsLogCompatManager.LOGS_CONSUMER.remove(this);
    }

    @WorkerThread
@@ -137,6 +148,7 @@ public class PredictionAppTracker extends AppLaunchTracker
                // Initialize the clients
                int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns;
                mHomeAppPredictor = createPredictor(Client.HOME, count);
                StatsLogCompatManager.LOGS_CONSUMER.add(this);
                return true;
            }
            case MSG_DESTROY: {
@@ -168,98 +180,142 @@ public class PredictionAppTracker extends AppLaunchTracker
        if (DBG) {
            Log.d(TAG, String.format("Sent immediate message to update %s", client));
        }

        // Relay onReturnedToHome to every plugin.
        mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome);
    }

    @Override
    @UiThread
    public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
                                String container) {
        // TODO: Use the full shortcut info
        AppTarget target = new AppTarget.Builder(
                new AppTargetId("shortcut:" + shortcutId), packageName, user)
                .setClassName(shortcutId)
    @AnyThread
    private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId) {
        AppTarget target = toAppTarget(atomInfo);
        if (target != null) {
            AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
                    .setLaunchLocation(getContainer(atomInfo))
                    .build();

        sendLaunch(target, container);

        // Relay onStartShortcut info to every connected plugin.
        mAppLaunchEventsPluginsList
                .forEach(plugin -> plugin.onStartShortcut(
                        packageName,
                        shortcutId,
                        user,
                        container != null ? container : CONTAINER_DEFAULT)
        );

            Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
        }
    }

    @Override
    @UiThread
    public void onStartApp(ComponentName cn, UserHandle user, String container) {
        if (cn != null) {
            AppTarget target = new AppTarget.Builder(
                    new AppTargetId("app:" + cn), cn.getPackageName(), user)
                    .setClassName(cn.getClassName())
                    .build();
            sendLaunch(target, container);

            // Relay onStartApp to every connected plugin.
            mAppLaunchEventsPluginsList
                    .forEach(plugin -> plugin.onStartApp(
                            cn,
                            user,
                            container != null ? container : CONTAINER_DEFAULT)
            );
    public void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
        if (event == LAUNCHER_APP_LAUNCH_TAP
                || event == LAUNCHER_TASK_LAUNCH_SWIPE_DOWN
                || event == LAUNCHER_TASK_LAUNCH_TAP
                || event == LAUNCHER_QUICKSWITCH_RIGHT
                || event == LAUNCHER_QUICKSWITCH_LEFT) {
            sendEvent(atomInfo, AppTargetEvent.ACTION_LAUNCH);
        } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) {
            sendEvent(atomInfo, AppTargetEvent.ACTION_DISMISS);
        }
    }

    @Override
    @UiThread
    public void onDismissApp(ComponentName cn, UserHandle user, String container) {
        if (cn == null) return;
        AppTarget target = new AppTarget.Builder(
                new AppTargetId("app: " + cn), cn.getPackageName(), user)
    @Nullable
    private AppTarget toAppTarget(LauncherAtom.ItemInfo info) {
        UserHandle userHandle = Process.myUserHandle();
        if (info.getIsWork()) {
            userHandle = UserCache.INSTANCE.get(mContext).getUserProfiles().stream()
                    .filter(((Predicate<UserHandle>) userHandle::equals).negate())
                    .findAny()
                    .orElse(null);
        }
        if (userHandle == null) {
            return null;
        }
        ComponentName cn = null;
        String id = null;

        switch (info.getItemCase()) {
            case APPLICATION: {
                LauncherAtom.Application app = info.getApplication();
                if ((cn = parseNullable(app.getComponentName())) != null) {
                    id = "app:" + cn.getPackageName();
                }
                break;
            }
            case SHORTCUT: {
                LauncherAtom.Shortcut si = info.getShortcut();
                if (!TextUtils.isEmpty(si.getShortcutId())
                        && (cn = parseNullable(si.getShortcutName())) != null) {
                    id = "shortcut:" + si.getShortcutId();
                }
                break;
            }
            case WIDGET: {
                LauncherAtom.Widget widget = info.getWidget();
                if ((cn = parseNullable(widget.getComponentName())) != null) {
                    id = "widget:" + cn.getPackageName();
                }
                break;
            }
            case TASK: {
                LauncherAtom.Task task = info.getTask();
                if ((cn = parseNullable(task.getComponentName())) != null) {
                    id = "app:" + cn.getPackageName();
                }
                break;
            }
            case FOLDER_ICON: {
                id = "folder:" + SystemClock.uptimeMillis();
                cn = new ComponentName(mContext.getPackageName(), "#folder");
            }
        }
        if (id != null && cn != null) {
            return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
                    .setClassName(cn.getClassName())
                    .build();
        sendDismiss(target, container);

        // Relay onDismissApp to every connected plugin.
        mAppLaunchEventsPluginsList
                .forEach(plugin -> plugin.onDismissApp(
                        cn,
                        user,
                        container != null ? container : CONTAINER_DEFAULT)
        );
        }

    @UiThread
    private void sendEvent(AppTarget target, String container, int eventId) {
        AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
                .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
                .build();
        Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
        return null;
    }

    @UiThread
    private void sendLaunch(AppTarget target, String container) {
        sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH);
    private String getContainer(LauncherAtom.ItemInfo info) {
        ContainerInfo ci = info.getContainerInfo();
        switch (ci.getContainerCase()) {
            case WORKSPACE: {
                // In case the item type is not widgets, the spaceX and spanY default to 1.
                int spanX = info.getWidget().getSpanX();
                int spanY = info.getWidget().getSpanY();
                return getWorkspaceContainerString(ci.getWorkspace(), spanX, spanY);
            }
            case HOTSEAT: {
                return getHotseatContainerString(ci.getHotseat());
            }
            case TASK_SWITCHER_CONTAINER: {
                return "task-switcher";
            }
            case ALL_APPS_CONTAINER: {
                return "all-apps";
            }
            case SEARCH_RESULT_CONTAINER: {
                return "search-results";
            }
            case PREDICTED_HOTSEAT_CONTAINER: {
                return "predictions/hotseat";
            }
            case PREDICTION_CONTAINER: {
                return "predictions";
            }
            case FOLDER: {
                FolderContainer fc = ci.getFolder();
                switch (fc.getParentContainerCase()) {
                    case WORKSPACE:
                        return "folder/" + getWorkspaceContainerString(fc.getWorkspace(), 1, 1);
                    case HOTSEAT:
                        return "folder/" + getHotseatContainerString(fc.getHotseat());
                }
                return "folder";
            }
        }
        return "";
    }

    @UiThread
    private void sendDismiss(AppTarget target, String container) {
        sendEvent(target, container, AppTargetEvent.ACTION_DISMISS);
    private static String getWorkspaceContainerString(WorkspaceContainer wc, int spanX, int spanY) {
        return String.format(Locale.ENGLISH, "workspace/%d/[%d,%d]/[%d,%d]",
                wc.getPageIndex(), wc.getGridX(), wc.getGridY(), spanX, spanY);
    }

    @Override
    public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) {
        mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin);
    private static String getHotseatContainerString(HotseatContainer hc) {
        return String.format(Locale.ENGLISH, "hotseat/%d", hc.getIndex());
    }

    @Override
    public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) {
        mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin);
    private static ComponentName parseNullable(String componentNameString) {
        return TextUtils.isEmpty(componentNameString)
                ? null : ComponentName.unflattenFromString(componentNameString);
    }
}
+1 −5
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -93,9 +92,6 @@ public class PredictionRowView extends LinearLayout implements
    private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
            (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;

    private static final OnClickListener PREDICTION_CLICK_LISTENER =
            ItemClickHandler.getInstance(AppLaunchTracker.CONTAINER_PREDICTIONS);

    private final Launcher mLauncher;
    private final PredictionUiStateManager mPredictionUiStateManager;
    private int mNumPredictedAppsPerRow;
@@ -246,7 +242,7 @@ public class PredictionRowView extends LinearLayout implements
            while (getChildCount() < mNumPredictedAppsPerRow) {
                BubbleTextView icon = (BubbleTextView) inflater.inflate(
                        R.layout.all_apps_icon, this, false);
                icon.setOnClickListener(PREDICTION_CLICK_LISTENER);
                icon.setOnClickListener(ItemClickHandler.INSTANCE);
                icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
                icon.setLongPressTimeoutFactor(1f);
                icon.setOnFocusChangeListener(mFocusHelper);
+2 −5
Original line number Diff line number Diff line
@@ -34,8 +34,6 @@ import android.os.Bundle;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;

import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -129,12 +127,11 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
    }

    @Override
    public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
            @Nullable String sourceContainer) {
    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
        if (mHotseatPredictionController != null) {
            mHotseatPredictionController.setPauseUIUpdate(true);
        }
        return super.startActivitySafely(v, intent, item, sourceContainer);
        return super.startActivitySafely(v, intent, item);
    }

    @Override
+30 −23
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowInsets;
import android.view.animation.Interpolator;

import androidx.annotation.Nullable;
import androidx.annotation.UiThread;

import com.android.launcher3.AbstractFloatingView;
@@ -69,6 +70,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -874,22 +876,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
        animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
    }

    private void doLogGesture(GestureEndTarget endTarget) {
        DeviceProfile dp = mDp;
        if (dp == null || mDownPos == null) {
            // We probably never received an animation controller, skip logging.
            return;
        }

        int pageIndex = endTarget == LAST_TASK
                ? LOG_NO_OP_PAGE_INDEX
                : mRecentsView.getNextPage();
        UserEventDispatcher.newInstance(mContext).logStateChangeAction(
                mLogAction, mLogDirection,
                (int) mDownPos.x, (int) mDownPos.y,
                ContainerType.NAVBAR, ContainerType.APP,
                endTarget.containerType,
                pageIndex);
    private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
        StatsLogManager.EventEnum event;
        switch (endTarget) {
            case HOME:
@@ -907,10 +894,29 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
            default:
                event = IGNORE;
        }
        StatsLogManager.newInstance(mContext).logger()
        StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
                .withSrcState(LAUNCHER_STATE_BACKGROUND)
                .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType))
                .log(event);
                .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType));
        if (targetTask != null) {
            logger.withItemInfo(targetTask.getItemInfo());
        }
        logger.log(event);


        DeviceProfile dp = mDp;
        if (dp == null || mDownPos == null) {
            // We probably never received an animation controller, skip logging.
            return;
        }
        int pageIndex = endTarget == LAST_TASK
                ? LOG_NO_OP_PAGE_INDEX
                : mRecentsView.getNextPage();
        UserEventDispatcher.newInstance(mContext).logStateChangeAction(
                mLogAction, mLogDirection,
                (int) mDownPos.x, (int) mDownPos.y,
                ContainerType.NAVBAR, ContainerType.APP,
                endTarget.containerType,
                pageIndex);
    }

    /** Animates to the given progress, where 0 is the current app and 1 is overview. */
@@ -1115,7 +1121,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
    private void resumeLastTask() {
        mRecentsAnimationController.finish(false /* toRecents */, null);
        ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
        doLogGesture(LAST_TASK);
        doLogGesture(LAST_TASK, null);
        reset();
    }

@@ -1130,6 +1136,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte

    @UiThread
    private void startNewTaskInternal() {
        TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
        startNewTask(success -> {
            if (!success) {
                reset();
@@ -1138,7 +1145,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
                endLauncherTransitionController();
                updateSysUiFlags(1 /* windowProgress == overview */);
            }
            doLogGesture(NEW_TASK);
            doLogGesture(NEW_TASK, taskToLaunch);
        });
    }

@@ -1284,7 +1291,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
        }
        ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
        doLogGesture(HOME);
        doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
    }

    protected abstract void finishRecentsControllerToHome(Runnable callback);
@@ -1299,7 +1306,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
        mRecentsView.onSwipeUpAnimationSuccess();

        SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
        doLogGesture(RECENTS);
        doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
        reset();
    }

Loading