ub-launcher3-qt-qpr1-dev @ build 5818303

Test: presubmit setup on source branch

Bug: 112282235  Starting an app from Launcher sometimes takes > 10 sec
Bug: 125027540  Apps to be searchable in different languages
Bug: 132336512  Notification dots on pinned shortcuts
Bug: 135218095  Disable unnecessary services
Bug: 136277517  Widgets often don’t support scroll direction up/down, left/right and use forward/backward instead
Bug: 137777105  Make clearcut launcher logging feature parity with westworld logging
Bug: 137952354  OnResume() and onStop logging doesn't reflect the real source id for -1 screen
Bug: 137953006  Swiping/fling up from workspace logged as hotseat
Bug: 138273985  The pause app message takes more than 30 seconds to display after restarting the device.
Bug: 138683199  Pass the captured snapshot data to Launcher when recents animation is cancelled
Bug: 138729456  [Flaky test] java.lang.AssertionError: http://go/tapl : Can't find an object with selector: BySelector [CLASS='\Qandroid.widget.TextView\E', PKG='\Qcom.google.android.apps.nexuslauncher\E', TEXT='\QShortcut 3\E'] (visible state: Workspace)
Bug: 138743758  [Flaky test] java.lang.AssertionError: App still doesn't have Pause action: com.google.android.calculator
Bug: 138743792  [Flaky test] java.lang.AssertionError: ensureScrollable didn't add Chrome app
Bug: 138964490  Investigate if ToggleableFlags can be connected with DeviceConfig
Bug: 139416391  CTS tests crash when app_prediction service is disabled
Bug: 139498375  Flake:     java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission
Bug: 139540363  Launcher goes in crash loop

