Adding a utility class to listen for app launches

Change-Id: I62e82a6e04b7101773d98c6e7aec574facd053fe
This commit is contained in:
Sunny Goyal 2019-03-26 15:03:57 -07:00
parent 4397d8d5ea
commit 369212aed9
9 changed files with 107 additions and 24 deletions

View File

@ -70,6 +70,7 @@
<string name="instant_app_resolver_class" translatable="false"></string>
<string name="main_process_initializer_class" translatable="false"></string>
<string name="system_shortcut_factory_class" translatable="false"></string>
<string name="app_launch_tracker_class" translatable="false"></string>
<!-- Package name of the default wallpaper picker. -->
<string name="wallpaper_picker_package" translatable="false"></string>

View File

@ -16,6 +16,8 @@
package com.android.launcher3;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_SEARCH;
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Intent;
@ -32,10 +34,13 @@ import android.widget.Toast;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import androidx.annotation.Nullable;
/**
* Extension of BaseActivity allowing support for drag-n-drop
*/
@ -148,7 +153,8 @@ public abstract class BaseDraggingActivity extends BaseActivity
public abstract ActivityOptions getActivityLaunchOptions(View v);
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
@Nullable String sourceContainer) {
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
@ -169,13 +175,17 @@ public abstract class BaseDraggingActivity extends BaseActivity
&& !((ShortcutInfo) item).isPromise();
if (isShortcut) {
// Shortcuts need some special checks due to legacy reasons.
startShortcutIntentSafely(intent, optsBundle, item);
startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
Process.myUserHandle(), sourceContainer);
} else {
LauncherAppsCompat.getInstance(this).startActivityForProfile(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
sourceContainer);
}
getUserEventDispatcher().logAppLaunch(v, intent);
getStatsLogManager().logAppLaunch(v, intent);
@ -187,7 +197,8 @@ public abstract class BaseDraggingActivity extends BaseActivity
return false;
}
private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
@Nullable String sourceContainer) {
try {
StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
try {
@ -202,6 +213,8 @@ public abstract class BaseDraggingActivity extends BaseActivity
String packageName = intent.getPackage();
DeepShortcutManager.getInstance(this).startShortcut(
packageName, id, intent.getSourceBounds(), optsBundle, info.user);
AppLaunchTracker.INSTANCE.get(this).onStartShortcut(packageName, id, info.user,
sourceContainer);
} else {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);

View File

@ -97,6 +97,7 @@ import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.popup.PopupContainerWithArrow;
@ -694,7 +695,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startActivitySafely(v, intent, null);
startActivitySafely(v, intent, null, null);
} else {
// TODO: Show a snack bar with link to settings
Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@ -798,6 +799,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
getUserEventDispatcher().startSession();
UiFactory.onLauncherStateOrResumeChanged(this);
AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
}
}
@ -1652,8 +1654,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
}
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
boolean success = super.startActivitySafely(v, intent, item);
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
@Nullable String sourceContainer) {
boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
if (success && v instanceof BubbleTextView) {
// This is set to the view that launched the activity that navigated the user away
// from launcher. Since there is no callback for when the activity has finished

View File

@ -32,6 +32,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageManagerHelper;
@ -263,12 +264,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
case VIEW_TYPE_SEARCH_MARKET:
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
parent, false);
searchMarketView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLauncher.startActivitySafely(v, mMarketSearchIntent, null);
}
});
searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely(
v, mMarketSearchIntent, null, AppLaunchTracker.CONTAINER_SEARCH));
return new ViewHolder(searchMarketView);
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(

View File

@ -28,6 +28,7 @@ import android.widget.TextView.OnEditorActionListener;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
@ -111,7 +112,8 @@ public class AllAppsSearchBarController
return false;
}
return mLauncher.startActivitySafely(v,
PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null);
PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null,
AppLaunchTracker.CONTAINER_SEARCH);
}
@Override

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2019 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.model;
import static com.android.launcher3.util.ResourceBasedOverride.Overrides.getObject;
import android.content.ComponentName;
import android.os.UserHandle;
import com.android.launcher3.R;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import androidx.annotation.Nullable;
/**
* Callback for receiving various app launch events
*/
public class AppLaunchTracker implements ResourceBasedOverride {
/**
* Derived from LauncherEvent proto.
* TODO: Use proper descriptive constants
*/
public static final String CONTAINER_DEFAULT = Integer.toString(ContainerType.WORKSPACE);
public static final String CONTAINER_ALL_APPS = Integer.toString(ContainerType.ALLAPPS);
public static final String CONTAINER_PREDICTIONS = Integer.toString(ContainerType.PREDICTION);
public static final String CONTAINER_SEARCH = Integer.toString(ContainerType.SEARCHRESULT);
public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
new MainThreadInitializedObject<>(c ->
getObject(AppLaunchTracker.class, c, R.string.app_launch_tracker_class));
public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
@Nullable String container) { }
public void onStartApp(ComponentName componentName, UserHandle user,
@Nullable String container) { }
public void onReturnedToHome() { }
}

View File

@ -195,7 +195,7 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
return view -> {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
itemInfo.getTargetComponent().getPackageName());
activity.startActivitySafely(view, intent, itemInfo);
activity.startActivitySafely(view, intent, itemInfo, null);
AbstractFloatingView.closeAllOpenViews(activity);
};
}

View File

@ -22,6 +22,8 @@ import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_DEFAULT;
import android.app.AlertDialog;
import android.content.Intent;
@ -48,6 +50,8 @@ import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetAddFlowHandler;
import androidx.annotation.Nullable;
/**
* Class for handling clicks on workspace and all-apps items
*/
@ -56,9 +60,13 @@ public class ItemClickHandler {
/**
* Instance used for click handling on items
*/
public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
public static final OnClickListener INSTANCE = getInstance(null);
private static void onClick(View v) {
public static final OnClickListener getInstance(String sourceContainer) {
return v -> onClick(v, sourceContainer);
}
private static void onClick(View v, String sourceContainer) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) {
@ -72,13 +80,14 @@ public class ItemClickHandler {
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
onClickAppShortcut(v, (ShortcutInfo) tag, launcher);
onClickAppShortcut(v, (ShortcutInfo) tag, launcher, sourceContainer);
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
} else if (tag instanceof AppInfo) {
startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
} else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
@ -154,7 +163,7 @@ public class ItemClickHandler {
private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
ItemInfo item = (ItemInfo) v.getTag();
Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
launcher.startActivitySafely(v, intent, item);
launcher.startActivitySafely(v, intent, item, null);
}
/**
@ -162,7 +171,8 @@ public class ItemClickHandler {
*
* @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
*/
public static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
public static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher,
@Nullable String sourceContainer) {
if (shortcut.isDisabled()) {
final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
if ((disabledFlags &
@ -201,10 +211,11 @@ public class ItemClickHandler {
}
// Start activities
startAppShortcutOrInfoActivity(v, shortcut, launcher);
startAppShortcutOrInfoActivity(v, shortcut, launcher, sourceContainer);
}
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
@Nullable String sourceContainer) {
Intent intent;
if (item instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
@ -227,6 +238,6 @@ public class ItemClickHandler {
intent.setPackage(null);
}
}
launcher.startActivitySafely(v, intent, item);
launcher.startActivitySafely(v, intent, item, sourceContainer);
}
}

View File

@ -217,7 +217,7 @@ public class OptionsPopupView extends ArrowPopup
if (!TextUtils.isEmpty(pickerPackage)) {
intent.setPackage(pickerPackage);
}
return launcher.startActivitySafely(v, intent, null);
return launcher.startActivitySafely(v, intent, null, null);
}
public static class OptionItem {