Snap for 6224475 from 98e484548c to rvc-release

Change-Id: I9a0f573059b3a664c2d90b1cad26343959f68998
This commit is contained in:
android-build-team Robot
2020-02-21 03:11:27 +00:00
130 changed files with 1626 additions and 974 deletions
+2 -1
View File
@@ -32,7 +32,7 @@ android_library {
}
java_library_static {
name: "launcher-log-protos-lite",
name: "launcher_log_protos_lite",
srcs: [
"protos/*.proto",
"proto_overrides/*.proto",
@@ -45,4 +45,5 @@ java_library_static {
"proto_overrides",
],
},
static_libs: ["libprotobuf-java-lite"],
}
+11 -3
View File
@@ -48,7 +48,9 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.preference_preference \
iconloader_base
LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
LOCAL_STATIC_JAVA_LIBRARIES := \
LauncherPluginLib \
launcher_log_protos_lite
LOCAL_SRC_FILES := \
$(call all-proto-files-under, protos) \
@@ -144,7 +146,10 @@ LOCAL_USE_AAPT2 := true
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
LOCAL_STATIC_JAVA_LIBRARIES := \
SystemUISharedLib \
launcherprotosnano \
launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -213,7 +218,10 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
LOCAL_STATIC_JAVA_LIBRARIES := \
SystemUISharedLib \
launcherprotosnano \
launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -22,7 +22,7 @@ import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetListRowEntry;
@@ -59,7 +59,7 @@ public class WidgetsModel {
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
public List<ComponentWithLabel> update(LauncherAppState app,
public List<ComponentWithLabelAndIcon> update(LauncherAppState app,
@Nullable PackageUserKey packageUser) {
return Collections.emptyList();
}
@@ -350,6 +350,17 @@ public class BaseIconFactory implements AutoCloseable {
iconDpi);
}
/**
* Badges the provided source with the badge info
*/
public BitmapInfo badgeBitmap(Bitmap source, BitmapInfo badgeInfo) {
Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(source, c);
badgeWithDrawable(c, new FixedSizeBitmapDrawable(badgeInfo.icon));
});
return BitmapInfo.of(icon, badgeInfo.color);
}
private int extractColor(Bitmap bitmap) {
return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap);
}
@@ -281,7 +281,8 @@ public abstract class BaseIconCache {
ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
addIconToDB(values, componentName, info, userSerial);
addIconToDB(values, componentName, info, userSerial,
cachingLogic.getLastUpdatedTime(object, info));
}
/**
@@ -289,10 +290,10 @@ public abstract class BaseIconCache {
* @param values {@link ContentValues} containing icon & title
*/
private void addIconToDB(ContentValues values, ComponentName key,
PackageInfo info, long userSerial) {
PackageInfo info, long userSerial, long lastUpdateTime) {
values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
values.put(IconDB.COLUMN_LAST_UPDATED, lastUpdateTime);
values.put(IconDB.COLUMN_VERSION, info.versionCode);
mIconDb.insertOrReplace(values);
}
@@ -362,7 +363,8 @@ public abstract class BaseIconCache {
}
if (object != null) {
entry.title = cachingLogic.getLabel(object);
entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
entry.contentDescription = mPackageManager.getUserBadgedLabel(
cachingLogic.getDescription(object, entry.title), user);
}
}
}
@@ -449,7 +451,8 @@ public abstract class BaseIconCache {
// package updates.
ContentValues values = newContentValues(
iconInfo, entry.title.toString(), packageName, null);
addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user),
info.lastUpdateTime);
} catch (NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
@@ -34,6 +34,10 @@ public interface CachingLogic<T> {
CharSequence getLabel(T object);
default CharSequence getDescription(T object, CharSequence fallback) {
return fallback;
}
@NonNull
BitmapInfo loadIcon(Context context, T object);
@@ -24,6 +24,7 @@ import android.database.sqlite.SQLiteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -61,7 +62,7 @@ public class IconCacheUpdateHandler {
private final HashMap<String, PackageInfo> mPkgInfoMap;
private final BaseIconCache mIconCache;
private final HashMap<UserHandle, Set<String>> mPackagesToIgnore = new HashMap<>();
private final ArrayMap<UserHandle, Set<String>> mPackagesToIgnore = new ArrayMap<>();
private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray();
private boolean mFilterMode = MODE_SET_INVALID_ITEMS;
@@ -77,8 +78,16 @@ public class IconCacheUpdateHandler {
createPackageInfoMap();
}
public void setPackagesToIgnore(UserHandle userHandle, Set<String> packages) {
mPackagesToIgnore.put(userHandle, packages);
/**
* Sets a package to ignore for processing
*/
public void addPackagesToIgnore(UserHandle userHandle, String packageName) {
Set<String> packages = mPackagesToIgnore.get(userHandle);
if (packages == null) {
packages = new HashSet<>();
mPackagesToIgnore.put(userHandle, packages);
}
packages.add(packageName);
}
private void createPackageInfoMap() {
@@ -180,6 +189,7 @@ public class IconCacheUpdateHandler {
}
continue;
}
if (app == null) {
if (mFilterMode == MODE_SET_INVALID_ITEMS) {
mIconCache.remove(component, user);
@@ -262,6 +272,7 @@ public class IconCacheUpdateHandler {
T app = mAppsToUpdate.pop();
String pkg = mCachingLogic.getComponent(app).getPackageName();
PackageInfo info = mPkgInfoMap.get(pkg);
mIconCache.addIconToDBAndMemCache(
app, mCachingLogic, info, mUserSerial, true /*replace existing*/);
mUpdatedPackages.add(pkg);
+3
View File
@@ -48,3 +48,6 @@
-dontwarn android.view.**
-dontwarn android.os.**
-dontwarn android.graphics.**
# Ignore warnings for hidden utility classes referenced from the shared lib
-dontwarn com.android.internal.util.**
+34 -1
View File
@@ -58,6 +58,35 @@ message Target {
optional TipType tip_type = 17;
optional int32 search_query_length = 18;
optional bool is_work_app = 19;
optional FromFolderLabelState from_folder_label_state = 20;
optional ToFolderLabelState to_folder_label_state = 21;
// Note: proto does not support duplicate enum values, even if they belong to different enum type.
// Hence "FROM" and "TO" prefix added.
enum FromFolderLabelState{
FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
FROM_EMPTY = 1;
FROM_CUSTOM = 2;
FROM_SUGGESTED = 3;
}
enum ToFolderLabelState{
TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
UNCHANGED = 14;
}
}
// Used to define what type of item a Target would represent.
@@ -120,6 +149,8 @@ enum ControlType {
BACK_GESTURE = 19;
UNDO = 20;
DISMISS_PREDICTION = 21;
HYBRID_HOTSEAT_ACCEPTED = 22;
HYBRID_HOTSEAT_CANCELED = 23;
}
enum TipType {
@@ -129,6 +160,7 @@ enum TipType {
QUICK_SCRUB_TEXT = 3;
PREDICTION_TEXT = 4;
DWB_TOAST = 5;
HYBRID_HOTSEAT = 6;
}
// Used to define the action component of the LauncherEvent.
@@ -138,7 +170,8 @@ message Action {
AUTOMATED = 1;
COMMAND = 2;
TIP = 3;
// SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
SOFT_KEYBOARD = 4;
// HARD_KEYBOARD, ASSIST
}
enum Touch {
@@ -43,7 +43,6 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.InstantAppResolver;
@@ -170,14 +169,7 @@ public class DynamicItemCache {
List<ShortcutInfo> details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL);
if (!details.isEmpty()) {
WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
si.bitmap = li.createShortcutIcon(details.get(0), true /* badged */, null);
} catch (Exception e) {
if (DEBUG) {
Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
}
return null;
}
mIconCache.getShortcutIcon(si, details.get(0));
return si;
}
if (DEBUG) {
@@ -296,7 +296,7 @@ public class PredictionRowView extends LinearLayout implements
predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
predictedApps.add(predictedApp);
} else {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Predicted app not found: " + mapper);
}
}
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.Configuration;
@@ -32,6 +35,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.AbstractSlideInView;
@@ -95,6 +99,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
handleClose(true);
mHotseatEduController.migrate();
mHotseatEduController.finishOnboarding();
logUserAction(true);
Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show();
}
@@ -102,6 +107,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
if (mHotseatEduController == null) return;
Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
mHotseatEduController.finishOnboarding();
logUserAction(false);
handleClose(true);
}
@@ -133,7 +139,28 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
}
private void logUserAction(boolean migrated) {
LauncherLogProto.Action action = new LauncherLogProto.Action();
LauncherLogProto.Target target = new LauncherLogProto.Target();
action.type = LauncherLogProto.Action.Type.TOUCH;
action.touch = LauncherLogProto.Action.Touch.TAP;
target.containerType = LauncherLogProto.ContainerType.TIP;
target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
: HYBRID_HOTSEAT_CANCELED;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
private void logOnBoardingSeen() {
LauncherLogProto.Action action = new LauncherLogProto.Action();
LauncherLogProto.Target target = new LauncherLogProto.Target();
action.type = LauncherLogProto.Action.Type.TIP;
target.containerType = LauncherLogProto.ContainerType.TIP;
target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
@@ -165,6 +192,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
return;
}
mLauncher.getDragLayer().addView(this);
logOnBoardingSeen();
animateOpen();
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
WorkspaceItemInfo info = predictions.get(i);
@@ -109,6 +109,7 @@ public class HotseatPredictionController implements DragController.DragListener,
private AppPredictor mAppPredictor;
private AllAppsStore mAllAppsStore;
private AnimatorSet mIconRemoveAnimators;
private boolean mUIUpdatePaused = false;
private HotseatEduController mHotseatEduController;
@@ -168,7 +169,7 @@ public class HotseatPredictionController implements DragController.DragListener,
}
private void fillGapsWithPrediction(boolean animate, Runnable callback) {
if (!isReady() || mDragObject != null) {
if (!isReady() || mUIUpdatePaused || mDragObject != null) {
return;
}
List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
@@ -249,6 +250,16 @@ public class HotseatPredictionController implements DragController.DragListener,
}
}
/**
* start and pauses predicted apps update on the hotseat
*/
public void setPauseUIUpdate(boolean paused) {
mUIUpdatePaused = paused;
if (!paused) {
fillGapsWithPrediction();
}
}
/**
* Creates App Predictor with all the current apps pinned on the hotseat
*/
@@ -447,17 +458,40 @@ public class HotseatPredictionController implements DragController.DragListener,
/**
* Unpins pinned app when it's converted into a folder
*/
public void folderCreatedFromIcon(ItemInfo info, FolderInfo folderInfo) {
public void folderCreatedFromWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
return;
}
AppTarget target = getAppTargetFromItemInfo(info);
if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && !isInHotseat(
info)) {
ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
if (isInHotseat(folderInfo) && !getPinnedAppTargetsInViewGroup(hotseatVG).contains(
target)) {
notifyItemAction(target, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
} else if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
&& folderInfo.screenId == Workspace.FIRST_SCREEN_ID && !isInFirstPage(info)) {
} else if (isInFirstPage(folderInfo) && !getPinnedAppTargetsInViewGroup(
firstScreenVG).contains(target)) {
notifyItemAction(target, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
}
}
/**
* Pins workspace item created when all folder items are removed but one
*/
public void folderConvertedToWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
return;
}
AppTarget target = getAppTargetFromItemInfo(info);
if (isInHotseat(info)) {
notifyItemAction(target, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
} else if (isInFirstPage(info)) {
notifyItemAction(target, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
}
}
@Override
public void onDragEnd() {
if (mDragObject == null) {
@@ -86,6 +86,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
mIsDrawingDot = true;
int count = canvas.save();
canvas.translate(-getWidth() * RING_EFFECT_RATIO, -getHeight() * RING_EFFECT_RATIO);
canvas.scale(1 + 2 * RING_EFFECT_RATIO, 1 + 2 * RING_EFFECT_RATIO);
super.drawDotIfNecessary(canvas);
canvas.restoreToCount(count);
mIsDrawingDot = false;
@@ -20,20 +20,24 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.popup.SystemShortcut;
@@ -161,6 +165,15 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
onStateOrResumeChanged();
}
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
@Nullable String sourceContainer) {
if (mHotseatPredictionController != null) {
mHotseatPredictionController.setPauseUIUpdate(true);
}
return super.startActivitySafely(v, intent, item, sourceContainer);
}
@Override
protected void onActivityFlagsChanged(int changeBits) {
super.onActivityFlagsChanged(changeBits);
@@ -170,16 +183,27 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
&& (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
onStateOrResumeChanged();
}
if ((changeBits & ACTIVITY_STATE_STARTED) != 0 && mHotseatPredictionController != null
&& (getActivityFlags() & ACTIVITY_STATE_USER_ACTIVE) == 0) {
mHotseatPredictionController.setPauseUIUpdate(false);
}
}
@Override
public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
int screenId, int cellX, int cellY) {
FolderIcon fi = super.addFolder(layout, info, container, screenId, cellX, cellY);
public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo) {
super.folderCreatedFromItem(folder, itemInfo);
if (mHotseatPredictionController != null) {
mHotseatPredictionController.folderCreatedFromIcon(info, fi.getFolder().getInfo());
mHotseatPredictionController.folderCreatedFromWorkspaceItem(itemInfo, folder.getInfo());
}
}
@Override
public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {
super.folderConvertedToItem(folder, itemInfo);
if (mHotseatPredictionController != null) {
mHotseatPredictionController.folderConvertedToWorkspaceItem(itemInfo, folder.getInfo());
}
return fi;
}
@Override
@@ -33,6 +33,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.graphics.Rect;
import android.view.View;
@@ -125,7 +126,8 @@ public class OverviewState extends LauncherState {
@Override
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()) {
if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()
&& removeShelfFromOverview(launcher)) {
// Treat the QSB as part of the hotseat so they move together.
return getHotseatScaleAndTranslation(launcher);
}
@@ -158,7 +160,7 @@ public class OverviewState extends LauncherState {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
} else {
if (ENABLE_OVERVIEW_ACTIONS.get()) {
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
}
@@ -229,6 +231,7 @@ public class OverviewState extends LauncherState {
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
&& removeShelfFromOverview(launcher)
? OVERSHOOT_1_2
: OVERSHOOT_1_7;
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
@@ -234,6 +234,7 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
public void adjustActivityControllerInterpolators() {
if (mAdjustInterpolatorsRunnable != null) {
mAdjustInterpolatorsRunnable.run();
mAdjustInterpolatorsRunnable = null;
}
}
@@ -21,7 +21,7 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
@@ -75,7 +75,6 @@ import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SharedApiCompat;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.views.LiveTileOverlay;
@@ -918,6 +917,12 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (mRecentsAnimationController == null) {
// If the recents animation is interrupted, we still end the running
// animation (not canceled) so this is still called. In that case, we can
// skip doing any future work here for the current gesture.
return;
}
// Finalize the state and notify of the change
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
@@ -939,6 +944,12 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (mRecentsAnimationController == null) {
// If the recents animation is interrupted, we still end the running
// animation (not canceled) so this is still called. In that case, we can
// skip doing any future work here for the current gesture.
return;
}
if (target == NEW_TASK && mRecentsView != null
&& mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
// We are about to launch the current running task, so use LAST_TASK state
@@ -973,7 +984,7 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
if (QUICKSTEP_SPRINGS.get()) {
if (UNSTABLE_SPRINGS.get()) {
mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
}
mLauncherTransitionController.getAnimationPlayer().start();
@@ -1142,8 +1153,7 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
SharedApiCompat.setWillFinishToHome(mRecentsAnimationController.getController(),
true /* willFinishToHome */);
mRecentsAnimationController.getController().setWillFinishToHome(true);
// Update the screenshot of the task
if (mTaskSnapshot == null) {
mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
@@ -1,23 +1,18 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class QuickstepTestInformationHandler extends TestInformationHandler {
@@ -46,33 +41,8 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
}
case TestProtocol.REQUEST_HOTSEAT_TOP: {
if (mLauncher == null) return null;
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
PortraitStatesTouchController.getHotseatTop(mLauncher));
return response;
}
case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: {
try {
final int leftMargin = MAIN_EXECUTOR.submit(() ->
getRecentsView().getLeftGestureMargin()).get();
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, leftMargin);
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
return response;
}
case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: {
try {
final int rightMargin = MAIN_EXECUTOR.submit(() ->
getRecentsView().getRightGestureMargin()).get();
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, rightMargin);
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
return response;
return getLauncherUIProperty(
Bundle::putInt, PortraitStatesTouchController::getHotseatTop);
}
case TestProtocol.REQUEST_RECENT_TASKS_LIST: {
@@ -99,11 +69,12 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
return super.call(method);
}
private RecentsView getRecentsView() {
@Override
protected Activity getCurrentActivity() {
OverviewComponentObserver observer = new OverviewComponentObserver(mContext,
new RecentsAnimationDeviceState(mContext));
try {
return observer.getActivityInterface().getCreatedActivity().getOverviewPanel();
return observer.getActivityInterface().getCreatedActivity();
} finally {
observer.onDestroy();
}
@@ -111,11 +82,6 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
@Override
protected boolean isLauncherInitialized() {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"isLauncherInitialized.TouchInteractionService.isInitialized=" +
TouchInteractionService.isInitialized());
}
return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
}
}
@@ -142,16 +142,13 @@ public class TouchInteractionService extends Service implements PluginListener<O
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
});
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS initialized");
}
sIsInitialized = true;
}
@BinderThread
@Override
public void onOverviewToggle() {
TestLogging.recordEvent("onOverviewToggle");
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
mOverviewCommandHelper.onOverviewToggle();
}
@@ -411,9 +408,6 @@ public class TouchInteractionService extends Service implements PluginListener<O
@Override
public void onDestroy() {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS destroyed");
}
sIsInitialized = false;
if (mDeviceState.isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
@@ -444,13 +438,17 @@ public class TouchInteractionService extends Service implements PluginListener<O
Log.e(TAG, "Unknown event " + ev);
return;
}
MotionEvent event = (MotionEvent) ev;
TestLogging.recordMotionEvent(
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
if (!mDeviceState.isUserUnlocked()) {
return;
}
Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
MotionEvent event = (MotionEvent) ev;
if (event.getAction() == ACTION_DOWN) {
GestureState newGestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
@@ -731,20 +729,15 @@ public class TouchInteractionService extends Service implements PluginListener<O
}
} else {
// Dump everything
FeatureFlags.dump(pw);
PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
mDeviceState.dump(pw);
mOverviewComponentObserver.dump(pw);
pw.println("TouchState:");
boolean resumed = mOverviewComponentObserver != null
&& mOverviewComponentObserver.getActivityInterface().isResumed();
pw.println(" resumed=" + resumed);
pw.println(" mConsumer=" + mConsumer.getName());
pw.println("FeatureFlags:");
pw.println(" APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
pw.println(" QUICKSTEP_SPRINGS=" + QUICKSTEP_SPRINGS.get());
pw.println(" UNSTABLE_SPRINGS=" + UNSTABLE_SPRINGS.get());
pw.println(" ADAPTIVE_ICON_WINDOW_ANIM=" + ADAPTIVE_ICON_WINDOW_ANIM.get());
pw.println(" ENABLE_QUICKSTEP_LIVE_TILE=" + ENABLE_QUICKSTEP_LIVE_TILE.get());
pw.println(" ENABLE_HINTS_IN_OVERVIEW=" + ENABLE_HINTS_IN_OVERVIEW.get());
pw.println(" FAKE_LANDSCAPE_UI=" + FAKE_LANDSCAPE_UI.get());
ActiveGestureLog.INSTANCE.dump("", pw);
pw.println("ProtoTrace:");
pw.println(" file="
@@ -3,6 +3,7 @@ package com.android.quickstep.inputconsumers;
import android.view.MotionEvent;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -35,7 +36,7 @@ public abstract class DelegateInputConsumer implements InputConsumer {
protected void setActive(MotionEvent ev) {
mState = STATE_ACTIVE;
TestLogging.recordEvent("pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
// Send cancel event
@@ -38,6 +38,7 @@ import android.view.ViewConfiguration;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
@@ -203,7 +204,7 @@ public class DeviceLockedInputConsumer implements InputConsumer,
private void startRecentsTransition() {
mThresholdCrossed = true;
TestLogging.recordEvent("pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitorCompat.pilferPointers();
Intent intent = new Intent(Intent.ACTION_MAIN)
@@ -45,6 +45,7 @@ import androidx.annotation.UiThread;
import com.android.launcher3.R;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.BaseActivityInterface;
@@ -303,7 +304,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (mInteractionHandler == null) {
return;
}
TestLogging.recordEvent("pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitorCompat.pilferPointers();
mActivityInterface.closeOverlay();
@@ -26,6 +26,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
@@ -107,7 +108,7 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
TestLogging.recordEvent("pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
@@ -23,6 +23,7 @@ import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.GestureState;
@@ -64,7 +65,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer {
private void onInterceptTouch() {
if (mInputMonitor != null) {
TestLogging.recordEvent("pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
@@ -73,7 +73,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ListView;
@@ -345,7 +344,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mTaskTopMargin = getResources()
.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
mTaskBottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
mTaskBottomMargin = LayoutUtils.thumbnailBottomMargin(context);
mSquaredTouchSlop = squaredTouchSlop(context);
mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -1251,7 +1250,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
public PendingAnimation createAllTasksDismissAnimation(long duration) {
if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
AnimatorSet anim = new AnimatorSet();
@@ -1595,7 +1594,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
@@ -1889,16 +1888,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
}
public int getLeftGestureMargin() {
final WindowInsets insets = getRootWindowInsets();
return Math.max(insets.getSystemGestureInsets().left, insets.getSystemWindowInsetLeft());
}
public int getRightGestureMargin() {
final WindowInsets insets = getRootWindowInsets();
return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight());
}
/** If it's in the live tile mode, switch the running task into screenshot mode. */
public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
@@ -54,6 +54,7 @@ import com.android.systemui.plugins.OverviewScreenshotActions;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ConfigurationCompat;
/**
* A task in the Recents view.
@@ -357,7 +358,8 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
final float thumbnailScale;
int thumbnailRotation = mThumbnailData.rotation;
int currentRotation = getDisplay() != null ? getDisplay().getRotation() : 0;
int currentRotation = ConfigurationCompat.getWindowConfigurationRotation(
getResources().getConfiguration());
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
// Landscape vs portrait change
boolean windowingModeSupportsRotation = !mActivity.isInMultiWindowMode()
@@ -22,6 +22,7 @@ import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LA
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -30,7 +31,6 @@ import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -58,6 +58,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.testing.TestLogging;
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;
@@ -221,7 +222,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams);
setOutlineProvider(mOutlineProvider);
}
@@ -230,13 +231,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
super.onFinishInflate();
mSnapshotView = findViewById(R.id.snapshot);
mIconView = findViewById(R.id.icon);
final Context context = getContext();
TaskView.LayoutParams thumbnailParams = (LayoutParams) mSnapshotView.getLayoutParams();
thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(context);
mSnapshotView.setLayoutParams(thumbnailParams);
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
mActionsView = mSnapshotView.getTaskOverlay().getActionsView();
if (mActionsView != null) {
TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT,
@@ -333,9 +335,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
if (mTask != null) {
final ActivityOptions opts;
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
TestLogging.recordEvent("startActivityFromRecentsAsync:" + mTask);
}
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
if (animate) {
opts = mActivity.getActivityLaunchOptions(this);
if (freezeTaskList) {
@@ -679,9 +680,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private final int mMarginBottom;
private FullscreenDrawParams mFullscreenParams;
TaskOutlineProvider(Resources res, FullscreenDrawParams fullscreenParams) {
mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
mMarginBottom = LayoutUtils.thumbnailBottomMargin(res);
TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams) {
mMarginTop = context.getResources().getDimensionPixelSize(
R.dimen.task_thumbnail_top_margin);
mMarginBottom = LayoutUtils.thumbnailBottomMargin(context);
mFullscreenParams = fullscreenParams;
}
+3 -3
View File
@@ -67,13 +67,13 @@
<string name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>
<!-- Hotseat migration notification title -->
<string translatable="false" name="hotseat_migrate_prompt_title">Get suggested apps on the home screen</string>
<string translatable="false" name="hotseat_migrate_prompt_title">Easily access your most-used apps</string>
<!-- Hotseat migration notification content -->
<string translatable="false" name="hotseat_migrate_prompt_content">Tap to set up</string>
<string translatable="false" name="hotseat_migrate_prompt_content">Pixel suggests your favorite apps based on your routines. Tap to learn more.</string>
<!-- Hotseat migration wizard title -->
<string translatable="false" name="hotseat_migrate_title">Suggested apps replace the bottom row of apps</string>
<!-- Hotseat migration wizard message -->
<string translatable="false" name="hotseat_migrate_message">To pin a favorite app, drag it over a suggested app. To hide a suggested app, touch &amp; hold it.</string>
<string translatable="false" name="hotseat_migrate_message">Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
<string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved to last screen</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
@@ -25,6 +25,7 @@ import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
@@ -35,6 +36,7 @@ import android.os.CancellationSignal;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -172,7 +174,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
if (requestCode != -1) {
mPendingActivityRequestCode = -1;
mPendingActivityRequestCode = requestCode;
StartActivityParams params = new StartActivityParams(this, requestCode);
params.intent = intent;
params.options = options;
@@ -193,6 +195,16 @@ public abstract class BaseQuickstepLauncher extends Launcher
}
}
@Override
protected void setupViews() {
super.setupViews();
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
getOverviewPanel().bringToFront();
}
}
@Override
protected StateHandler[] createStateHandlers() {
return new StateHandler[] {
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2018 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.os.Handler;
import com.android.systemui.shared.system.RotationWatcher;
/**
* Utility class for listening for rotation changes
*/
public class DisplayRotationListener extends RotationWatcher {
private final Runnable mCallback;
private Handler mHandler;
public DisplayRotationListener(Context context, Runnable callback) {
super(context);
mCallback = callback;
}
@Override
public void enable() {
if (mHandler == null) {
mHandler = new Handler();
}
super.enable();
}
@Override
protected void onRotationChanged(int i) {
mHandler.post(mCallback);
}
}
@@ -14,8 +14,12 @@
package com.android.launcher3.uioverrides.plugins;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.plugins.Plugin;
@@ -24,6 +28,9 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class PluginManagerWrapper {
@@ -75,4 +82,27 @@ public class PluginManagerWrapper {
public static boolean hasPlugins(Context context) {
return PluginPrefs.hasPlugins(context);
}
public void dump(PrintWriter pw) {
final List<ComponentName> enabledPlugins = new ArrayList<>();
final List<ComponentName> disabledPlugins = new ArrayList<>();
for (String action : getPluginActions()) {
for (ResolveInfo resolveInfo : mContext.getPackageManager().queryIntentServices(
new Intent(action), MATCH_DISABLED_COMPONENTS)) {
ComponentName installedPlugin = new ComponentName(
resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
if (mPluginEnabler.isEnabled(installedPlugin)) {
enabledPlugins.add(installedPlugin);
} else {
disabledPlugins.add(installedPlugin);
}
}
}
pw.println("PluginManager:");
pw.println(" numEnabledPlugins=" + enabledPlugins.size());
pw.println(" numDisabledPlugins=" + disabledPlugins.size());
pw.println(" enabledPlugins=" + enabledPlugins);
pw.println(" disabledPlugins=" + disabledPlugins);
}
}
@@ -25,7 +25,7 @@ import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRE
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.TimeInterpolator;
@@ -277,7 +277,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
private void handleFirstSwipeToOverview(final ValueAnimator animator,
final long expectedDuration, final LauncherState targetState, final float velocity,
final boolean isFling) {
if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
&& targetState == OVERVIEW) {
mFinishFastOnSecondTouch = true;
} else if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
@@ -130,7 +130,7 @@ public class MultiStateCallback {
final LinkedList<Runnable> callbacks;
if (mCallbacks.indexOfKey(stateMask) >= 0) {
callbacks = mCallbacks.get(stateMask);
if (FeatureFlags.IS_DOGFOOD_BUILD && callbacks.contains(callback)) {
if (FeatureFlags.IS_STUDIO_BUILD && callbacks.contains(callback)) {
throw new IllegalStateException("Existing callback for state found");
}
} else {
@@ -35,6 +35,7 @@ import android.util.SparseIntArray;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
@@ -233,4 +234,13 @@ public final class OverviewComponentObserver {
public BaseActivityInterface getActivityInterface() {
return mActivityInterface;
}
public void dump(PrintWriter pw) {
pw.println("OverviewComponentObserver:");
pw.println(" isDefaultHome=" + mIsDefaultHome);
pw.println(" isHomeDisabled=" + mIsHomeDisabled);
pw.println(" homeAndOverviewSame=" + mIsHomeAndOverviewSame);
pw.println(" overviewIntent=" + mOverviewIntent);
pw.println(" homeIntent=" + mCurrentHomeIntent);
}
}
@@ -15,11 +15,6 @@
*/
package com.android.quickstep;
import static android.content.Context.MODE_PRIVATE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserManager;
@@ -27,7 +22,6 @@ import android.util.Log;
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
import com.android.launcher3.config.FeatureFlags;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
@@ -55,22 +49,7 @@ public class QuickstepProcessInitializer extends MainProcessInitializer {
super.init(context);
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
// Force disable some feature flags based on the system ui navigation mode.
SysUINavigationMode.Mode currMode = SysUINavigationMode.INSTANCE.get(context)
.addModeChangeListener(mode -> disableFeatureFlagsForSysuiNavMode(context, mode));
disableFeatureFlagsForSysuiNavMode(context, currMode);
}
private void disableFeatureFlagsForSysuiNavMode(Context ctx, SysUINavigationMode.Mode mode) {
if (mode == SysUINavigationMode.Mode.TWO_BUTTONS) {
ctx.getSharedPreferences(FLAGS_PREF_NAME, MODE_PRIVATE)
.edit()
.putBoolean(ENABLE_OVERVIEW_ACTIONS.key, false)
.apply();
FeatureFlags.initialize(ctx);
}
ThreadedRendererCompat.setContextPriority(
ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
}
@@ -62,6 +62,12 @@ public class RecentsAnimationCallbacks implements
mListeners.remove(listener);
}
@UiThread
public void removeAllListeners() {
Preconditions.assertUIThread();
mListeners.clear();
}
public void notifyAnimationCanceled() {
mCancelled = true;
onAnimationCanceled(null);
@@ -198,6 +198,7 @@ public class RecentsAnimationController {
if (mInputConsumerController != null) {
mInputConsumerController.setInputListener(null);
}
mInputProxySupplier = null;
}
private boolean onInputConsumerEvent(InputEvent ev) {
@@ -180,7 +180,7 @@ public class RecentsAnimationDeviceState implements
mDefaultDisplay.addChangeListener(this);
}
if (mMode == NO_BUTTON) {
if (newMode == NO_BUTTON) {
mExclusionListener.register();
} else {
mExclusionListener.unregister();
@@ -122,6 +122,12 @@ public class SysUINavigationMode {
}
}
/** @return Whether we can remove the shelf from overview. */
public static boolean removeShelfFromOverview(Context context) {
// The shelf is core to the two-button mode model, so we need to continue supporting it.
return getMode(context) != Mode.TWO_BUTTONS;
}
public interface NavigationModeChangeListener {
void onNavigationModeChanged(Mode newMode);
@@ -29,7 +29,6 @@ import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.quickstep.util.SharedApiCompat;
import com.android.systemui.shared.recents.ISystemUiProxy;
/**
@@ -278,7 +277,7 @@ public class SystemUiProxy implements ISystemUiProxy {
mLastShelfVisible = visible;
mLastShelfHeight = shelfHeight;
try {
SharedApiCompat.setShelfHeight(mSystemUiProxy, visible, shelfHeight);
mSystemUiProxy.setShelfHeight(visible, shelfHeight);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setShelfHeight visible: " + visible
+ " height: " + shelfHeight, e);
@@ -55,7 +55,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalArgumentException(msg);
} else {
Log.e("TaskAnimationManager", msg, new Exception());
@@ -156,9 +156,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
mTargets.release();
}
// Remove gesture state from callbacks
if (mCallbacks != null && mLastGestureState != null) {
mCallbacks.removeListener(mLastGestureState);
// Clean up all listeners to ensure we don't get subsequent callbacks
if (mCallbacks != null) {
mCallbacks.removeAllListeners();
}
mController = null;
@@ -16,7 +16,10 @@
package com.android.quickstep.interaction;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.Window;
@@ -26,6 +29,7 @@ import com.android.launcher3.R;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
import java.util.List;
import java.util.Optional;
/** Shows the Back gesture interactive tutorial in full screen mode. */
@@ -46,6 +50,12 @@ public class BackGestureTutorialActivity extends FragmentActivity {
.commit();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
disableSystemGestures();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
@@ -70,4 +80,14 @@ public class BackGestureTutorialActivity extends FragmentActivity {
| View.SYSTEM_UI_FLAG_FULLSCREEN);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
private void disableSystemGestures() {
Display display = getDisplay();
if (display != null) {
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
getWindow().setSystemGestureExclusionRects(
List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));
}
}
}
@@ -31,7 +31,7 @@ public class BinderTracker {
private static final String TAG = "BinderTracker";
public static void start() {
if (!FeatureFlags.IS_DOGFOOD_BUILD) {
if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
@@ -40,7 +40,7 @@ public class BinderTracker {
}
public static void stop() {
if (!FeatureFlags.IS_DOGFOOD_BUILD) {
if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -59,7 +60,7 @@ public class LayoutUtils {
} else {
Resources res = context.getResources();
if (ENABLE_OVERVIEW_ACTIONS.get()) {
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
//TODO: this needs to account for the swipe gesture height and accessibility
// UI when shown.
extraSpace = 0;
@@ -83,6 +84,7 @@ public class LayoutUtils {
float taskWidth, taskHeight, paddingHorz;
Resources res = context.getResources();
Rect insets = dp.getInsets();
final boolean overviewActionsEnabled = ENABLE_OVERVIEW_ACTIONS.get();
if (dp.isMultiWindowMode) {
if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
@@ -112,7 +114,7 @@ public class LayoutUtils {
final int paddingResId;
if (dp.isVerticalBarLayout()) {
paddingResId = R.dimen.landscape_task_card_horz_space;
} else if (ENABLE_OVERVIEW_ACTIONS.get()) {
} else if (overviewActionsEnabled && removeShelfFromOverview(context)) {
paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
} else {
paddingResId = R.dimen.portrait_task_card_horz_space;
@@ -120,9 +122,11 @@ public class LayoutUtils {
paddingHorz = res.getDimension(paddingResId);
}
float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
float bottomMargin = thumbnailBottomMargin(res);
float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
float bottomMargin = thumbnailBottomMargin(context);
float paddingVert = overviewActionsEnabled && removeShelfFromOverview(context)
? 0 : res.getDimension(R.dimen.task_card_vert_space);
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
// we override the insets ourselves.
@@ -140,14 +144,14 @@ public class LayoutUtils {
// Center in the visible space
float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
float y = insets.top + Math.max(topIconMargin,
(launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
(launcherVisibleHeight - extraVerticalSpace - outHeight - bottomMargin) / 2);
outRect.set(Math.round(x), Math.round(y),
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
}
public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
// Track the bottom of the window.
if (ENABLE_OVERVIEW_ACTIONS.get()) {
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
Rect taskSize = new Rect();
calculateLauncherTaskSize(context, dp, taskSize);
return (dp.heightPx - taskSize.height()) / 2;
@@ -162,9 +166,9 @@ public class LayoutUtils {
* Get the margin that the task thumbnail view should use.
* @return the margin in pixels.
*/
public static int thumbnailBottomMargin(Resources resources) {
if (ENABLE_OVERVIEW_ACTIONS.get()) {
return resources.getDimensionPixelSize(R.dimen.overview_actions_height);
public static int thumbnailBottomMargin(Context context) {
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
return context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height);
} else {
return 0;
}
@@ -160,7 +160,8 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
Context context = getContext();
if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
mDragHandleProgress = 1;
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
&& SysUINavigationMode.removeShelfFromOverview(context)) {
// Fade in all apps background quickly to distinguish from swiping from nav bar.
mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
@@ -197,7 +197,8 @@ public class NavigationModeSwitchRule implements TestRule {
Wait.atMost(() -> "Switching nav mode: "
+ launcher.getNavigationModeMismatchError(),
() -> launcher.getNavigationModeMismatchError() == null, WAIT_TIME_MS, launcher);
() -> launcher.getNavigationModeMismatchError() == null,
60000 /* b/148422894 */, launcher);
return true;
}
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Intent;
import android.os.RemoteException;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,7 +47,6 @@ import com.android.quickstep.views.RecentsView;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,17 +81,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
isInBackground(launcher)));
}
@Test
@PortraitLandscape
@Ignore // Enable after b/131115533
public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
mDevice.pressRecentApps();
waitForState("Launcher internal state didn't switch to Overview",
() -> LauncherState.OVERVIEW);
assertNotNull("getOverview() returned null", mLauncher.getOverview());
}
@Test
@NavigationModeSwitch
@PortraitLandscape
@@ -271,10 +258,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Test
@NavigationModeSwitch
@PortraitLandscape
// b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
// b/143285809 Remove @Stability on 02/27/20 if the test doesn't flake.
@TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
// b/143285809
@Ignore
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
startTestActivity(3);
+2 -2
View File
@@ -46,9 +46,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
style="@style/TextHeadline"
android:layout_weight="1"
android:background="@android:color/transparent"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:gravity="center_horizontal"
android:hint="@string/folder_hint_text"
@@ -58,7 +58,7 @@
android:singleLine="true"
android:textColor="?attr/folderTextColor"
android:textColorHighlight="?android:attr/colorControlHighlight"
android:textColorHint="?attr/folderTextColor"
android:textColorHint="?attr/folderHintColor"
android:textSize="@dimen/folder_label_text_size" />
<com.android.launcher3.pageindicators.PageIndicatorDots
+5 -5
View File
@@ -23,14 +23,14 @@
<ImageView
android:id="@+id/icon"
android:contentDescription="@string/work_apps_paused_title"
android:layout_width="48dp"
android:layout_height="48dp"
android:tint="?attr/folderTextColor"
android:layout_width="32dp"
android:layout_height="32dp"
android:tint="?attr/workProfileOverlayTextColor"
android:src="@drawable/ic_corp_off" />
<TextView
style="@style/TextHeadline"
android:textColor="?attr/folderTextColor"
android:textColor="?attr/workProfileOverlayTextColor"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -43,7 +43,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/folderTextColor"
android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_paused_body"
android:textAlignment="center"
android:textSize="16sp" />
+2
View File
@@ -32,6 +32,8 @@
android:layout_weight="1"
android:drawableStart="@drawable/ic_corp"
android:drawablePadding="3dp"
android:drawableTint="?attr/workProfileOverlayTextColor"
android:textColor="?attr/workProfileOverlayTextColor"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
+3
View File
@@ -40,6 +40,8 @@
<attr name="folderIconRadius" format="float" />
<attr name="folderIconBorderColor" format="color" />
<attr name="folderTextColor" format="color" />
<attr name="folderHintColor" format="color" />
<attr name="workProfileOverlayTextColor" format="color" />
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
@@ -115,6 +117,7 @@
<attr name="numFolderColumns" format="integer" />
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
<attr name="dbFile" format="string" />
<attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
</declare-styleable>
+1 -1
View File
@@ -142,7 +142,7 @@
<string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
<!-- Default folder title -->
<string name="folder_hint_text">Unnamed Folder</string>
<string name="folder_hint_text">Edit Name</string>
<!-- Accessibility -->
<!-- The format string for when an app is temporarily disabled. -->
+5
View File
@@ -48,7 +48,9 @@
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
<item name="folderTextColor">#FF212121</item>
<item name="folderHintColor">#FF616161</item>
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="workProfileOverlayTextColor">#FF212121</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
@@ -76,6 +78,7 @@
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
</style>
<style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -96,8 +99,10 @@
<item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">@android:color/white</item>
<item name="folderHintColor">#FFCCCCCC</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
<item name="workProfileOverlayTextColor">@android:color/white</item>
</style>
<style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
+3
View File
@@ -24,6 +24,7 @@
launcher:numFolderRows="2"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="3"
launcher:dbFile="launcher_3_by_3.db"
launcher:defaultLayoutId="@xml/default_workspace_3x3" >
<display-option
@@ -51,6 +52,7 @@
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="4"
launcher:dbFile="launcher_4_by_4.db"
launcher:defaultLayoutId="@xml/default_workspace_4x4" >
<display-option
@@ -102,6 +104,7 @@
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:dbFile="launcher.db"
launcher:defaultLayoutId="@xml/default_workspace_5x5" >
<display-option
+1 -1
View File
@@ -52,7 +52,7 @@ LOCAL_JAVA_LIBRARIES := LauncherRoboTests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_TEST_PACKAGE := Launcher3
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/Launcher3/src
LOCAL_ROBOTEST_TIMEOUT := 36000
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.folder;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import android.content.ComponentName;
import android.content.Context;
@@ -61,15 +61,16 @@ public final class FolderNameProviderTest {
ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
list.add(mItem1);
list.add(mItem2);
String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
assertTrue(suggestedNameOut[0].equals("Work"));
FolderNameInfo[] nameInfos =
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
assertEquals("Work", nameInfos[0].getLabel());
suggestedNameOut[0] = "candidate1";
suggestedNameOut[1] = "candidate2";
suggestedNameOut[2] = "candidate3";
new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
assertTrue(suggestedNameOut[3].equals("Work"));
nameInfos[0] = new FolderNameInfo("candidate1", 0.9);
nameInfos[1] = new FolderNameInfo("candidate2", 0.8);
nameInfos[2] = new FolderNameInfo("candidate3", 0.7);
new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
assertEquals("Work", nameInfos[3].getLabel());
}
}
@@ -49,8 +49,9 @@ public class FileLogTest {
@After
public void tearDown() {
// Clear existing logs
new File(mTempDir, "log-0").delete();
new File(mTempDir, "log-1").delete();
for (int i = 0; i < FileLog.LOG_DAYS; i++) {
new File(mTempDir, "log-" + i).delete();
}
mTempDir.delete();
mTestActive = false;
@@ -89,8 +90,9 @@ public class FileLogTest {
Calendar threeDaysAgo = Calendar.getInstance();
threeDaysAgo.add(Calendar.HOUR, -72);
new File(mTempDir, "log-0").setLastModified(threeDaysAgo.getTimeInMillis());
new File(mTempDir, "log-1").setLastModified(threeDaysAgo.getTimeInMillis());
for (int i = 0; i < FileLog.LOG_DAYS; i++) {
new File(mTempDir, "log-" + i).setLastModified(threeDaysAgo.getTimeInMillis());
}
FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
writer = new StringWriter();
+2 -3
View File
@@ -42,6 +42,7 @@ import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
@@ -330,9 +331,7 @@ public abstract class BaseActivity extends Activity
return;
}
try {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
TestLogging.recordEvent("start: shortcut: " + packageName);
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: shortcut", packageName);
getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds,
startActivityOptions, user);
} catch (SecurityException | IllegalStateException e) {
@@ -16,6 +16,8 @@
package com.android.launcher3;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Intent;
@@ -37,9 +39,12 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.DefaultDisplay.DisplayInfoChangeListener;
import com.android.launcher3.util.DefaultDisplay.Info;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
@@ -48,7 +53,7 @@ import com.android.launcher3.util.TraceHelper;
* Extension of BaseActivity allowing support for drag-n-drop
*/
public abstract class BaseDraggingActivity extends BaseActivity
implements WallpaperColorInfo.OnChangeListener {
implements WallpaperColorInfo.OnChangeListener, DisplayInfoChangeListener {
private static final String TAG = "BaseDraggingActivity";
@@ -63,8 +68,6 @@ public abstract class BaseDraggingActivity extends BaseActivity
private int mThemeRes = R.style.AppTheme;
private DisplayRotationListener mRotationListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -72,7 +75,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
// Update theme
WallpaperColorInfo.INSTANCE.get(this).addOnChangeListener(this);
@@ -167,9 +170,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
TestLogging.recordEvent("start: activity: " + intent);
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: activity", intent);
startActivity(intent, optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
Process.myUserHandle(), sourceContainer);
@@ -238,7 +239,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
protected void onDestroy() {
super.onDestroy();
WallpaperColorInfo.INSTANCE.get(this).removeOnChangeListener(this);
mRotationListener.disable();
DefaultDisplay.INSTANCE.get(this).removeChangeListener(this);
}
public void runOnceOnStart(Runnable action) {
@@ -251,15 +252,13 @@ public abstract class BaseDraggingActivity extends BaseActivity
protected void onDeviceProfileInitiated() {
if (mDeviceProfile.isVerticalBarLayout()) {
mRotationListener.enable();
mDeviceProfile.updateIsSeascape(this);
} else {
mRotationListener.disable();
}
}
private void onDeviceRotationChanged() {
if (mDeviceProfile.updateIsSeascape(this)) {
@Override
public void onDisplayInfoChanged(Info info, int flags) {
if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.updateIsSeascape(this)) {
reapplyUi();
}
}
+1 -1
View File
@@ -538,7 +538,7 @@ public class CellLayout extends ViewGroup implements Transposable {
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
+1 -1
View File
@@ -271,7 +271,7 @@ public class DeviceProfile {
// In multi-window mode, we can have widthPx = availableWidthPx
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
// widthPx and heightPx values where it's needed.
DeviceProfile profile = new DeviceProfile(context, inv, originalIdp, mwSize, mwSize,
DeviceProfile profile = new DeviceProfile(context, inv, null, mwSize, mwSize,
mwSize.x, mwSize.y, isLandscape, true);
// If there isn't enough vertical cell padding with the labels displayed, hide the labels.
+1 -1
View File
@@ -96,7 +96,7 @@ public class FocusHelper {
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
@@ -48,6 +48,8 @@ public class FolderInfo extends ItemInfo {
public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
public int options;
public Intent suggestedFolderNames;
@@ -17,7 +17,6 @@
package com.android.launcher3;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -44,7 +43,6 @@ import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
@@ -61,7 +59,6 @@ import org.json.JSONStringer;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
@@ -491,13 +488,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
return Pair.create(si, null);
} else if (shortcutInfo != null) {
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
} else {
LauncherIcons li = LauncherIcons.obtain(mContext);
itemInfo.bitmap = li.createShortcutIcon(shortcutInfo);
li.recycle();
}
LauncherAppState.getInstance(mContext).getIconCache().getShortcutIcon(
itemInfo, shortcutInfo);
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
@@ -119,6 +119,7 @@ public class InvariantDeviceProfile {
*/
public int numAllAppsColumns;
public String dbFile;
public int defaultLayoutId;
int demoModeLayoutId;
@@ -146,6 +147,7 @@ public class InvariantDeviceProfile {
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
dbFile = p.dbFile;
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
@@ -292,6 +294,7 @@ public class InvariantDeviceProfile {
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
@@ -559,6 +562,7 @@ public class InvariantDeviceProfile {
private final int numHotseatIcons;
private final String dbFile;
private final int defaultLayoutId;
private final int demoModeLayoutId;
@@ -571,6 +575,7 @@ public class InvariantDeviceProfile {
numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0);
numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
defaultLayoutId = a.getResourceId(
R.styleable.GridDisplayOption_defaultLayoutId, 0);
demoModeLayoutId = a.getResourceId(
+17 -15
View File
@@ -100,6 +100,7 @@ import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.RotationMode;
@@ -1155,7 +1156,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
/**
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
protected void setupViews() {
mDragLayer = findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
@@ -1191,11 +1192,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mDropTargetBar.setup(mDragController);
mAllAppsController.setupViews(mAppsView);
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
mOverviewPanel.bringToFront();
}
}
/**
@@ -1745,8 +1741,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
/**
* Creates and adds new folder to CellLayout
*/
public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
final int screenId, int cellX, int cellY) {
public FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
int cellY) {
final FolderInfo folderInfo = new FolderInfo();
folderInfo.title = "";
@@ -1763,6 +1759,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return newFolder;
}
/**
* Called when a workspace item is converted into a folder
*/
public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo){}
/**
* Called when a folder is converted into a workspace item
*/
public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {}
/**
* Unbinds the view for the specified item, and removes the item and all its children.
*
@@ -1805,17 +1811,13 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
TestLogging.recordEvent("Key event: " + event);
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event);
return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS && ev.getAction() != MotionEvent.ACTION_MOVE) {
TestLogging.recordEvent("Touch event: " + ev);
}
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
@@ -2164,7 +2166,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
@@ -91,7 +91,7 @@ public class LauncherAppState {
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
@@ -7,10 +7,12 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.UserHandle;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
/**
* This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@@ -19,7 +21,7 @@ import com.android.launcher3.icons.ComponentWithLabel;
* as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
*/
public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
implements ComponentWithLabel {
implements ComponentWithLabelAndIcon {
public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
@@ -111,4 +113,9 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
public final UserHandle getUser() {
return getProfile();
}
@Override
public Drawable getFullResIcon(IconCache cache) {
return cache.getFullResIcon(provider.getPackageName(), icon);
}
}
+10 -1
View File
@@ -15,8 +15,13 @@ public class LauncherFiles {
private static final String XML = ".xml";
public static final String LAUNCHER_DB = "launcher.db";
public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
public static final String BACKUP_DB = "backup.db";
public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
public static final String MANAGED_USER_PREFERENCES_KEY = "com.android.launcher3.managedusers.prefs";
public static final String MANAGED_USER_PREFERENCES_KEY =
"com.android.launcher3.managedusers.prefs";
// This preference file is not backed up to cloud.
public static final String DEVICE_PREFERENCES_KEY = "com.android.launcher3.device.prefs";
@@ -25,6 +30,10 @@ public class LauncherFiles {
public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
LAUNCHER_DB,
LAUNCHER_4_BY_4_DB,
LAUNCHER_3_BY_3_DB,
LAUNCHER_2_BY_2_DB,
BACKUP_DB,
SHARED_PREFERENCES_KEY + XML,
WIDGET_PREVIEWS_DB,
MANAGED_USER_PREFERENCES_KEY + XML,
+3 -33
View File
@@ -17,7 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -37,7 +37,6 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.AllAppsList;
@@ -56,7 +55,6 @@ import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
@@ -68,7 +66,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -97,10 +94,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
private boolean mModelLoaded;
public boolean isModelLoaded() {
synchronized (mLock) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"isModelLoaded: " + mModelLoaded + ", " + mLoaderTask);
}
return mModelLoaded && mLoaderTask == null;
}
}
@@ -132,16 +125,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
}
/**
* Returns AppInfo with corresponding package name.
* TODO: move to enqueueModelTask
*/
public Optional<AppInfo> getAppInfoByPackageName(String pkg) {
return mBgAllAppsList.data.stream()
.filter(info -> info.componentName.getPackageName().equals(pkg))
.findAny();
}
/**
* Adds the provided items to the workspace.
*/
@@ -255,7 +238,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
} else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
} else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
for (Callbacks cb : getCallbacks()) {
if (cb instanceof Launcher) {
((Launcher) cb).recreate();
@@ -373,9 +356,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
public boolean stopLoader() {
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "LauncherModel.stopLoader");
}
mLoaderTask = null;
if (oldTask != null) {
oldTask.stopLocked();
@@ -389,10 +369,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, mBgDataModel, results);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"LauncherModel.startLoaderForResults " + mLoaderTask);
}
// Always post the loader task, instead of running directly (even on same thread) so
// that we exit any nested synchronized blocks
@@ -494,10 +470,6 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
public void close() {
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"LauncherModel.close " + mLoaderTask + ", " + mTask);
}
if (mLoaderTask == mTask) {
mLoaderTask = null;
}
@@ -573,9 +545,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
si.bitmap = li.createShortcutIcon(info);
li.recycle();
mApp.getIconCache().getShortcutIcon(si, info);
return si;
});
}
+54 -15
View File
@@ -16,6 +16,7 @@
package com.android.launcher3;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -118,7 +119,7 @@ public class LauncherProvider extends ContentProvider {
@Override
public boolean onCreate() {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
Log.d(TAG, "Launcher process started");
}
@@ -157,6 +158,17 @@ public class LauncherProvider extends ContentProvider {
}
}
private synchronized boolean updateCurrentOpenHelper() {
final InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
if (TextUtils.equals(idp.dbFile, mOpenHelper.getDatabaseName())) {
return false;
}
mOpenHelper.close();
mOpenHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
@@ -210,7 +222,7 @@ public class LauncherProvider extends ContentProvider {
addModifiedTime(initialValues);
final int rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
if (rowId < 0) return null;
mOpenHelper.onAddOrDeleteOp(db);
onAddOrDeleteOp(db);
uri = ContentUris.withAppendedId(uri, rowId);
reloadLauncherIfExternal();
@@ -268,7 +280,7 @@ public class LauncherProvider extends ContentProvider {
return 0;
}
}
mOpenHelper.onAddOrDeleteOp(db);
onAddOrDeleteOp(db);
t.commit();
}
@@ -294,7 +306,7 @@ public class LauncherProvider extends ContentProvider {
results[i].count != null && results[i].count > 0;
}
if (isAddOrDelete) {
mOpenHelper.onAddOrDeleteOp(t.getDb());
onAddOrDeleteOp(t.getDb());
}
t.commit();
@@ -316,7 +328,7 @@ public class LauncherProvider extends ContentProvider {
}
int count = db.delete(args.table, args.where, args.args);
if (count > 0) {
mOpenHelper.onAddOrDeleteOp(db);
onAddOrDeleteOp(db);
reloadLauncherIfExternal();
}
return count;
@@ -360,12 +372,14 @@ public class LauncherProvider extends ContentProvider {
}
case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
Bundle result = new Bundle();
result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
mOpenHelper.generateNewItemId());
return result;
}
case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
Bundle result = new Bundle();
result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
mOpenHelper.generateNewScreenId());
return result;
}
case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
@@ -387,8 +401,12 @@ public class LauncherProvider extends ContentProvider {
return result;
}
case LauncherSettings.Settings.METHOD_REFRESH_BACKUP_TABLE: {
mOpenHelper.mBackupTableExists =
tableExists(mOpenHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
// TODO(pinyaoting): Update the behavior here.
if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
mOpenHelper.mBackupTableExists =
tableExists(mOpenHelper.getReadableDatabase(),
Favorites.BACKUP_TABLE_NAME);
}
return null;
}
case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: {
@@ -399,10 +417,26 @@ public class LauncherProvider extends ContentProvider {
TOKEN_RESTORE_BACKUP_TABLE, RESTORE_BACKUP_TABLE_DELAY);
return null;
}
case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
updateCurrentOpenHelper());
return result;
}
}
}
return null;
}
private void onAddOrDeleteOp(SQLiteDatabase db) {
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
// TODO(pingyaoting): Implement the behavior here.
} else {
mOpenHelper.onAddOrDeleteOp(db);
}
}
/**
* Deletes any empty folder from the DB.
* @return Ids of deleted folders.
@@ -551,14 +585,16 @@ public class LauncherProvider extends ContentProvider {
/**
* The class is subclassed in tests to create an in-memory db.
*/
public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
public static class DatabaseHelper extends NoLocaleSQLiteHelper implements
LayoutParserCallback {
private final Context mContext;
private int mMaxItemId = -1;
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
DatabaseHelper(Context context) {
this(context, LauncherFiles.LAUNCHER_DB);
this(context, MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
context).dbFile : LauncherFiles.LAUNCHER_DB);
// Table creation sometimes fails silently, which leads to a crash loop.
// This way, we will try to create a table every time after crash, so the device
// would eventually be able to recover.
@@ -567,7 +603,10 @@ public class LauncherProvider extends ContentProvider {
// This operation is a no-op if the table already exists.
addFavoritesTable(getWritableDatabase(), true);
}
mBackupTableExists = tableExists(getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
mBackupTableExists = tableExists(getReadableDatabase(),
Favorites.BACKUP_TABLE_NAME);
}
initIds();
}
@@ -575,8 +614,8 @@ public class LauncherProvider extends ContentProvider {
/**
* Constructor used in tests and for restore.
*/
public DatabaseHelper(Context context, String tableName) {
super(context, tableName, SCHEMA_VERSION);
public DatabaseHelper(Context context, String dbName) {
super(context, dbName, SCHEMA_VERSION);
mContext = context;
}
@@ -606,7 +645,7 @@ public class LauncherProvider extends ContentProvider {
}
protected void onAddOrDeleteOp(SQLiteDatabase db) {
if (mBackupTableExists) {
if (!MULTI_DB_GRID_MIRATION_ALGO.get() && mBackupTableExists) {
dropTable(db, Favorites.BACKUP_TABLE_NAME);
mBackupTableExists = false;
}
@@ -302,6 +302,8 @@ public class LauncherSettings {
public static final String METHOD_RESTORE_BACKUP_TABLE = "restore_backup_table";
public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {
+9 -2
View File
@@ -41,8 +41,10 @@ import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.HintState;
import com.android.launcher3.states.SpringLoadedState;
@@ -337,8 +339,13 @@ public abstract class LauncherState {
hotseat.setScaleX(0.92f);
hotseat.setScaleY(0.92f);
if (ENABLE_OVERVIEW_ACTIONS.get()) {
launcher.getAppsView().setScaleX(0.92f);
launcher.getAppsView().setScaleY(0.92f);
AllAppsContainerView qsbContainer = launcher.getAppsView();
View qsb = qsbContainer.getSearchView();
boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
if (!qsbVisible) {
qsbContainer.setScaleX(0.92f);
qsbContainer.setScaleY(0.92f);
}
}
}
} else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
+1 -1
View File
@@ -1502,7 +1502,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
return false;
}
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
duration *= Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 1);
}
+2 -3
View File
@@ -584,9 +584,8 @@ public final class Utilities {
return new FixedSizeEmptyDrawable(iconSize);
}
ShortcutInfo si = (ShortcutInfo) obj;
LauncherIcons li = LauncherIcons.obtain(appState.getContext());
Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).bitmap.icon;
li.recycle();
Bitmap badge = LauncherAppState.getInstance(appState.getContext())
.getIconCache().getShortcutInfoBadge(si).icon;
float badgeSize = LauncherIcons.getBadgeSizeForIconSize(iconSize);
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
+7 -6
View File
@@ -1702,8 +1702,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
target.removeView(v);
FolderIcon fi = mLauncher.addFolder(target, sourceInfo, container, screenId,
targetCell[0], targetCell[1]);
FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
targetCell[1]);
destInfo.cellX = -1;
destInfo.cellY = -1;
sourceInfo.cellX = -1;
@@ -1722,6 +1722,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
fi.addItem(destInfo);
fi.addItem(sourceInfo);
}
mLauncher.folderCreatedFromItem(fi.getFolder(), destInfo);
return true;
}
return false;
@@ -1857,7 +1858,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
} else if (FeatureFlags.IS_DOGFOOD_BUILD) {
} else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("mDragInfo.cell has null parent");
}
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2152,7 +2153,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
ItemInfo item = d.dragInfo;
if (item == null) {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("DragObject has null info");
}
return;
@@ -2774,7 +2775,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mDragInfo.container, mDragInfo.screenId);
if (cellLayout != null) {
cellLayout.onDropChild(mDragInfo.cell);
} else if (FeatureFlags.IS_DOGFOOD_BUILD) {
} else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
}
@@ -2793,7 +2794,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
CellLayout parentCell = getParentCellLayoutForView(v);
if (parentCell != null) {
parentCell.removeView(v);
} else if (FeatureFlags.IS_DOGFOOD_BUILD) {
} else if (FeatureFlags.IS_STUDIO_BUILD) {
// When an app is uninstalled using the drop target, we wait until resume to remove
// the icon. We also remove all the corresponding items from the workspace at
// {@link Launcher#bindComponentsRemoved}. That call can come before or after
@@ -153,6 +153,11 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
return mMultiValueAlpha.getProperty(index);
}
public WorkFooterContainer getWorkFooterContainer() {
return mWorkFooterContainer;
}
@Override
protected void setDampedScrollShift(float shift) {
// Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
@@ -11,7 +11,7 @@ import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRE
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
@@ -185,7 +185,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
}
public Animator createSpringAnimation(float... progressValues) {
if (QUICKSTEP_SPRINGS.get()) {
if (UNSTABLE_SPRINGS.get()) {
return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
}
@@ -22,6 +22,7 @@ import com.android.launcher3.BuildConfig;
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.DeviceFlag;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -42,7 +43,10 @@ public final class FeatureFlags {
return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
}
public static final boolean IS_DOGFOOD_BUILD = BuildConfig.DEBUG;
/**
* True when the build has come from Android Studio and is being used for local debugging.
*/
public static final boolean IS_STUDIO_BUILD = BuildConfig.DEBUG;
/**
* Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
@@ -90,13 +94,13 @@ public final class FeatureFlags {
"FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout");
public static final BooleanFlag FOLDER_NAME_SUGGEST = getDebugFlag(
"FOLDER_NAME_SUGGEST", false, "Suggests folder names instead of blank text.");
"FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text.");
public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
"APP_SEARCH_IMPROVEMENTS", false,
"Adds localized title and keyword search and ranking");
public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag(
"ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
@@ -106,12 +110,15 @@ public final class FeatureFlags {
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag(
"MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm");
public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", false,
"Show launcher preview in grid picker");
@@ -142,6 +149,15 @@ public final class FeatureFlags {
}
}
public static void dump(PrintWriter pw) {
pw.println("FeatureFlags:");
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
pw.println(" " + flag.key + "=" + flag.get());
}
}
}
public static class BooleanFlag {
public final String key;
@@ -160,8 +160,8 @@ public class DragController implements DragDriver.EventListener, TouchController
mOptions = options;
if (mOptions.systemDndStartPoint != null) {
mMotionDownX = mOptions.systemDndStartPoint.x;
mMotionDownY = mOptions.systemDndStartPoint.y;
mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
}
final int registrationX = mMotionDownX - dragLayerX;
+207 -49
View File
@@ -16,9 +16,23 @@
package com.android.launcher3.folder;
import static android.text.TextUtils.isEmpty;
import static androidx.core.util.Preconditions.checkNotNull;
import static com.android.launcher3.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -75,20 +89,24 @@ import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.userevent.LauncherLogProto.Action;
import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.LauncherLogProto.Target;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -186,6 +204,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
@Thunk int mScrollHintDir = SCROLL_NONE;
@Thunk int mCurrentScrollDir = SCROLL_NONE;
private String mPreviousLabel;
private boolean mIsPreviousLabelSuggested;
/**
* Used to inflate the Workspace from XML.
*
@@ -300,13 +321,13 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
public void startEditingFolderName() {
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
if (TextUtils.isEmpty(mFolderName.getText())) {
String[] suggestedNames =
mInfo.suggestedFolderNames.getStringArrayExtra("suggest");
mFolderName.setText(suggestedNames[0]);
mFolderName.displayCompletions(Arrays.asList(suggestedNames).subList(1,
suggestedNames.length));
mFolderName.setEnteredCompose(false);
if (isEmpty(mFolderName.getText())) {
FolderNameInfo[] nameInfos =
(FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
if (nameInfos != null) {
showLabelSuggestion(nameInfos, false);
}
}
}
mFolderName.setHint("");
@@ -324,23 +345,16 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
mInfo.title = newTitle;
mInfo.setOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mFolderName.setText(mInfo.title);
// TODO: depending on whether the title was manually edited or automatically
// suggested, apply different hint.
mFolderName.setHint("");
if (TextUtils.isEmpty(mInfo.title)) {
mFolderName.setHint(R.string.folder_hint_text);
mFolderName.setText("");
} else {
if (TextUtils.isEmpty(mInfo.title)) {
mFolderName.setHint(R.string.folder_hint_text);
mFolderName.setText("");
} else {
mFolderName.setHint(null);
}
mFolderName.setHint(null);
}
sendCustomAccessibilityEvent(
@@ -423,8 +437,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mItemsInvalidated = true;
mInfo.addListener(this);
if (!TextUtils.isEmpty(mInfo.title)) {
if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
mPreviousLabel = mInfo.title.toString();
mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
mFolderName.setHint(null);
} else {
mFolderName.setText("");
@@ -443,26 +459,53 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
/**
* Show suggested folder title.
* Show suggested folder title in FolderEditText, push InputMethodManager suggestions and save
* the suggestedFolderNames.
*/
public void showSuggestedTitle(String[] suggestName) {
public void showSuggestedTitle(FolderNameInfo[] nameInfos) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mInfo.suggestedFolderNames = new Intent().putExtra("suggest", suggestName);
if (TextUtils.isEmpty(mFolderName.getText().toString())
&& !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
mFolderName.setHint("");
mFolderName.setText(suggestName[0]);
mInfo.title = suggestName[0];
animateOpen(mInfo.contents, 0, true);
mFolderName.showKeyboard();
mFolderName.displayCompletions(
Arrays.asList(suggestName).subList(1, suggestName.length));
}
mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
nameInfos);
if (isEmpty(mFolderName.getText().toString())
&& !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
showLabelSuggestion(nameInfos, true);
}
}
}
/**
* Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
* InputMethodManager suggestions.
*/
private void showLabelSuggestion(FolderNameInfo[] nameInfos, boolean animate) {
if (nameInfos == null) {
return;
}
// Open the Folder and Keyboard when the first or second suggestion is valid non-empty
// string.
boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !isEmpty(
nameInfos[0].getLabel())
|| nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
nameInfos[1].getLabel());
CharSequence firstLabel = nameInfos[0].getLabel();
if (shouldOpen) {
if (!isEmpty(firstLabel)) {
mFolderName.setHint("");
mFolderName.setText(firstLabel);
}
if (animate) {
animateOpen(mInfo.contents, 0, true);
}
mFolderName.showKeyboard();
mFolderName.displayCompletions(
asList(nameInfos).subList(1, nameInfos.length).stream()
.filter(Objects::nonNull)
.map(s -> s.getLabel().toString())
.collect(Collectors.toList()));
}
}
/**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
@@ -580,7 +623,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
dragLayer.addView(this);
mDragController.addDropTarget(this);
} else {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ getParent());
}
@@ -608,9 +651,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
if (!skipUserEventLog) {
mLauncher.getUserEventDispatcher().logActionOnItem(
Touch.TAP,
Direction.NONE,
ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
LauncherLogProto.Action.Touch.TAP,
LauncherLogProto.Action.Direction.NONE,
LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
}
@@ -1109,13 +1152,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
int itemCount = getItemCount();
if (itemCount <= 1) {
View newIcon = null;
WorkspaceItemInfo finalItem = null;
if (itemCount == 1) {
// Move the item from the folder to the workspace, in the position of the
// folder
CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container,
mInfo.screenId);
WorkspaceItemInfo finalItem = mInfo.contents.remove(0);
finalItem = mInfo.contents.remove(0);
newIcon = mLauncher.createShortcut(cellLayout, finalItem);
mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
@@ -1136,6 +1180,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
// Focus the newly created child
newIcon.requestFocus();
}
if (finalItem != null) {
mLauncher.folderConvertedToItem(mFolderIcon.getFolder(), finalItem);
}
}
}
};
@@ -1388,6 +1435,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
if (hasFocus) {
startEditingFolderName();
} else {
logEditFolderLabel();
mFolderName.dispatchBackKey();
}
}
@@ -1401,11 +1449,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
@Override
public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
LauncherLogProto.Target targetParent) {
target.gridX = info.cellX;
target.gridY = info.cellY;
target.pageIndex = mContent.getCurrentPage();
targetParent.containerType = ContainerType.FOLDER;
targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
}
private class OnScrollHintListener implements OnAlarmListener {
@@ -1503,7 +1552,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
@Override
public int getLogContainerType() {
return ContainerType.FOLDER;
return LauncherLogProto.ContainerType.FOLDER;
}
/**
@@ -1538,7 +1587,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
} else {
mLauncher.getUserEventDispatcher().logActionTapOutside(
LoggerUtils.newContainerTarget(ContainerType.FOLDER));
LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
close(true);
return true;
}
@@ -1568,4 +1617,113 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
super.draw(canvas);
}
}
private void logEditFolderLabel() {
LauncherEvent launcherEvent = LauncherEvent.newBuilder()
.setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
.addSrcTarget(newEditTextTargetBuilder()
.setFromFolderLabelState(getFromFolderLabelState())
.setToFolderLabelState(getToFolderLabelState()))
.addSrcTarget(newFolderTargetBuilder())
.addSrcTarget(newParentContainerTarget())
.build();
mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
mPreviousLabel = mFolderName.getText().toString();
mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
}
private Target.FromFolderLabelState getFromFolderLabelState() {
return mPreviousLabel == null
? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
: mPreviousLabel.isEmpty()
? FROM_EMPTY
: mIsPreviousLabelSuggested
? FROM_SUGGESTED
: FROM_CUSTOM;
}
private Target.ToFolderLabelState getToFolderLabelState() {
String newLabel =
checkNotNull(mFolderName.getText().toString(),
"Expected valid folder label, but found null");
Optional<String[]> suggestedLabels = Optional.ofNullable(
(FolderNameInfo[]) mInfo.suggestedFolderNames
.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
.map(folderNameInfoArray ->
stream(folderNameInfoArray)
.filter(Objects::nonNull)
.map(FolderNameInfo::getLabel)
.map(CharSequence::toString)
.toArray(String[]::new));
int accepted_suggestion_index = suggestedLabels
.map(folderNameInfoArray ->
IntStream.range(0, folderNameInfoArray.length)
.filter(index -> newLabel.equalsIgnoreCase(
folderNameInfoArray[index]))
.findFirst()
.orElse(-1)
).orElse(-1);
boolean hasValidPrimary = suggestedLabels
.map(labels -> labels.length > 0 && !isEmpty(labels[0]))
.orElse(false);
String primarySuffix = hasValidPrimary
? "_WITH_VALID_PRIMARY"
: "_WITH_EMPTY_PRIMARY";
boolean isEmptySuggestions = suggestedLabels
.map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
.orElse(true);
boolean isSuggestionsEnabled = FeatureFlags.FOLDER_NAME_SUGGEST.get();
String suggestionsSuffix = isSuggestionsEnabled
? isEmptySuggestions
? "_WITH_EMPTY_SUGGESTIONS"
: "_WITH_VALID_SUGGESTIONS"
: "_WITH_SUGGESTIONS_DISABLED";
return newLabel.equals(mPreviousLabel)
? Target.ToFolderLabelState.UNCHANGED
: newLabel.isEmpty()
? Target.ToFolderLabelState.valueOf("TO_EMPTY" + suggestionsSuffix)
: accepted_suggestion_index >= 0
? Target.ToFolderLabelState.valueOf("TO_SUGGESTION"
+ accepted_suggestion_index
+ primarySuffix)
: Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
}
private Target.Builder newEditTextTargetBuilder() {
return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
}
private Target.Builder newFolderTargetBuilder() {
return Target.newBuilder()
.setType(Target.Type.CONTAINER)
.setContainerType(ContainerType.FOLDER)
.setPageIndex(mInfo.screenId)
.setGridX(mInfo.cellX)
.setGridY(mInfo.cellY)
.setCardinality(mInfo.contents.size());
}
private Target.Builder newParentContainerTarget() {
Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
switch (mInfo.container) {
case CONTAINER_HOTSEAT:
return builder.setContainerType(ContainerType.HOTSEAT);
case CONTAINER_DESKTOP:
return builder.setContainerType(ContainerType.WORKSPACE);
default:
throw new AssertionError(String
.format("Expected container to be either %s or %s but found %s.",
CONTAINER_HOTSEAT,
CONTAINER_DESKTOP,
mInfo.container));
}
}
}
@@ -393,15 +393,16 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
final int finalIndex = index;
String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
FolderNameInfo[] nameInfos =
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
Executors.UI_HELPER_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
getContext(), mInfo.contents, suggestedNameOut);
showFinalView(finalIndex, item, suggestedNameOut);
getContext(), mInfo.contents, nameInfos);
showFinalView(finalIndex, item, nameInfos);
});
} else {
showFinalView(finalIndex, item, suggestedNameOut);
showFinalView(finalIndex, item, nameInfos);
}
} else {
addItem(item);
@@ -409,12 +410,12 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
String[] suggestedNameOut) {
FolderNameInfo[] nameInfos) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
invalidate();
mFolder.showSuggestedTitle(suggestedNameOut);
mFolder.showSuggestedTitle(nameInfos);
}, DROP_IN_ANIMATION_DURATION);
}
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2020 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.folder;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.NonNull;
/**
* Information about a single label suggestions of the Folder.
*/
public final class FolderNameInfo implements Parcelable {
private final double mScore;
private final CharSequence mLabel;
/**
* Create a simple completion with label.
*
* @param label The text that should be inserted into the editor and pushed to
* InputMethodManager suggestions.
* @param score The score for the label between 0.0 and 1.0.
*/
public FolderNameInfo(CharSequence label, double score) {
mScore = score;
mLabel = label;
}
private FolderNameInfo(Parcel source) {
mScore = source.readDouble();
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
public CharSequence getLabel() {
return mLabel;
}
/**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
* @param flags The flags used for parceling.
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeDouble(mScore);
TextUtils.writeToParcel(mLabel, dest, flags);
}
/**
* Used to make this class parcelable.
*/
@NonNull
public static final Parcelable.Creator<FolderNameInfo> CREATOR =
new Parcelable.Creator<FolderNameInfo>() {
public FolderNameInfo createFromParcel(Parcel source) {
return new FolderNameInfo(source);
}
public FolderNameInfo[] newArray(int size) {
return new FolderNameInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
}
@@ -21,15 +21,21 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ResourceBasedOverride;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -49,6 +55,8 @@ public class FolderNameProvider implements ResourceBasedOverride {
* name edit box can also be used to provide suggestion.
*/
public static final int SUGGEST_MAX = 4;
protected IntSparseArrayMap<FolderInfo> mFolderInfos;
protected List<AppInfo> mAppInfos;
/**
* Retrieve instance of this object that can be overridden in runtime based on the build
@@ -57,78 +65,113 @@ public class FolderNameProvider implements ResourceBasedOverride {
public static FolderNameProvider newInstance(Context context) {
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
context.getApplicationContext(), R.string.folder_name_provider_class);
fnp.load(context);
return fnp;
}
public CharSequence getSuggestedFolderName(Context context,
ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) {
public static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
IntSparseArrayMap<FolderInfo> folderInfos) {
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
context.getApplicationContext(), R.string.folder_name_provider_class);
fnp.load(appInfos, folderInfos);
return fnp;
}
private void load(Context context) {
LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(
new FolderNameWorker());
}
private void load(List<AppInfo> appInfos, IntSparseArrayMap<FolderInfo> folderInfos) {
mAppInfos = appInfos;
mFolderInfos = folderInfos;
}
/**
* Generate and rank the suggested Folder names.
*/
public void getSuggestedFolderName(Context context,
ArrayList<WorkspaceItemInfo> workspaceItemInfos,
FolderNameInfo[] nameInfos) {
if (DEBUG) {
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
// If all the icons are from work profile,
// Then, suggest "Work" as the folder name
List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
.filter(distinctByKey(p-> p.user))
.filter(distinctByKey(p -> p.user))
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1
&& !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
// Place it as last viable suggestion
setAsLastSuggestion(candidates,
setAsLastSuggestion(nameInfos,
context.getResources().getString(R.string.work_folder_name));
}
// If all the icons are from same package (e.g., main icon, shortcut, shortcut)
// Then, suggest the package's title as the folder name
distinctItemInfos = workspaceItemInfos.stream()
.filter(distinctByKey(p-> p.getTargetComponent() != null
.filter(distinctByKey(p -> p.getTargetComponent() != null
? p.getTargetComponent().getPackageName() : ""))
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1) {
Optional<AppInfo> info = LauncherAppState.getInstance(context).getModel()
.getAppInfoByPackageName(distinctItemInfos.get(0).getTargetComponent()
.getPackageName());
Optional<AppInfo> info = getAppInfoByPackageName(
distinctItemInfos.get(0).getTargetComponent().getPackageName());
// Place it as first viable suggestion and shift everything else
info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString()));
info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
}
if (DEBUG) {
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
return candidates[0];
}
private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
if (contains(candidatesOut, candidate)) {
private Optional<AppInfo> getAppInfoByPackageName(String packageName) {
if (mAppInfos == null || mAppInfos.isEmpty()) {
return Optional.empty();
}
return mAppInfos.stream()
.filter(info -> info.componentName.getPackageName().equals(packageName))
.findAny();
}
private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
for (int i = candidatesOut.length - 1; i > 0; i--) {
if (!TextUtils.isEmpty(candidatesOut[i - 1])) {
candidatesOut[i] = candidatesOut[i - 1];
for (int i = nameInfos.length - 1; i > 0; i--) {
if (nameInfos[i - 1] != null && !TextUtils.isEmpty(nameInfos[i - 1].getLabel())) {
nameInfos[i] = nameInfos[i - 1];
}
}
candidatesOut[0] = candidate;
nameInfos[0] = new FolderNameInfo(label, 1.0);
}
private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
if (contains(candidatesOut, candidate)) {
private void setAsLastSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
for (int i = 0; i < candidate.length(); i++) {
if (TextUtils.isEmpty(candidatesOut[i])) {
candidatesOut[i] = candidate;
for (int i = 0; i < nameInfos.length; i++) {
if (nameInfos[i] == null || TextUtils.isEmpty(nameInfos[i].getLabel())) {
nameInfos[i] = new FolderNameInfo(label, 1.0);
return;
}
}
candidatesOut[candidate.length() - 1] = candidate;
// Overwrite the last suggestion.
int lastIndex = nameInfos.length - 1;
nameInfos[lastIndex] = new FolderNameInfo(label, 1.0);
}
private boolean contains(CharSequence[] list, CharSequence key) {
return Arrays.asList(list).stream()
.filter(s -> s != null)
.anyMatch(s -> s.toString().equalsIgnoreCase(key.toString()));
private boolean contains(FolderNameInfo[] nameInfos, CharSequence label) {
return Arrays.stream(nameInfos)
.filter(Objects::nonNull)
.anyMatch(nameInfo -> nameInfo.getLabel().toString().equalsIgnoreCase(
label.toString()));
}
// This method can be moved to some Utility class location.
@@ -136,4 +179,13 @@ public class FolderNameProvider implements ResourceBasedOverride {
Map<Object, Boolean> map = new ConcurrentHashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
private class FolderNameWorker extends BaseModelUpdateTask {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
mFolderInfos = dataModel.folders.clone();
mAppInfos = Arrays.asList(apps.copyData());
}
}
}
@@ -152,7 +152,7 @@ public class DragPreviewProvider {
}
public final void generateDragOutline(Bitmap preview) {
if (FeatureFlags.IS_DOGFOOD_BUILD && mOutlineGeneratorCallback != null) {
if (FeatureFlags.IS_STUDIO_BUILD && mOutlineGeneratorCallback != null) {
throw new RuntimeException("Drag outline generated twice");
}
@@ -31,7 +31,7 @@ public interface ComponentWithLabel {
CharSequence getLabel(PackageManager pm);
class ComponentCachingLogic implements CachingLogic<ComponentWithLabel> {
class ComponentCachingLogic<T extends ComponentWithLabel> implements CachingLogic<T> {
private final PackageManager mPackageManager;
private final boolean mAddToMemCache;
@@ -42,22 +42,22 @@ public interface ComponentWithLabel {
}
@Override
public ComponentName getComponent(ComponentWithLabel object) {
public ComponentName getComponent(T object) {
return object.getComponent();
}
@Override
public UserHandle getUser(ComponentWithLabel object) {
public UserHandle getUser(T object) {
return object.getUser();
}
@Override
public CharSequence getLabel(ComponentWithLabel object) {
public CharSequence getLabel(T object) {
return object.getLabel(mPackageManager);
}
@Override
public BitmapInfo loadIcon(Context context, ComponentWithLabel object) {
public BitmapInfo loadIcon(Context context, T object) {
return BitmapInfo.LOW_RES_INFO;
}
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2020 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.icons;
import android.content.Context;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import com.android.launcher3.LauncherAppState;
/**
* Extension of ComponentWithLabel to also support loading icons
*/
public interface ComponentWithLabelAndIcon extends ComponentWithLabel {
/**
* Provide an icon for this object
*/
Drawable getFullResIcon(IconCache cache);
class ComponentWithIconCachingLogic extends ComponentCachingLogic<ComponentWithLabelAndIcon> {
public ComponentWithIconCachingLogic(Context context, boolean addToMemCache) {
super(context, addToMemCache);
}
@NonNull
@Override
public BitmapInfo loadIcon(Context context, ComponentWithLabelAndIcon object) {
Drawable d = object.getFullResIcon(LauncherAppState.getInstance(context)
.getIconCache());
if (d == null) {
return super.loadIcon(context, object);
}
try (LauncherIcons li = LauncherIcons.obtain(context)) {
return li.createBadgedIconBitmap(d, object.getUser(), 0);
}
}
}
}
@@ -19,6 +19,7 @@ package com.android.launcher3.icons;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -56,6 +57,7 @@ import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
@@ -65,6 +67,9 @@ public class IconCache extends BaseIconCache {
private static final String TAG = "Launcher.IconCache";
private final Predicate<ItemInfoWithIcon> mIsUsingFallbackIconCheck = w -> w.bitmap != null
&& w.bitmap.isNullOrLowRes() && !isDefaultIcon(w.bitmap, w.user);
private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
private final CachingLogic<ShortcutInfo> mShortcutCachingLogic;
@@ -179,6 +184,77 @@ public class IconCache extends BaseIconCache {
getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
}
/**
* Fill in {@param info} with the icon for {@param si}
*/
public void getShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
getShortcutIcon(info, si, true, mIsUsingFallbackIconCheck);
}
/**
* Fill in {@param info} with an unbadged icon for {@param si}
*/
public void getUnbadgedShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
getShortcutIcon(info, si, false, mIsUsingFallbackIconCheck);
}
/**
* Fill in {@param info} with the icon and label for {@param si}. If the icon is not
* available, and fallback check returns true, it keeps the old icon.
*/
public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
@NonNull Predicate<T> fallbackIconCheck) {
getShortcutIcon(info, si, true /* use badged */, fallbackIconCheck);
}
private synchronized <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
boolean useBadged, @NonNull Predicate<T> fallbackIconCheck) {
BitmapInfo bitmapInfo;
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName, si.getUserHandle(),
() -> si, mShortcutCachingLogic, false, false).bitmap;
} else {
// If caching is disabled, load the full icon
bitmapInfo = mShortcutCachingLogic.loadIcon(mContext, si);
}
if (bitmapInfo.isNullOrLowRes()) {
bitmapInfo = getDefaultIcon(si.getUserHandle());
}
if (isDefaultIcon(bitmapInfo, si.getUserHandle()) && fallbackIconCheck.test(info)) {
return;
}
info.bitmap = bitmapInfo;
if (useBadged) {
BitmapInfo badgeInfo = getShortcutInfoBadge(si);
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
info.bitmap = li.badgeBitmap(info.bitmap.icon, badgeInfo);
}
}
}
/**
* Returns the badging info for the shortcut
*/
public BitmapInfo getShortcutInfoBadge(ShortcutInfo shortcutInfo) {
ComponentName cn = shortcutInfo.getActivity();
if (cn != null) {
// Get the app info for the source activity.
AppInfo appInfo = new AppInfo();
appInfo.user = shortcutInfo.getUserHandle();
appInfo.componentName = cn;
appInfo.intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(cn);
getTitleAndIcon(appInfo, false);
return appInfo.bitmap;
} else {
PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
getTitleAndIconForApp(pkgInfo, false);
return pkgInfo.bitmap;
}
}
/**
* Fill in info with the icon and label for deep shortcut.
*/
@@ -16,28 +16,11 @@
package com.android.launcher3.icons;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Process;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.Themes;
import java.util.function.Supplier;
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@@ -111,114 +94,4 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
public void close() {
recycle();
}
// below methods should also migrate to BaseIconFactory
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
return createShortcutIcon(shortcutInfo, true /* badged */);
}
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
return createShortcutIcon(shortcutInfo, badged, null);
}
public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
@Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
return createShortcutIconCached(shortcutInfo, badged, true, fallbackIconProvider);
} else {
return createShortcutIconLegacy(shortcutInfo, badged, fallbackIconProvider);
}
}
public BitmapInfo createShortcutIconLegacy(ShortcutInfo shortcutInfo, boolean badged,
@Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
mContext, shortcutInfo, mFillResIconDpi);
IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
final Bitmap unbadgedBitmap;
if (unbadgedDrawable != null) {
unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
} else {
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
if (fullIcon != null && fullIcon.bitmap != null) {
return fullIcon.bitmap;
}
}
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
if (!badged) {
return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
}
final Bitmap unbadgedfinal = unbadgedBitmap;
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
});
return BitmapInfo.of(icon, badge.bitmap.color);
}
@WorkerThread
public BitmapInfo createShortcutIconCached(ShortcutInfo shortcutInfo, boolean badged,
boolean useCache, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
final BitmapInfo bitmapInfo;
if (useCache) {
bitmapInfo = cache.getDeepShortcutTitleAndIcon(shortcutInfo).bitmap;
} else {
bitmapInfo = new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo);
}
final Bitmap unbadgedBitmap;
if (bitmapInfo.icon != null) {
unbadgedBitmap = bitmapInfo.icon;
} else {
if (fallbackIconProvider != null) {
// Fallback icons are already badged and with appropriate shadow
ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
if (fullIcon != null && fullIcon.bitmap != null) {
return fullIcon.bitmap;
}
}
unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
if (!badged) {
return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
}
final Bitmap unbadgedfinal = unbadgedBitmap;
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
});
return BitmapInfo.of(icon, badge.bitmap.color);
}
public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
String badgePkg = shortcutInfo.getPackage();
boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
if (cn != null && !hasBadgePkgSet) {
// Get the app info for the source activity.
AppInfo appInfo = new AppInfo();
appInfo.user = shortcutInfo.getUserHandle();
appInfo.componentName = cn;
appInfo.intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(cn);
cache.getTitleAndIcon(appInfo, false);
return appInfo;
} else {
PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
cache.getTitleAndIconForApp(pkgInfo, false);
return pkgInfo;
}
}
}
@@ -25,6 +25,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -57,6 +58,12 @@ public class ShortcutCachingLogic implements CachingLogic<ShortcutInfo> {
return info.getShortLabel();
}
@Override
public CharSequence getDescription(ShortcutInfo object, CharSequence fallback) {
CharSequence label = object.getLongLabel();
return TextUtils.isEmpty(label) ? fallback : label;
}
@NonNull
@Override
public BitmapInfo loadIcon(Context context, ShortcutInfo info) {
@@ -42,7 +42,7 @@ public final class FileLog {
private static Handler sHandler = null;
private static File sLogsDirectory = null;
private static final int LOG_DAYS = 2;
public static final int LOG_DAYS = 4;
public static void setDir(File logsDir) {
if (ENABLED) {
@@ -69,8 +69,7 @@ import java.util.UUID;
public class UserEventDispatcher implements ResourceBasedOverride {
private static final String TAG = "UserEvent";
private static final boolean IS_VERBOSE =
FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static final String UUID_STORAGE = "uuid";
public static UserEventDispatcher newInstance(Context context,
@@ -372,6 +371,25 @@ public class UserEventDispatcher implements ResourceBasedOverride {
dispatchUserEvent(event, null);
}
/**
* Logs proto lite version of LauncherEvent object to clearcut.
*/
public void logLauncherEvent(
com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
if (mPreviousHomeGesture) {
mPreviousHomeGesture = false;
}
mAppOrTaskLaunch = false;
launcherEvent.toBuilder()
.setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
.setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
if (!IS_VERBOSE) {
return;
}
Log.d(TAG, launcherEvent.toString());
}
public void logDeepShortcutsOpen(View icon) {
LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
@@ -273,7 +273,7 @@ public class BgDataModel {
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
folders.remove(item.id);
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
for (ItemInfo info : itemsIdMap) {
if (info.container == item.id) {
// We are deleting a folder which still contains items that
@@ -74,8 +74,8 @@ public class GridBackupTable {
@IntDef({STATE_NOT_FOUND, STATE_RAW, STATE_SANITIZED})
private @interface BackupState { }
public GridBackupTable(Context context, SQLiteDatabase db,
int hotseatSize, int gridX, int gridY) {
public GridBackupTable(Context context, SQLiteDatabase db, int hotseatSize, int gridX,
int gridY) {
mContext = context;
mDb = db;
@@ -105,7 +105,7 @@ public class GridBackupTable {
}
long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
Process.myUserHandle());
copyTable(BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
copyTable(mDb, BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
Log.d(TAG, "Backup table found");
return true;
}
@@ -118,10 +118,10 @@ public class GridBackupTable {
/**
* Copy valid grid entries from one table to another.
*/
private void copyTable(String from, String to, long userSerial) {
dropTable(mDb, to);
Favorites.addTableToDb(mDb, userSerial, false, to);
mDb.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
private static void copyTable(SQLiteDatabase db, String from, String to, long userSerial) {
dropTable(db, to);
Favorites.addTableToDb(db, userSerial, false, to);
db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
}
private void encodeDBProperties(int options) {
@@ -174,7 +174,7 @@ public class GridBackupTable {
// skip restore if dimensions in backup table differs from current setup.
return false;
}
copyTable(Favorites.BACKUP_TABLE_NAME, Favorites.TABLE_NAME, oldProfileId);
copyTable(mDb, Favorites.BACKUP_TABLE_NAME, Favorites.TABLE_NAME, oldProfileId);
Log.d(TAG, "Backup restored");
return true;
}
@@ -183,7 +183,7 @@ public class GridBackupTable {
* Performs a backup on the workspace layout.
*/
public void doBackup(long profileId, int options) {
copyTable(Favorites.TABLE_NAME, Favorites.BACKUP_TABLE_NAME, profileId);
copyTable(mDb, Favorites.TABLE_NAME, Favorites.BACKUP_TABLE_NAME, profileId);
encodeDBProperties(options);
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 The Android Open Source Project
* Copyright (C) 2020 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.
@@ -13,25 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.uioverrides;
package com.android.launcher3.model;
import android.content.Context;
import android.view.OrientationEventListener;
/**
* Utility class for listening for rotation changes
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
* result of restoring from a larger device or device density change.
*/
public class DisplayRotationListener extends OrientationEventListener {
public class GridSizeMigrationTaskV2 {
private final Runnable mCallback;
private GridSizeMigrationTaskV2(Context context) {
public DisplayRotationListener(Context context, Runnable callback) {
super(context);
mCallback = callback;
}
@Override
public void onOrientationChanged(int i) {
mCallback.run();
/**
* Migrates the workspace and hotseat in case their sizes changed.
*
* @return false if the migration failed.
*/
public static boolean migrateGridIfNeeded(Context context) {
// To be implemented.
return true;
}
}
@@ -16,6 +16,8 @@
package com.android.launcher3.model;
import static android.graphics.BitmapFactory.decodeByteArray;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
@@ -26,7 +28,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.graphics.BitmapFactory;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -166,37 +167,30 @@ public class LoaderCursor extends CursorWrapper {
*/
protected boolean loadIcon(WorkspaceItemInfo info) {
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
return loadIcon(info, li);
}
}
/**
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
*/
protected boolean loadIcon(WorkspaceItemInfo info, LauncherIcons li) {
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
String packageName = getString(iconPackageIndex);
String resourceName = getString(iconResourceIndex);
if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
info.iconResource = new ShortcutIconResource();
info.iconResource.packageName = packageName;
info.iconResource.resourceName = resourceName;
BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
if (iconInfo != null) {
info.bitmap = iconInfo;
return true;
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
String packageName = getString(iconPackageIndex);
String resourceName = getString(iconResourceIndex);
if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
info.iconResource = new ShortcutIconResource();
info.iconResource.packageName = packageName;
info.iconResource.resourceName = resourceName;
BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
if (iconInfo != null) {
info.bitmap = iconInfo;
return true;
}
}
}
}
// Failed to load from resource, try loading from DB.
byte[] data = getBlob(iconIndex);
try {
info.bitmap = li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + info, e);
return false;
// Failed to load from resource, try loading from DB.
byte[] data = getBlob(iconIndex);
try {
info.bitmap = li.createIconBitmap(decodeByteArray(data, 0, data.length));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + info, e);
return false;
}
}
}
+26 -49
View File
@@ -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.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -35,7 +36,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.ShortcutInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -50,7 +50,6 @@ import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
@@ -60,12 +59,12 @@ import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderNameInfo;
import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherActivityCachingLogic;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShortcutCachingLogic;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
@@ -94,7 +93,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.function.Supplier;
/**
* Runnable for the thread that loads the contents of the launcher:
@@ -170,32 +168,15 @@ public class LoaderTask implements Runnable {
}
public void run() {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"LoaderTask1 " + this);
}
synchronized (this) {
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"LoaderTask2 " + this);
}
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
TimingLogger logger = TestProtocol.sDebugTracing ?
new TimingLogger(TAG, "run") {
@Override
public void addSplit(String splitLabel) {
super.addSplit(splitLabel);
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"LoaderTask.addSplit " + splitLabel);
}
}
: new TimingLogger(TAG, "run");
TimingLogger logger = new TimingLogger(TAG, "run");
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts);
@@ -263,7 +244,8 @@ public class LoaderTask implements Runnable {
verifyNotStopped();
// fourth step
List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
logger.addSplit("load widgets");
verifyNotStopped();
@@ -271,8 +253,9 @@ public class LoaderTask implements Runnable {
logger.addSplit("bindWidgets");
verifyNotStopped();
updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(
mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated);
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
logger.addSplit("save widgets in icon cache");
// fifth step
@@ -284,10 +267,6 @@ public class LoaderTask implements Runnable {
updateHandler.finish();
logger.addSplit("finish icon update");
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
"LoaderTask3 " + this);
}
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
@@ -320,7 +299,9 @@ public class LoaderTask implements Runnable {
clearDb = true;
}
if (!clearDb && !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
if (!clearDb && (MULTI_DB_GRID_MIRATION_ALGO.get()
? !GridSizeMigrationTaskV2.migrateGridIfNeeded(context)
: !GridSizeMigrationTask.migrateGridIfNeeded(context))) {
// Migration failed. Clear workspace.
clearDb = true;
}
@@ -544,17 +525,10 @@ public class LoaderTask implements Runnable {
continue;
}
info = new WorkspaceItemInfo(pinnedShortcut, context);
final WorkspaceItemInfo finalInfo = info;
LauncherIcons li = LauncherIcons.obtain(context);
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
c.loadIcon(finalInfo, li) ? finalInfo : null;
info.bitmap = li.createShortcutIcon(
pinnedShortcut, true /* badged */,
fallbackIconProvider);
li.recycle();
mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
@@ -854,23 +828,23 @@ public class LoaderTask implements Runnable {
private void setIgnorePackages(IconCacheUpdateHandler updateHandler) {
// Ignore packages which have a promise icon.
HashSet<String> packagesToIgnore = new HashSet<>();
synchronized (mBgDataModel) {
for (ItemInfo info : mBgDataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
if (si.isPromise() && si.getTargetComponent() != null) {
packagesToIgnore.add(si.getTargetComponent().getPackageName());
updateHandler.addPackagesToIgnore(
si.user, si.getTargetComponent().getPackageName());
}
} else if (info instanceof LauncherAppWidgetInfo) {
LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
packagesToIgnore.add(lawi.providerName.getPackageName());
updateHandler.addPackagesToIgnore(
lawi.user, lawi.providerName.getPackageName());
}
}
}
}
updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore);
}
private List<LauncherActivityInfo> loadAllApps() {
@@ -927,15 +901,18 @@ public class LoaderTask implements Runnable {
}
private void loadFolderNames() {
FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext());
FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
mBgAllAppsList.data, mBgDataModel.folders);
synchronized (mBgDataModel) {
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
String[] suggestedOut = new String[FolderNameProvider.SUGGEST_MAX];
FolderNameInfo[] suggestionInfos =
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
FolderInfo info = mBgDataModel.folders.valueAt(i);
if (info.suggestedFolderNames == null) {
provider.getSuggestedFolderName(mApp.getContext(), info.contents, suggestedOut);
info.suggestedFolderNames = new Intent().putExtra("suggest", suggestedOut);
provider.getSuggestedFolderName(mApp.getContext(), info.contents,
suggestionInfos);
info.suggestedFolderNames = new Intent().putExtra("suggest", suggestionInfos);
}
}
}
@@ -97,7 +97,7 @@ public class ModelUtils {
return Integer.compare(lhs.screenId, rhs.screenId);
}
default:
if (FeatureFlags.IS_DOGFOOD_BUILD) {
if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException(
"Unexpected container type when sorting workspace items.");
}
@@ -115,8 +115,9 @@ public class ModelWriter {
ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// check all the data is consistent
if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
modelItem instanceof WorkspaceItemInfo && item instanceof WorkspaceItemInfo) {
if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_STUDIO_BUILD
&& modelItem instanceof WorkspaceItemInfo
&& item instanceof WorkspaceItemInfo) {
if (modelItem.title.toString().equals(item.title.toString()) &&
modelItem.getIntent().filterEquals(item.getIntent()) &&
modelItem.id == item.id &&
@@ -320,7 +321,7 @@ public class ModelWriter {
*/
public void prepareToUndoDelete() {
if (!mPreparingToUndo) {
if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_DOGFOOD_BUILD) {
if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("There are still uncommitted delete operations!");
}
mDeleteRunnables.clear();

Some files were not shown because too many files have changed in this diff Show More