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:
@@ -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" />
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ public class OverviewToAllAppsTouchController extends PortraitStatesTouchControl
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLogContainerTypeForNormalState() {
|
||||
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
|
||||
return LauncherLogProto.ContainerType.WORKSPACE;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -154,7 +154,7 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLogContainerTypeForNormalState() {
|
||||
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
|
||||
return LauncherLogProto.ContainerType.NAVBAR;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchContro
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLogContainerTypeForNormalState() {
|
||||
protected int getLogContainerTypeForNormalState(MotionEvent ev) {
|
||||
return LauncherLogProto.ContainerType.NAVBAR;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
vadimt@google.com
|
||||
sunnygoyal@google.com
|
||||
winsonc@google.com
|
||||
hyunyoungs@google.com
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() { }
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+18
-7
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user