Merge "Show multiple icons for GroupedTaskView" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 359f436..68dfac7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -19,10 +19,12 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.graphics.Rect;
import android.view.MotionEvent;
@@ -42,6 +44,7 @@
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.OnboardingPrefs;
@@ -76,6 +79,16 @@
private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
this::onStashedInAppChanged;
+ private final StateManager.StateListener<LauncherState> mStateListener =
+ new StateManager.StateListener<LauncherState>() {
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ TaskbarStashController controller = mControllers.taskbarStashController;
+ controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+ finalState.isTaskbarStashed());
+ }
+ };
+
// Initialized in init.
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
@@ -118,6 +131,7 @@
onStashedInAppChanged(mLauncher.getDeviceProfile());
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+ mLauncher.getStateManager().addStateListener(mStateListener);
}
@Override
@@ -127,6 +141,7 @@
mIconAlignmentForGestureState.finishAnimation();
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+ mLauncher.getStateManager().removeStateListener(mStateListener);
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.setTaskbarUIController(null);
}
@@ -184,24 +199,28 @@
*/
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@NonNull RecentsAnimationCallbacks callbacks, long duration) {
+ AnimatorSet animatorSet = new AnimatorSet();
TaskbarStashController stashController = mControllers.taskbarStashController;
- ObjectAnimator animator = mIconAlignmentForGestureState
- .animateToValue(1)
- .setDuration(duration);
- animator.addListener(new AnimatorListenerAdapter() {
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+ toState.isTaskbarStashed());
+ if (toState.isTaskbarStashed()) {
+ animatorSet.play(stashController.applyStateWithoutStart(duration));
+ } else {
+ animatorSet.play(mIconAlignmentForGestureState
+ .animateToValue(1)
+ .setDuration(duration));
+ }
+ animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationEnd(Animator animator) {
mTargetStateOverride = null;
animator.removeListener(this);
}
@Override
- public void onAnimationStart(Animator animation) {
+ public void onAnimationStart(Animator animator) {
mTargetStateOverride = toState;
mIsAnimatingToLauncherViaGesture = true;
- // TODO: FLAG_IN_APP might be sufficient for now, but in the future we do want to
- // add another flag for LauncherState as well. We will need to decide whether to
- // show hotseat or the task bar.
stashController.updateStateForFlag(FLAG_IN_APP, false);
stashController.applyState(duration);
}
@@ -215,7 +234,7 @@
callbacks.removeListener(listener);
});
- return animator;
+ return animatorSet;
}
private float getCurrentIconAlignmentRatio() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index fc277cc..a9ff03b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -41,6 +41,7 @@
public static final int FLAG_IN_APP = 1 << 0;
public static final int FLAG_STASHED_IN_APP = 1 << 1;
+ public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 2;
/**
* How long to stash/unstash when manually invoked via long press.
@@ -83,9 +84,6 @@
private final int mStashedHeight;
private final int mUnstashedHeight;
- private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
- flags -> (((flags & FLAG_IN_APP) != 0) && (flags & FLAG_STASHED_IN_APP) != 0));
-
// Initialized in init.
private TaskbarControllers mControllers;
// Taskbar background properties.
@@ -106,6 +104,18 @@
private @Nullable AnimatorSet mAnimator;
+ // Evaluate whether the handle should be stashed
+ private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
+ flags -> {
+ if (!supportsStashing()) {
+ return false;
+ }
+ boolean inApp = (flags & FLAG_IN_APP) != 0;
+ boolean stashedInApp = (flags & FLAG_STASHED_IN_APP) != 0;
+ boolean stashedLauncherState = (flags & FLAG_IN_STASHED_LAUNCHER_STATE) != 0;
+ return (inApp && stashedInApp) || (!inApp && stashedLauncherState);
+ });
+
public TaskbarStashController(TaskbarActivityContext activity) {
mActivity = activity;
mPrefs = Utilities.getPrefs(mActivity);
@@ -331,7 +341,15 @@
}
public void applyState(long duration) {
- mStatePropertyHolder.setState(mState, duration);
+ mStatePropertyHolder.setState(mState, duration, true);
+ }
+
+ public Animator applyStateWithoutStart() {
+ return applyStateWithoutStart(TASKBAR_STASH_DURATION);
+ }
+
+ public Animator applyStateWithoutStart(long duration) {
+ return mStatePropertyHolder.setState(mState, duration, false);
}
/**
@@ -360,12 +378,16 @@
mStashCondition = stashCondition;
}
- public void setState(int flags, long duration) {
+ public Animator setState(int flags, long duration, boolean start) {
boolean isStashed = mStashCondition.test(flags);
if (mIsStashed != isStashed) {
mIsStashed = isStashed;
- createAnimToIsStashed(mIsStashed, duration).start();
+ Animator animator = createAnimToIsStashed(mIsStashed, duration);
+ if (start) {
+ animator.start();
+ }
}
+ return mAnimator;
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 1302ac0..d396018 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -97,6 +97,11 @@
}
@Override
+ public boolean isTaskbarStashed() {
+ return true;
+ }
+
+ @Override
public int getWorkspaceScrimColor(Launcher launcher) {
return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
}
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 65b46cf..720d736 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -180,6 +180,16 @@
<!-- These min cell values are only used if GridDisplayOption#isScalable is true-->
<attr name="minCellHeightDps" format="float" />
<attr name="minCellWidthDps" format="float" />
+ <!-- twoPanelPortraitMinCellWidthDps defaults to minCellHeightDps, if not specified -->
+ <attr name="twoPanelPortraitMinCellHeightDps" format="float" />
+ <!-- twoPanelPortraitMinCellHeightDps defaults to minCellWidthDps, if not specified -->
+ <attr name="twoPanelPortraitMinCellWidthDps" format="float" />
+ <!-- twoPanelLandscapeMinCellHeightDps defaults to twoPanelPortraitMinCellHeightDps,
+ if not specified -->
+ <attr name="twoPanelLandscapeMinCellHeightDps" format="float" />
+ <!-- twoPanelLandscapeMinCellWidthDps defaults to twoPanelPortraitMinCellWidthDps,
+ if not specified -->
+ <attr name="twoPanelLandscapeMinCellWidthDps" format="float" />
<attr name="borderSpacingDps" format="float" />
diff --git a/src/com/android/launcher3/DevicePaddings.java b/src/com/android/launcher3/DevicePaddings.java
index 7c387b1..08fb47b 100644
--- a/src/com/android/launcher3/DevicePaddings.java
+++ b/src/com/android/launcher3/DevicePaddings.java
@@ -36,12 +36,12 @@
* The unused or "extra" height is allocated to three different variable heights:
* - The space above the workspace
* - The space between the workspace and hotseat
- * - The espace below the hotseat
+ * - The space below the hotseat
*/
public class DevicePaddings {
- private static final String DEVICE_PADDING = "device-paddings";
- private static final String DEVICE_PADDINGS = "device-padding";
+ private static final String DEVICE_PADDINGS = "device-paddings";
+ private static final String DEVICE_PADDING = "device-padding";
private static final String WORKSPACE_TOP_PADDING = "workspaceTopPadding";
private static final String WORKSPACE_BOTTOM_PADDING = "workspaceBottomPadding";
@@ -58,13 +58,13 @@
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
- if ((type == XmlPullParser.START_TAG) && DEVICE_PADDING.equals(parser.getName())) {
+ if ((type == XmlPullParser.START_TAG) && DEVICE_PADDINGS.equals(parser.getName())) {
final int displayDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > displayDepth)
&& type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
- && DEVICE_PADDINGS.equals(parser.getName())) {
+ && DEVICE_PADDING.equals(parser.getName())) {
TypedArray a = context.obtainStyledAttributes(
Xml.asAttributeSet(parser), R.styleable.DevicePadding);
int maxWidthPx = a.getDimensionPixelSize(
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index b7d0481..3121bfc 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -33,6 +33,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
+import android.util.Pair;
import android.view.Surface;
import com.android.launcher3.CellLayout.ContainerType;
@@ -576,8 +577,9 @@
// We scale to fit the cellWidth and cellHeight in the available space.
// The benefit of scalable grids is that we can get consistent aspect ratios between
// devices.
- float usedWidth = (cellWidthPx * inv.numColumns)
- + (cellLayoutBorderSpacingPx * (inv.numColumns - 1))
+ int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+ float usedWidth = (cellWidthPx * numColumns)
+ + (cellLayoutBorderSpacingPx * (numColumns - 1))
+ (desiredWorkspaceLeftRightMarginPx * 2);
// We do not subtract padding here, as we also scale the workspace padding if needed.
scaleX = availableWidthPx / usedWidth;
@@ -638,8 +640,9 @@
setCellLayoutBorderSpacing((int) (cellLayoutBorderSpacingOriginalPx * scale));
if (isScalableGrid) {
- cellWidthPx = pxFromDp(inv.minCellWidth, mMetrics, scale);
- cellHeightPx = pxFromDp(inv.minCellHeight, mMetrics, scale);
+ PointF minCellHeightAndWidth = getMinCellHeightAndWidth();
+ cellWidthPx = pxFromDp(minCellHeightAndWidth.x, mMetrics, scale);
+ cellHeightPx = pxFromDp(minCellHeightAndWidth.y, mMetrics, scale);
int cellContentHeight = iconSizePx + iconDrawablePaddingPx
+ Utilities.calculateTextHeight(iconTextSizePx);
cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
@@ -698,6 +701,28 @@
folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
}
+ /**
+ * Returns the minimum cell height and width as a pair.
+ */
+ private PointF getMinCellHeightAndWidth() {
+ PointF result = new PointF();
+
+ if (isTwoPanels) {
+ if (isLandscape) {
+ result.x = inv.twoPanelLandscapeMinCellWidthDps;
+ result.y = inv.twoPanelLandscapeMinCellHeightDps;
+ } else {
+ result.x = inv.twoPanelPortraitMinCellWidthDps;
+ result.y = inv.twoPanelPortraitMinCellHeightDps;
+ }
+ } else {
+ result.x = inv.minCellWidth;
+ result.y = inv.minCellHeight;
+ }
+
+ return result;
+ }
+
private void updateAvailableFolderCellDimensions(Resources res) {
updateFolderCellSize(1f, res);
@@ -781,17 +806,14 @@
if (result == null) {
result = new Point();
}
+
// Since we are only concerned with the overall padding, layout direction does
// not matter.
Point padding = getTotalWorkspacePadding();
- // availableWidthPx is the screen width of the device. In 2 panels mode, each panel should
- // only have half of the screen width. In addition, there is only cellLayoutPadding in the
- // left side of the left most panel and the right most side of the right panel. There is no
- // cellLayoutPadding in the middle.
- int screenWidthPx = isTwoPanels
- ? availableWidthPx / 2 - padding.x - cellLayoutPaddingLeftRightPx
- : availableWidthPx - padding.x - cellLayoutPaddingLeftRightPx * 2;
- result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacingPx, inv.numColumns);
+
+ int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+ int screenWidthPx = availableWidthPx - padding.x;
+ result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacingPx, numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.y
- cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
return result;
@@ -1038,6 +1060,14 @@
writer.println(prefix + "\tinv.minCellWidth:" + inv.minCellWidth + "dp");
writer.println(prefix + "\tinv.minCellHeight:" + inv.minCellHeight + "dp");
+ writer.println(prefix + "\tinv.twoPanelPortraitMinCellHeightDps:"
+ + inv.twoPanelPortraitMinCellHeightDps + "dp");
+ writer.println(prefix + "\tinv.twoPanelPortraitMinCellWidthDps:"
+ + inv.twoPanelPortraitMinCellWidthDps + "dp");
+ writer.println(prefix + "\tinv.twoPanelLandscapeMinCellHeightDps:"
+ + inv.twoPanelLandscapeMinCellHeightDps + "dp");
+ writer.println(prefix + "\tinv.twoPanelLandscapeMinCellWidthDps:"
+ + inv.twoPanelLandscapeMinCellWidthDps + "dp");
writer.println(prefix + "\tinv.numColumns:" + inv.numColumns);
writer.println(prefix + "\tinv.numRows:" + inv.numRows);
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index 21bc479..92432a8 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -26,7 +26,6 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.views.ActivityContext;
@@ -99,18 +98,6 @@
}
}
- /**
- * Sets whether EditText background should be visible
- * @param maxAlpha defines the maximum alpha the background should animates to
- */
- public void setBackgroundVisibility(boolean visible, float maxAlpha) {}
-
- /**
- * Returns whether a visible background is set on EditText
- */
- public boolean getBackgroundVisibility() {
- return getBackground() != null;
- }
public void showKeyboard() {
mShowImeAfterFirstLayout = !showSoftInput();
@@ -150,9 +137,6 @@
if (!TextUtils.isEmpty(getText())) {
setText("");
}
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- return;
- }
if (isFocused()) {
View nextFocus = focusSearch(View.FOCUS_DOWN);
if (nextFocus != null) {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 244cb59..1799f26 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -112,6 +112,10 @@
public float minCellHeight;
public float minCellWidth;
+ public float twoPanelPortraitMinCellHeightDps;
+ public float twoPanelPortraitMinCellWidthDps;
+ public float twoPanelLandscapeMinCellHeightDps;
+ public float twoPanelLandscapeMinCellWidthDps;
public float borderSpacing;
private SparseArray<TypedValue> mExtraAttrs;
@@ -274,6 +278,10 @@
minCellHeight = displayOption.minCellHeight;
minCellWidth = displayOption.minCellWidth;
+ twoPanelPortraitMinCellHeightDps = displayOption.twoPanelPortraitMinCellHeightDps;
+ twoPanelPortraitMinCellWidthDps = displayOption.twoPanelPortraitMinCellWidthDps;
+ twoPanelLandscapeMinCellHeightDps = displayOption.twoPanelLandscapeMinCellHeightDps;
+ twoPanelLandscapeMinCellWidthDps = displayOption.twoPanelLandscapeMinCellWidthDps;
borderSpacing = displayOption.borderSpacing;
numShownHotseatIcons = closestProfile.numHotseatIcons;
@@ -707,6 +715,10 @@
private float minCellHeight;
private float minCellWidth;
+ private float twoPanelPortraitMinCellHeightDps;
+ private float twoPanelPortraitMinCellWidthDps;
+ private float twoPanelLandscapeMinCellHeightDps;
+ private float twoPanelLandscapeMinCellWidthDps;
private float borderSpacing;
private final float[] iconSizes = new float[COUNT_TOTAL];
@@ -726,6 +738,17 @@
minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
+ twoPanelPortraitMinCellHeightDps = a.getFloat(
+ R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellHeightDps,
+ minCellHeight);
+ twoPanelPortraitMinCellWidthDps = a.getFloat(
+ R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellWidthDps, minCellWidth);
+ twoPanelLandscapeMinCellHeightDps = a.getFloat(
+ R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellHeightDps,
+ twoPanelPortraitMinCellHeightDps);
+ twoPanelLandscapeMinCellWidthDps = a.getFloat(
+ R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellWidthDps,
+ twoPanelPortraitMinCellWidthDps);
borderSpacing = a.getFloat(R.styleable.ProfileDisplayOption_borderSpacingDps, 0);
iconSizes[INDEX_DEFAULT] =
@@ -782,6 +805,10 @@
}
minCellHeight *= w;
minCellWidth *= w;
+ twoPanelPortraitMinCellHeightDps *= w;
+ twoPanelPortraitMinCellWidthDps *= w;
+ twoPanelLandscapeMinCellHeightDps *= w;
+ twoPanelLandscapeMinCellWidthDps *= w;
borderSpacing *= w;
return this;
}
@@ -793,6 +820,10 @@
}
minCellHeight += p.minCellHeight;
minCellWidth += p.minCellWidth;
+ twoPanelPortraitMinCellHeightDps += p.twoPanelPortraitMinCellHeightDps;
+ twoPanelPortraitMinCellWidthDps += p.twoPanelPortraitMinCellWidthDps;
+ twoPanelLandscapeMinCellHeightDps += p.twoPanelLandscapeMinCellHeightDps;
+ twoPanelLandscapeMinCellWidthDps += p.twoPanelLandscapeMinCellWidthDps;
borderSpacing += p.borderSpacing;
return this;
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ed9f044..85dd3b3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -87,6 +87,7 @@
import android.os.Process;
import android.os.StrictMode;
import android.os.SystemClock;
+import android.os.Trace;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.Log;
@@ -280,6 +281,11 @@
private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
+ private static final String DISPLAY_WORKSPACE_TRACE_METHOD_NAME = "DisplayWorkspaceFirstFrame";
+ private static final String DISPLAY_ALL_APPS_TRACE_METHOD_NAME = "DisplayAllApps";
+ public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0;
+ public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1;
+
private Configuration mOldConfig;
@Thunk
@@ -366,7 +372,15 @@
private LauncherState mPrevLauncherState;
@Override
+ @TargetApi(Build.VERSION_CODES.S)
protected void onCreate(Bundle savedInstanceState) {
+ // Only use a hard-coded cookie since we only want to trace this once.
+ if (Utilities.ATLEAST_S) {
+ Trace.beginAsyncSection(
+ DISPLAY_WORKSPACE_TRACE_METHOD_NAME, DISPLAY_WORKSPACE_TRACE_COOKIE);
+ Trace.beginAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
+ DISPLAY_ALL_APPS_TRACE_COOKIE);
+ }
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
TraceHelper.FLAG_UI_EVENT);
if (DEBUG_STRICT_MODE) {
@@ -2584,6 +2598,7 @@
}
@Override
+ @TargetApi(Build.VERSION_CODES.S)
public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
mSynchronouslyBoundPages = boundPages;
mPagesToBindSynchronously = new IntSet();
@@ -2606,6 +2621,10 @@
executor.onLoadAnimationCompleted();
}
executor.attachTo(this);
+ if (Utilities.ATLEAST_S) {
+ Trace.endAsyncSection(DISPLAY_WORKSPACE_TRACE_METHOD_NAME,
+ DISPLAY_WORKSPACE_TRACE_COOKIE);
+ }
}
/**
@@ -2669,9 +2688,14 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
+ @TargetApi(Build.VERSION_CODES.S)
public void bindAllApplications(AppInfo[] apps, int flags) {
mAppsView.getAppsStore().setApps(apps, flags);
PopupContainerWithArrow.dismissInvalidPopup(this);
+ if (Utilities.ATLEAST_S) {
+ Trace.endAsyncSection(DISPLAY_ALL_APPS_TRACE_METHOD_NAME,
+ DISPLAY_ALL_APPS_TRACE_COOKIE);
+ }
}
/**
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index d663480..03e4ee7 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -199,6 +199,7 @@
public static final int CONTAINER_WIDGETS_TRAY = -105;
public static final int CONTAINER_BOTTOM_WIDGETS_TRAY = -112;
public static final int CONTAINER_PIN_WIDGETS = -113;
+ public static final int CONTAINER_WALLPAPERS = -114;
// Represents search results view.
public static final int CONTAINER_SEARCH_RESULTS = -106;
public static final int CONTAINER_SHORTCUTS = -107;
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 7985ab5..15378e0 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -197,6 +197,10 @@
return (getVisibleElements(launcher) & elements) == elements;
}
+ public boolean isTaskbarStashed() {
+ return false;
+ }
+
/**
* Fraction shift in the vertical translation UI and related properties
*
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 54b2c96..bd2a14f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -347,13 +347,13 @@
if (panelCount > 1) {
if (i % panelCount == leftPanelModulus) {
paddingLeft = paddingLeftRight;
- paddingRight = 0;
- } else if (i % panelCount == rightPanelModulus) {
- paddingLeft = 0;
+ paddingRight = grid.cellLayoutBorderSpacingPx / 2;
+ } else if (i % panelCount == rightPanelModulus) { // right side panel
+ paddingLeft = grid.cellLayoutBorderSpacingPx / 2;
paddingRight = paddingLeftRight;
} else { // middle panel
- paddingLeft = 0;
- paddingRight = 0;
+ paddingLeft = grid.cellLayoutBorderSpacingPx / 2;
+ paddingRight = grid.cellLayoutBorderSpacingPx / 2;
}
}
// SparseArrayMap doesn't keep the order
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index e779ee8..ee5f7e4 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -59,7 +59,6 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
@@ -624,6 +623,9 @@
for (int i = 0; i < mAH.length; i++) {
mAH[i].padding.top = padding;
mAH[i].applyPadding();
+ if (mAH[i].recyclerView != null) {
+ mAH[i].recyclerView.scrollToTop();
+ }
}
mHeaderTop = mHeader.getTop();
}
@@ -639,6 +641,7 @@
public void onClearSearchResult() {
mIsSearching = false;
+ mHeader.setCollapsed(false);
rebindAdapters();
getActiveRecyclerView().scrollToTop();
}
@@ -814,14 +817,13 @@
invalidateHeader();
}
if (mSearchUiManager.getEditText() != null) {
- ExtendedEditText editText = mSearchUiManager.getEditText();
- boolean bgVisible = editText.getBackgroundVisibility();
+ boolean bgVisible = mSearchUiManager.getBackgroundVisibility();
if (scrolledOffset == 0 && !mIsSearching) {
bgVisible = true;
} else if (scrolledOffset > mHeaderThreshold) {
bgVisible = false;
}
- editText.setBackgroundVisibility(bgVisible, 1 - prog);
+ mSearchUiManager.setBackgroundVisibility(bgVisible, 1 - prog);
}
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 9bf6043..6ff2132 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -47,6 +47,8 @@
/**
* Scrolls the content vertically.
+ * @param scroll scrolled distance in pixels for active recyclerview.
+ * @param isScrolledOut bool to determine if row is scrolled out of view
*/
void setVerticalScroll(int scroll, boolean isScrolledOut);
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 924a392..7478b53 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -49,6 +49,19 @@
ExtendedEditText getEditText();
/**
+ * Sets whether EditText background should be visible
+ * @param maxAlpha defines the maximum alpha the background should animates to
+ */
+ default void setBackgroundVisibility(boolean visible, float maxAlpha) {}
+
+ /**
+ * Returns whether a visible background is set on EditText
+ */
+ default boolean getBackgroundVisibility() {
+ return false;
+ }
+
+ /**
* sets highlight result's title
*/
default void setFocusedResultTitle(@Nullable CharSequence title) { }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index f091262..382f7a7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -147,6 +147,11 @@
public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
"ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace");
+ public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag(
+ "ENABLE_BULK_WORKSPACE_ICON_LOADING",
+ false,
+ "Enable loading workspace icons in bulk.");
+
// Keep as DeviceFlag for remote disable in emergency.
public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
"ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 94fc708..b4907e8 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -230,13 +230,13 @@
CellLayout firstScreen = mRootView.findViewById(R.id.workspace);
firstScreen.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
mDp.workspacePadding.top,
- mDp.workspacePadding.right,
+ mDp.workspacePadding.right + mDp.cellLayoutBorderSpacingPx / 2,
mDp.workspacePadding.bottom);
mWorkspaceScreens.put(FIRST_SCREEN_ID, firstScreen);
if (mDp.isTwoPanels) {
CellLayout rightPanel = mRootView.findViewById(R.id.workspace_right);
- rightPanel.setPadding(mDp.workspacePadding.left,
+ rightPanel.setPadding(mDp.workspacePadding.left + mDp.cellLayoutBorderSpacingPx / 2,
mDp.workspacePadding.top,
mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
mDp.workspacePadding.bottom);
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 1a468ae..60d6e83 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -19,6 +19,8 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static java.util.stream.Collectors.groupingBy;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -30,10 +32,15 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
import android.graphics.drawable.Drawable;
import android.os.Process;
+import android.os.Trace;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.NonNull;
@@ -47,6 +54,7 @@
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -56,8 +64,13 @@
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Stream;
/**
* Cache of application icons. Icons can be made from any thread.
@@ -306,6 +319,87 @@
applyCacheEntry(entry, infoInOut);
}
+ /**
+ * Creates an sql cursor for a query of a set of ItemInfoWithIcon icons and titles.
+ *
+ * @param iconRequestInfos List of IconRequestInfos representing titles and icons to query.
+ * @param user UserHandle all the given iconRequestInfos share
+ * @param useLowResIcons whether we should exclude the icon column from the sql results.
+ */
+ private <T extends ItemInfoWithIcon> Cursor createBulkQueryCursor(
+ List<IconRequestInfo<T>> iconRequestInfos, UserHandle user, boolean useLowResIcons)
+ throws SQLiteException {
+ String[] queryParams = Stream.concat(
+ iconRequestInfos.stream()
+ .map(r -> r.itemInfo.getTargetComponent())
+ .filter(Objects::nonNull)
+ .distinct()
+ .map(ComponentName::flattenToString),
+ Stream.of(Long.toString(getSerialNumberForUser(user)))).toArray(String[]::new);
+ String componentNameQuery = TextUtils.join(
+ ",", Collections.nCopies(queryParams.length - 1, "?"));
+
+ return mIconDb.query(
+ useLowResIcons ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
+ IconDB.COLUMN_COMPONENT
+ + " IN ( " + componentNameQuery + " )"
+ + " AND " + IconDB.COLUMN_USER + " = ?",
+ queryParams);
+ }
+
+ /**
+ * Load and fill icons requested in iconRequestInfos using a single bulk sql query.
+ */
+ public synchronized <T extends ItemInfoWithIcon> void getTitlesAndIconsInBulk(
+ List<IconRequestInfo<T>> iconRequestInfos) {
+ Map<Pair<UserHandle, Boolean>, List<IconRequestInfo<T>>> iconLoadSubsectionsMap =
+ iconRequestInfos.stream()
+ .collect(groupingBy(iconRequest ->
+ Pair.create(iconRequest.itemInfo.user, iconRequest.useLowResIcon)));
+
+ Trace.beginSection("loadIconsInBulk");
+ iconLoadSubsectionsMap.forEach((sectionKey, filteredList) -> {
+ Map<ComponentName, List<IconRequestInfo<T>>> duplicateIconRequestsMap =
+ filteredList.stream()
+ .collect(groupingBy(iconRequest ->
+ iconRequest.itemInfo.getTargetComponent()));
+
+ Trace.beginSection("loadIconSubsectionInBulk");
+ try (Cursor c = createBulkQueryCursor(
+ filteredList,
+ /* user = */ sectionKey.first,
+ /* useLowResIcons = */ sectionKey.second)) {
+ int componentNameColumnIndex = c.getColumnIndexOrThrow(IconDB.COLUMN_COMPONENT);
+ while (c.moveToNext()) {
+ ComponentName cn = ComponentName.unflattenFromString(
+ c.getString(componentNameColumnIndex));
+ List<IconRequestInfo<T>> duplicateIconRequests =
+ duplicateIconRequestsMap.get(cn);
+
+ if (cn != null) {
+ CacheEntry entry = cacheLocked(
+ cn,
+ /* user = */ sectionKey.first,
+ () -> duplicateIconRequests.get(0).launcherActivityInfo,
+ mLauncherActivityInfoCachingLogic,
+ c,
+ /* usePackageIcon= */ false,
+ /* useLowResIcons = */ sectionKey.second);
+
+ for (IconRequestInfo<T> iconRequest : duplicateIconRequests) {
+ applyCacheEntry(entry, iconRequest.itemInfo);
+ }
+ }
+ }
+ } catch (SQLiteException e) {
+ Log.d(TAG, "Error reading icon cache", e);
+ } finally {
+ Trace.endSection();
+ }
+ });
+ Trace.endSection();
+ }
+
/**
* Fill in {@param infoInOut} with the corresponding icon and label.
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index e2df16d..0fc4c2d 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -119,12 +119,23 @@
}
@Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DeviceGridState that = (DeviceGridState) o;
- return mNumHotseat == that.mNumHotseat
- && deviceTypeCompatible(mDeviceType, that.mDeviceType)
- && Objects.equals(mGridSizeString, that.mGridSizeString);
+ public String toString() {
+ return "DeviceGridState{"
+ + "mGridSizeString='" + mGridSizeString + '\''
+ + ", mNumHotseat=" + mNumHotseat
+ + ", mDeviceType=" + mDeviceType
+ + '}';
+ }
+
+ /**
+ * Returns true if the database from another DeviceGridState can be loaded into the current
+ * DeviceGridState without migration, or false otherwise.
+ */
+ public boolean isCompatible(DeviceGridState other) {
+ if (this == other) return true;
+ if (other == null) return false;
+ return mNumHotseat == other.mNumHotseat
+ && deviceTypeCompatible(mDeviceType, other.mDeviceType)
+ && Objects.equals(mGridSizeString, other.mGridSizeString);
}
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index e7d0749..fc86cf8 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -103,7 +103,13 @@
* Check given a new IDP, if migration is necessary.
*/
public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
- return !new DeviceGridState(idp).equals(new DeviceGridState(context));
+ DeviceGridState idpGridState = new DeviceGridState(idp);
+ DeviceGridState contextGridState = new DeviceGridState(context);
+ boolean needsToMigrate = !idpGridState.isCompatible(contextGridState);
+ // TODO: Revert this change after b/200010396 is fixed
+ Log.d(TAG, "Migration is needed. idpGridState: " + idpGridState
+ + ", contextGridState: " + contextGridState);
+ return needsToMigrate;
}
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 7e3bcee..8a5a9bf 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,13 +16,10 @@
package com.android.launcher3.model;
-import static android.graphics.BitmapFactory.decodeByteArray;
-
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
@@ -45,11 +42,10 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -184,32 +180,21 @@
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
*/
protected boolean loadIcon(WorkspaceItemInfo info) {
- try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
- 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;
- }
- }
- }
+ return createIconRequestInfo(info, false).loadWorkspaceIcon(mContext);
+ }
- // 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;
- }
- }
+ public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
+ WorkspaceItemInfo wai, boolean useLowResIcon) {
+ String packageName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+ ? getString(iconPackageIndex) : null;
+ String resourceName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+ ? getString(iconResourceIndex) : null;
+ byte[] iconBlob = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+ || restoreFlag != 0
+ ? getBlob(iconIndex) : null;
+
+ return new IconRequestInfo<>(
+ wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon);
}
/**
@@ -262,6 +247,11 @@
*/
public WorkspaceItemInfo getAppShortcutInfo(
Intent intent, boolean allowMissingTarget, boolean useLowResIcon) {
+ return getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, true);
+ }
+
+ public WorkspaceItemInfo getAppShortcutInfo(
+ Intent intent, boolean allowMissingTarget, boolean useLowResIcon, boolean loadIcon) {
if (user == null) {
Log.d(TAG, "Null user found in getShortcutInfo");
return null;
@@ -288,9 +278,11 @@
info.user = user;
info.intent = newIntent;
- mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
- if (mIconCache.isDefaultIcon(info.bitmap, user)) {
- loadIcon(info);
+ if (loadIcon) {
+ mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
+ if (mIconCache.isDefaultIcon(info.bitmap, user)) {
+ loadIcon(info);
+ }
}
if (mActivityInfo != null) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f4a0eb8..1249606 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -43,6 +43,7 @@
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -71,6 +72,7 @@
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -197,7 +199,12 @@
TimingLogger logger = new TimingLogger(TAG, "run");
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
- loadWorkspace(allShortcuts);
+ Trace.beginSection("LoadWorkspace");
+ try {
+ loadWorkspace(allShortcuts);
+ } finally {
+ Trace.endSection();
+ }
logASplit(logger, "loadWorkspace");
// Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
@@ -225,7 +232,13 @@
verifyNotStopped();
// second step
- List<LauncherActivityInfo> allActivityList = loadAllApps();
+ Trace.beginSection("LoadAllApps");
+ List<LauncherActivityInfo> allActivityList;
+ try {
+ allActivityList = loadAllApps();
+ } finally {
+ Trace.endSection();
+ }
logASplit(logger, "loadAllApps");
verifyNotStopped();
@@ -408,6 +421,7 @@
LauncherAppWidgetProviderInfo widgetProviderInfo;
Intent intent;
String targetPkg;
+ List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
while (!mStopped && c.moveToNext()) {
try {
@@ -530,7 +544,10 @@
} else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
info = c.getAppShortcutInfo(
- intent, allowMissingTarget, useLowResIcon);
+ intent,
+ allowMissingTarget,
+ useLowResIcon,
+ !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get());
} else if (c.itemType ==
LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
@@ -582,6 +599,8 @@
}
if (info != null) {
+ iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon));
+
c.applyCommonProperties(info);
info.intent = intent;
@@ -799,6 +818,21 @@
Log.e(TAG, "Desktop items loading interrupted", e);
}
}
+ if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) {
+ Trace.beginSection("LoadWorkspaceIconsInBulk");
+ try {
+ mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
+ for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo :
+ iconRequestInfos) {
+ WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
+ if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
+ iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
+ }
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
} finally {
IOUtils.closeSilently(c);
}
diff --git a/src/com/android/launcher3/model/data/IconRequestInfo.java b/src/com/android/launcher3/model/data/IconRequestInfo.java
new file mode 100644
index 0000000..2f566f6
--- /dev/null
+++ b/src/com/android/launcher3/model/data/IconRequestInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model.data;
+
+import static android.graphics.BitmapFactory.decodeByteArray;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
+
+/**
+ * Class representing one request for an icon to be queried in a sql database.
+ *
+ * @param <T> ItemInfoWithIcon subclass whose title and icon can be loaded and filled by an sql
+ * query.
+ */
+public class IconRequestInfo<T extends ItemInfoWithIcon> {
+
+ private static final String TAG = "IconRequestInfo";
+
+ @NonNull public final T itemInfo;
+ @Nullable public final LauncherActivityInfo launcherActivityInfo;
+ @Nullable public final String packageName;
+ @Nullable public final String resourceName;
+ @Nullable public final byte[] iconBlob;
+ public final boolean useLowResIcon;
+
+ public IconRequestInfo(
+ @NonNull T itemInfo,
+ @Nullable LauncherActivityInfo launcherActivityInfo,
+ @Nullable String packageName,
+ @Nullable String resourceName,
+ @Nullable byte[] iconBlob,
+ boolean useLowResIcon) {
+ this.itemInfo = itemInfo;
+ this.launcherActivityInfo = launcherActivityInfo;
+ this.packageName = packageName;
+ this.resourceName = resourceName;
+ this.iconBlob = iconBlob;
+ this.useLowResIcon = useLowResIcon;
+ }
+
+ /** Loads */
+ public boolean loadWorkspaceIcon(Context context) {
+ if (!(itemInfo instanceof WorkspaceItemInfo)) {
+ throw new IllegalStateException(
+ "loadWorkspaceIcon should only be use for a WorkspaceItemInfos: " + itemInfo);
+ }
+
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ WorkspaceItemInfo info = (WorkspaceItemInfo) itemInfo;
+ if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
+ info.iconResource = new Intent.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.
+ try {
+ if (iconBlob == null) {
+ return false;
+ }
+ info.bitmap = li.createIconBitmap(decodeByteArray(
+ iconBlob, 0, iconBlob.length));
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to decode byte array for info " + info, e);
+ return false;
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 1a6ce8c..cd0c7f2 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -153,8 +153,6 @@
public static String dumpHprofData() {
String result;
if (sDumpWasGenerated) {
- Log.d("b/195319692", "dump has already been generated by another test",
- new Exception());
result = "dump has already been generated by another test";
} else {
try {
@@ -169,7 +167,6 @@
"am dumpheap " + device.getLauncherPackageName() + " " + fileName);
}
sDumpWasGenerated = true;
- Log.d("b/195319692", "sDumpWasGenerated := true", new Exception());
result = "memory dump filename: " + fileName;
} catch (Throwable e) {
Log.e(TAG, "dumpHprofData failed", e);