Merge "Merge Android10 QPR1 into AOSP master"
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" />
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
@@ -10,3 +10,6 @@ mrcasey@google.com
|
||||
sunnygoyal@google.com
|
||||
twickham@google.com
|
||||
winsonc@google.com
|
||||
|
||||
per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com
|
||||
per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com
|
||||
|
||||
@@ -32,6 +32,7 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -115,10 +116,10 @@ public class OverviewState extends LauncherState {
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(Launcher launcher) {
|
||||
return getDefaultSwipeHeight(launcher.getDeviceProfile());
|
||||
return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(DeviceProfile dp) {
|
||||
public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
|
||||
return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
}
|
||||
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-12
@@ -18,16 +18,11 @@ package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
|
||||
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
|
||||
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
|
||||
|
||||
import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
|
||||
import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
|
||||
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
@@ -35,18 +30,17 @@ import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.anim.SpringAnimationBuilder;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
|
||||
* {@link RecentsView}.
|
||||
@@ -156,8 +150,11 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
|
||||
RecentsView.CONTENT_ALPHA, values);
|
||||
case INDEX_RECENTS_TRANSLATE_X_ANIM:
|
||||
return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(),
|
||||
VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values);
|
||||
return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X)
|
||||
.setDampingRatio(0.8f)
|
||||
.setStiffness(250)
|
||||
.setValues(values)
|
||||
.build(mLauncher);
|
||||
default:
|
||||
return super.createStateElementAnimation(index, values);
|
||||
}
|
||||
|
||||
+4
@@ -95,6 +95,10 @@ public class PredictionAppTracker extends AppLaunchTracker {
|
||||
private AppPredictor createPredictor(Client client, int count) {
|
||||
AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
|
||||
|
||||
if (apm == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AppPredictor predictor = apm.createAppPredictionSession(
|
||||
new AppPredictionContext.Builder(mContext)
|
||||
.setUiSurface(client.id)
|
||||
|
||||
+11
-7
@@ -24,7 +24,6 @@ import android.app.prediction.AppPredictor;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
|
||||
import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
@@ -32,6 +31,8 @@ import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
|
||||
import com.android.launcher3.ItemInfoWithIcon;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager.StateListener;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
|
||||
@@ -58,7 +59,7 @@ import java.util.List;
|
||||
* 4) Maintains the current active client id (for the predictions) and all updates are performed on
|
||||
* that client id.
|
||||
*/
|
||||
public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
|
||||
public class PredictionUiStateManager implements StateListener, ItemInfoUpdateReceiver,
|
||||
OnIDPChangeListener, OnUpdateListener {
|
||||
|
||||
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
|
||||
@@ -153,7 +154,10 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
|
||||
public void reapplyItemInfo(ItemInfoWithIcon info) { }
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
public void onStateTransitionStart(LauncherState toState) { }
|
||||
|
||||
@Override
|
||||
public void onStateTransitionComplete(LauncherState state) {
|
||||
if (mAppsView == null) {
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +166,8 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
|
||||
mPendingState = null;
|
||||
}
|
||||
if (mPendingState == null) {
|
||||
mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
Launcher.getLauncher(mAppsView.getContext()).getStateManager()
|
||||
.removeStateListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +175,8 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
|
||||
boolean registerListener = mPendingState == null;
|
||||
mPendingState = state;
|
||||
if (registerListener) {
|
||||
// OnGlobalLayoutListener is called whenever a view in the view tree changes
|
||||
// visibility. Add a listener and wait until appsView is invisible again.
|
||||
mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
||||
// Add a listener and wait until appsView is invisible again.
|
||||
Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -32,6 +32,7 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
|
||||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
@@ -159,11 +160,15 @@ public class OverviewState extends LauncherState {
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(Launcher launcher) {
|
||||
return getDefaultSwipeHeight(launcher.getDeviceProfile());
|
||||
return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
|
||||
}
|
||||
|
||||
public static float getDefaultSwipeHeight(DeviceProfile dp) {
|
||||
return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) {
|
||||
float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
|
||||
if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) {
|
||||
swipeHeight -= dp.getInsets().bottom;
|
||||
}
|
||||
return swipeHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -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;
|
||||
@@ -101,6 +102,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mHelper.onSwipeUpToRecentsComplete(mActivity);
|
||||
if (mRecentsView != null) {
|
||||
mRecentsView.animateUpRunningTaskIconScale();
|
||||
}
|
||||
|
||||
+4
-5
@@ -40,7 +40,6 @@ import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
@@ -58,7 +57,6 @@ import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
@@ -202,9 +200,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
// This ensures then the next swipe up to all-apps starts from scroll 0.
|
||||
activity.getAppsView().reset(false /* animate */);
|
||||
|
||||
// Optimization, hide the all apps view to prevent layout while initializing
|
||||
activity.getAppsView().getContentView().setVisibility(View.GONE);
|
||||
|
||||
return new AnimationFactory() {
|
||||
private ShelfAnimState mShelfState;
|
||||
private boolean mIsAttachedToWindow;
|
||||
@@ -386,6 +381,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
TaskView runningTaskView = recentsView.getRunningTaskView();
|
||||
if (runningTaskView == null) {
|
||||
runningTaskView = recentsView.getTaskViewAt(recentsView.getCurrentPage());
|
||||
if (runningTaskView == null) {
|
||||
// There are no task views in LockTask mode when Overview is enabled.
|
||||
return;
|
||||
}
|
||||
}
|
||||
TimeInterpolator oldInterpolator = translateY.getInterpolator();
|
||||
Rect fallbackInsets = launcher.getDeviceProfile().getInsets();
|
||||
|
||||
@@ -25,11 +25,13 @@ import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.MainThreadExecutor;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -99,6 +101,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) {
|
||||
@@ -161,6 +164,9 @@ public class OverviewCommandHelper {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "RecentsActivityCommand.run");
|
||||
}
|
||||
long elapsedTime = mCreateTime - mLastToggleTime;
|
||||
mLastToggleTime = mCreateTime;
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
|
||||
switch (method) {
|
||||
case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
|
||||
final float swipeHeight =
|
||||
OverviewState.getDefaultSwipeHeight(mDeviceProfile);
|
||||
OverviewState.getDefaultSwipeHeight(mContext, mDeviceProfile);
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import static android.view.MotionEvent.ACTION_CANCEL;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.InputEvent;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
@@ -38,6 +40,8 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public class RecentsAnimationWrapper {
|
||||
|
||||
private static final String TAG = "RecentsAnimationWrapper";
|
||||
|
||||
// A list of callbacks to run when we receive the recents animation target. There are different
|
||||
// than the state callbacks as these run on the current worker thread.
|
||||
private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
|
||||
@@ -125,6 +129,7 @@ public class RecentsAnimationWrapper {
|
||||
boolean sendUserLeaveHint) {
|
||||
SwipeAnimationTargetSet controller = targetSet;
|
||||
targetSet = null;
|
||||
disableInputProxy();
|
||||
if (controller != null) {
|
||||
controller.finishController(toRecents, onFinishComplete, sendUserLeaveHint);
|
||||
}
|
||||
@@ -153,6 +158,16 @@ public class RecentsAnimationWrapper {
|
||||
mInputConsumerController.setInputListener(this::onInputConsumerEvent);
|
||||
}
|
||||
|
||||
private void disableInputProxy() {
|
||||
if (mInputConsumer != null && mTouchInProgress) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
MotionEvent dummyCancel = MotionEvent.obtain(now, now, ACTION_CANCEL, 0, 0, 0);
|
||||
mInputConsumer.onMotionEvent(dummyCancel);
|
||||
dummyCancel.recycle();
|
||||
}
|
||||
mInputConsumerController.setInputListener(null);
|
||||
}
|
||||
|
||||
private boolean onInputConsumerEvent(InputEvent ev) {
|
||||
if (ev instanceof MotionEvent) {
|
||||
onInputConsumerMotionEvent((MotionEvent) ev);
|
||||
@@ -168,6 +183,18 @@ public class RecentsAnimationWrapper {
|
||||
|
||||
private boolean onInputConsumerMotionEvent(MotionEvent ev) {
|
||||
int action = ev.getAction();
|
||||
|
||||
// Just to be safe, verify that ACTION_DOWN comes before any other action,
|
||||
// and ignore any ACTION_DOWN after the first one (though that should not happen).
|
||||
if (!mTouchInProgress && action != ACTION_DOWN) {
|
||||
Log.w(TAG, "Received non-down motion before down motion: " + action);
|
||||
return false;
|
||||
}
|
||||
if (mTouchInProgress && action == ACTION_DOWN) {
|
||||
Log.w(TAG, "Received down motion while touch was already in progress");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (action == ACTION_DOWN) {
|
||||
mTouchInProgress = true;
|
||||
if (mInputConsumer == 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<>();
|
||||
|
||||
+29
-5
@@ -217,6 +217,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
|
||||
private AnimationFactory mAnimationFactory = (t) -> { };
|
||||
private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
|
||||
private boolean mLiveTileOverlayAttached = false;
|
||||
|
||||
private boolean mWasLauncherAlreadyVisible;
|
||||
|
||||
@@ -323,8 +324,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
|
||||
mRecentsView = activity.getOverviewPanel();
|
||||
linkRecentsViewScroll();
|
||||
mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
|
||||
mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
|
||||
addLiveTileOverlay();
|
||||
|
||||
mStateCallback.setState(STATE_LAUNCHER_PRESENT);
|
||||
if (alreadyOnHome) {
|
||||
@@ -923,7 +923,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
windowAnim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
setStateOnUiThread(target.endState);
|
||||
if (target == NEW_TASK && mRecentsView != null
|
||||
&& mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
|
||||
// We are about to launch the current running task, so use LAST_TASK state
|
||||
// instead of NEW_TASK. This could happen, for example, if our scroll is
|
||||
// aborted after we determined the target to be NEW_TASK.
|
||||
setStateOnUiThread(LAST_TASK.endState);
|
||||
} else {
|
||||
setStateOnUiThread(target.endState);
|
||||
}
|
||||
}
|
||||
});
|
||||
windowAnim.start();
|
||||
@@ -972,7 +980,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
if (mActivity != null) {
|
||||
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
|
||||
removeLiveTileOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1071,7 +1079,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
mRecentsView.onGestureAnimationEnd();
|
||||
|
||||
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
|
||||
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
|
||||
removeLiveTileOverlay();
|
||||
}
|
||||
|
||||
private void endLauncherTransitionController() {
|
||||
@@ -1201,6 +1209,22 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
updateFinalShift();
|
||||
}
|
||||
|
||||
private synchronized void addLiveTileOverlay() {
|
||||
if (!mLiveTileOverlayAttached) {
|
||||
mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
|
||||
mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
|
||||
mLiveTileOverlayAttached = true;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void removeLiveTileOverlay() {
|
||||
if (mLiveTileOverlayAttached) {
|
||||
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
|
||||
mRecentsView.setLiveTileOverlay(null);
|
||||
mLiveTileOverlayAttached = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
|
||||
if (!isNotInRecents(app)) {
|
||||
return 0;
|
||||
|
||||
+12
-7
@@ -1,3 +1,4 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
@@ -82,7 +83,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
private int mDirection;
|
||||
private ActivityControlHelper mActivityControlHelper;
|
||||
|
||||
private final float mDistThreshold;
|
||||
private final float mDragDistThreshold;
|
||||
private final float mFlingDistThreshold;
|
||||
private final long mTimeThreshold;
|
||||
private final int mAngleThreshold;
|
||||
private final float mSquaredSlop;
|
||||
@@ -97,7 +99,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
final Resources res = context.getResources();
|
||||
mContext = context;
|
||||
mSysUiProxy = systemUiProxy;
|
||||
mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
|
||||
mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
|
||||
mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold);
|
||||
mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
|
||||
mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
|
||||
|
||||
@@ -117,8 +120,6 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
// TODO add logging
|
||||
mGestureDetector.onTouchEvent(ev);
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case ACTION_DOWN: {
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
@@ -213,6 +214,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
break;
|
||||
}
|
||||
|
||||
mGestureDetector.onTouchEvent(ev);
|
||||
|
||||
if (mState != STATE_ACTIVE) {
|
||||
mDelegate.onMotionEvent(ev);
|
||||
}
|
||||
@@ -220,9 +223,9 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
|
||||
private void updateAssistantProgress() {
|
||||
if (!mLaunchedAssistant) {
|
||||
mLastProgress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction;
|
||||
mLastProgress = Math.min(mDistance * 1f / mDragDistThreshold, 1) * mTimeFraction;
|
||||
try {
|
||||
if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
|
||||
if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) {
|
||||
mSysUiProxy.onAssistantGestureCompletion(0);
|
||||
startAssistantInternal(SWIPE);
|
||||
|
||||
@@ -271,7 +274,9 @@ public class AssistantTouchConsumer extends DelegateInputConsumer {
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
if (isValidAssistantGestureAngle(velocityX, -velocityY)
|
||||
&& !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) {
|
||||
&& mDistance >= mFlingDistThreshold
|
||||
&& !mLaunchedAssistant
|
||||
&& mState != STATE_DELEGATE_ACTIVE) {
|
||||
mLastProgress = 1;
|
||||
try {
|
||||
mSysUiProxy.onAssistantGestureCompletion(
|
||||
|
||||
+5
-4
@@ -23,6 +23,7 @@ import android.util.ArraySet;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
||||
import com.android.systemui.shared.system.RecentsAnimationListener;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
@@ -39,7 +40,7 @@ import androidx.annotation.UiThread;
|
||||
public class RecentsAnimationListenerSet implements RecentsAnimationListener {
|
||||
|
||||
// The actual app surface is replaced by a screenshot upon recents animation cancelation when
|
||||
// deferredWithScreenshot is true. Launcher takes the responsibility to clean up this screenshot
|
||||
// the thumbnailData exists. Launcher takes the responsibility to clean up this screenshot
|
||||
// after app transition is finished. This delay is introduced to cover the app transition
|
||||
// period of time.
|
||||
private final int TRANSITION_DELAY = 100;
|
||||
@@ -90,14 +91,14 @@ public class RecentsAnimationListenerSet implements RecentsAnimationListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onAnimationCanceled(boolean deferredWithScreenshot) {
|
||||
public final void onAnimationCanceled(ThumbnailData thumbnailData) {
|
||||
Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
|
||||
for (SwipeAnimationListener listener : getListeners()) {
|
||||
listener.onRecentsAnimationCanceled();
|
||||
}
|
||||
});
|
||||
// TODO: handle the transition better instead of simply using a transition delay.
|
||||
if (deferredWithScreenshot) {
|
||||
if (thumbnailData != null) {
|
||||
MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(),
|
||||
TRANSITION_DELAY);
|
||||
}
|
||||
@@ -109,6 +110,6 @@ public class RecentsAnimationListenerSet implements RecentsAnimationListener {
|
||||
|
||||
public void cancelListener() {
|
||||
mCancelled = true;
|
||||
onAnimationCanceled(false);
|
||||
onAnimationCanceled(null);
|
||||
}
|
||||
}
|
||||
|
||||
+43
-12
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
@@ -40,7 +41,9 @@ import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.graphics.OverviewScrim;
|
||||
import com.android.launcher3.views.IconLabelDotView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -62,7 +65,9 @@ public class StaggeredWorkspaceAnim {
|
||||
|
||||
private final float mVelocity;
|
||||
private final float mSpringTransY;
|
||||
private final View mViewToIgnore;
|
||||
|
||||
// The original view of the {@link FloatingIconView}.
|
||||
private final View mOriginalView;
|
||||
|
||||
private final List<Animator> mAnimators = new ArrayList<>();
|
||||
|
||||
@@ -72,9 +77,7 @@ public class StaggeredWorkspaceAnim {
|
||||
public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView,
|
||||
float velocity) {
|
||||
mVelocity = velocity;
|
||||
// We ignore this view since it's visibility and position is controlled by
|
||||
// the FloatingIconView.
|
||||
mViewToIgnore = floatingViewOriginalView;
|
||||
mOriginalView = floatingViewOriginalView;
|
||||
|
||||
// Scale the translationY based on the initial velocity to better sync the workspace items
|
||||
// with the floating view.
|
||||
@@ -86,16 +89,21 @@ public class StaggeredWorkspaceAnim {
|
||||
Workspace workspace = launcher.getWorkspace();
|
||||
CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
|
||||
ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
|
||||
ViewGroup hotseat = launcher.getHotseat();
|
||||
|
||||
boolean workspaceClipChildren = workspace.getClipChildren();
|
||||
boolean workspaceClipToPadding = workspace.getClipToPadding();
|
||||
boolean cellLayoutClipChildren = cellLayout.getClipChildren();
|
||||
boolean cellLayoutClipToPadding = cellLayout.getClipToPadding();
|
||||
boolean hotseatClipChildren = hotseat.getClipChildren();
|
||||
boolean hotseatClipToPadding = hotseat.getClipToPadding();
|
||||
|
||||
workspace.setClipChildren(false);
|
||||
workspace.setClipToPadding(false);
|
||||
cellLayout.setClipChildren(false);
|
||||
cellLayout.setClipToPadding(false);
|
||||
hotseat.setClipChildren(false);
|
||||
hotseat.setClipToPadding(false);
|
||||
|
||||
// Hotseat and QSB takes up two additional rows.
|
||||
int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
|
||||
@@ -108,16 +116,18 @@ public class StaggeredWorkspaceAnim {
|
||||
}
|
||||
|
||||
// Set up springs for the hotseat and qsb.
|
||||
ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0);
|
||||
if (grid.isVerticalBarLayout()) {
|
||||
ViewGroup hotseat = (ViewGroup) launcher.getHotseat().getChildAt(0);
|
||||
for (int i = hotseat.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = hotseat.getChildAt(i);
|
||||
for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = hotseatChild.getChildAt(i);
|
||||
CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
|
||||
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
|
||||
}
|
||||
} else {
|
||||
View hotseat = launcher.getHotseat().getChildAt(0);
|
||||
addStaggeredAnimationForView(hotseat, grid.inv.numRows + 1, totalRows);
|
||||
for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
|
||||
View child = hotseatChild.getChildAt(i);
|
||||
addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows);
|
||||
}
|
||||
|
||||
View qsb = launcher.findViewById(R.id.search_container_all_apps);
|
||||
addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
|
||||
@@ -140,6 +150,8 @@ public class StaggeredWorkspaceAnim {
|
||||
workspace.setClipToPadding(workspaceClipToPadding);
|
||||
cellLayout.setClipChildren(cellLayoutClipChildren);
|
||||
cellLayout.setClipToPadding(cellLayoutClipToPadding);
|
||||
hotseat.setClipChildren(hotseatClipChildren);
|
||||
hotseat.setClipToPadding(hotseatClipToPadding);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,16 +192,35 @@ public class StaggeredWorkspaceAnim {
|
||||
springTransY.setStartDelay(startDelay);
|
||||
mAnimators.add(springTransY);
|
||||
|
||||
if (v == mViewToIgnore) {
|
||||
return;
|
||||
ObjectAnimator alpha = getAlphaAnimator(v, startDelay);
|
||||
if (v == mOriginalView) {
|
||||
// For IconLabelDotViews, we just want the label to fade in.
|
||||
// Icon, badge, and dots will animate in separately (controlled via FloatingIconView)
|
||||
if (v instanceof IconLabelDotView) {
|
||||
alpha.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
IconLabelDotView view = (IconLabelDotView) v;
|
||||
view.setIconVisible(false);
|
||||
view.setForceHideDot(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
v.setAlpha(0);
|
||||
mAnimators.add(alpha);
|
||||
}
|
||||
|
||||
private ObjectAnimator getAlphaAnimator(View v, long startDelay) {
|
||||
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
|
||||
alpha.setInterpolator(LINEAR);
|
||||
alpha.setDuration(ALPHA_DURATION_MS);
|
||||
alpha.setStartDelay(startDelay);
|
||||
mAnimators.add(alpha);
|
||||
return alpha;
|
||||
|
||||
}
|
||||
|
||||
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.quickstep.views;
|
||||
|
||||
import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
|
||||
|
||||
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
|
||||
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
@@ -97,6 +96,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;
|
||||
@@ -571,9 +571,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
final TaskView taskView = (TaskView) getChildAt(pageIndex);
|
||||
taskView.bind(task);
|
||||
}
|
||||
TaskView runningTaskView = getRunningTaskView();
|
||||
if (runningTaskView != null) {
|
||||
setCurrentPage(indexOfChild(runningTaskView));
|
||||
|
||||
if (mNextPage == INVALID_PAGE) {
|
||||
// Set the current page to the running task, but not if settling on new task.
|
||||
TaskView runningTaskView = getRunningTaskView();
|
||||
if (runningTaskView != null) {
|
||||
setCurrentPage(indexOfChild(runningTaskView));
|
||||
}
|
||||
}
|
||||
|
||||
if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreRestTaskView) {
|
||||
@@ -785,6 +789,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
unloadVisibleTaskData();
|
||||
setCurrentPage(0);
|
||||
mDwbToastShown = false;
|
||||
mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
|
||||
}
|
||||
|
||||
public @Nullable TaskView getRunningTaskView() {
|
||||
@@ -823,7 +828,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
*/
|
||||
public void onSwipeUpAnimationSuccess() {
|
||||
if (getRunningTaskView() != null) {
|
||||
float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get()
|
||||
float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlay != null
|
||||
? mLiveTileOverlay.cancelIconAnimation()
|
||||
: 0f;
|
||||
animateUpRunningTaskIconScale(startProgress);
|
||||
@@ -939,6 +944,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTaskIconScaledDown(TaskView taskView) {
|
||||
return mRunningTaskIconScaledDown && getRunningTaskView() == taskView;
|
||||
}
|
||||
|
||||
private void applyRunningTaskIconScale() {
|
||||
TaskView firstTask = getRunningTaskView();
|
||||
if (firstTask != null) {
|
||||
@@ -1050,9 +1059,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
@@ -54,7 +53,6 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
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;
|
||||
@@ -818,8 +816,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
/ (getWidth() + currentInsetsLeft + currentInsetsRight));
|
||||
}
|
||||
|
||||
// Some of the items in here are dependent on the current fullscreen params
|
||||
setIconScaleAndDim(progress, true /* invert */);
|
||||
if (!getRecentsView().isTaskIconScaledDown(this)) {
|
||||
// Some of the items in here are dependent on the current fullscreen params, but don't
|
||||
// update them if the icon is supposed to be scaled down.
|
||||
setIconScaleAndDim(progress, true /* invert */);
|
||||
}
|
||||
|
||||
thumbnail.setFullscreenParams(mCurrentFullscreenParams);
|
||||
mOutlineProvider.setFullscreenParams(mCurrentFullscreenParams);
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<!-- Distance from the vertical edges of the screen in which assist gestures are recognized -->
|
||||
<dimen name="gestures_assistant_width">48dp</dimen>
|
||||
<dimen name="gestures_assistant_drag_threshold">55dp</dimen>
|
||||
<dimen name="gestures_assistant_fling_threshold">55dp</dimen>
|
||||
|
||||
<!-- Distance to move elements when swiping up to go home from launcher -->
|
||||
<dimen name="home_pullback_distance">28dp</dimen>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.provider.DeviceConfig;
|
||||
import com.android.launcher3.config.BaseFlags.BaseTogglableFlag;
|
||||
|
||||
public class TogglableFlag extends BaseTogglableFlag {
|
||||
public static final String NAMESPACE_LAUNCHER = "launcher";
|
||||
public static final String TAG = "TogglableFlag";
|
||||
|
||||
public TogglableFlag(String key, boolean defaultValue, String description) {
|
||||
super(key, defaultValue, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOverridenDefaultValue(boolean value) {
|
||||
return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChangeListener(Context context, Runnable r) {
|
||||
DeviceConfig.addOnPropertiesChangedListener(
|
||||
NAMESPACE_LAUNCHER,
|
||||
context.getMainExecutor(),
|
||||
(properties) -> {
|
||||
if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) {
|
||||
return;
|
||||
}
|
||||
initialize(context);
|
||||
r.run();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -16,18 +16,19 @@
|
||||
|
||||
package com.android.quickstep.logging;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.stats.launcher.nano.LauncherExtension;
|
||||
import android.stats.launcher.nano.LauncherTarget;
|
||||
|
||||
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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.stats.launcher.nano.Launcher;
|
||||
import android.stats.launcher.nano.LauncherExtension;
|
||||
import android.stats.launcher.nano.LauncherTarget;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.ItemInfo;
|
||||
@@ -38,8 +39,6 @@ import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.systemui.shared.system.StatsLogCompat;
|
||||
import com.google.protobuf.nano.MessageNano;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This method calls the StatsLog hidden method until they are made available public.
|
||||
*
|
||||
@@ -74,6 +73,27 @@ 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) {
|
||||
LauncherExtension ext = new LauncherExtension();
|
||||
ext.srcTarget = new LauncherTarget[1];
|
||||
int srcState = mStateProvider.getCurrentState();
|
||||
fillInLauncherExtensionWithPageId(ext, pageId);
|
||||
int launcherAction = isSwipingToLeft ? Launcher.SWIPE_LEFT : Launcher.SWIPE_RIGHT;
|
||||
StatsLogCompat.write(launcherAction, srcState, srcState,
|
||||
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) {
|
||||
@@ -88,6 +108,13 @@ public class StatsLogCompatManager extends StatsLogManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) {
|
||||
Target target = new Target();
|
||||
target.pageIndex = pageId;
|
||||
copy(target, ext.srcTarget[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void copy(Target src, LauncherTarget dst) {
|
||||
// fill in
|
||||
}
|
||||
|
||||
@@ -156,12 +156,14 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
|
||||
mDragHandleProgress = 1;
|
||||
mMidAlpha = 0;
|
||||
} else {
|
||||
mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
|
||||
Context context = getContext();
|
||||
mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
|
||||
mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
|
||||
Rect hotseatPadding = dp.getHotseatLayoutPadding();
|
||||
int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
|
||||
- hotseatPadding.bottom - hotseatPadding.top;
|
||||
float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
|
||||
float dragHandleTop =
|
||||
Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp));
|
||||
mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
|
||||
}
|
||||
mTopOffset = dp.getInsets().top - mShelfOffset;
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
vadimt@google.com
|
||||
sunnygoyal@google.com
|
||||
winsonc@google.com
|
||||
hyunyoungs@google.com
|
||||
|
||||
@@ -69,10 +69,9 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
|
||||
private DigitalWellBeingToast getToast() {
|
||||
executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
|
||||
waitForState("Launcher internal state didn't switch to Overview", OVERVIEW);
|
||||
waitForLauncherCondition("No latest task", launcher -> getLatestTask(launcher) != null);
|
||||
final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
|
||||
|
||||
return getFromLauncher(launcher -> {
|
||||
final TaskView task = getLatestTask(launcher);
|
||||
assertTrue("Latest task is not Calculator",
|
||||
CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName()));
|
||||
return task.getDigitalWellBeingToast();
|
||||
|
||||
@@ -34,9 +34,9 @@ import androidx.test.uiautomator.UiDevice;
|
||||
|
||||
import com.android.launcher3.tapl.LauncherInstrumentation;
|
||||
import com.android.launcher3.tapl.TestHelpers;
|
||||
import com.android.launcher3.util.rule.FailureWatcher;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
@@ -79,6 +79,14 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
description.getAnnotation(NavigationModeSwitch.class) != null) {
|
||||
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
|
||||
return new Statement() {
|
||||
private void assertTrue(String message, boolean condition) {
|
||||
if(!condition) {
|
||||
final AssertionError assertionError = new AssertionError(message);
|
||||
FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
|
||||
throw assertionError;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
mLauncher.enableDebugTracing();
|
||||
@@ -107,9 +115,9 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
Log.e(TAG, "Exception", e);
|
||||
throw e;
|
||||
} finally {
|
||||
Assert.assertTrue(setActiveOverlay(prevOverlayPkg, originalMode));
|
||||
assertTrue("Couldn't set overlay",
|
||||
setActiveOverlay(prevOverlayPkg, originalMode));
|
||||
}
|
||||
mLauncher.disableDebugTracing();
|
||||
}
|
||||
|
||||
private void evaluateWithThreeButtons() throws Throwable {
|
||||
@@ -176,7 +184,7 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
targetContext.getMainExecutor().execute(() ->
|
||||
sysUINavigationMode.removeModeChangeListener(listener));
|
||||
Assert.assertTrue("Navigation mode didn't change to " + expectedMode,
|
||||
assertTrue("Navigation mode didn't change to " + expectedMode,
|
||||
currentSysUiNavigationMode() == expectedMode);
|
||||
}
|
||||
|
||||
@@ -184,7 +192,7 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
if (mLauncher.getNavigationModel() == expectedMode) break;
|
||||
Thread.sleep(100);
|
||||
}
|
||||
Assert.assertTrue("Couldn't switch to " + overlayPackage,
|
||||
assertTrue("Couldn't switch to " + overlayPackage,
|
||||
mLauncher.getNavigationModel() == expectedMode);
|
||||
|
||||
for (int i = 0; i != 100; ++i) {
|
||||
@@ -192,7 +200,7 @@ public class NavigationModeSwitchRule implements TestRule {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
final String error = mLauncher.getNavigationModeMismatchError();
|
||||
Assert.assertTrue("Switching nav mode: " + error, error == null);
|
||||
assertTrue("Switching nav mode: " + error, error == null);
|
||||
|
||||
Thread.sleep(5000);
|
||||
return true;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -29,7 +29,8 @@ public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestC
|
||||
|
||||
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
|
||||
int state = PackageInstallerCompat.STATUS_INSTALLING;
|
||||
PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
|
||||
PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress,
|
||||
android.os.Process.myUserHandle());
|
||||
return new PackageInstallStateChangedTask(installInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ public class AllAppsList {
|
||||
public void addPromiseApp(Context context,
|
||||
PackageInstallerCompat.PackageInstallInfo installInfo) {
|
||||
ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
|
||||
.getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle());
|
||||
.getApplicationInfo(installInfo.packageName, 0, installInfo.user);
|
||||
// only if not yet installed
|
||||
if (applicationInfo == null) {
|
||||
PromiseAppInfo info = new PromiseAppInfo(installInfo);
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -171,4 +172,13 @@ public abstract class BaseRecyclerView extends RecyclerView {
|
||||
* <p>Override in each subclass of this base class.
|
||||
*/
|
||||
public void onFastScrollCompleted() {}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(int state) {
|
||||
super.onScrollStateChanged(state);
|
||||
|
||||
if (state == SCROLL_STATE_IDLE) {
|
||||
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils.TruncateAt;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.util.TypedValue;
|
||||
import android.view.KeyEvent;
|
||||
@@ -54,8 +53,8 @@ import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.icons.IconCache.IconLoadRequest;
|
||||
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.IconLabelDotView;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
|
||||
@@ -64,7 +63,8 @@ import java.text.NumberFormat;
|
||||
* because we want to make the bubble taller than the text and TextView's clip is
|
||||
* too aggressive.
|
||||
*/
|
||||
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback {
|
||||
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
|
||||
IconLabelDotView {
|
||||
|
||||
private static final int DISPLAY_WORKSPACE = 0;
|
||||
private static final int DISPLAY_ALL_APPS = 1;
|
||||
@@ -413,7 +413,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
}
|
||||
}
|
||||
|
||||
public void forceHideDot(boolean forceHideDot) {
|
||||
@Override
|
||||
public void setForceHideDot(boolean forceHideDot) {
|
||||
if (mForceHideDot == forceHideDot) {
|
||||
return;
|
||||
}
|
||||
@@ -602,6 +603,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIconVisible(boolean visible) {
|
||||
mIsIconVisible = visible;
|
||||
Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
|
||||
|
||||
@@ -142,8 +142,11 @@ public class FastBitmapDrawable extends Drawable {
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mAlpha = alpha;
|
||||
mPaint.setAlpha(alpha);
|
||||
if (mAlpha != alpha) {
|
||||
mAlpha = alpha;
|
||||
mPaint.setAlpha(alpha);
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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() { }
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.launcher3;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -29,7 +30,6 @@ import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Process;
|
||||
@@ -141,7 +141,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
|
||||
String pkg = getIntentPackage(info.launchIntent);
|
||||
if (!TextUtils.isEmpty(pkg)
|
||||
&& !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
|
||||
&& !launcherApps.isPackageEnabledForProfile(pkg, info.user)
|
||||
&& !info.isActivity) {
|
||||
if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
|
||||
+ info.launchIntent);
|
||||
continue;
|
||||
@@ -250,7 +251,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
|
||||
return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
|
||||
return (WorkspaceItemInfo)
|
||||
new PendingInstallShortcutInfo(info, context).getItemInfo().first;
|
||||
}
|
||||
|
||||
public static void queueShortcut(ShortcutInfo info, Context context) {
|
||||
@@ -261,8 +263,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
|
||||
}
|
||||
|
||||
public static void queueActivityInfo(LauncherActivityInfo activity, Context context) {
|
||||
queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context);
|
||||
public static void queueApplication(Intent data, UserHandle user, Context context) {
|
||||
queuePendingShortcutInfo(new PendingInstallShortcutInfo(data, context, user),
|
||||
context);
|
||||
}
|
||||
|
||||
public static HashSet<ShortcutKey> getPendingShortcuts(Context context) {
|
||||
@@ -326,7 +329,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
|
||||
private static class PendingInstallShortcutInfo {
|
||||
|
||||
final LauncherActivityInfo activityInfo;
|
||||
final boolean isActivity;
|
||||
final ShortcutInfo shortcutInfo;
|
||||
final AppWidgetProviderInfo providerInfo;
|
||||
|
||||
@@ -340,7 +343,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
* Initializes a PendingInstallShortcutInfo received from a different app.
|
||||
*/
|
||||
public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) {
|
||||
activityInfo = null;
|
||||
isActivity = false;
|
||||
shortcutInfo = null;
|
||||
providerInfo = null;
|
||||
|
||||
@@ -350,14 +353,13 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
|
||||
launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
|
||||
label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
|
||||
*/
|
||||
public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
|
||||
activityInfo = info;
|
||||
isActivity = true;
|
||||
shortcutInfo = null;
|
||||
providerInfo = null;
|
||||
|
||||
@@ -369,11 +371,27 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
label = info.getLabel().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
|
||||
*/
|
||||
public PendingInstallShortcutInfo(Intent data, Context context, UserHandle user) {
|
||||
isActivity = true;
|
||||
shortcutInfo = null;
|
||||
providerInfo = null;
|
||||
|
||||
this.data = data;
|
||||
this.user = user;
|
||||
mContext = context;
|
||||
|
||||
launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
|
||||
label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
|
||||
*/
|
||||
public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
|
||||
activityInfo = null;
|
||||
isActivity = false;
|
||||
shortcutInfo = info;
|
||||
providerInfo = null;
|
||||
|
||||
@@ -390,7 +408,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
*/
|
||||
public PendingInstallShortcutInfo(
|
||||
AppWidgetProviderInfo info, int widgetId, Context context) {
|
||||
activityInfo = null;
|
||||
isActivity = false;
|
||||
shortcutInfo = null;
|
||||
providerInfo = info;
|
||||
|
||||
@@ -405,17 +423,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
|
||||
public String encodeToString() {
|
||||
try {
|
||||
if (activityInfo != null) {
|
||||
// If it a launcher target, we only need component name, and user to
|
||||
// recreate this.
|
||||
return new JSONStringer()
|
||||
.object()
|
||||
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
|
||||
.key(APP_SHORTCUT_TYPE_KEY).value(true)
|
||||
.key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext)
|
||||
.getSerialNumberForUser(user))
|
||||
.endObject().toString();
|
||||
} else if (shortcutInfo != null) {
|
||||
if (shortcutInfo != null) {
|
||||
// If it a launcher target, we only need component name, and user to
|
||||
// recreate this.
|
||||
return new JSONStringer()
|
||||
@@ -457,7 +465,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
JSONStringer json = new JSONStringer()
|
||||
.object()
|
||||
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
|
||||
.key(NAME_KEY).value(name);
|
||||
.key(NAME_KEY).value(name)
|
||||
.key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
|
||||
if (icon != null) {
|
||||
byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
|
||||
json = json.key(ICON_KEY).value(
|
||||
@@ -477,29 +486,18 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
public Pair<ItemInfo, Object> getItemInfo() {
|
||||
if (activityInfo != null) {
|
||||
AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
|
||||
final LauncherAppState app = LauncherAppState.getInstance(mContext);
|
||||
// Set default values until proper values is loaded.
|
||||
appInfo.title = "";
|
||||
appInfo.applyFrom(app.getIconCache().getDefaultIcon(user));
|
||||
final WorkspaceItemInfo si = appInfo.makeWorkspaceItem();
|
||||
if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
|
||||
app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
|
||||
} else {
|
||||
app.getModel().updateAndBindWorkspaceItem(() -> {
|
||||
app.getIconCache().getTitleAndIcon(
|
||||
si, activityInfo, false /* useLowResIcon */);
|
||||
return si;
|
||||
});
|
||||
}
|
||||
return Pair.create((ItemInfo) si, (Object) activityInfo);
|
||||
if (isActivity) {
|
||||
WorkspaceItemInfo si = createWorkspaceItemInfo(data,
|
||||
LauncherAppState.getInstance(mContext));
|
||||
si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
|
||||
return Pair.create(si, null);
|
||||
} else if (shortcutInfo != null) {
|
||||
WorkspaceItemInfo si = new WorkspaceItemInfo(shortcutInfo, mContext);
|
||||
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
|
||||
LauncherIcons li = LauncherIcons.obtain(mContext);
|
||||
si.applyFrom(li.createShortcutIcon(shortcutInfo));
|
||||
itemInfo.applyFrom(li.createShortcutIcon(shortcutInfo));
|
||||
li.recycle();
|
||||
return Pair.create((ItemInfo) si, (Object) shortcutInfo);
|
||||
return Pair.create(itemInfo, shortcutInfo);
|
||||
} else if (providerInfo != null) {
|
||||
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
|
||||
.fromProviderInfo(mContext, providerInfo);
|
||||
@@ -511,15 +509,16 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
widgetInfo.minSpanY = info.minSpanY;
|
||||
widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
|
||||
widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
|
||||
return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
|
||||
return Pair.create(widgetInfo, providerInfo);
|
||||
} else {
|
||||
WorkspaceItemInfo si = createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
|
||||
return Pair.create((ItemInfo) si, null);
|
||||
WorkspaceItemInfo itemInfo =
|
||||
createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
|
||||
return Pair.create(itemInfo, null);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLauncherActivity() {
|
||||
return activityInfo != null;
|
||||
return isActivity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +533,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
|
||||
LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
|
||||
.resolveActivity(decoder.launcherIntent, decoder.user);
|
||||
return info == null ? null : new PendingInstallShortcutInfo(info, context);
|
||||
if (info != null) {
|
||||
return new PendingInstallShortcutInfo(info, context);
|
||||
}
|
||||
} else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
|
||||
DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
|
||||
List<ShortcutInfo> si = sm.queryForFullDetails(
|
||||
@@ -578,7 +579,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
|
||||
}
|
||||
|
||||
return new PendingInstallShortcutInfo(data, decoder.user, context);
|
||||
if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
|
||||
return new PendingInstallShortcutInfo(data, context, decoder.user);
|
||||
} else {
|
||||
return new PendingInstallShortcutInfo(data, decoder.user, context);
|
||||
}
|
||||
} catch (JSONException | URISyntaxException e) {
|
||||
Log.d(TAG, "Exception reading shortcut to add: " + e);
|
||||
}
|
||||
@@ -626,6 +631,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, LauncherAppState app) {
|
||||
if (data == null) {
|
||||
Log.e(TAG, "Can't construct WorkspaceItemInfo with null data");
|
||||
return null;
|
||||
}
|
||||
|
||||
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
|
||||
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
|
||||
Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
|
||||
|
||||
@@ -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;
|
||||
@@ -156,6 +157,7 @@ import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Default launcher application.
|
||||
@@ -208,9 +210,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
|
||||
|
||||
// How long to wait before the new-shortcut animation automatically pans the workspace
|
||||
private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
|
||||
@VisibleForTesting public static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
|
||||
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
|
||||
@Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
|
||||
@Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
|
||||
|
||||
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
|
||||
private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
|
||||
@@ -873,9 +875,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 +901,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 +928,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 +2504,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;
|
||||
@@ -96,6 +94,7 @@ public class LauncherAppState {
|
||||
if (FeatureFlags.IS_DOGFOOD_BUILD) {
|
||||
filter.addAction(ACTION_FORCE_ROLOAD);
|
||||
}
|
||||
FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
|
||||
|
||||
mContext.registerReceiver(mModel, filter);
|
||||
UserManagerCompat.getInstance(mContext).enableAndResetCache();
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
|
||||
import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
@@ -51,6 +52,7 @@ import com.android.launcher3.model.UserLockStateChangedTask;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
@@ -211,6 +213,30 @@ public class LauncherModel extends BroadcastReceiver
|
||||
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
|
||||
}
|
||||
|
||||
public void onSessionFailure(String packageName, UserHandle user) {
|
||||
enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||
final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
|
||||
synchronized (dataModel) {
|
||||
for (ItemInfo info : dataModel.itemsIdMap) {
|
||||
if (info instanceof WorkspaceItemInfo
|
||||
&& ((WorkspaceItemInfo) info).hasPromiseIconUi()
|
||||
&& user.equals(info.user)
|
||||
&& info.getIntent() != null
|
||||
&& TextUtils.equals(packageName, info.getIntent().getPackage())) {
|
||||
removedIds.put(info.id, true /* remove */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!removedIds.isEmpty()) {
|
||||
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, UserHandle user) {
|
||||
onPackagesRemoved(user, packageName);
|
||||
|
||||
@@ -227,6 +227,11 @@ public class LauncherStateManager {
|
||||
|
||||
private void goToState(LauncherState state, boolean animated, long delay,
|
||||
final Runnable onCompleteRunnable) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " +
|
||||
state.getClass().getSimpleName() +
|
||||
" @ " + Log.getStackTraceString(new Throwable()));
|
||||
}
|
||||
animated &= Utilities.areAnimationsEnabled(mLauncher);
|
||||
if (mLauncher.isInState(state)) {
|
||||
if (mConfig.mCurrentAnimation == null) {
|
||||
@@ -407,6 +412,11 @@ public class LauncherStateManager {
|
||||
mState.onStateDisabled(mLauncher);
|
||||
}
|
||||
mState = state;
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.STABLE_STATE_MISMATCH, "onStateTransitionStart: " +
|
||||
state.getClass().getSimpleName() +
|
||||
" @ " + Log.getStackTraceString(new Throwable()));
|
||||
}
|
||||
mState.onStateEnabled(mLauncher);
|
||||
mLauncher.onStateSet(mState);
|
||||
|
||||
@@ -426,6 +436,11 @@ public class LauncherStateManager {
|
||||
if (state != mCurrentStableState) {
|
||||
mLastStableState = state.getHistoryForState(mCurrentStableState);
|
||||
mCurrentStableState = state;
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " +
|
||||
state.getClass().getSimpleName() +
|
||||
" @ " + Log.getStackTraceString(new Throwable()));
|
||||
}
|
||||
}
|
||||
|
||||
state.onStateTransitionEnd(mLauncher);
|
||||
|
||||
@@ -890,23 +890,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
mTotalMotionX = 0;
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
|
||||
/*
|
||||
* If being flinged and user touches the screen, initiate drag;
|
||||
* otherwise don't. mScroller.isFinished should be false when
|
||||
* being flinged.
|
||||
*/
|
||||
final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
|
||||
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
|
||||
|
||||
if (finishedScrolling) {
|
||||
mIsBeingDragged = false;
|
||||
if (!mScroller.isFinished() && !mFreeScroll) {
|
||||
setCurrentPage(getNextPage());
|
||||
pageEndTransition();
|
||||
}
|
||||
} else {
|
||||
mIsBeingDragged = true;
|
||||
}
|
||||
updateIsBeingDraggedOnTouchDown();
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -929,6 +913,25 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
return mIsBeingDragged;
|
||||
}
|
||||
|
||||
/**
|
||||
* If being flinged and user touches the screen, initiate drag; otherwise don't.
|
||||
*/
|
||||
private void updateIsBeingDraggedOnTouchDown() {
|
||||
// mScroller.isFinished should be false when being flinged.
|
||||
final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
|
||||
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
|
||||
|
||||
if (finishedScrolling) {
|
||||
mIsBeingDragged = false;
|
||||
if (!mScroller.isFinished() && !mFreeScroll) {
|
||||
setCurrentPage(getNextPage());
|
||||
pageEndTransition();
|
||||
}
|
||||
} else {
|
||||
mIsBeingDragged = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHandlingTouch() {
|
||||
return mIsBeingDragged;
|
||||
}
|
||||
@@ -1085,7 +1088,9 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
if (mFreeScroll) {
|
||||
setCurrentPage(getNextPage());
|
||||
} else if (wasFreeScroll) {
|
||||
snapToPage(getNextPage());
|
||||
if (getScrollForPage(getNextPage()) != getScrollX()) {
|
||||
snapToPage(getNextPage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,6 +1109,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
|
||||
switch (action & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
updateIsBeingDraggedOnTouchDown();
|
||||
|
||||
/*
|
||||
* If being flinged and user touches, stop the fling. isFinished
|
||||
* will be false if being flinged.
|
||||
@@ -1562,12 +1569,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 +1622,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;
|
||||
}
|
||||
|
||||
@@ -18,28 +18,33 @@ package com.android.launcher3;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
import com.android.launcher3.compat.PackageInstallerCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
|
||||
|
||||
/**
|
||||
* BroadcastReceiver to handle session commit intent.
|
||||
*/
|
||||
@@ -66,15 +71,29 @@ public class SessionCommitReceiver extends BroadcastReceiver {
|
||||
|
||||
SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
|
||||
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
|
||||
PackageInstallerCompat packageInstallerCompat = PackageInstallerCompat.getInstance(context);
|
||||
|
||||
if (TextUtils.isEmpty(info.getAppPackageName()) ||
|
||||
info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
|
||||
if (TextUtils.isEmpty(info.getAppPackageName())
|
||||
|| info.getInstallReason() != PackageManager.INSTALL_REASON_USER
|
||||
|| packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
|
||||
packageInstallerCompat.removePromiseIconId(info.getSessionId());
|
||||
return;
|
||||
}
|
||||
|
||||
queueAppIconAddition(context, info.getAppPackageName(), user);
|
||||
}
|
||||
|
||||
public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) {
|
||||
String packageName = sessionInfo.getAppPackageName();
|
||||
List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
|
||||
.getActivityList(packageName, getUserHandle(sessionInfo));
|
||||
if (activities == null || activities.isEmpty()) {
|
||||
// Ensure application isn't already installed.
|
||||
queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(),
|
||||
sessionInfo.getAppIcon(), getUserHandle(sessionInfo));
|
||||
}
|
||||
}
|
||||
|
||||
public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
|
||||
List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
|
||||
.getActivityList(packageName, user);
|
||||
@@ -82,7 +101,18 @@ public class SessionCommitReceiver extends BroadcastReceiver {
|
||||
// no activity found
|
||||
return;
|
||||
}
|
||||
InstallShortcutReceiver.queueActivityInfo(activities.get(0), context);
|
||||
queueAppIconAddition(context, packageName, activities.get(0).getLabel(), null, user);
|
||||
}
|
||||
|
||||
private static void queueAppIconAddition(Context context, String packageName,
|
||||
CharSequence label, Bitmap icon, UserHandle user) {
|
||||
Intent data = new Intent();
|
||||
data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
|
||||
new ComponentName(packageName, "")).setPackage(packageName));
|
||||
data.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
|
||||
data.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
|
||||
|
||||
InstallShortcutReceiver.queueApplication(data, user, context);
|
||||
}
|
||||
|
||||
public static boolean isEnabled(Context context) {
|
||||
|
||||
@@ -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 =
|
||||
@@ -728,7 +732,7 @@ public final class Utilities {
|
||||
int[] array = new int[tokenizer.countTokens()];
|
||||
int count = 0;
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
array[count] = Integer.parseInt(tokenizer.nextToken());
|
||||
array[count] = Integer.parseInt(tokenizer.nextToken().trim());
|
||||
count++;
|
||||
}
|
||||
return array;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1048,6 +1052,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
if (!mOverlayShown) {
|
||||
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
|
||||
Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
|
||||
mLauncher.getStatsLogManager().logSwipeOnContainer(true, 0);
|
||||
}
|
||||
mOverlayShown = true;
|
||||
// Not announcing the overlay page for accessibility since it announces itself.
|
||||
@@ -1057,6 +1062,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
if (!ued.isPreviousHomeGesture()) {
|
||||
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
|
||||
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
|
||||
mLauncher.getStatsLogManager().logSwipeOnContainer(false, -1);
|
||||
}
|
||||
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
|
||||
// When arriving to 0 overscroll from non-zero overscroll, announce page for
|
||||
@@ -1741,6 +1747,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 +2427,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 +3263,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.
|
||||
*/
|
||||
@@ -44,24 +50,26 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
|
||||
* The icon was added as an auto-install app, and is not ready to be used. This flag can't
|
||||
* be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
|
||||
* parsing.
|
||||
*
|
||||
* OR this icon was added due to it being an active install session created by the user.
|
||||
*/
|
||||
public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
|
||||
public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
|
||||
|
||||
/**
|
||||
* The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
|
||||
* is set, then the icon is either being installed or is in a broken state.
|
||||
*/
|
||||
public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100;
|
||||
public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
|
||||
|
||||
/**
|
||||
* Indicates that the widget restore has started.
|
||||
*/
|
||||
public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
|
||||
public static final int FLAG_RESTORE_STARTED = 1 << 3;
|
||||
|
||||
/**
|
||||
* Web UI supported.
|
||||
*/
|
||||
public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
|
||||
public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
|
||||
|
||||
/**
|
||||
* The intent used to start the application.
|
||||
@@ -82,11 +90,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 +113,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 +191,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,11 +203,16 @@ 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();
|
||||
if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
|
||||
|| hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
|
||||
|| hasStatusFlag(FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON))) {
|
||||
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
|
||||
// a packageName. In that case create a dummy componentName instead of adding additional
|
||||
// check everywhere.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.android.launcher3.accessibility;
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
|
||||
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
@@ -30,16 +32,17 @@ import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.PendingAddItemInfo;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.keyboard.CustomActionsPopup;
|
||||
import com.android.launcher3.notification.NotificationListener;
|
||||
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 +118,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));
|
||||
}
|
||||
@@ -163,6 +166,13 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
|
||||
public boolean performAction(final View host, final ItemInfo item, int action) {
|
||||
if (action == ACTION_LONG_CLICK && ShortcutUtil.isDeepShortcut(item)) {
|
||||
CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
|
||||
if (popup.canShow()) {
|
||||
popup.show();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (action == MOVE) {
|
||||
beginAccessibleDrag(host, item);
|
||||
} else if (action == ADD_TO_WORKSPACE) {
|
||||
|
||||
@@ -628,20 +628,4 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
|
||||
return super.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
final boolean result = super.dispatchTouchEvent(ev);
|
||||
switch (ev.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (result) mAllAppsStore.enableDeferUpdates(
|
||||
AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mAllAppsStore.disableDeferUpdates(AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +26,15 @@ import android.util.SparseIntArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.BaseRecyclerView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
@@ -41,8 +43,6 @@ import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* A RecyclerView with custom fast scroll support for the all apps view.
|
||||
*/
|
||||
@@ -114,6 +114,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
|
||||
if (mScrollbar != null) {
|
||||
mScrollbar.reattachThumbToScroll();
|
||||
}
|
||||
if (getLayoutManager() instanceof AppsGridLayoutManager) {
|
||||
AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager();
|
||||
if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
|
||||
// We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
|
||||
return;
|
||||
}
|
||||
}
|
||||
scrollToPosition(0);
|
||||
}
|
||||
|
||||
@@ -420,13 +427,4 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(int state) {
|
||||
super.onScrollStateChanged(state);
|
||||
|
||||
if (state == SCROLL_STATE_IDLE) {
|
||||
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,8 @@ public class AllAppsStore {
|
||||
|
||||
// Defer updates flag used to defer all apps updates to the next draw.
|
||||
public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0;
|
||||
// Defer updates flag used to defer all apps updates while the user interacts with all apps.
|
||||
public static final int DEFER_UPDATES_USER_INTERACTION = 1 << 1;
|
||||
// Defer updates flag used to defer all apps updates by a test's request.
|
||||
public static final int DEFER_UPDATES_TEST = 1 << 2;
|
||||
public static final int DEFER_UPDATES_TEST = 1 << 1;
|
||||
|
||||
private PackageUserKey mTempKey = new PackageUserKey(null, null);
|
||||
private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
|
||||
|
||||
@@ -18,7 +18,6 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.util.FloatProperty;
|
||||
import android.util.Log;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -32,7 +31,6 @@ import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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.anim;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.util.FloatProperty;
|
||||
|
||||
import com.android.launcher3.util.DefaultDisplay;
|
||||
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
/**
|
||||
* Utility class to build an object animator which follows the same path as a spring animation for
|
||||
* an underdamped spring.
|
||||
*/
|
||||
public class SpringAnimationBuilder<T> extends FloatProperty<T> {
|
||||
|
||||
private final T mTarget;
|
||||
private final FloatProperty<T> mProperty;
|
||||
|
||||
private float mStartValue;
|
||||
private float mEndValue;
|
||||
private float mVelocity = 0;
|
||||
|
||||
private float mStiffness = SpringForce.STIFFNESS_MEDIUM;
|
||||
private float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
|
||||
private float mMinVisibleChange = 1;
|
||||
|
||||
// Multiplier to the min visible change value for value threshold
|
||||
private static final float THRESHOLD_MULTIPLIER = 0.65f;
|
||||
|
||||
/**
|
||||
* The spring equation is given as
|
||||
* x = e^(-beta*t/2) * (a cos(gamma * t) + b sin(gamma * t)
|
||||
* v = e^(-beta*t/2) * ((2 * a * gamma + beta * b) * sin(gamma * t)
|
||||
* + (a * beta - 2 * b * gamma) * cos(gamma * t)) / 2
|
||||
*
|
||||
* a = x(0)
|
||||
* b = beta * x(0) / (2 * gamma) + v(0) / gamma
|
||||
*/
|
||||
private double beta;
|
||||
private double gamma;
|
||||
|
||||
private double a, b;
|
||||
private double va, vb;
|
||||
|
||||
// Threshold for velocity and value to determine when it's reasonable to assume that the spring
|
||||
// is approximately at rest.
|
||||
private double mValueThreshold;
|
||||
private double mVelocityThreshold;
|
||||
|
||||
private float mCurrentTime = 0;
|
||||
|
||||
public SpringAnimationBuilder(T target, FloatProperty<T> property) {
|
||||
super("dynamic-spring-property");
|
||||
mTarget = target;
|
||||
mProperty = property;
|
||||
|
||||
mStartValue = mProperty.get(target);
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setEndValue(float value) {
|
||||
mEndValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStartValue(float value) {
|
||||
mStartValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setValues(float... values) {
|
||||
if (values.length > 1) {
|
||||
mStartValue = values[0];
|
||||
mEndValue = values[values.length - 1];
|
||||
} else {
|
||||
mEndValue = values[0];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStiffness(
|
||||
@FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
|
||||
if (stiffness <= 0) {
|
||||
throw new IllegalArgumentException("Spring stiffness constant must be positive.");
|
||||
}
|
||||
mStiffness = stiffness;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setDampingRatio(
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
|
||||
float dampingRatio) {
|
||||
if (dampingRatio <= 0 || dampingRatio >= 1) {
|
||||
throw new IllegalArgumentException("Damping ratio must be between 0 and 1");
|
||||
}
|
||||
mDampingRatio = dampingRatio;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setMinimumVisibleChange(
|
||||
@FloatRange(from = 0.0, fromInclusive = false) float minimumVisibleChange) {
|
||||
if (minimumVisibleChange <= 0) {
|
||||
throw new IllegalArgumentException("Minimum visible change must be positive.");
|
||||
}
|
||||
mMinVisibleChange = minimumVisibleChange;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringAnimationBuilder<T> setStartVelocity(float startVelocity) {
|
||||
mVelocity = startVelocity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T object, float time) {
|
||||
mCurrentTime = time;
|
||||
mProperty.setValue(
|
||||
object, (float) (exponentialComponent(time) * cosSinX(time)) + mEndValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(T t) {
|
||||
return mCurrentTime;
|
||||
}
|
||||
|
||||
public ObjectAnimator build(Context context) {
|
||||
int singleFrameMs = DefaultDisplay.getSingleFrameMs(context);
|
||||
double naturalFreq = Math.sqrt(mStiffness);
|
||||
double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
|
||||
|
||||
// All the calculations assume the stable position to be 0, shift the values accordingly.
|
||||
beta = 2 * mDampingRatio * naturalFreq;
|
||||
gamma = dampedFreq;
|
||||
a = mStartValue - mEndValue;
|
||||
b = beta * a / (2 * gamma) + mVelocity / gamma;
|
||||
|
||||
va = a * beta / 2 - b * gamma;
|
||||
vb = a * gamma + beta * b / 2;
|
||||
|
||||
mValueThreshold = mMinVisibleChange * THRESHOLD_MULTIPLIER;
|
||||
|
||||
// This multiplier is used to calculate the velocity threshold given a certain value
|
||||
// threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
|
||||
// then the velocity is a reasonable threshold.
|
||||
mVelocityThreshold = mValueThreshold * 1000.0 / singleFrameMs;
|
||||
|
||||
// Find the duration (in seconds) for the spring to reach equilibrium.
|
||||
// equilibrium is reached when x = 0
|
||||
double duration = Math.atan2(-a, b) / gamma;
|
||||
|
||||
// Keep moving ahead until the velocity reaches equilibrium.
|
||||
double piByG = Math.PI / gamma;
|
||||
while (duration < 0 || Math.abs(exponentialComponent(duration) * cosSinV(duration))
|
||||
>= mVelocityThreshold) {
|
||||
duration += piByG;
|
||||
}
|
||||
|
||||
// Find the shortest time
|
||||
double edgeTime = Math.max(0, duration - piByG / 2);
|
||||
double minDiff = singleFrameMs / 2000.0; // Half frame time in seconds
|
||||
|
||||
do {
|
||||
if ((duration - edgeTime) < minDiff) {
|
||||
break;
|
||||
}
|
||||
double mid = (edgeTime + duration) / 2;
|
||||
if (isAtEquilibrium(mid)) {
|
||||
duration = mid;
|
||||
} else {
|
||||
edgeTime = mid;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
|
||||
long durationMs = (long) (1000.0 * duration);
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(mTarget, this, 0, (float) duration);
|
||||
animator.setDuration(durationMs).setInterpolator(Interpolators.LINEAR);
|
||||
animator.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mProperty.setValue(mTarget, mEndValue);
|
||||
}
|
||||
});
|
||||
return animator;
|
||||
}
|
||||
|
||||
private boolean isAtEquilibrium(double t) {
|
||||
double ec = exponentialComponent(t);
|
||||
|
||||
if (Math.abs(ec * cosSinX(t)) >= mValueThreshold) {
|
||||
return false;
|
||||
}
|
||||
return Math.abs(ec * cosSinV(t)) < mVelocityThreshold;
|
||||
}
|
||||
|
||||
private double exponentialComponent(double t) {
|
||||
return Math.pow(Math.E, - beta * t / 2);
|
||||
}
|
||||
|
||||
private double cosSinX(double t) {
|
||||
return cosSin(t, a, b);
|
||||
}
|
||||
|
||||
private double cosSinV(double t) {
|
||||
return cosSin(t, va, vb);
|
||||
}
|
||||
|
||||
private double cosSin(double t, double cosFactor, double sinFactor) {
|
||||
double angle = t * gamma;
|
||||
return cosFactor * Math.cos(angle) + sinFactor * Math.sin(angle);
|
||||
}
|
||||
}
|
||||
@@ -31,11 +31,14 @@ import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -167,6 +170,10 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
|
||||
|
||||
@Override
|
||||
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.APP_NOT_DISABLED, "onPackagesSuspended: " +
|
||||
Arrays.toString(packageNames));
|
||||
}
|
||||
mCallback.onPackagesSuspended(packageNames, user);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,25 @@ package com.android.launcher3.compat;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionInfo;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
|
||||
public abstract class PackageInstallerCompat {
|
||||
|
||||
// Set<String> of session ids of promise icons that have been added to the home screen
|
||||
// as FLAG_PROMISE_NEW_INSTALLS.
|
||||
protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
|
||||
|
||||
public static final int STATUS_INSTALLED = 0;
|
||||
public static final int STATUS_INSTALLING = 1;
|
||||
public static final int STATUS_FAILED = 2;
|
||||
@@ -44,15 +54,19 @@ public abstract class PackageInstallerCompat {
|
||||
}
|
||||
}
|
||||
|
||||
public static UserHandle getUserHandle(SessionInfo info) {
|
||||
return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a map of active installs to their progress
|
||||
*/
|
||||
public abstract HashMap<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
|
||||
public abstract HashMap<PackageUserKey, SessionInfo> updateAndGetActiveSessionCache();
|
||||
|
||||
/**
|
||||
* @return an active SessionInfo for {@param pkg} or null if none exists.
|
||||
*/
|
||||
public abstract PackageInstaller.SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
|
||||
public abstract SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
|
||||
|
||||
public abstract void onStop();
|
||||
|
||||
@@ -61,30 +75,44 @@ public abstract class PackageInstallerCompat {
|
||||
public final String packageName;
|
||||
public final int state;
|
||||
public final int progress;
|
||||
public final UserHandle user;
|
||||
|
||||
private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
|
||||
private PackageInstallInfo(@NonNull SessionInfo info) {
|
||||
this.state = STATUS_INSTALLING;
|
||||
this.packageName = info.getAppPackageName();
|
||||
this.componentName = new ComponentName(packageName, "");
|
||||
this.progress = (int) (info.getProgress() * 100f);
|
||||
this.user = getUserHandle(info);
|
||||
}
|
||||
|
||||
public PackageInstallInfo(String packageName, int state, int progress) {
|
||||
public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) {
|
||||
this.state = state;
|
||||
this.packageName = packageName;
|
||||
this.componentName = new ComponentName(packageName, "");
|
||||
this.progress = progress;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
|
||||
public static PackageInstallInfo fromInstallingState(SessionInfo info) {
|
||||
return new PackageInstallInfo(info);
|
||||
}
|
||||
|
||||
public static PackageInstallInfo fromState(int state, String packageName) {
|
||||
return new PackageInstallInfo(packageName, state, 0 /* progress */);
|
||||
public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
|
||||
return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract List<PackageInstaller.SessionInfo> getAllVerifiedSessions();
|
||||
public abstract List<SessionInfo> getAllVerifiedSessions();
|
||||
|
||||
/**
|
||||
* Returns true if a promise icon was already added to the home screen for {@param sessionId}.
|
||||
* Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
|
||||
*/
|
||||
public abstract boolean promiseIconAddedForId(int sessionId);
|
||||
|
||||
/**
|
||||
* Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
|
||||
*/
|
||||
public abstract void removePromiseIconId(int sessionId);
|
||||
}
|
||||
|
||||
@@ -21,17 +21,21 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.SessionCallback;
|
||||
import android.content.pm.PackageInstaller.SessionInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.launcher3.SessionCommitReceiver;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -39,11 +43,13 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.android.launcher3.Utilities.getPrefs;
|
||||
|
||||
public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@Thunk final SparseArray<String> mActiveSessions = new SparseArray<>();
|
||||
@Thunk final SparseArray<PackageUserKey> mActiveSessions = new SparseArray<>();
|
||||
|
||||
@Thunk final PackageInstaller mInstaller;
|
||||
private final IconCache mCache;
|
||||
@@ -51,6 +57,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
private final Context mAppContext;
|
||||
private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
|
||||
private final LauncherAppsCompat mLauncherApps;
|
||||
private final IntSet mPromiseIconIds;
|
||||
|
||||
PackageInstallerCompatVL(Context context) {
|
||||
mAppContext = context.getApplicationContext();
|
||||
@@ -59,17 +66,39 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
mWorker = new Handler(LauncherModel.getWorkerLooper());
|
||||
mInstaller.registerSessionCallback(mCallback, mWorker);
|
||||
mLauncherApps = LauncherAppsCompat.getInstance(context);
|
||||
mPromiseIconIds = IntSet.wrap(IntArray.wrap(Utilities.getIntArrayFromString(
|
||||
getPrefs(context).getString(PROMISE_ICON_IDS, ""))));
|
||||
|
||||
cleanUpPromiseIconIds();
|
||||
}
|
||||
|
||||
private void cleanUpPromiseIconIds() {
|
||||
IntArray existingIds = new IntArray();
|
||||
for (SessionInfo info : updateAndGetActiveSessionCache().values()) {
|
||||
existingIds.add(info.getSessionId());
|
||||
}
|
||||
IntArray idsToRemove = new IntArray();
|
||||
|
||||
for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
|
||||
if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
|
||||
idsToRemove.add(mPromiseIconIds.getArray().get(i));
|
||||
}
|
||||
}
|
||||
for (int i = idsToRemove.size() - 1; i >= 0; --i) {
|
||||
mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
|
||||
HashMap<String, SessionInfo> activePackages = new HashMap<>();
|
||||
UserHandle primaryUser = Process.myUserHandle();
|
||||
public HashMap<PackageUserKey, SessionInfo> updateAndGetActiveSessionCache() {
|
||||
HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
|
||||
for (SessionInfo info : getAllVerifiedSessions()) {
|
||||
addSessionInfoToCache(info, Utilities.ATLEAST_Q ? info.getUser() : primaryUser);
|
||||
addSessionInfoToCache(info, getUserHandle(info));
|
||||
if (info.getAppPackageName() != null) {
|
||||
activePackages.put(info.getAppPackageName(), info);
|
||||
mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
|
||||
activePackages.put(new PackageUserKey(info.getAppPackageName(),
|
||||
getUserHandle(info)), info);
|
||||
mActiveSessions.put(info.getSessionId(),
|
||||
new PackageUserKey(info.getAppPackageName(), getUserHandle(info)));
|
||||
}
|
||||
}
|
||||
return activePackages;
|
||||
@@ -78,7 +107,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
|
||||
for (SessionInfo info : getAllVerifiedSessions()) {
|
||||
boolean match = pkg.equals(info.getAppPackageName());
|
||||
if (Utilities.ATLEAST_Q && !user.equals(info.getUser())) {
|
||||
if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
|
||||
match = false;
|
||||
}
|
||||
if (match) {
|
||||
@@ -108,6 +137,30 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a promise app icon to the workspace iff:
|
||||
* - The settings for it are enabled
|
||||
* - The user installed the app
|
||||
* - There is an app icon and label (For apps with no launching activity, no icon is provided).
|
||||
* - The app is not already installed
|
||||
* - A promise icon for the session has not already been created
|
||||
*/
|
||||
private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
|
||||
if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
|
||||
&& SessionCommitReceiver.isEnabled(mAppContext)
|
||||
&& verify(sessionInfo) != null
|
||||
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
|
||||
&& sessionInfo.getAppIcon() != null
|
||||
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
|
||||
&& !mPromiseIconIds.contains(sessionInfo.getSessionId())
|
||||
&& mLauncherApps.getApplicationInfo(sessionInfo.getAppPackageName(), 0,
|
||||
getUserHandle(sessionInfo)) == null) {
|
||||
SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
|
||||
mPromiseIconIds.add(sessionInfo.getSessionId());
|
||||
updatePromiseIconPrefs();
|
||||
}
|
||||
}
|
||||
|
||||
private final SessionCallback mCallback = new SessionCallback() {
|
||||
|
||||
@Override
|
||||
@@ -120,19 +173,31 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
PackageInstallInfo.fromInstallingState(sessionInfo));
|
||||
}
|
||||
}
|
||||
|
||||
tryQueuePromiseAppIcon(sessionInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(int sessionId, boolean success) {
|
||||
// For a finished session, we can't get the session info. So use the
|
||||
// packageName from our local cache.
|
||||
String packageName = mActiveSessions.get(sessionId);
|
||||
PackageUserKey key = mActiveSessions.get(sessionId);
|
||||
mActiveSessions.remove(sessionId);
|
||||
|
||||
if (packageName != null) {
|
||||
sendUpdate(PackageInstallInfo.fromState(
|
||||
success ? STATUS_INSTALLED : STATUS_FAILED,
|
||||
packageName));
|
||||
if (key != null && key.mPackageName != null) {
|
||||
String packageName = key.mPackageName;
|
||||
sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
|
||||
packageName, key.mUser));
|
||||
|
||||
if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
|
||||
&& mPromiseIconIds.contains(sessionId)) {
|
||||
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
|
||||
if (appState != null) {
|
||||
appState.getModel().onSessionFailure(packageName, key.mUser);
|
||||
}
|
||||
// If it is successful, the id is removed in the the package added flow.
|
||||
removePromiseIconId(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,14 +214,18 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
|
||||
@Override
|
||||
public void onBadgingChanged(int sessionId) {
|
||||
pushSessionDisplayToLauncher(sessionId);
|
||||
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
|
||||
if (sessionInfo != null) {
|
||||
tryQueuePromiseAppIcon(sessionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
|
||||
SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
|
||||
if (session != null && session.getAppPackageName() != null) {
|
||||
mActiveSessions.put(sessionId, session.getAppPackageName());
|
||||
addSessionInfoToCache(session, Process.myUserHandle());
|
||||
mActiveSessions.put(session.getSessionId(),
|
||||
new PackageUserKey(session.getAppPackageName(), getUserHandle(session)));
|
||||
addSessionInfoToCache(session, getUserHandle(session));
|
||||
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
|
||||
if (app != null) {
|
||||
app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
|
||||
@@ -178,7 +247,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
if (!mSessionVerifiedMap.containsKey(pkg)) {
|
||||
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
|
||||
boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
|
||||
ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null;
|
||||
ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null;
|
||||
mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
|
||||
}
|
||||
}
|
||||
@@ -198,4 +267,23 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promiseIconAddedForId(int sessionId) {
|
||||
return mPromiseIconIds.contains(sessionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePromiseIconId(int sessionId) {
|
||||
if (mPromiseIconIds.contains(sessionId)) {
|
||||
mPromiseIconIds.getArray().removeValue(sessionId);
|
||||
updatePromiseIconPrefs();
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePromiseIconPrefs() {
|
||||
getPrefs(mAppContext).edit()
|
||||
.putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
@@ -66,6 +60,11 @@ abstract class BaseFlags {
|
||||
// When enabled the promise icon is visible in all apps while installation an app.
|
||||
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
|
||||
|
||||
// When enabled a promise icon is added to the home screen when install session is active.
|
||||
public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS =
|
||||
new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true,
|
||||
"Adds a promise icon to the home screen for new install sessions.");
|
||||
|
||||
// Enable moving the QSB on the 0th screen of the workspace
|
||||
public static final boolean QSB_ON_FIRST_SCREEN = true;
|
||||
|
||||
@@ -105,18 +104,22 @@ abstract class BaseFlags {
|
||||
"ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
|
||||
|
||||
public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
|
||||
"ENABLE_HINTS_IN_OVERVIEW", false,
|
||||
"ENABLE_HINTS_IN_OVERVIEW", true,
|
||||
"Show chip hints and gleams on the overview screen");
|
||||
|
||||
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
|
||||
"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 +135,30 @@ 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;
|
||||
// should be value that is hardcoded in client side.
|
||||
// Comparatively, getDefaultValue() can be overridden.
|
||||
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.description = checkNotNull(description);
|
||||
|
||||
synchronized (sLock) {
|
||||
sFlags.add(this);
|
||||
sFlags.add((TogglableFlag)this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,18 +168,22 @@ abstract class BaseFlags {
|
||||
currentValue = value;
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
void initialize(Context context) {
|
||||
currentValue = getFromStorage(context, defaultValue);
|
||||
|
||||
protected void initialize(Context context) {
|
||||
currentValue = getFromStorage(context, getDefaultValue());
|
||||
}
|
||||
|
||||
protected abstract boolean getOverridenDefaultValue(boolean value);
|
||||
|
||||
protected abstract void addChangeListener(Context context, Runnable r);
|
||||
|
||||
public void updateStorage(Context context, boolean value) {
|
||||
SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
|
||||
Context.MODE_PRIVATE).edit();
|
||||
if (value == defaultValue) {
|
||||
if (value == getDefaultValue()) {
|
||||
editor.remove(key).apply();
|
||||
} else {
|
||||
editor.putBoolean(key, value).apply();
|
||||
@@ -182,11 +192,11 @@ abstract class BaseFlags {
|
||||
|
||||
boolean getFromStorage(Context context, boolean defaultValue) {
|
||||
return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getBoolean(key, defaultValue);
|
||||
.getBoolean(key, getDefaultValue());
|
||||
}
|
||||
|
||||
boolean getDefaultValue() {
|
||||
return defaultValue;
|
||||
return getOverridenDefaultValue(defaultValue);
|
||||
}
|
||||
|
||||
/** Returns the value of the flag at process start, including any overrides present. */
|
||||
@@ -203,6 +213,8 @@ abstract class BaseFlags {
|
||||
return "TogglableFlag{"
|
||||
+ "key=" + key + ", "
|
||||
+ "defaultValue=" + defaultValue + ", "
|
||||
+ "overriddenDefaultValue=" + getOverridenDefaultValue(defaultValue) + ", "
|
||||
+ "currentValue=" + currentValue + ", "
|
||||
+ "description=" + description
|
||||
+ "}";
|
||||
}
|
||||
@@ -213,9 +225,9 @@ 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.getDefaultValue() == that.getDefaultValue())
|
||||
&& (this.description.equals(that.getDescription()));
|
||||
}
|
||||
return false;
|
||||
@@ -227,54 +239,10 @@ abstract class BaseFlags {
|
||||
h$ *= 1000003;
|
||||
h$ ^= key.hashCode();
|
||||
h$ *= 1000003;
|
||||
h$ ^= defaultValue ? 1231 : 1237;
|
||||
h$ ^= getDefaultValue() ? 1231 : 1237;
|
||||
h$ *= 1000003;
|
||||
h$ ^= description.hashCode();
|
||||
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,10 +54,16 @@ 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;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE, "DragDriver.ACTION_CANCEL");
|
||||
}
|
||||
mEventListener.onDriverDragCancel();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -516,7 +516,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mFolderIcon.setBackgroundVisible(false);
|
||||
mFolderIcon.setIconVisible(false);
|
||||
mFolderIcon.drawLeaveBehindIfExists();
|
||||
}
|
||||
@Override
|
||||
@@ -646,7 +646,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
clearFocus();
|
||||
if (mFolderIcon != null) {
|
||||
mFolderIcon.setVisibility(View.VISIBLE);
|
||||
mFolderIcon.setBackgroundVisible(true);
|
||||
mFolderIcon.setIconVisible(true);
|
||||
mFolderIcon.mFolderName.setTextVisibility(true);
|
||||
if (wasAnimated) {
|
||||
mFolderIcon.animateBgShadowAndStroke();
|
||||
|
||||
@@ -65,6 +65,7 @@ import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.views.IconLabelDotView;
|
||||
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -73,7 +74,7 @@ import java.util.List;
|
||||
/**
|
||||
* An icon that can appear on in the workspace representing an {@link Folder}.
|
||||
*/
|
||||
public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView {
|
||||
|
||||
@Thunk Launcher mLauncher;
|
||||
@Thunk Folder mFolder;
|
||||
@@ -107,6 +108,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
|
||||
private Alarm mOpenAlarm = new Alarm();
|
||||
|
||||
private boolean mForceHideDot;
|
||||
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
|
||||
private FolderDotInfo mDotInfo = new FolderDotInfo();
|
||||
private DotRenderer mDotRenderer;
|
||||
@@ -409,6 +411,20 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
return mPreviewLayoutRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceHideDot(boolean forceHideDot) {
|
||||
if (mForceHideDot == forceHideDot) {
|
||||
return;
|
||||
}
|
||||
mForceHideDot = forceHideDot;
|
||||
|
||||
if (forceHideDot) {
|
||||
invalidate();
|
||||
} else if (hasDot()) {
|
||||
animateDotScale(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false
|
||||
* (the dot is being added or removed).
|
||||
@@ -468,7 +484,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
mBackground.setInvalidateDelegate(this);
|
||||
}
|
||||
|
||||
public void setBackgroundVisible(boolean visible) {
|
||||
@Override
|
||||
public void setIconVisible(boolean visible) {
|
||||
mBackgroundIsVisible = visible;
|
||||
invalidate();
|
||||
}
|
||||
@@ -509,7 +526,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||
}
|
||||
|
||||
public void drawDot(Canvas canvas) {
|
||||
if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) {
|
||||
if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
|
||||
Rect iconBounds = mDotParams.iconBounds;
|
||||
BubbleTextView.getIconBounds(this, iconBounds,
|
||||
mLauncher.getWallpaperDeviceProfile().iconSizePx);
|
||||
|
||||
@@ -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,10 +38,11 @@ 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.config.FeatureFlags;
|
||||
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
|
||||
import com.android.launcher3.icons.cache.BaseIconCache;
|
||||
import com.android.launcher3.icons.cache.CachingLogic;
|
||||
@@ -50,8 +53,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 +76,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
|
||||
@@ -237,7 +238,8 @@ public class IconCache extends BaseIconCache {
|
||||
|
||||
@Override
|
||||
protected String getIconSystemState(String packageName) {
|
||||
return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
|
||||
return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
|
||||
+ ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
|
||||
}
|
||||
|
||||
public static abstract class IconLoadRequest extends HandlerRunnable {
|
||||
|
||||
+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,7 @@ 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 logSwipeOnContainer(boolean isSwipingToLeft, int pageId) { }
|
||||
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) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.content.pm.PackageInstaller.SessionInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.Pair;
|
||||
@@ -32,6 +34,8 @@ import com.android.launcher3.LauncherModel.Callbacks;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.compat.LauncherAppsCompat;
|
||||
import com.android.launcher3.compat.PackageInstallerCompat;
|
||||
import com.android.launcher3.util.GridOccupancy;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
|
||||
@@ -85,6 +89,10 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
|
||||
}
|
||||
}
|
||||
|
||||
PackageInstallerCompat packageInstaller =
|
||||
PackageInstallerCompat.getInstance(app.getContext());
|
||||
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(app.getContext());
|
||||
|
||||
for (ItemInfo item : filteredItems) {
|
||||
// Find appropriate space for the item.
|
||||
int[] coords = findSpaceForItem(app, dataModel, workspaceScreens,
|
||||
@@ -101,6 +109,36 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
|
||||
throw new RuntimeException("Unexpected info type");
|
||||
}
|
||||
|
||||
if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) {
|
||||
WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item;
|
||||
String packageName = item.getTargetComponent() != null
|
||||
? item.getTargetComponent().getPackageName() : null;
|
||||
if (packageName == null) {
|
||||
continue;
|
||||
}
|
||||
SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
|
||||
packageName);
|
||||
if (sessionInfo == null) {
|
||||
List<LauncherActivityInfo> activities = launcherApps
|
||||
.getActivityList(packageName, item.user);
|
||||
if (activities != null && !activities.isEmpty()) {
|
||||
// App was installed while launcher was in the background.
|
||||
itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
|
||||
.makeWorkspaceItem();
|
||||
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
|
||||
wii.title = "";
|
||||
wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
|
||||
app.getIconCache().getTitleAndIcon(wii,
|
||||
((WorkspaceItemInfo) itemInfo).usingLowResIcon());
|
||||
} else {
|
||||
// Session was cancelled, do not add.
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
|
||||
}
|
||||
}
|
||||
|
||||
// Add the shortcut to the db
|
||||
getModelWriter().addItemToDatabase(itemInfo,
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
|
||||
|
||||
@@ -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) { }
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.util.MultiHashMap;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -34,6 +35,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static android.os.Process.myUserHandle;
|
||||
|
||||
/**
|
||||
* Helper class to send broadcasts to package installers that have:
|
||||
* - Items on the first screen
|
||||
@@ -60,7 +63,7 @@ public class FirstScreenBroadcast {
|
||||
|
||||
private final MultiHashMap<String, String> mPackagesForInstaller;
|
||||
|
||||
public FirstScreenBroadcast(HashMap<String, SessionInfo> sessionInfoForPackage) {
|
||||
public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
|
||||
mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
|
||||
}
|
||||
|
||||
@@ -69,11 +72,13 @@ public class FirstScreenBroadcast {
|
||||
* of packages with active sessions for that installer.
|
||||
*/
|
||||
private MultiHashMap<String, String> getPackagesForInstaller(
|
||||
HashMap<String, SessionInfo> sessionInfoForPackage) {
|
||||
HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
|
||||
MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
|
||||
for (Map.Entry<String, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
|
||||
packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
|
||||
entry.getKey());
|
||||
for (Map.Entry<PackageUserKey, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
|
||||
if (myUserHandle().equals(entry.getKey().mUser)) {
|
||||
packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
|
||||
entry.getKey().mPackageName);
|
||||
}
|
||||
}
|
||||
return packagesForInstaller;
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
|
||||
import com.android.launcher3.util.GridOccupancy;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
@@ -970,8 +972,9 @@ public class GridSizeMigrationTask {
|
||||
.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
|
||||
validPackages.add(info.packageName);
|
||||
}
|
||||
validPackages.addAll(PackageInstallerCompat.getInstance(context)
|
||||
.updateAndGetActiveSessionCache().keySet());
|
||||
PackageInstallerCompat.getInstance(context)
|
||||
.updateAndGetActiveSessionCache().keySet()
|
||||
.forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
|
||||
return validPackages;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ public class LoaderCursor extends CursorWrapper {
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
info.title = Utilities.trim(title);
|
||||
}
|
||||
} else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
|
||||
} else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
|
||||
if (TextUtils.isEmpty(info.title)) {
|
||||
info.title = getTitle();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.launcher3.model;
|
||||
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
|
||||
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
|
||||
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
|
||||
import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
|
||||
import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
@@ -49,8 +50,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 +62,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;
|
||||
@@ -72,6 +73,7 @@ import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.LooperIdleLock;
|
||||
import com.android.launcher3.util.MultiHashMap;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -196,7 +198,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
|
||||
@@ -281,8 +283,9 @@ public class LoaderTask implements Runnable {
|
||||
synchronized (mBgDataModel) {
|
||||
mBgDataModel.clear();
|
||||
|
||||
final HashMap<String, SessionInfo> installingPkgs =
|
||||
final HashMap<PackageUserKey, SessionInfo> installingPkgs =
|
||||
mPackageInstaller.updateAndGetActiveSessionCache();
|
||||
final PackageUserKey tempPackageKey = new PackageUserKey(null, null);
|
||||
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
|
||||
|
||||
Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
|
||||
@@ -419,9 +422,10 @@ public class LoaderTask implements Runnable {
|
||||
// installed later.
|
||||
FileLog.d(TAG, "package not yet restored: " + targetPkg);
|
||||
|
||||
tempPackageKey.update(targetPkg, c.user);
|
||||
if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) {
|
||||
// Restore has started once.
|
||||
} else if (installingPkgs.containsKey(targetPkg)) {
|
||||
} else if (installingPkgs.containsKey(tempPackageKey)) {
|
||||
// App restore has started. Update the flag
|
||||
c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
|
||||
c.updater().put(LauncherSettings.Favorites.RESTORED,
|
||||
@@ -536,7 +540,8 @@ public class LoaderTask implements Runnable {
|
||||
}
|
||||
|
||||
if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
|
||||
SessionInfo si = installingPkgs.get(targetPkg);
|
||||
tempPackageKey.update(targetPkg, c.user);
|
||||
SessionInfo si = installingPkgs.get(tempPackageKey);
|
||||
if (si == null) {
|
||||
info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
|
||||
} else {
|
||||
@@ -630,8 +635,10 @@ public class LoaderTask implements Runnable {
|
||||
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
|
||||
component);
|
||||
appWidgetInfo.restoreStatus = c.restoreFlag;
|
||||
|
||||
tempPackageKey.update(component.getPackageName(), c.user);
|
||||
SessionInfo si =
|
||||
installingPkgs.get(component.getPackageName());
|
||||
installingPkgs.get(tempPackageKey);
|
||||
Integer installProgress = si == null
|
||||
? null
|
||||
: (int) (si.getProgress() * 100);
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.launcher3.model;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
|
||||
import com.android.launcher3.AllAppsList;
|
||||
import com.android.launcher3.AppInfo;
|
||||
@@ -56,7 +55,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
|
||||
ApplicationInfo ai = app.getContext()
|
||||
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
|
||||
if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
|
||||
app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
|
||||
app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Ignore
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.logging.FileLog;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.FlagOp;
|
||||
import com.android.launcher3.util.IntSparseArrayMap;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
@@ -55,6 +56,8 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
|
||||
|
||||
/**
|
||||
* Handles updates due to changes in package manager (app installed/updated/removed)
|
||||
* or when a user availability changes.
|
||||
@@ -85,6 +88,10 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.APP_NOT_DISABLED, "PackageUpdatedTask: " + mOp + ", " +
|
||||
Arrays.toString(mPackages));
|
||||
}
|
||||
final Context context = app.getContext();
|
||||
final IconCache iconCache = app.getIconCache();
|
||||
|
||||
@@ -99,7 +106,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
|
||||
iconCache.updateIconsForPkg(packages[i], mUser);
|
||||
if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
|
||||
appsList.removePackage(packages[i], Process.myUserHandle());
|
||||
appsList.removePackage(packages[i], mUser);
|
||||
}
|
||||
appsList.addPackage(context, packages[i], mUser);
|
||||
|
||||
@@ -227,8 +234,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
|
||||
isTargetValid = LauncherAppsCompat.getInstance(context)
|
||||
.isActivityEnabledForProfile(cn, mUser);
|
||||
}
|
||||
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
|
||||
&& !isTargetValid) {
|
||||
if (si.hasStatusFlag(FLAG_AUTOINSTALL_ICON)) {
|
||||
if (updateWorkspaceItemIntent(context, si, packageName)) {
|
||||
infoUpdated = true;
|
||||
} else if (si.hasPromiseIconUi()) {
|
||||
|
||||
@@ -17,13 +17,17 @@
|
||||
package com.android.launcher3.notification;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Person;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* The key data associated with the notification, used to determine what to include
|
||||
* in dots and dummy popup views before they are populated.
|
||||
@@ -33,20 +37,27 @@ import androidx.annotation.NonNull;
|
||||
public class NotificationKeyData {
|
||||
public final String notificationKey;
|
||||
public final String shortcutId;
|
||||
@NonNull
|
||||
public final String[] personKeysFromNotification;
|
||||
public int count;
|
||||
|
||||
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 +65,14 @@ 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().filter(person -> person.getKey() != null)
|
||||
.map(Person::getKey).sorted().toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof NotificationKeyData)) {
|
||||
|
||||
@@ -36,7 +36,6 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@@ -53,7 +52,6 @@ import com.android.launcher3.ItemInfoWithIcon;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
@@ -65,13 +63,12 @@ import com.android.launcher3.notification.NotificationInfo;
|
||||
import com.android.launcher3.notification.NotificationItemView;
|
||||
import com.android.launcher3.notification.NotificationKeyData;
|
||||
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||
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 +198,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;
|
||||
}
|
||||
|
||||
@@ -300,7 +297,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
|
||||
}
|
||||
|
||||
mLauncher.getDragController().addDragListener(this);
|
||||
mOriginalIcon.forceHideDot(true);
|
||||
mOriginalIcon.setForceHideDot(true);
|
||||
|
||||
// All views are added. Animate layout from now on.
|
||||
setLayoutTransition(new LayoutTransition());
|
||||
@@ -563,14 +560,14 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
|
||||
protected void onCreateCloseAnimation(AnimatorSet anim) {
|
||||
// Animate original icon's text back in.
|
||||
anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
|
||||
mOriginalIcon.forceHideDot(false);
|
||||
mOriginalIcon.setForceHideDot(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeComplete() {
|
||||
super.closeComplete();
|
||||
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
|
||||
mOriginalIcon.forceHideDot(false);
|
||||
mOriginalIcon.setForceHideDot(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -173,12 +173,6 @@ public class RestoreDbTask {
|
||||
values.put(Favorites.PROFILE_ID, newProfileId);
|
||||
db.update(Favorites.TABLE_NAME, values, "profileId = ?",
|
||||
new String[]{Long.toString(oldProfileId)});
|
||||
|
||||
// Change default value of the column.
|
||||
db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
|
||||
Favorites.addTableToDb(db, newProfileId, false);
|
||||
db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
|
||||
dropTable(db, "favorites_old");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,13 @@
|
||||
*/
|
||||
package com.android.launcher3.testing;
|
||||
|
||||
import static android.graphics.Bitmap.Config.ARGB_8888;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
@@ -28,6 +33,7 @@ import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class TestInformationHandler implements ResourceBasedOverride {
|
||||
@@ -41,6 +47,7 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
||||
protected DeviceProfile mDeviceProfile;
|
||||
protected LauncherAppState mLauncherAppState;
|
||||
protected Launcher mLauncher;
|
||||
private static LinkedList mLeaks;
|
||||
|
||||
public void init(Context context) {
|
||||
mContext = context;
|
||||
@@ -112,7 +119,37 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_TOTAL_PSS_KB: {
|
||||
Debug.MemoryInfo mem = new Debug.MemoryInfo();
|
||||
Debug.getMemoryInfo(mem);
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_JAVA_LEAK: {
|
||||
if (mLeaks == null) mLeaks = new LinkedList();
|
||||
|
||||
// Allocate and dirty the memory.
|
||||
final int leakSize = 1024 * 1024;
|
||||
final byte[] bytes = new byte[leakSize];
|
||||
for (int i = 0; i < leakSize; i += 239) {
|
||||
bytes[i] = (byte) (i % 256);
|
||||
}
|
||||
mLeaks.add(bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_NATIVE_LEAK: {
|
||||
if (mLeaks == null) mLeaks = new LinkedList();
|
||||
|
||||
// Allocate and dirty a bitmap.
|
||||
final Bitmap bitmap = Bitmap.createBitmap(512, 512, ARGB_8888);
|
||||
bitmap.eraseColor(Color.RED);
|
||||
mLeaks.add(bitmap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,10 +73,17 @@ 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_TOTAL_PSS_KB = "total_pss";
|
||||
public static final String REQUEST_JAVA_LEAK = "java-leak";
|
||||
public static final String REQUEST_NATIVE_LEAK = "native-leak";
|
||||
|
||||
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";
|
||||
public static final String APP_NOT_DISABLED = "b/139891609";
|
||||
public static final String ALL_APPS_UPON_RECENTS = "b/139941530";
|
||||
public static final String STABLE_STATE_MISMATCH = "b/140311911";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -27,7 +31,7 @@ public class PackageUserKey {
|
||||
update(packageName, user);
|
||||
}
|
||||
|
||||
private void update(String packageName, UserHandle user) {
|
||||
public void update(String packageName, UserHandle user) {
|
||||
mPackageName = packageName;
|
||||
mUser = user;
|
||||
mHashCode = Arrays.hashCode(new Object[] {packageName, 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,79 @@
|
||||
/*
|
||||
* 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 {
|
||||
/**
|
||||
* Returns true when we should show shortcut menu for the item.
|
||||
*/
|
||||
public static boolean supportsShortcuts(ItemInfo info) {
|
||||
return isActive(info) && (isApp(info) || isPinnedShortcut(info));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when we should show depp shortcuts in shortcut menu for the item.
|
||||
*/
|
||||
public static boolean supportsDeepShortcuts(ItemInfo info) {
|
||||
return isActive(info) && isApp(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shortcut id if the item is a pinned shortcut.
|
||||
*/
|
||||
public static String getShortcutIdIfPinnedShortcut(ItemInfo info) {
|
||||
return isActive(info) && isPinnedShortcut(info)
|
||||
? ShortcutKey.fromItemInfo(info).getId() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the person keys associated with the item. (Has no function right now.)
|
||||
*/
|
||||
public static String[] getPersonKeysIfPinnedShortcut(ItemInfo info) {
|
||||
return isActive(info) && isPinnedShortcut(info)
|
||||
? ((WorkspaceItemInfo) info).getPersonKeys() : Utilities.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the item is a deep shortcut.
|
||||
*/
|
||||
public static boolean isDeepShortcut(ItemInfo info) {
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||
&& info instanceof WorkspaceItemInfo;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -153,15 +153,15 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
||||
}
|
||||
|
||||
protected void handleClose(boolean animate, long defaultDuration) {
|
||||
if (mIsOpen && !animate) {
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
}
|
||||
if (!animate) {
|
||||
mOpenCloseAnimator.cancel();
|
||||
setTranslationShift(TRANSLATION_SHIFT_CLOSED);
|
||||
onCloseComplete();
|
||||
return;
|
||||
}
|
||||
if (!mIsOpen) {
|
||||
return;
|
||||
}
|
||||
mOpenCloseAnimator.setValues(
|
||||
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
|
||||
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@@ -41,6 +42,7 @@ import android.widget.FrameLayout;
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.InsettableFrameLayout;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
@@ -261,6 +263,10 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
}
|
||||
case ACTION_CANCEL:
|
||||
case ACTION_UP:
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_DRAG_TO_WORKSPACE,
|
||||
"BaseDragLayer.ACTION_UP/CANCEL " + ev);
|
||||
}
|
||||
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
|
||||
mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
|
||||
break;
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.launcher3.views;
|
||||
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
|
||||
import static com.android.launcher3.Utilities.getBadge;
|
||||
import static com.android.launcher3.Utilities.getFullDrawable;
|
||||
import static com.android.launcher3.Utilities.isRtl;
|
||||
import static com.android.launcher3.Utilities.mapToRange;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
|
||||
@@ -564,11 +563,6 @@ public class FloatingIconView extends View implements
|
||||
*/
|
||||
private void checkIconResult(View originalView, boolean isOpening) {
|
||||
CancellationSignal cancellationSignal = new CancellationSignal();
|
||||
if (!isOpening) {
|
||||
// Hide immediately since the floating view starts at a different location.
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
cancellationSignal.setOnCancelListener(() -> originalView.setVisibility(VISIBLE));
|
||||
}
|
||||
|
||||
if (mIconLoadResult == null) {
|
||||
Log.w(TAG, "No icon load result found in checkIconResult");
|
||||
@@ -580,7 +574,7 @@ public class FloatingIconView extends View implements
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
if (isOpening) {
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
hideOriginalView(originalView);
|
||||
}
|
||||
} else {
|
||||
mIconLoadResult.onIconLoaded = () -> {
|
||||
@@ -591,15 +585,26 @@ public class FloatingIconView extends View implements
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
|
||||
// Delay swapping views until the icon is loaded to prevent a flash.
|
||||
setVisibility(VISIBLE);
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
if (isOpening) {
|
||||
// Delay swapping views until the icon is loaded to prevent a flash.
|
||||
hideOriginalView(originalView);
|
||||
}
|
||||
};
|
||||
mLoadIconSignal = cancellationSignal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void hideOriginalView(View originalView) {
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
((BubbleTextView) originalView).setIconVisible(false);
|
||||
((BubbleTextView) originalView).setForceHideDot(true);
|
||||
} else {
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackgroundDrawableBounds(float scale) {
|
||||
sTmpRect.set(mFinalDrawableBounds);
|
||||
Utilities.scaleRectAboutCenter(sTmpRect, scale);
|
||||
@@ -716,7 +721,7 @@ public class FloatingIconView extends View implements
|
||||
*/
|
||||
@UiThread
|
||||
public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
|
||||
IconLoadResult result = new IconLoadResult();
|
||||
IconLoadResult result = new IconLoadResult(info);
|
||||
new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
|
||||
RectF position = new RectF();
|
||||
getLocationBoundsForView(l, v, isOpening, position);
|
||||
@@ -745,10 +750,13 @@ public class FloatingIconView extends View implements
|
||||
|
||||
// Get the drawable on the background thread
|
||||
boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
|
||||
view.mIconLoadResult = sIconLoadResult;
|
||||
if (shouldLoadIcon && view.mIconLoadResult == null) {
|
||||
view.mIconLoadResult = fetchIcon(launcher, originalView,
|
||||
(ItemInfo) originalView.getTag(), isOpening);
|
||||
if (shouldLoadIcon) {
|
||||
if (sIconLoadResult != null && sIconLoadResult.itemInfo == originalView.getTag()) {
|
||||
view.mIconLoadResult = sIconLoadResult;
|
||||
} else {
|
||||
view.mIconLoadResult = fetchIcon(launcher, originalView,
|
||||
(ItemInfo) originalView.getTag(), isOpening);
|
||||
}
|
||||
}
|
||||
sIconLoadResult = null;
|
||||
|
||||
@@ -776,7 +784,12 @@ public class FloatingIconView extends View implements
|
||||
|
||||
if (hideOriginal) {
|
||||
if (isOpening) {
|
||||
originalView.setVisibility(VISIBLE);
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
((BubbleTextView) originalView).setIconVisible(true);
|
||||
((BubbleTextView) originalView).setForceHideDot(false);
|
||||
} else {
|
||||
originalView.setVisibility(VISIBLE);
|
||||
}
|
||||
view.finish(dragLayer);
|
||||
} else {
|
||||
view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
|
||||
@@ -804,38 +817,33 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
});
|
||||
|
||||
if (mBadge != null && !(mOriginalIcon instanceof FolderIcon)) {
|
||||
if (mBadge != null) {
|
||||
ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
|
||||
badgeFade.addUpdateListener(valueAnimator -> invalidate());
|
||||
fade.play(badgeFade);
|
||||
}
|
||||
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
BubbleTextView btv = (BubbleTextView) originalView;
|
||||
btv.forceHideDot(true);
|
||||
if (originalView instanceof IconLabelDotView) {
|
||||
IconLabelDotView view = (IconLabelDotView) originalView;
|
||||
fade.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
btv.forceHideDot(false);
|
||||
view.setIconVisible(true);
|
||||
view.setForceHideDot(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (originalView instanceof FolderIcon) {
|
||||
FolderIcon folderIcon = (FolderIcon) originalView;
|
||||
folderIcon.setBackgroundVisible(false);
|
||||
folderIcon.getFolderName().setTextVisibility(false);
|
||||
fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
BubbleTextView btv = (BubbleTextView) originalView;
|
||||
fade.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
folderIcon.setBackgroundVisible(true);
|
||||
if (folderIcon.hasDot()) {
|
||||
folderIcon.animateDotScale(0, 1f);
|
||||
}
|
||||
public void onAnimationStart(Animator animation) {
|
||||
btv.setIconVisible(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
|
||||
} else if (!(originalView instanceof FolderIcon)) {
|
||||
fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
|
||||
}
|
||||
|
||||
@@ -890,10 +898,15 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
|
||||
private static class IconLoadResult {
|
||||
final ItemInfo itemInfo;
|
||||
Drawable drawable;
|
||||
Drawable badge;
|
||||
int iconOffset;
|
||||
Runnable onIconLoaded;
|
||||
boolean isIconLoaded;
|
||||
|
||||
public IconLoadResult(ItemInfo itemInfo) {
|
||||
this.itemInfo = itemInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.views;
|
||||
|
||||
/**
|
||||
* A view that has an icon, label, and notification dot.
|
||||
*/
|
||||
public interface IconLabelDotView {
|
||||
void setIconVisible(boolean visible);
|
||||
void setForceHideDot(boolean hide);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user