Change-Id: Iaba4ed67e9fe4f8f5858324201e4c4e8711f59fa
This commit is contained in:
Hyunyoung Song
2019-08-21 02:25:20 -07:00
70 changed files with 611 additions and 262 deletions
+1
View File
@@ -43,6 +43,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!--
@@ -64,7 +64,7 @@ public final class LandscapeStatesTouchController extends PortraitStatesTouchCon
}
@Override
protected int getLogContainerTypeForNormalState() {
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
return LauncherLogProto.ContainerType.WORKSPACE;
}
}
@@ -25,6 +25,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.notification.NotificationKeyData;
import java.util.Collections;
import java.util.List;
@@ -48,10 +49,6 @@ public class DeepShortcutManager {
private DeepShortcutManager(Context context) {
}
public static boolean supportsShortcuts(ItemInfo info) {
return false;
}
public boolean wasLastCallSuccess() {
return false;
}
@@ -36,12 +36,16 @@ import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapRenderer;
@@ -57,8 +61,6 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import androidx.annotation.NonNull;
public abstract class BaseIconCache {
private static final String TAG = "BaseIconCache";
@@ -84,6 +86,7 @@ public abstract class BaseIconCache {
protected int mIconDpi;
protected IconDB mIconDb;
protected LocaleList mLocaleList = LocaleList.getEmptyLocaleList();
protected String mSystemState = "";
private final String mDbFileName;
@@ -227,12 +230,12 @@ public abstract class BaseIconCache {
/**
* Refreshes the system state definition used to check the validity of the cache. It
* incorporates all the properties that can affect the cache like locale and system-version.
* incorporates all the properties that can affect the cache like the list of enabled locale
* and system-version.
*/
private void updateSystemState() {
final String locale =
mContext.getResources().getConfiguration().getLocales().toLanguageTags();
mSystemState = locale + "," + Build.VERSION.SDK_INT;
mLocaleList = mContext.getResources().getConfiguration().getLocales();
mSystemState = mLocaleList.toLanguageTags() + "," + Build.VERSION.SDK_INT;
}
protected String getIconSystemState(String packageName) {
@@ -269,7 +272,7 @@ public abstract class BaseIconCache {
mCache.put(key, entry);
ContentValues values = newContentValues(entry, entry.title.toString(),
componentName.getPackageName());
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
addIconToDB(values, componentName, info, userSerial);
}
@@ -445,7 +448,7 @@ public abstract class BaseIconCache {
// Add the icon in the DB here, since these do not get written during
// package updates.
ContentValues values = newContentValues(
iconInfo, entry.title.toString(), packageName);
iconInfo, entry.title.toString(), packageName, null);
addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
} catch (NameNotFoundException e) {
@@ -504,23 +507,35 @@ public abstract class BaseIconCache {
return false;
}
static final class IconDB extends SQLiteCacheHelper {
private final static int RELEASE_VERSION = 26;
/**
* Returns a cursor for an arbitrary query to the cache db
*/
public synchronized Cursor queryCacheDb(String[] columns, String selection,
String[] selectionArgs) {
return mIconDb.query(columns, selection, selectionArgs);
}
public final static String TABLE_NAME = "icons";
public final static String COLUMN_ROWID = "rowid";
public final static String COLUMN_COMPONENT = "componentName";
public final static String COLUMN_USER = "profileId";
public final static String COLUMN_LAST_UPDATED = "lastUpdated";
public final static String COLUMN_VERSION = "version";
public final static String COLUMN_ICON = "icon";
public final static String COLUMN_ICON_COLOR = "icon_color";
public final static String COLUMN_LABEL = "label";
public final static String COLUMN_SYSTEM_STATE = "system_state";
/**
* Cache class to store the actual entries on disk
*/
public static final class IconDB extends SQLiteCacheHelper {
private static final int RELEASE_VERSION = 27;
public final static String[] COLUMNS_HIGH_RES = new String[] {
public static final String TABLE_NAME = "icons";
public static final String COLUMN_ROWID = "rowid";
public static final String COLUMN_COMPONENT = "componentName";
public static final String COLUMN_USER = "profileId";
public static final String COLUMN_LAST_UPDATED = "lastUpdated";
public static final String COLUMN_VERSION = "version";
public static final String COLUMN_ICON = "icon";
public static final String COLUMN_ICON_COLOR = "icon_color";
public static final String COLUMN_LABEL = "label";
public static final String COLUMN_SYSTEM_STATE = "system_state";
public static final String COLUMN_KEYWORDS = "keywords";
public static final String[] COLUMNS_HIGH_RES = new String[] {
IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON };
public final static String[] COLUMNS_LOW_RES = new String[] {
public static final String[] COLUMNS_LOW_RES = new String[] {
IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL };
public IconDB(Context context, String dbFileName, int iconPixelSize) {
@@ -529,21 +544,23 @@ public abstract class BaseIconCache {
@Override
protected void onCreateTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
COLUMN_COMPONENT + " TEXT NOT NULL, " +
COLUMN_USER + " INTEGER NOT NULL, " +
COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_ICON + " BLOB, " +
COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_LABEL + " TEXT, " +
COLUMN_SYSTEM_STATE + " TEXT, " +
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
");");
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+ COLUMN_COMPONENT + " TEXT NOT NULL, "
+ COLUMN_USER + " INTEGER NOT NULL, "
+ COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_ICON + " BLOB, "
+ COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, "
+ COLUMN_LABEL + " TEXT, "
+ COLUMN_SYSTEM_STATE + " TEXT, "
+ COLUMN_KEYWORDS + " TEXT, "
+ "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") "
+ ");");
}
}
private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, String packageName) {
private ContentValues newContentValues(BitmapInfo bitmapInfo, String label,
String packageName, @Nullable String keywords) {
ContentValues values = new ContentValues();
values.put(IconDB.COLUMN_ICON,
bitmapInfo.isLowRes() ? null : GraphicsUtils.flattenBitmap(bitmapInfo.icon));
@@ -551,7 +568,7 @@ public abstract class BaseIconCache {
values.put(IconDB.COLUMN_LABEL, label);
values.put(IconDB.COLUMN_SYSTEM_STATE, getIconSystemState(packageName));
values.put(IconDB.COLUMN_KEYWORDS, keywords);
return values;
}
@@ -17,8 +17,11 @@ package com.android.launcher3.icons.cache;
import android.content.ComponentName;
import android.content.Context;
import android.os.LocaleList;
import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.launcher3.icons.BitmapInfo;
public interface CachingLogic<T> {
@@ -30,4 +33,12 @@ public interface CachingLogic<T> {
CharSequence getLabel(T object);
void loadIcon(Context context, T object, BitmapInfo target);
/**
* Provides a option list of keywords to associate with this object
*/
@Nullable
default String getKeywords(T object, LocaleList localeList) {
return null;
}
}
@@ -75,7 +75,7 @@ public class OverviewToAllAppsTouchController extends PortraitStatesTouchControl
}
@Override
protected int getLogContainerTypeForNormalState() {
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
return LauncherLogProto.ContainerType.WORKSPACE;
}
}
@@ -154,7 +154,7 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
}
@Override
protected int getLogContainerTypeForNormalState() {
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
return LauncherLogProto.ContainerType.NAVBAR;
}
@@ -81,6 +81,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
});
factory.onRemoteAnimationReceived(null);
factory.createActivityController(RECENTS_LAUNCH_DURATION);
factory.setRecentsAttachedToAppWindow(true, false);
mActivity = activity;
mRecentsView = mActivity.getOverviewPanel();
return false;
@@ -99,6 +99,7 @@ public class OverviewCommandHelper {
@Override
protected void onTransitionComplete() {
// TODO(b/138729100) This doesn't execute first time launcher is run
if (mTriggeredFromAltTab) {
RecentsView rv = (RecentsView) mHelper.getVisibleRecentsView();
if (rv == null) {
@@ -16,6 +16,8 @@
package com.android.quickstep;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.graphics.Matrix;
import android.view.View;
@@ -47,8 +49,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
};
public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
c, R.string.task_overlay_factory_class));
forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
@@ -97,6 +97,7 @@ import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
@@ -1051,9 +1052,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (task != null) {
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
if (shouldLog) {
ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
onEndListener.logAction, Direction.UP, index,
TaskUtils.getLaunchComponentKeyForTask(task.key));
onEndListener.logAction, Direction.UP, index, componentKey);
mActivity.getStatsLogManager().logTaskDismiss(this, componentKey);
}
}
}
@@ -0,0 +1,32 @@
/*
* 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.uioverrides;
import android.provider.DeviceConfig;
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
public class TogglableFlag extends BaseTogglableFlag {
public TogglableFlag(String key, boolean defaultValue, String description) {
super(key, defaultValue, description);
}
@Override
public boolean getInitialValue(boolean value) {
return DeviceConfig.getBoolean("launcher", getKey(), value);
}
}
@@ -32,9 +32,11 @@ import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.util.Base64;
@@ -244,4 +246,9 @@ public class UiFactory extends RecentsUiFactory {
}
return new ScaleAndTranslation(1.1f, 0f, 0f);
}
public static Person[] getPersons(ShortcutInfo si) {
Person[] persons = si.getPersons();
return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
}
}
@@ -46,7 +46,7 @@ public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchContro
}
@Override
protected int getLogContainerTypeForNormalState() {
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
return LauncherLogProto.ContainerType.NAVBAR;
}
@@ -147,8 +147,8 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
}
@Override
protected int getLogContainerTypeForNormalState() {
return ContainerType.HOTSEAT;
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
}
private AnimatorSetBuilder getNormalToOverviewAnimation() {
@@ -16,15 +16,12 @@
package com.android.quickstep;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Process;
import android.os.RemoteException;
@@ -35,7 +32,6 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
@@ -52,7 +48,7 @@ public class RecentsModel extends TaskStackChangeListener {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(c -> new RecentsModel(c));
new MainThreadInitializedObject<>(RecentsModel::new);
private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
@@ -25,6 +25,7 @@ import static android.stats.launcher.nano.Launcher.ALLAPPS;
import static android.stats.launcher.nano.Launcher.HOME;
import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
import static android.stats.launcher.nano.Launcher.DISMISS_TASK;
import static android.stats.launcher.nano.Launcher.BACKGROUND;
import static android.stats.launcher.nano.Launcher.OVERVIEW;
@@ -74,6 +75,16 @@ public class StatsLogCompatManager extends StatsLogManager {
MessageNano.toByteArray(ext), true);
}
@Override
public void logTaskDismiss(View v, ComponentKey componentKey) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
int srcState = OVERVIEW;
fillInLauncherExtension(v, ext);
StatsLogCompat.write(DISMISS_TASK, srcState, BACKGROUND /* dstState */,
MessageNano.toByteArray(ext), true);
}
public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
+3
View File
@@ -1 +1,4 @@
vadimt@google.com
sunnygoyal@google.com
winsonc@google.com
hyunyoungs@google.com
+1
View File
@@ -72,6 +72,7 @@
<string name="system_shortcut_factory_class" translatable="false"></string>
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
<!-- Package name of the default wallpaper picker. -->
<string name="wallpaper_picker_package" translatable="false"></string>
@@ -1,6 +1,8 @@
package com.android.launcher3.config;
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
import com.android.launcher3.uioverrides.TogglableFlag;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -70,7 +72,7 @@ public final class FlagOverrideRule implements TestRule {
};
}
private void override(BaseFlags.TogglableFlag flag, boolean newValue) {
private void override(BaseTogglableFlag flag, boolean newValue) {
if (!ruleInProgress) {
throw new IllegalStateException(
"Rule isn't in progress. Did you remember to mark it with @Rule?");
@@ -93,7 +95,7 @@ public final class FlagOverrideRule implements TestRule {
private void applyAnnotation(FlagOverride flagOverride) {
boolean found = false;
for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
if (flag.getKey().equals(flagOverride.key())) {
override(flag, flagOverride.value());
found = true;
@@ -109,7 +111,7 @@ public final class FlagOverrideRule implements TestRule {
* Resets all flags to their default values.
*/
private void clearOverrides() {
for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
flag.setForTests(flag.getDefaultValue());
}
}
+5 -4
View File
@@ -1,16 +1,17 @@
package com.android.launcher3;
import android.content.Context;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.content.pm.LauncherActivityInfo;
import android.graphics.drawable.Drawable;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
public class IconProvider implements ResourceBasedOverride {
public static IconProvider newInstance(Context context) {
return Overrides.getObject(IconProvider.class, context, R.string.icon_provider_class);
}
public static MainThreadInitializedObject<IconProvider> INSTANCE =
forOverride(IconProvider.class, R.string.icon_provider_class);
public IconProvider() { }
+3
View File
@@ -22,6 +22,8 @@ import android.content.Intent;
import android.os.Process;
import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.launcher3.util.ContentWriter;
/**
@@ -134,6 +136,7 @@ public class ItemInfo {
return null;
}
@Nullable
public ComponentName getTargetComponent() {
Intent intent = getIntent();
if (intent != null) {
+15 -6
View File
@@ -127,6 +127,7 @@ import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -873,9 +874,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onStop();
}
getUserEventDispatcher().logActionCommand(Action.Command.STOP,
mStateManager.getState().containerType, -1);
logStopAndResume(Action.Command.STOP);
mAppWidgetHost.setListenIfResumed(false);
@@ -901,8 +900,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private void handleDeferredResume() {
if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
mStateManager.getState().containerType, -1);
logStopAndResume(Action.Command.RESUME);
getUserEventDispatcher().startSession();
UiFactory.onLauncherStateOrResumeChanged(this);
@@ -929,6 +927,17 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
}
private void logStopAndResume(int command) {
int containerType = mStateManager.getState().containerType;
if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
getUserEventDispatcher().logActionCommand(command,
containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
} else {
getUserEventDispatcher().logActionCommand(command, containerType, -1);
}
}
protected void onStateSet(LauncherState state) {
getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
if (mDeferredResumePending) {
@@ -2494,7 +2503,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
}
if (currentFocus.getTag() instanceof ItemInfo
&& DeepShortcutManager.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
&& ShortcutUtil.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
shortcutInfos.add(new KeyboardShortcutInfo(
getString(R.string.shortcuts_menu_with_notifications_description),
KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
@@ -19,14 +19,12 @@ package com.android.launcher3;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Process;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -46,7 +44,7 @@ public class LauncherAppState {
// We do not need any synchronization for this variable as its only written on UI thread.
private static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
new MainThreadInitializedObject<>((c) -> new LauncherAppState(c));
new MainThreadInitializedObject<>(LauncherAppState::new);
private final Context mContext;
private final LauncherModel mModel;
+26 -5
View File
@@ -1562,12 +1562,20 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
final boolean pagesFlipped = isPageOrderFlipped();
info.setScrollable(getPageCount() > 1);
if (getCurrentPage() < getPageCount() - 1) {
info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
: AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
info.addAction(pagesFlipped ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
: AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
info.addAction(mIsRtl ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT
: AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);
}
if (getCurrentPage() > 0) {
info.addAction(pagesFlipped ? AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
: AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
info.addAction(pagesFlipped ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
: AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.addAction(mIsRtl ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT
: AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);
}
// Accessibility-wise, PagedView doesn't support long click, so disabling it.
@@ -1607,8 +1615,21 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (pagesFlipped ? scrollRight() : scrollLeft()) {
return true;
}
} break;
case android.R.id.accessibilityActionPageRight: {
if (!mIsRtl) {
return scrollRight();
} else {
return scrollLeft();
}
}
case android.R.id.accessibilityActionPageLeft: {
if (!mIsRtl) {
return scrollLeft();
} else {
return scrollRight();
}
}
break;
}
return false;
}
+4
View File
@@ -21,6 +21,7 @@ import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.Person;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -109,6 +110,9 @@ public final class Utilities {
private static final Matrix sMatrix = new Matrix();
private static final Matrix sInverseMatrix = new Matrix();
public static final String[] EMPTY_STRING_ARRAY = new String[0];
public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
public static final boolean ATLEAST_P =
+14
View File
@@ -85,6 +85,7 @@ import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -420,6 +421,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
// Always enter the spring loaded mode
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Switching to SPRING_LOADED");
}
mLauncher.getStateManager().goToState(SPRING_LOADED);
}
@@ -1741,6 +1745,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
public void prepareAccessibilityDrop() { }
public void onDrop(final DragObject d, DragOptions options) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDrop");
}
mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
CellLayout dropTargetLayout = mDropToLayout;
@@ -2418,6 +2425,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
* to add an item to one of the workspace screens.
*/
private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "Workspace.onDropExternal");
}
if (d.dragInfo instanceof PendingAddShortcutInfo) {
WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo)
.activityInfo.createWorkspaceItemInfo();
@@ -3251,6 +3261,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
}
public boolean isOverlayShown() {
return mOverlayShown;
}
void moveToDefaultScreen() {
int page = DEFAULT_PAGE;
if (!workspaceInModalState() && getNextPage() != page) {
@@ -16,17 +16,23 @@
package com.android.launcher3;
import android.app.Person;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ContentWriter;
import java.util.Arrays;
/**
* Represents a launchable icon on the workspaces and in folders.
*/
@@ -82,11 +88,18 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
public int status;
/**
* A set of person's Id associated with the WorkspaceItemInfo, this is only used if the item
* represents a deep shortcut.
*/
@NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
/**
* The installation progress [0-100] of the package that this shortcut represents.
*/
private int mInstallProgress;
public WorkspaceItemInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
@@ -98,6 +111,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
iconResource = info.iconResource;
status = info.status;
mInstallProgress = info.mInstallProgress;
personKeys = info.personKeys.clone();
}
/** TODO: Remove this. It's only called by ApplicationInfo.makeWorkspaceItem. */
@@ -175,6 +189,10 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
}
disabledMessage = shortcutInfo.getDisabledMessage();
Person[] persons = UiFactory.getPersons(shortcutInfo);
personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
: Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
}
/** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
@@ -183,6 +201,11 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
}
@NonNull
public String[] getPersonKeys() {
return personKeys;
}
@Override
public ComponentName getTargetComponent() {
ComponentName cn = super.getTargetComponent();
@@ -40,6 +40,7 @@ import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -115,7 +116,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
// If the request came from keyboard, do not add custom shortcuts as that is already
// exposed as a direct shortcut
if (!fromKeyboard && DeepShortcutManager.supportsShortcuts(item)) {
if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) {
info.addAction(mActions.get(NotificationListener.getInstanceIfConnected() != null
? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
}
+17 -61
View File
@@ -18,20 +18,16 @@ package com.android.launcher3.config;
import static androidx.core.util.Preconditions.checkNotNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import androidx.annotation.GuardedBy;
import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.TogglableFlag;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
@@ -41,11 +37,9 @@ import java.util.TreeMap;
* Defines a set of flags used to control various launcher behaviors.
*
* <p>All the flags should be defined here with appropriate default values.
*
* <p>This class is kept package-private to prevent direct access.
*/
@Keep
abstract class BaseFlags {
public abstract class BaseFlags {
private static final Object sLock = new Object();
@GuardedBy("sLock")
@@ -112,11 +106,15 @@ abstract class BaseFlags {
"FAKE_LANDSCAPE_UI", false,
"Rotate launcher UI instead of using transposed layout");
public static final TogglableFlag APP_SEARCH_IMPROVEMENTS = new TogglableFlag(
"APP_SEARCH_IMPROVEMENTS", false,
"Adds localized title and keyword search and ranking");
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
synchronized (sLock) {
for (TogglableFlag flag : sFlags) {
for (BaseTogglableFlag flag : sFlags) {
flag.initialize(context);
}
}
@@ -132,27 +130,27 @@ abstract class BaseFlags {
SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>();
synchronized (sLock) {
for (TogglableFlag flag : sFlags) {
flagsByKey.put(flag.key, flag);
flagsByKey.put(((BaseTogglableFlag) flag).getKey(), flag);
}
}
return new ArrayList<>(flagsByKey.values());
}
public static class TogglableFlag {
public static abstract class BaseTogglableFlag {
private final String key;
private final boolean defaultValue;
private final String description;
private boolean currentValue;
TogglableFlag(
public BaseTogglableFlag(
String key,
boolean defaultValue,
String description) {
this.key = checkNotNull(key);
this.currentValue = this.defaultValue = defaultValue;
this.currentValue = this.defaultValue = getInitialValue(defaultValue);
this.description = checkNotNull(description);
synchronized (sLock) {
sFlags.add(this);
sFlags.add((TogglableFlag)this);
}
}
@@ -162,14 +160,16 @@ abstract class BaseFlags {
currentValue = value;
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public String getKey() {
return key;
}
void initialize(Context context) {
currentValue = getFromStorage(context, defaultValue);
}
protected abstract boolean getInitialValue(boolean value);
public void updateStorage(Context context, boolean value) {
SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
Context.MODE_PRIVATE).edit();
@@ -213,7 +213,7 @@ abstract class BaseFlags {
return true;
}
if (o instanceof TogglableFlag) {
TogglableFlag that = (TogglableFlag) o;
BaseTogglableFlag that = (BaseTogglableFlag) o;
return (this.key.equals(that.getKey()))
&& (this.defaultValue == that.getDefaultValue())
&& (this.description.equals(that.getDescription()));
@@ -233,48 +233,4 @@ abstract class BaseFlags {
return h$;
}
}
/**
* Stores the FeatureFlag's value in Settings.Global instead of our SharedPrefs.
* This is useful if we want to be able to control this flag from another process.
*/
public static final class ToggleableGlobalSettingsFlag extends TogglableFlag {
private ContentResolver contentResolver;
ToggleableGlobalSettingsFlag(String key, boolean defaultValue, String description) {
super(key, defaultValue, description);
}
@Override
public void initialize(Context context) {
contentResolver = context.getContentResolver();
contentResolver.registerContentObserver(Settings.Global.getUriFor(getKey()), true,
new ContentObserver(new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
superInitialize(context);
}});
superInitialize(context);
}
private void superInitialize(Context context) {
super.initialize(context);
}
@Override
public void updateStorage(Context context, boolean value) {
if (contentResolver == null) {
return;
}
Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
}
@Override
boolean getFromStorage(Context context, boolean defaultValue) {
if (contentResolver == null) {
return defaultValue;
}
return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
}
}
}
@@ -26,12 +26,13 @@ import android.view.MenuItem;
import android.widget.Toast;
import com.android.launcher3.R;
import com.android.launcher3.config.BaseFlags.TogglableFlag;
import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragment;
import androidx.preference.PreferenceGroup;
import androidx.preference.SwitchPreference;
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
import com.android.launcher3.uioverrides.TogglableFlag;
/**
* Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
@@ -62,7 +63,7 @@ public final class FlagTogglerPrefUi {
@Override
public boolean getBoolean(String key, boolean defaultValue) {
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
if (flag.getKey().equals(key)) {
return flag.getFromStorage(mContext, defaultValue);
}
@@ -83,7 +84,7 @@ public final class FlagTogglerPrefUi {
// flag with a different value than the default. That way, when we flip flags in
// future, engineers will pick up the new value immediately. To accomplish this, we use a
// custom preference data store.
for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
for (BaseTogglableFlag flag : FeatureFlags.getTogglableFlags()) {
SwitchPreference switchPreference = new SwitchPreference(mContext);
switchPreference.setKey(flag.getKey());
switchPreference.setDefaultValue(flag.getDefaultValue());
@@ -99,7 +100,7 @@ public final class FlagTogglerPrefUi {
/**
* Updates the summary to show the description and whether the flag overrides the default value.
*/
private void updateSummary(SwitchPreference switchPreference, TogglableFlag flag) {
private void updateSummary(SwitchPreference switchPreference, BaseTogglableFlag flag) {
String onWarning = flag.getDefaultValue() ? "" : "<b>OVERRIDDEN</b><br>";
String offWarning = flag.getDefaultValue() ? "<b>OVERRIDDEN</b><br>" : "";
switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
@@ -134,7 +135,7 @@ public final class FlagTogglerPrefUi {
}
}
private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
private boolean getFlagStateFromSharedPrefs(BaseTogglableFlag flag) {
return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
}
@@ -579,6 +579,9 @@ public class DragController implements DragDriver.EventListener, TouchController
}
private void drop(DropTarget dropTarget, Runnable flingAnimation) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragController.drop");
}
final int[] coordinates = mCoordinatesTemp;
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
@@ -17,10 +17,12 @@
package com.android.launcher3.dragndrop;
import android.content.Context;
import android.util.Log;
import android.view.DragEvent;
import android.view.MotionEvent;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.testing.TestProtocol;
/**
* Base class for driving a drag/drop operation.
@@ -52,6 +54,9 @@ public abstract class DragDriver {
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_UP:
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_UP");
}
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
break;
@@ -17,6 +17,7 @@
package com.android.launcher3.graphics;
import static com.android.launcher3.graphics.IconShape.getShapePath;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -31,6 +32,8 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
import androidx.annotation.UiThread;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
@@ -38,16 +41,13 @@ import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import androidx.annotation.UiThread;
/**
* Factory for creating new drawables.
*/
public class DrawableFactory implements ResourceBasedOverride {
public static final MainThreadInitializedObject<DrawableFactory> INSTANCE =
new MainThreadInitializedObject<>(c -> Overrides.getObject(DrawableFactory.class,
c.getApplicationContext(), R.string.drawable_factory_class));
forOverride(DrawableFactory.class, R.string.drawable_factory_class);
protected final UserHandle mMyUser = Process.myUserHandle();
protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
@@ -29,6 +29,8 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.launcher3.AppInfo;
import com.android.launcher3.IconProvider;
import com.android.launcher3.InvariantDeviceProfile;
@@ -36,8 +38,8 @@ import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
@@ -50,8 +52,6 @@ import com.android.launcher3.util.Preconditions;
import java.util.function.Supplier;
import androidx.annotation.NonNull;
/**
* Cache of application icons. Icons can be made from any thread.
*/
@@ -75,11 +75,11 @@ public class IconCache extends BaseIconCache {
super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(),
inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
mComponentWithLabelCachingLogic = new ComponentCachingLogic(context);
mLauncherActivityInfoCachingLogic = new LauncherActivtiyCachingLogic(this);
mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mUserManager = UserManagerCompat.getInstance(mContext);
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
mIconProvider = IconProvider.newInstance(context);
mIconProvider = IconProvider.INSTANCE.get(context);
}
@Override
@@ -20,14 +20,23 @@ import android.content.Context;
import android.content.pm.LauncherActivityInfo;
import android.os.UserHandle;
import com.android.launcher3.IconProvider;
import com.android.launcher3.R;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.util.ResourceBasedOverride;
public class LauncherActivtiyCachingLogic implements CachingLogic<LauncherActivityInfo> {
/**
* Caching logic for LauncherActivityInfo.
*/
public class LauncherActivityCachingLogic
implements CachingLogic<LauncherActivityInfo>, ResourceBasedOverride {
private final IconCache mCache;
public LauncherActivtiyCachingLogic(IconCache cache) {
mCache = cache;
/**
* Creates and returns a new instance
*/
public static LauncherActivityCachingLogic newInstance(Context context) {
return Overrides.getObject(LauncherActivityCachingLogic.class, context,
R.string.launcher_activity_logic_class);
}
@Override
@@ -49,8 +58,10 @@ public class LauncherActivtiyCachingLogic implements CachingLogic<LauncherActivi
public void loadIcon(Context context, LauncherActivityInfo object,
BitmapInfo target) {
LauncherIcons li = LauncherIcons.obtain(context);
li.createBadgedIconBitmap(mCache.getFullResIcon(object),
li.createBadgedIconBitmap(
IconProvider.INSTANCE.get(context)
.getIcon(object, li.mFillResIconDpi, true /* flattenDrawable */),
object.getUser(), object.getApplicationInfo().targetSdkVersion).applyTo(target);
li.recycle();
}
}
}
@@ -40,5 +40,6 @@ public class StatsLogManager implements ResourceBasedOverride {
public void logAppLaunch(View v, Intent intent) { }
public void logTaskLaunch(View v, ComponentKey key) { }
public void logTaskDismiss(View v, ComponentKey key) { }
public void verify() {} // TODO: should move into robo tests
}
@@ -187,6 +187,14 @@ public class UserEventDispatcher implements ResourceBasedOverride {
dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
}
public void logActionCommand(int command, int srcContainerType, int dstContainerType,
int pageIndex) {
Target srcTarget = newContainerTarget(srcContainerType);
srcTarget.pageIndex = pageIndex;
logActionCommand(command, srcTarget,
dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
}
public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
if (command == Action.Command.STOP) {
@@ -15,18 +15,18 @@
*/
package com.android.launcher3.model;
import static com.android.launcher3.util.ResourceBasedOverride.Overrides.getObject;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.content.ComponentName;
import android.os.UserHandle;
import androidx.annotation.Nullable;
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
*/
@@ -43,8 +43,7 @@ public class AppLaunchTracker implements ResourceBasedOverride {
public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
new MainThreadInitializedObject<>(c ->
getObject(AppLaunchTracker.class, c, R.string.app_launch_tracker_class));
forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
@Nullable String container) { }
@@ -49,8 +49,8 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
@@ -61,7 +61,7 @@ import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherActivtiyCachingLogic;
import com.android.launcher3.icons.LauncherActivityCachingLogic;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
@@ -196,7 +196,7 @@ public class LoaderTask implements Runnable {
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
new LauncherActivtiyCachingLogic(mApp.getIconCache()),
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
// Take a break
@@ -17,12 +17,18 @@
package com.android.launcher3.notification;
import android.app.Notification;
import android.app.Person;
import android.service.notification.StatusBarNotification;
import com.android.launcher3.Utilities;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* The key data associated with the notification, used to determine what to include
@@ -34,19 +40,25 @@ public class NotificationKeyData {
public final String notificationKey;
public final String shortcutId;
public int count;
@NonNull public final String[] personKeysFromNotification;
private NotificationKeyData(String notificationKey, String shortcutId, int count) {
private NotificationKeyData(String notificationKey, String shortcutId, int count,
String[] personKeysFromNotification) {
this.notificationKey = notificationKey;
this.shortcutId = shortcutId;
this.count = Math.max(1, count);
this.personKeysFromNotification = personKeysFromNotification;
}
public static NotificationKeyData fromNotification(StatusBarNotification sbn) {
Notification notif = sbn.getNotification();
return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number);
return new NotificationKeyData(sbn.getKey(), notif.getShortcutId(), notif.number,
extractPersonKeyOnly(notif.extras.getParcelableArrayList(
Notification.EXTRA_PEOPLE_LIST)));
}
public static List<String> extractKeysOnly(@NonNull List<NotificationKeyData> notificationKeys) {
public static List<String> extractKeysOnly(
@NonNull List<NotificationKeyData> notificationKeys) {
List<String> keysOnly = new ArrayList<>(notificationKeys.size());
for (NotificationKeyData notificationKeyData : notificationKeys) {
keysOnly.add(notificationKeyData.notificationKey);
@@ -54,6 +66,13 @@ public class NotificationKeyData {
return keysOnly;
}
private static String[] extractPersonKeyOnly(@Nullable ArrayList<Person> people) {
if (people == null || people.isEmpty()) {
return Utilities.EMPTY_STRING_ARRAY;
}
return people.stream().map(Person::getKey).sorted().toArray(String[]::new);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NotificationKeyData)) {
@@ -72,6 +72,7 @@ import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
@@ -201,7 +202,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
return null;
}
ItemInfo itemInfo = (ItemInfo) icon.getTag();
if (!DeepShortcutManager.supportsShortcuts(itemInfo)) {
if (!ShortcutUtil.supportsShortcuts(itemInfo)) {
return null;
}
@@ -29,17 +29,22 @@ import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.widget.WidgetListRowEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Provides data for the popup menu that appears after long-clicking on apps.
@@ -129,7 +134,8 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
for (PackageUserKey packageUserKey : mPackageUserToDotInfos.keySet()) {
DotInfo prevDot = updatedDots.get(packageUserKey);
DotInfo newDot = mPackageUserToDotInfos.get(packageUserKey);
if (prevDot == null) {
if (prevDot == null
|| prevDot.getNotificationCount() != newDot.getNotificationCount()) {
updatedDots.put(packageUserKey, newDot);
} else {
// No need to update the dot if it already existed (no visual change).
@@ -155,7 +161,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
}
public int getShortcutCountForItem(ItemInfo info) {
if (!DeepShortcutManager.supportsShortcuts(info)) {
if (!ShortcutUtil.supportsDeepShortcuts(info)) {
return 0;
}
ComponentName component = info.getTargetComponent();
@@ -167,17 +173,26 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
return count == null ? 0 : count;
}
public DotInfo getDotInfoForItem(ItemInfo info) {
if (!DeepShortcutManager.supportsShortcuts(info)) {
public @Nullable DotInfo getDotInfoForItem(@NonNull ItemInfo info) {
if (!ShortcutUtil.supportsShortcuts(info)) {
return null;
}
return mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
DotInfo dotInfo = mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
if (dotInfo == null) {
return null;
}
List<NotificationKeyData> notifications = getNotificationsForItem(
info, dotInfo.getNotificationKeys());
if (notifications.isEmpty()) {
return null;
}
return dotInfo;
}
public @NonNull List<NotificationKeyData> getNotificationKeysForItem(ItemInfo info) {
DotInfo dotInfo = getDotInfoForItem(info);
return dotInfo == null ? Collections.EMPTY_LIST : dotInfo.getNotificationKeys();
return dotInfo == null ? Collections.EMPTY_LIST
: getNotificationsForItem(info, dotInfo.getNotificationKeys());
}
/** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -226,6 +241,27 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
return null;
}
/**
* Returns a list of notifications that are relevant to given ItemInfo.
*/
public static @NonNull List<NotificationKeyData> getNotificationsForItem(
@NonNull ItemInfo info, @NonNull List<NotificationKeyData> notifications) {
String shortcutId = ShortcutUtil.getShortcutIdIfPinnedShortcut(info);
if (shortcutId == null) {
return notifications;
}
String[] personKeys = ShortcutUtil.getPersonKeysIfPinnedShortcut(info);
return notifications.stream().filter((NotificationKeyData notification) -> {
if (notification.shortcutId != null) {
return notification.shortcutId.equals(shortcutId);
}
if (notification.personKeysFromNotification.length != 0) {
return Arrays.equals(notification.personKeysFromNotification, personKeys);
}
return false;
}).collect(Collectors.toList());
}
public interface PopupDataChangeListener {
PopupDataChangeListener INSTANCE = new PopupDataChangeListener() { };
@@ -29,11 +29,12 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
private static final String TAG = "RemoteActionShortcut";
private static final boolean DEBUG = false;
private static final boolean DEBUG = Utilities.IS_DEBUG_DEVICE;
private final RemoteAction mAction;
@@ -131,6 +131,7 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
@Override
public View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo) {
if (itemInfo.getTargetComponent() == null) return null;
final List<WidgetItem> widgets =
launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
@@ -15,6 +15,10 @@
*/
package com.android.launcher3.popup;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import androidx.annotation.NonNull;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -24,13 +28,10 @@ import com.android.launcher3.util.ResourceBasedOverride;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
public class SystemShortcutFactory implements ResourceBasedOverride {
public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
new MainThreadInitializedObject<>(c -> Overrides.getObject(
SystemShortcutFactory.class, c, R.string.system_shortcut_factory_class));
forOverride(SystemShortcutFactory.class, R.string.system_shortcut_factory_class);
/** Note that these are in order of priority. */
private final SystemShortcut[] mAllShortcuts;
@@ -112,6 +112,13 @@ public class TestInformationHandler implements ResourceBasedOverride {
}
break;
}
case TestProtocol.REQUEST_ALLOCATED_MEMORY: {
final Runtime runtime = Runtime.getRuntime();
response.putLong(TestProtocol.TEST_INFO_RESPONSE_FIELD,
runtime.totalMemory() - runtime.freeMemory());
break;
}
}
return response;
}
@@ -73,10 +73,12 @@ public final class TestProtocol {
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
public static final String REQUEST_ALLOCATED_MEMORY = "allocated-memory";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
}
@@ -69,6 +69,7 @@ public abstract class AbstractStateChangeTouchController
protected final SwipeDetector.Direction mSwipeDirection;
private boolean mNoIntercept;
private boolean mIsLogContainerSet;
protected int mStartContainerType;
protected LauncherState mStartState;
@@ -180,7 +181,7 @@ public abstract class AbstractStateChangeTouchController
/**
* Returns the container that the touch started from when leaving NORMAL state.
*/
protected abstract int getLogContainerTypeForNormalState();
protected abstract int getLogContainerTypeForNormalState(MotionEvent ev);
private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
@@ -231,13 +232,7 @@ public abstract class AbstractStateChangeTouchController
@Override
public void onDragStart(boolean start) {
mStartState = mLauncher.getStateManager().getState();
if (mStartState == ALL_APPS) {
mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
} else if (mStartState == NORMAL) {
mStartContainerType = getLogContainerTypeForNormalState();
} else if (mStartState == OVERVIEW){
mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
}
mIsLogContainerSet = false;
if (mCurrentAnimation == null) {
mFromState = mStartState;
mToState = null;
@@ -285,6 +280,21 @@ public abstract class AbstractStateChangeTouchController
return true;
}
@Override
public boolean onDrag(float displacement, MotionEvent ev) {
if (!mIsLogContainerSet) {
if (mStartState == ALL_APPS) {
mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
} else if (mStartState == NORMAL) {
mStartContainerType = getLogContainerTypeForNormalState(ev);
} else if (mStartState == OVERVIEW) {
mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
}
mIsLogContainerSet = true;
}
return onDrag(displacement);
}
protected void updateProgress(float fraction) {
mCurrentAnimation.setPlayFraction(fraction);
if (mAtomicComponentsController != null) {
@@ -18,12 +18,13 @@ package com.android.launcher3.util;
import android.content.Context;
import android.os.Looper;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import java.util.concurrent.ExecutionException;
import androidx.annotation.VisibleForTesting;
/**
* Utility class for defining singletons which are initiated on main thread.
*/
@@ -60,6 +61,14 @@ public class MainThreadInitializedObject<T> {
mValue = value;
}
/**
* Initializes a provider based on resource overrides
*/
public static <T extends ResourceBasedOverride> MainThreadInitializedObject<T> forOverride(
Class<T> clazz, int resourceId) {
return new MainThreadInitializedObject<>(c -> Overrides.getObject(clazz, c, resourceId));
}
public interface ObjectProvider<T> {
T get(Context context);
@@ -3,6 +3,8 @@ package com.android.launcher3.util;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import androidx.annotation.Nullable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -15,7 +17,9 @@ public class PackageUserKey {
public UserHandle mUser;
private int mHashCode;
@Nullable
public static PackageUserKey fromItemInfo(ItemInfo info) {
if (info.getTargetComponent() == null) return null;
return new PackageUserKey(info.getTargetComponent().getPackageName(), info.user);
}
@@ -38,7 +42,8 @@ public class PackageUserKey {
* @return Whether this PackageUserKey was successfully updated - it shouldn't be used if not.
*/
public boolean updateFromItemInfo(ItemInfo info) {
if (DeepShortcutManager.supportsShortcuts(info)) {
if (info.getTargetComponent() == null) return false;
if (ShortcutUtil.supportsShortcuts(info)) {
update(info.getTargetComponent().getPackageName(), info.user);
return true;
}
@@ -0,0 +1,59 @@
/*
* 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.util;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.shortcuts.ShortcutKey;
public class ShortcutUtil {
public static boolean supportsShortcuts(ItemInfo info) {
return isActive(info) && (isApp(info) || isPinnedShortcut(info));
}
public static boolean supportsDeepShortcuts(ItemInfo info) {
return isActive(info) && isApp(info);
}
public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
return isActive(info) && isPinnedShortcut(info) ?
ShortcutKey.fromItemInfo(info).getId() : null;
}
public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
return isActive(info) && isPinnedShortcut(info) ?
((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
}
private static boolean isActive(ItemInfo info) {
boolean isLoading = info instanceof WorkspaceItemInfo
&& ((WorkspaceItemInfo) info).hasPromiseIconUi();
return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS;
}
private static boolean isApp(ItemInfo info) {
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
}
private static boolean isPinnedShortcut(ItemInfo info) {
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& info.container != ItemInfo.NO_ID
&& info instanceof WorkspaceItemInfo;
}
}
@@ -27,9 +27,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
@@ -63,13 +61,6 @@ public class DeepShortcutManager {
mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
}
public static boolean supportsShortcuts(ItemInfo info) {
boolean isItemPromise = info instanceof WorkspaceItemInfo
&& ((WorkspaceItemInfo) info).hasPromiseIconUi();
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
&& !info.isDisabled() && !isItemPromise;
}
public boolean wasLastCallSuccess() {
return mWasLastCallSuccess;
}
@@ -89,8 +80,9 @@ public class DeepShortcutManager {
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
public List<ShortcutInfo> queryForShortcutsContainer(@Nullable ComponentName activity,
UserHandle user) {
if (activity == null) return Collections.EMPTY_LIST;
return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
activity.getPackageName(), activity, null, user);
}
@@ -57,7 +57,7 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
}
@Override
protected int getLogContainerTypeForNormalState() {
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent) ?
ContainerType.HOTSEAT : ContainerType.WORKSPACE;
}
@@ -0,0 +1,31 @@
/*
* 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.uioverrides;
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
public class TogglableFlag extends BaseTogglableFlag {
public TogglableFlag(String key, boolean defaultValue, String description) {
super(key, defaultValue, description);
}
@Override
public boolean getInitialValue(boolean value) {
return value;
}
}
@@ -17,9 +17,11 @@
package com.android.launcher3.uioverrides;
import android.app.Activity;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -27,6 +29,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.TouchController;
@@ -95,4 +98,7 @@ public class UiFactory {
public static void clearSwipeSharedState(boolean finishAnimation) {}
public static Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY;
}
}
+3
View File
@@ -20,6 +20,9 @@
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner"/>
+3
View File
@@ -1 +1,4 @@
vadimt@google.com
sunnygoyal@google.com
winsonc@google.com
hyunyoungs@google.com
@@ -38,6 +38,7 @@ import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
@@ -330,30 +331,27 @@ public abstract class AbstractLauncherUiTest {
}
protected void startAppFast(String packageName) {
final Instrumentation instrumentation = getInstrumentation();
final Intent intent = instrumentation.getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
instrumentation.getTargetContext().startActivity(intent);
assertTrue(packageName + " didn't start",
mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), DEFAULT_UI_TIMEOUT));
startIntent(
getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage(
packageName),
By.pkg(packageName).depth(0));
}
protected void startTestActivity(int activityNumber) {
final String packageName = getAppPackageName();
final Instrumentation instrumentation = getInstrumentation();
final Intent intent = instrumentation.getContext().getPackageManager().
final Intent intent = getInstrumentation().getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.tests.Activity" + activityNumber));
instrumentation.getTargetContext().startActivity(intent);
assertTrue(packageName + " didn't start",
mDevice.wait(
Until.hasObject(By.pkg(packageName).text("TestActivity" + activityNumber)),
DEFAULT_UI_TIMEOUT));
startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber));
}
private void startIntent(Intent intent, BySelector selector) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
mDevice.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
}
public static String resolveSystemApp(String category) {
@@ -173,6 +173,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
@Test
public void testWorkspace() throws Exception {
mLauncher.enableDebugTracing();
final Workspace workspace = mLauncher.getWorkspace();
// Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
@@ -190,7 +191,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
isWorkspaceScrollable(launcher)));
assertNotNull("ensureScrollable didn't add Chrome app",
workspace.tryGetWorkspaceAppIcon("Chrome"));
workspace.getWorkspaceAppIcon("Chrome"));
// Test flinging workspace.
workspace.flingBackward();
@@ -206,8 +207,9 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
// Test starting a workspace app.
final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
final AppIcon app = workspace.getWorkspaceAppIcon("Chrome");
assertNotNull("No Chrome app in workspace", app);
mLauncher.disableDebugTracing();
}
public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -298,6 +300,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
@Test
@PortraitLandscape
public void testDragAppIcon() throws Throwable {
mLauncher.enableDebugTracing();
// 1. Open all apps and wait for load complete.
// 2. Drag icon to homescreen.
// 3. Verify that the icon works on homescreen.
@@ -314,11 +317,13 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
isInBackground(launcher)));
mLauncher.disableDebugTracing();
}
@Test
@PortraitLandscape
public void testDragShortcut() throws Throwable {
mLauncher.enableDebugTracing();
// 1. Open all apps and wait for load complete.
// 2. Find the app and long press it to show shortcuts.
// 3. Press icon center until shortcuts appear
@@ -338,6 +343,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
} finally {
allApps.unfreeze();
}
mLauncher.disableDebugTracing();
}
public static String getAppPackageName() {
@@ -1,6 +1,7 @@
package com.android.launcher3.util;
import android.os.SystemClock;
import android.util.Log;
import org.junit.Assert;
@@ -16,7 +17,9 @@ public class Wait {
}
public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
long endTime = SystemClock.uptimeMillis() + timeout;
final long startTime = SystemClock.uptimeMillis();
long endTime = startTime + timeout;
Log.d("Wait", "atMost: " + startTime + " - " + endTime);
while (SystemClock.uptimeMillis() < endTime) {
try {
if (condition.isTrue()) {
@@ -36,6 +39,7 @@ public class Wait {
} catch (Throwable t) {
throw new RuntimeException(t);
}
Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
Assert.fail(message);
}
}
@@ -15,7 +15,6 @@ import java.io.IOException;
public class FailureWatcher extends TestWatcher {
private static final String TAG = "FailureWatcher";
private static int sScreenshotCount = 0;
final private UiDevice mDevice;
public FailureWatcher(UiDevice device) {
@@ -40,7 +39,8 @@ public class FailureWatcher extends TestWatcher {
protected void failed(Throwable e, Description description) {
if (mDevice == null) return;
final String pathname = getInstrumentation().getTargetContext().
getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
getFilesDir().getPath() + "/TestScreenshot-" + description.getMethodName()
+ ".png";
Log.e(TAG, "Failed test " + description.getMethodName() +
", screenshot will be saved to " + pathname +
", track trace is below, UI object dump is further below:\n" +
@@ -19,6 +19,7 @@ import android.app.Activity;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
import androidx.test.InstrumentationRegistry;
import com.android.launcher3.Launcher;
@@ -84,19 +85,27 @@ public class LauncherActivityRule implements TestRule {
}
@Override
public void onActivityStarted(Activity activity) { }
public void onActivityStarted(Activity activity) {
if (activity instanceof Launcher) {
mActivity.getRotationHelper().forceAllowRotationForTesting(true);
}
}
@Override
public void onActivityResumed(Activity activity) { }
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) { }
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) { }
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
@@ -120,7 +120,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
appIconSelector);
return new AppIcon(mLauncher, appIcon);
}
@@ -74,7 +74,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
flingForward();
}
mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"dismissed all tasks")) {
return new Workspace(mLauncher);
@@ -16,6 +16,8 @@
package com.android.launcher3.tapl;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import android.graphics.Point;
import androidx.test.uiautomator.By;
@@ -23,13 +25,10 @@ import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import com.android.launcher3.testing.TestProtocol;
/**
* Ancestor for AppIcon and AppMenuItem.
*/
abstract class Launchable {
private static final int WAIT_TIME_MS = 60000;
protected final LauncherInstrumentation mLauncher;
protected final UiObject2 mObject;
@@ -53,9 +52,12 @@ abstract class Launchable {
private Background launch(BySelector selector) {
LauncherInstrumentation.log("Launchable.launch before click " +
mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
mLauncher.assertTrue(
"Launching an app didn't open a new window: " + mObject.getText(),
mObject.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
mLauncher.executeAndWaitForEvent(
() -> mObject.click(),
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
"Launching an app didn't open a new window: " + mObject.getText());
mLauncher.assertTrue(
"App didn't start: " + selector,
mLauncher.getDevice().wait(Until.hasObject(selector),
@@ -124,7 +124,7 @@ public final class LauncherInstrumentation {
private static final String APPS_RES_ID = "apps_view";
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "widgets_list_view";
public static final int WAIT_TIME_MS = 60000;
public static final int WAIT_TIME_MS = 10000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
@@ -178,6 +178,7 @@ public final class LauncherInstrumentation {
PackageManager pm = getContext().getPackageManager();
ProviderInfo pi = pm.resolveContentProvider(
testProviderAuthority, MATCH_ALL | MATCH_DISABLED_COMPONENTS);
assertNotNull("Cannot find content provider for " + testProviderAuthority, pi);
ComponentName cn = new ComponentName(pi.packageName, pi.name);
if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
@@ -677,13 +678,6 @@ public final class LauncherInstrumentation {
return object;
}
@NonNull
UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
final UiObject2 object = container.findObject(selector);
assertNotNull("Can't find an object with selector: " + selector, object);
return object;
}
@NonNull
List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
return container.findObjects(getLauncherObjectSelector(resName));
@@ -888,6 +882,7 @@ public final class LauncherInstrumentation {
}
long movePointer(long downTime, long startTime, long duration, Point from, Point to) {
log("movePointer: " + from + " to " + to);
final Point point = new Point();
long steps = duration / GESTURE_STEP_MS;
long currentTime = startTime;
@@ -962,4 +957,9 @@ public final class LauncherInstrumentation {
public void disableDebugTracing() {
getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
}
public long getAllocatedMemory() {
return getTestInfo(TestProtocol.REQUEST_ALLOCATED_MEMORY).
getLong(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
}
@@ -16,18 +16,16 @@
package com.android.launcher3.tapl;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import android.graphics.Rect;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import com.android.launcher3.testing.TestProtocol;
/**
* A recent task in the overview panel carousel.
*/
public final class OverviewTask {
private static final long WAIT_TIME_MS = 60000;
private final LauncherInstrumentation mLauncher;
private final UiObject2 mTask;
private final BaseOverview mOverview;
@@ -66,9 +64,11 @@ public final class OverviewTask {
verifyActiveContainer();
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"clicking an overview task")) {
mLauncher.assertTrue("Launching task didn't open a new window: " +
mTask.getParent().getContentDescription(),
mTask.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
mLauncher.executeAndWaitForEvent(
() -> mTask.click(),
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
"Launching task didn't open a new window: " +
mTask.getParent().getContentDescription());
}
return new Background(mLauncher);
}
@@ -109,7 +109,7 @@ public class TestHelpers {
DropBoxManager.Entry entry;
StringBuilder errorDetails = new StringBuilder();
while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
if (errorDetails.length() != 0) errorDetails.append("------------------------------");
errorDetails.append("------------------------------\n");
timestamp = entry.getTimeMillis();
errorDetails.append(new Date(timestamp));
errorDetails.append(": ");
@@ -108,10 +108,13 @@ public final class Workspace extends Home {
*/
@NonNull
public AppIcon getWorkspaceAppIcon(String appName) {
return new AppIcon(mLauncher,
mLauncher.getObjectInContainer(
verifyActiveContainer(),
AppIcon.getAppIconSelector(appName, mLauncher)));
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get a workspace icon")) {
return new AppIcon(mLauncher,
mLauncher.waitForObjectInContainer(
verifyActiveContainer(),
AppIcon.getAppIconSelector(appName, mLauncher)));
}
}
/**
@@ -142,13 +145,13 @@ public final class Workspace extends Home {
@NonNull
public AppIcon getHotseatAppIcon(String appName) {
return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer(
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
}
@NonNull
public Folder getHotseatFolder(String appName) {
return new Folder(mLauncher, mLauncher.getObjectInContainer(
return new Folder(mLauncher, mLauncher.waitForObjectInContainer(
mHotseat, Folder.getSelector(appName, mLauncher)));
}