folderItems = new ArrayList<>();
int type;
int folderDepth = parser.getDepth();
@@ -617,7 +614,7 @@ public class AutoInstallsLayout {
}
}
- protected static final void beginDocument(XmlPullParser parser, String firstElementName)
+ protected static void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -671,7 +668,7 @@ public class AutoInstallsLayout {
return value;
}
- public static interface LayoutParserCallback {
+ public interface LayoutParserCallback {
long generateNewItemId();
long insertAndCheck(SQLiteDatabase db, ContentValues values);
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 08cd955a54..e49649502d 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -19,14 +19,17 @@ package com.android.launcher3;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.util.SystemUiController;
public abstract class BaseActivity extends Activity {
protected DeviceProfile mDeviceProfile;
protected UserEventDispatcher mUserEventDispatcher;
+ protected SystemUiController mSystemUiController;
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
@@ -54,4 +57,16 @@ public abstract class BaseActivity extends Activity {
}
return ((BaseActivity) ((ContextWrapper) context).getBaseContext());
}
+
+ public SystemUiController getSystemUiController() {
+ if (mSystemUiController == null) {
+ mSystemUiController = new SystemUiController(getWindow());
+ }
+ return mSystemUiController;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index ac7cbafeab..82175b721d 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -62,7 +62,7 @@ public abstract class BaseContainerView extends FrameLayout
public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && this instanceof AllAppsContainerView) {
+ if (this instanceof AllAppsContainerView) {
mBaseDrawable = new ColorDrawable();
} else {
TypedArray a = context.obtainStyledAttributes(attrs,
@@ -113,20 +113,18 @@ public abstract class BaseContainerView extends FrameLayout
* Calculate the background padding as it can change due to insets/content padding change.
*/
private void updatePaddings() {
- Context context = getContext();
- int paddingLeft;
- int paddingRight;
- int paddingTop;
- int paddingBottom;
-
- DeviceProfile grid = Launcher.getLauncher(context).getDeviceProfile();
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
int[] padding = grid.getContainerPadding();
- paddingLeft = padding[0] + grid.edgeMarginPx;
- paddingRight = padding[1] + grid.edgeMarginPx;
+
+ int paddingLeft = padding[0];
+ int paddingRight = padding[1];
+ int paddingTop = 0;
+ int paddingBottom = 0;
+
if (!grid.isVerticalBarLayout()) {
+ paddingLeft += grid.edgeMarginPx;
+ paddingRight += grid.edgeMarginPx;
paddingTop = paddingBottom = grid.edgeMarginPx;
- } else {
- paddingTop = paddingBottom = 0;
}
updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 6fdf454505..3ee6e51b8d 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -22,8 +22,9 @@ import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
+import android.widget.TextView;
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.RecyclerViewFastScroller;
/**
@@ -36,17 +37,7 @@ import com.android.launcher3.util.Thunk;
public abstract class BaseRecyclerView extends RecyclerView
implements RecyclerView.OnItemTouchListener {
- private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
-
- /** Keeps the last known scrolling delta/velocity along y-axis. */
- @Thunk int mDy = 0;
- private float mDeltaThreshold;
-
- protected final BaseRecyclerViewFastScrollBar mScrollbar;
-
- private int mDownX;
- private int mDownY;
- private int mLastY;
+ protected RecyclerViewFastScroller mScrollbar;
public BaseRecyclerView(Context context) {
this(context, null);
@@ -58,32 +49,6 @@ public abstract class BaseRecyclerView extends RecyclerView
public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
- mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources());
-
- ScrollListener listener = new ScrollListener();
- setOnScrollListener(listener);
- }
-
- private class ScrollListener extends OnScrollListener {
- public ScrollListener() {
- // Do nothing
- }
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- mDy = dy;
-
- // TODO(winsonc): If we want to animate the section heads while scrolling, we can
- // initiate that here if the recycler view scroll state is not
- // RecyclerView.SCROLL_STATE_IDLE.
-
- onUpdateScrollbar(dy);
- }
- }
-
- public void reset() {
- mScrollbar.reattachThumbToScroll();
}
@Override
@@ -95,7 +60,9 @@ public abstract class BaseRecyclerView extends RecyclerView
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mScrollbar.setPopupView(((ViewGroup) getParent()).findViewById(R.id.fast_scroller_popup));
+ ViewGroup parent = (ViewGroup) getParent();
+ mScrollbar = parent.findViewById(R.id.fast_scroller);
+ mScrollbar.setRecyclerView(this, (TextView) parent.findViewById(R.id.fast_scroller_popup));
}
/**
@@ -117,56 +84,26 @@ public abstract class BaseRecyclerView extends RecyclerView
* it is already showing).
*/
private boolean handleTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- int x = (int) ev.getX();
- int y = (int) ev.getY();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- // Keep track of the down positions
- mDownX = x;
- mDownY = mLastY = y;
- if (shouldStopScroll(ev)) {
- stopScroll();
- }
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
- break;
- case MotionEvent.ACTION_MOVE:
- mLastY = y;
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- onFastScrollCompleted();
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
- break;
+ // Move to mScrollbar's coordinate system.
+ int left = getLeft() - mScrollbar.getLeft();
+ int top = getTop() - mScrollbar.getTop();
+ ev.offsetLocation(left, top);
+ try {
+ return mScrollbar.handleTouchEvent(ev);
+ } finally {
+ ev.offsetLocation(-left, -top);
}
- return mScrollbar.isDraggingThumb();
}
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
}
- /**
- * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
- */
- protected boolean shouldStopScroll(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if ((Math.abs(mDy) < mDeltaThreshold &&
- getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
- // now the touch events are being passed to the {@link WidgetCell} until the
- // touch sequence goes over the touch slop.
- return true;
- }
- }
- return false;
- }
-
/**
* Returns the height of the fast scroll bar
*/
- protected int getScrollbarTrackHeight() {
- return getHeight();
+ public int getScrollbarTrackHeight() {
+ return getHeight() - getPaddingTop() - getPaddingBottom();
}
/**
@@ -184,25 +121,17 @@ public abstract class BaseRecyclerView extends RecyclerView
return availableScrollBarHeight;
}
- /**
- * Returns the track color (ignoring alpha), can be overridden by each subclass.
- */
- public int getFastScrollerTrackColor(int defaultTrackColor) {
- return defaultTrackColor;
- }
-
/**
* Returns the scrollbar for this recycler view.
*/
- public BaseRecyclerViewFastScrollBar getScrollBar() {
+ public RecyclerViewFastScroller getScrollBar() {
return mScrollbar;
}
@Override
protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
onUpdateScrollbar(0);
- mScrollbar.draw(canvas);
+ super.dispatchDraw(canvas);
}
/**
@@ -233,7 +162,7 @@ public abstract class BaseRecyclerView extends RecyclerView
/**
* @return whether fast scrolling is supported in the current state.
*/
- protected boolean supportsFastScrolling() {
+ public boolean supportsFastScrolling() {
return true;
}
@@ -249,16 +178,16 @@ public abstract class BaseRecyclerView extends RecyclerView
* Maps the touch (from 0..1) to the adapter position that should be visible.
* Override in each subclass of this base class.
*/
- protected abstract String scrollToPositionAtProgress(float touchFraction);
+ public abstract String scrollToPositionAtProgress(float touchFraction);
/**
* Updates the bounds for the scrollbar.
*
Override in each subclass of this base class.
*/
- protected abstract void onUpdateScrollbar(int dy);
+ public abstract void onUpdateScrollbar(int dy);
/**
*
Override in each subclass of this base class.
*/
- protected void onFastScrollCompleted() {}
+ public void onFastScrollCompleted() {}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3c3e224e46..ac842f92e6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -19,15 +19,16 @@ package com.android.launcher3;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.Region;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.util.Property;
import android.util.TypedValue;
@@ -44,6 +45,7 @@ import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.graphics.IconPalette;
@@ -59,13 +61,6 @@ import java.text.NumberFormat;
*/
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
- // Dimensions in DP
- private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
- private static final float KEY_SHADOW_RADIUS = 1f;
- private static final float KEY_SHADOW_OFFSET = 0.5f;
- private static final int AMBIENT_SHADOW_COLOR = 0x33000000;
- private static final int KEY_SHADOW_COLOR = 0x66000000;
-
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
private static final int DISPLAY_FOLDER = 2;
@@ -75,24 +70,20 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
private final Launcher mLauncher;
private Drawable mIcon;
private final boolean mCenterVertically;
- private final Drawable mBackground;
- private OnLongClickListener mOnLongClickListener;
+
private final CheckLongPressHelper mLongPressHelper;
private final HolographicOutlineHelper mOutlineHelper;
private final StylusEventHelper mStylusEventHelper;
-
- private boolean mBackgroundSizeChanged;
+ private final float mSlop;
private Bitmap mPressedBackground;
- private float mSlop;
-
private final boolean mDeferShadowGenerationOnTouch;
- private final boolean mCustomShadowsEnabled;
private final boolean mLayoutHorizontal;
private final int mIconSize;
@ViewDebug.ExportedProperty(category = "launcher")
private int mTextColor;
+ private boolean mIsIconVisible = true;
private BadgeInfo mBadgeInfo;
private BadgeRenderer mBadgeRenderer;
@@ -116,6 +107,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
}
};
+ public static final Property TEXT_ALPHA_PROPERTY
+ = new Property(Integer.class, "textAlpha") {
+ @Override
+ public Integer get(BubbleTextView bubbleTextView) {
+ return bubbleTextView.getTextAlpha();
+ }
+
+ @Override
+ public void set(BubbleTextView bubbleTextView, Integer alpha) {
+ bubbleTextView.setTextAlpha(alpha);
+ }
+ };
+
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mStayPressed;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -137,10 +141,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(context);
DeviceProfile grid = mLauncher.getDeviceProfile();
+ mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
- mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, false);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
mDeferShadowGenerationOnTouch =
a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
@@ -149,6 +153,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
int defaultIconSize = grid.iconSizePx;
if (display == DISPLAY_WORKSPACE) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
+ setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
} else if (display == DISPLAY_ALL_APPS) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
@@ -164,23 +169,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
defaultIconSize);
a.recycle();
- if (mCustomShadowsEnabled) {
- // Draw the background itself as the parent is drawn twice.
- mBackground = getBackground();
- setBackground(null);
-
- // Set shadow layer as the larger shadow to that the textView does not clip the shadow.
- float density = getResources().getDisplayMetrics().density;
- setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
- } else {
- mBackground = null;
- }
-
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mOutlineHelper = HolographicOutlineHelper.getInstance(getContext());
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+
}
public void applyFromShortcutInfo(ShortcutInfo info) {
@@ -190,7 +184,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
applyIconAndLabel(info.iconBitmap, info);
setTag(info);
- if (promiseStateChanged || info.isPromise()) {
+ if (promiseStateChanged || (info.hasPromiseIconUi())) {
applyPromiseState(promiseStateChanged);
}
@@ -206,6 +200,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
// Verify high res immediately
verifyHighRes();
+ if (info instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+ applyProgressLevel(promiseAppInfo.level);
+ }
applyBadgeState(info, false /* animate */);
}
@@ -237,19 +235,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
mLongPressHelper.setLongPressTimeout(longPressTimeout);
}
- @Override
- protected boolean setFrame(int left, int top, int right, int bottom) {
- if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
- mBackgroundSizeChanged = true;
- }
- return super.setFrame(left, top, right, bottom);
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return who == mBackground || super.verifyDrawable(who);
- }
-
@Override
public void setTag(Object tag) {
if (tag != null) {
@@ -279,21 +264,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
return mIcon;
}
- /** Returns whether the layout is horizontal. */
- public boolean isLayoutHorizontal() {
- return mLayoutHorizontal;
- }
-
- @Override
- public void setOnLongClickListener(OnLongClickListener l) {
- super.setOnLongClickListener(l);
- mOnLongClickListener = l;
- }
-
- public OnLongClickListener getOnLongClickListener() {
- return mOnLongClickListener;
- }
-
@Override
public boolean onTouchEvent(MotionEvent event) {
// Call the superclass onTouchEvent first, because sometimes it changes the state to
@@ -391,54 +361,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
return result;
}
+ @SuppressWarnings("wrongcall")
+ protected void drawWithoutBadge(Canvas canvas) {
+ super.onDraw(canvas);
+ }
+
@Override
- public void draw(Canvas canvas) {
- if (!mCustomShadowsEnabled) {
- super.draw(canvas);
- drawBadgeIfNecessary(canvas);
- return;
- }
-
- final Drawable background = mBackground;
- if (background != null) {
- final int scrollX = getScrollX();
- final int scrollY = getScrollY();
-
- if (mBackgroundSizeChanged) {
- background.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop());
- mBackgroundSizeChanged = false;
- }
-
- if ((scrollX | scrollY) == 0) {
- background.draw(canvas);
- } else {
- canvas.translate(scrollX, scrollY);
- background.draw(canvas);
- canvas.translate(-scrollX, -scrollY);
- }
- }
-
- // If text is transparent, don't draw any shadow
- if ((getCurrentTextColor() >> 24) == 0) {
- getPaint().clearShadowLayer();
- super.draw(canvas);
- drawBadgeIfNecessary(canvas);
- return;
- }
-
- // We enhance the shadow by drawing the shadow twice
- float density = getResources().getDisplayMetrics().density;
- getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
- super.draw(canvas);
- canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
- getScrollX() + getWidth(),
- getScrollY() + getHeight(), Region.Op.INTERSECT);
- getPaint().setShadowLayer(
- density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
- super.draw(canvas);
- canvas.restore();
-
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
drawBadgeIfNecessary(canvas);
}
@@ -446,7 +376,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
* Draws the icon badge in the top right corner of the icon bounds.
* @param canvas The canvas to draw to.
*/
- private void drawBadgeIfNecessary(Canvas canvas) {
+ protected void drawBadgeIfNecessary(Canvas canvas) {
if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
getIconBounds(mTempIconBounds);
mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
@@ -484,14 +414,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
outBounds.set(left, top, right, bottom);
}
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mBackground != null) mBackground.setCallback(this);
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mCenterVertically) {
@@ -505,12 +427,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mBackground != null) mBackground.setCallback(null);
- }
-
@Override
public void setTextColor(int color) {
mTextColor = color;
@@ -523,15 +439,38 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
super.setTextColor(colors);
}
+ public boolean shouldTextBeVisible() {
+ // Text should be visible everywhere but the hotseat.
+ Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
+ ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
+ return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ }
+
public void setTextVisibility(boolean visible) {
- Resources res = getResources();
if (visible) {
super.setTextColor(mTextColor);
} else {
- super.setTextColor(res.getColor(android.R.color.transparent));
+ setTextAlpha(0);
}
}
+ private void setTextAlpha(int alpha) {
+ super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha));
+ }
+
+ private int getTextAlpha() {
+ return Color.alpha(getCurrentTextColor());
+ }
+
+ /**
+ * Creates an animator to fade the text in or out.
+ * @param fadeIn Whether the text should fade in or fade out.
+ */
+ public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
+ int toAlpha = shouldTextBeVisible() && fadeIn ? Color.alpha(mTextColor) : 0;
+ return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, toAlpha);
+ }
+
@Override
public void cancelLongPress() {
super.cancelLongPress();
@@ -542,32 +481,41 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
public void applyPromiseState(boolean promiseStateChanged) {
if (getTag() instanceof ShortcutInfo) {
ShortcutInfo info = (ShortcutInfo) getTag();
- final boolean isPromise = info.isPromise();
+ final boolean isPromise = info.hasPromiseIconUi();
final int progressLevel = isPromise ?
((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
info.getInstallProgress() : 0)) : 100;
- setContentDescription(progressLevel > 0 ?
- getContext().getString(R.string.app_downloading_title, info.title,
- NumberFormat.getPercentInstance().format(progressLevel * 0.01)) :
- getContext().getString(R.string.app_waiting_download_title, info.title));
+ PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+ if (preloadDrawable != null && promiseStateChanged) {
+ preloadDrawable.maybePerformFinishedAnimation();
+ }
+ }
+ }
+
+ public PreloadIconDrawable applyProgressLevel(int progressLevel) {
+ if (getTag() instanceof ItemInfoWithIcon) {
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ setContentDescription(progressLevel > 0
+ ? getContext().getString(R.string.app_downloading_title, info.title,
+ NumberFormat.getPercentInstance().format(progressLevel * 0.01))
+ : getContext().getString(R.string.app_waiting_download_title, info.title));
if (mIcon != null) {
final PreloadIconDrawable preloadDrawable;
if (mIcon instanceof PreloadIconDrawable) {
preloadDrawable = (PreloadIconDrawable) mIcon;
+ preloadDrawable.setLevel(progressLevel);
} else {
preloadDrawable = DrawableFactory.get(getContext())
.newPendingIcon(info.iconBitmap, getContext());
+ preloadDrawable.setLevel(progressLevel);
setIcon(preloadDrawable);
}
-
- preloadDrawable.setLevel(progressLevel);
- if (promiseStateChanged) {
- preloadDrawable.maybePerformFinishedAnimation();
- }
+ return preloadDrawable;
}
}
+ return null;
}
public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
@@ -603,7 +551,21 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
private void setIcon(Drawable icon) {
mIcon = icon;
mIcon.setBounds(0, 0, mIconSize, mIconSize);
- applyCompoundDrawables(mIcon);
+ if (mIsIconVisible) {
+ applyCompoundDrawables(mIcon);
+ }
+ }
+
+ public void setIconVisible(boolean visible) {
+ mIsIconVisible = visible;
+ mDisableRelayout = true;
+ Drawable icon = mIcon;
+ if (!visible) {
+ icon = new ColorDrawable(Color.TRANSPARENT);
+ icon.setBounds(0, 0, mIconSize, mIconSize);
+ }
+ applyCompoundDrawables(icon);
+ mDisableRelayout = false;
}
protected void applyCompoundDrawables(Drawable icon) {
@@ -630,11 +592,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
mIconLoadRequest = null;
mDisableRelayout = true;
+ // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
+ info.iconBitmap.prepareToDraw();
+
if (info instanceof AppInfo) {
applyFromApplicationInfo((AppInfo) info);
} else if (info instanceof ShortcutInfo) {
applyFromShortcutInfo((ShortcutInfo) info);
- if ((info.rank < FolderIcon.NUM_ITEMS_IN_PREVIEW) && (info.container >= 0)) {
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+ if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
View folderIcon =
mLauncher.getWorkspace().getHomescreenIconByItemId(info.container);
if (folderIcon != null) {
@@ -666,6 +633,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
}
}
+ public int getIconSize() {
+ return mIconSize;
+ }
+
/**
* Interface to be implemented by the grand parent to allow click shadow effect.
*/
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 8a477d809f..632e490590 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -29,6 +29,7 @@ import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
@@ -69,6 +70,7 @@ public abstract class ButtonDropTarget extends TextView
/** The paint applied to the drag view on hover */
protected int mHoverColor = 0;
+ protected CharSequence mText;
protected ColorStateList mOriginalTextColor;
protected Drawable mDrawable;
@@ -96,14 +98,15 @@ public abstract class ButtonDropTarget extends TextView
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mText = getText();
mOriginalTextColor = getTextColors();
}
protected void setDrawable(int resId) {
// We do not set the drawable in the xml as that inflates two drawables corresponding to
// drawableLeft and drawableStart.
- mDrawable = getResources().getDrawable(resId);
- setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+ setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
+ mDrawable = getCompoundDrawablesRelative()[0];
}
public void setDropTargetBar(DropTargetBar dropTargetBar) {
@@ -297,4 +300,30 @@ public abstract class ButtonDropTarget extends TextView
public int getTextColor() {
return getTextColors().getDefaultColor();
}
+
+ /**
+ * Returns True if any update was made.
+ */
+ public boolean updateText(boolean hide) {
+ if ((hide && getText().toString().isEmpty()) || (!hide && mText.equals(getText()))) {
+ return false;
+ }
+
+ setText(hide ? "" : mText);
+ return true;
+ }
+
+ public boolean isTextTruncated() {
+ int availableWidth = getMeasuredWidth();
+ if (mHideParentOnDisable) {
+ ViewGroup parent = (ViewGroup) getParent();
+ availableWidth = parent.getMeasuredWidth() - parent.getPaddingLeft()
+ - parent.getPaddingRight();
+ }
+ availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
+ + getCompoundDrawablePadding());
+ CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth,
+ TextUtils.TruncateAt.END);
+ return !mText.equals(displayedText);
+ }
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 8179dad29a..a75c6d1c4d 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -35,6 +35,7 @@ import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.support.annotation.IntDef;
import android.support.v4.view.ViewCompat;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -44,28 +45,27 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
-
import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.Stack;
public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
@@ -75,7 +75,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private static final String TAG = "CellLayout";
private static final boolean LOGD = false;
- private Launcher mLauncher;
+ private final Launcher mLauncher;
@ViewDebug.ExportedProperty(category = "launcher")
@Thunk int mCellWidth;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -101,10 +101,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private GridOccupancy mTmpOccupied;
private OnTouchListener mInterceptTouchListener;
- private StylusEventHelper mStylusEventHelper;
+ private final StylusEventHelper mStylusEventHelper;
- private ArrayList mFolderBackgrounds = new ArrayList();
- FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
+ private final ArrayList mFolderBackgrounds = new ArrayList<>();
+ final PreviewBackground mFolderLeaveBehind = new PreviewBackground();
private float mBackgroundAlpha;
@@ -121,9 +121,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// These arrays are used to implement the drag visualization on x-large screens.
// They are used as circular arrays, indexed by mDragOutlineCurrent.
- @Thunk Rect[] mDragOutlines = new Rect[4];
- @Thunk float[] mDragOutlineAlphas = new float[mDragOutlines.length];
- private InterruptibleInOutAnimator[] mDragOutlineAnims =
+ @Thunk final Rect[] mDragOutlines = new Rect[4];
+ @Thunk final float[] mDragOutlineAlphas = new float[mDragOutlines.length];
+ private final InterruptibleInOutAnimator[] mDragOutlineAnims =
new InterruptibleInOutAnimator[mDragOutlines.length];
// Used as an index into the above 3 arrays; indicates which is the most current value.
@@ -132,8 +132,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private final ClickShadowView mTouchFeedbackView;
- @Thunk HashMap mReorderAnimators = new HashMap<>();
- @Thunk HashMap mShakeAnimators = new HashMap<>();
+ @Thunk final ArrayMap mReorderAnimators = new ArrayMap<>();
+ @Thunk final ArrayMap mShakeAnimators = new ArrayMap<>();
private boolean mItemPlacementDirty = false;
@@ -142,8 +142,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private boolean mDragging = false;
- private TimeInterpolator mEaseOutInterpolator;
- private ShortcutAndWidgetContainer mShortcutsAndWidgets;
+ private final TimeInterpolator mEaseOutInterpolator;
+ private final ShortcutAndWidgetContainer mShortcutsAndWidgets;
@Retention(RetentionPolicy.SOURCE)
@IntDef({WORKSPACE, HOTSEAT, FOLDER})
@@ -154,7 +154,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
@ContainerType private final int mContainerType;
- private final float mChildScale;
+ private final float mChildScale = 1f;
public static final int MODE_SHOW_REORDER_HINT = 0;
public static final int MODE_DRAG_OVER = 1;
@@ -168,10 +168,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
private static final int REORDER_ANIMATION_DURATION = 150;
@Thunk final float mReorderPreviewAnimationMagnitude;
- private ArrayList mIntersectingViews = new ArrayList();
- private Rect mOccupiedRect = new Rect();
- private int[] mDirectionVector = new int[2];
- int[] mPreviousReorderDirection = new int[2];
+ private final ArrayList mIntersectingViews = new ArrayList<>();
+ private final Rect mOccupiedRect = new Rect();
+ private final int[] mDirectionVector = new int[2];
+ final int[] mPreviousReorderDirection = new int[2];
private static final int INVALID_DIRECTION = -100;
private final Rect mTempRect = new Rect();
@@ -218,8 +218,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mFolderLeaveBehind.delegateCellX = -1;
mFolderLeaveBehind.delegateCellY = -1;
- mChildScale = mContainerType == HOTSEAT ? grid.inv.hotseatScale : 1f;
-
setAlwaysDrawnWithCacheEnabled(false);
final Resources res = getResources();
@@ -235,7 +233,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
for (int i = 0; i < mDragOutlines.length; i++) {
mDragOutlines[i] = new Rect(-1, -1, -1, -1);
}
- mDragOutlinePaint.setColor(getResources().getColor(R.color.outline_color));
+ mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
// When dragging things around the home screens, we show a green outline of
// where the item will land. The outlines gradually fade out, leaving a trail
@@ -496,7 +494,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
}
for (int i = 0; i < mFolderBackgrounds.size(); i++) {
- FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+ PreviewBackground bg = mFolderBackgrounds.get(i);
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
@@ -522,7 +520,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
super.dispatchDraw(canvas);
for (int i = 0; i < mFolderBackgrounds.size(); i++) {
- FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+ PreviewBackground bg = mFolderBackgrounds.get(i);
if (bg.isClipping) {
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
@@ -533,19 +531,16 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
}
}
- public void addFolderBackground(FolderIcon.PreviewBackground bg) {
+ public void addFolderBackground(PreviewBackground bg) {
mFolderBackgrounds.add(bg);
}
- public void removeFolderBackground(FolderIcon.PreviewBackground bg) {
+ public void removeFolderBackground(PreviewBackground bg) {
mFolderBackgrounds.remove(bg);
}
public void setFolderLeaveBehindCell(int x, int y) {
-
- DeviceProfile grid = mLauncher.getDeviceProfile();
View child = getChildAt(x, y);
-
- mFolderLeaveBehind.setup(getResources().getDisplayMetrics(), grid, null,
+ mFolderLeaveBehind.setup(mLauncher, null,
child.getMeasuredWidth(), child.getPaddingTop());
mFolderLeaveBehind.delegateCellX = x;
@@ -568,7 +563,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
@@ -785,7 +780,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
return mCellWidth;
}
- int getCellHeight() {
+ public int getCellHeight() {
return mCellHeight;
}
@@ -865,10 +860,10 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// Expand the background drawing bounds by the padding baked into the background drawable
mBackground.getPadding(mTempRect);
mBackground.setBounds(
- left - mTempRect.left,
- top - mTempRect.top,
- right + mTempRect.right,
- bottom + mTempRect.bottom);
+ left - mTempRect.left - getPaddingLeft(),
+ top - mTempRect.top - getPaddingTop(),
+ right + mTempRect.right + getPaddingRight(),
+ bottom + mTempRect.bottom + getPaddingBottom());
}
/**
@@ -1102,7 +1097,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
result, resultSpan);
}
- private final Stack mTempRectStack = new Stack();
+ private final Stack mTempRectStack = new Stack<>();
private void lazyInitTempRectStack() {
if (mTempRectStack.isEmpty()) {
for (int i = 0; i < mCountX * mCountY; i++) {
@@ -1147,7 +1142,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
final int[] bestXY = result != null ? result : new int[2];
double bestDistance = Double.MAX_VALUE;
final Rect bestRect = new Rect(-1, -1, -1, -1);
- final Stack validRegions = new Stack();
+ final Stack validRegions = new Stack<>();
final int countX = mCountX;
final int countY = mCountY;
@@ -1349,14 +1344,14 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
final static int RIGHT = 1 << 2;
final static int BOTTOM = 1 << 3;
- ArrayList views;
- ItemConfiguration config;
- Rect boundingRect = new Rect();
+ final ArrayList views;
+ final ItemConfiguration config;
+ final Rect boundingRect = new Rect();
- int[] leftEdge = new int[mCountY];
- int[] rightEdge = new int[mCountY];
- int[] topEdge = new int[mCountX];
- int[] bottomEdge = new int[mCountX];
+ final int[] leftEdge = new int[mCountY];
+ final int[] rightEdge = new int[mCountY];
+ final int[] topEdge = new int[mCountX];
+ final int[] bottomEdge = new int[mCountX];
int dirtyEdges;
boolean boundingRectDirty;
@@ -1496,7 +1491,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
return boundingRect;
}
- PositionComparator comparator = new PositionComparator();
+ final PositionComparator comparator = new PositionComparator();
class PositionComparator implements Comparator {
int whichEdge = 0;
public int compare(View left, View right) {
@@ -1796,7 +1791,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
}
}
- solution.intersectingViews = new ArrayList(mIntersectingViews);
+ solution.intersectingViews = new ArrayList<>(mIntersectingViews);
// First we try to find a solution which respects the push mechanic. That is,
// we try to find a solution such that no displaced item travels through another item
@@ -1852,7 +1847,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
int result[] = new int[2];
result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
- boolean success = false;
+ boolean success;
// First we try the exact nearest position of the item being dragged,
// we will then want to try to move this around to other neighbouring positions
success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
@@ -1960,14 +1955,14 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// Class which represents the reorder preview animations. These animations show that an item is
// in a temporary state, and hint at where the item will return to.
class ReorderPreviewAnimation {
- View child;
+ final View child;
float finalDeltaX;
float finalDeltaY;
float initDeltaX;
float initDeltaY;
- float finalScale;
+ final float finalScale;
float initScale;
- int mode;
+ final int mode;
boolean repeating = false;
private static final int PREVIEW_DURATION = 300;
private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT;
@@ -2417,9 +2412,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
}
private static class ItemConfiguration extends CellAndSpan {
- HashMap map = new HashMap();
- private HashMap savedMap = new HashMap();
- ArrayList sortedViews = new ArrayList();
+ final ArrayMap map = new ArrayMap<>();
+ private final ArrayMap savedMap = new ArrayMap<>();
+ final ArrayList sortedViews = new ArrayList<>();
ArrayList intersectingViews;
boolean isSolution = false;
@@ -2469,7 +2464,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
* @param pixelY The Y location at which you want to search for a vacant area.
* @param spanX Horizontal span of the object.
* @param spanY Vertical span of the object.
- * @param ignoreView Considers space occupied by this view as unoccupied
* @param result Previously returned value to possibly recycle.
* @return The X, Y cell of a vacant area that can contain this object,
* nearest the requested location.
@@ -2781,9 +2775,9 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// cellX and cellY coordinates and which page was clicked. We then set this as a tag on
// the CellLayout that was long clicked
public static final class CellInfo extends CellAndSpan {
- public View cell;
- long screenId;
- long container;
+ public final View cell;
+ final long screenId;
+ final long container;
public CellInfo(View v, ItemInfo info) {
cellX = info.cellX;
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 05911ab7fa..1ec30ba68d 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -13,18 +13,15 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
-
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.IOException;
import java.net.URISyntaxException;
-import java.util.HashMap;
import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
/**
* Implements the layout parser with rules for internal layouts and partner layouts.
@@ -55,20 +52,20 @@ public class DefaultLayoutParser extends AutoInstallsLayout {
}
@Override
- protected HashMap getFolderElementsMap() {
+ protected ArrayMap getFolderElementsMap() {
return getFolderElementsMap(mSourceRes);
}
- @Thunk HashMap getFolderElementsMap(Resources res) {
- HashMap parsers = new HashMap();
+ @Thunk ArrayMap getFolderElementsMap(Resources res) {
+ ArrayMap parsers = new ArrayMap<>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(res));
return parsers;
}
@Override
- protected HashMap getLayoutElementsMap() {
- HashMap parsers = new HashMap();
+ protected ArrayMap getLayoutElementsMap() {
+ ArrayMap parsers = new ArrayMap<>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_APPWIDGET, new AppWidgetParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
diff --git a/src/com/android/launcher3/DeferredHandler.java b/src/com/android/launcher3/DeferredHandler.java
deleted file mode 100644
index a43ab67239..0000000000
--- a/src/com/android/launcher3/DeferredHandler.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue;
-
-import com.android.launcher3.util.Thunk;
-
-import java.util.LinkedList;
-
-/**
- * Queue of things to run on a looper thread. Items posted with {@link #post} will not
- * be actually enqued on the handler until after the last one has run, to keep from
- * starving the thread.
- *
- * This class is fifo.
- */
-public class DeferredHandler {
- @Thunk LinkedList mQueue = new LinkedList<>();
- private MessageQueue mMessageQueue = Looper.myQueue();
- private Impl mHandler = new Impl();
-
- @Thunk class Impl extends Handler implements MessageQueue.IdleHandler {
- public void handleMessage(Message msg) {
- Runnable r;
- synchronized (mQueue) {
- if (mQueue.size() == 0) {
- return;
- }
- r = mQueue.removeFirst();
- }
- r.run();
- synchronized (mQueue) {
- scheduleNextLocked();
- }
- }
-
- public boolean queueIdle() {
- handleMessage(null);
- return false;
- }
- }
-
- private class IdleRunnable implements Runnable {
- Runnable mRunnable;
-
- IdleRunnable(Runnable r) {
- mRunnable = r;
- }
-
- public void run() {
- mRunnable.run();
- }
- }
-
- public DeferredHandler() {
- }
-
- /** Schedule runnable to run after everything that's on the queue right now. */
- public void post(Runnable runnable) {
- synchronized (mQueue) {
- mQueue.add(runnable);
- if (mQueue.size() == 1) {
- scheduleNextLocked();
- }
- }
- }
-
- /** Schedule runnable to run when the queue goes idle. */
- public void postIdle(final Runnable runnable) {
- post(new IdleRunnable(runnable));
- }
-
- public void cancelAll() {
- synchronized (mQueue) {
- mQueue.clear();
- }
- }
-
- /** Runs all queued Runnables from the calling thread. */
- public void flush() {
- LinkedList queue = new LinkedList<>();
- synchronized (mQueue) {
- queue.addAll(mQueue);
- mQueue.clear();
- }
- for (Runnable r : queue) {
- r.run();
- }
- }
-
- void scheduleNextLocked() {
- if (mQueue.size() > 0) {
- Runnable peek = mQueue.getFirst();
- if (peek instanceof IdleRunnable) {
- mMessageQueue.addIdleHandler(mHandler);
- } else {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-}
-
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9097ed23d7..4dcb64f01d 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -40,7 +40,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
// Get the hover color
mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
- setDrawable(R.drawable.ic_remove_launcher);
+ setDrawable(R.drawable.ic_remove_shadow);
}
@Override
@@ -65,9 +65,11 @@ public class DeleteDropTarget extends ButtonDropTarget {
* Set the drop target's text to either "Remove" or "Cancel" depending on the drag source.
*/
public void setTextBasedOnDragSource(DragSource dragSource) {
- if (!TextUtils.isEmpty(getText())) {
- setText(dragSource.supportsDeleteDropTarget() ? R.string.remove_drop_target_label
+ if (!TextUtils.isEmpty(mText)) {
+ mText = getResources().getString(dragSource.supportsDeleteDropTarget()
+ ? R.string.remove_drop_target_label
: android.R.string.cancel);
+ requestLayout();
}
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e47031ae8c..69ee03e9e3 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -19,6 +19,7 @@ package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -32,7 +33,6 @@ import android.widget.FrameLayout;
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
@@ -63,6 +63,8 @@ public class DeviceProfile {
*/
private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f;
+ private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f;
+
// Overview mode
private final int overviewModeMinIconZoneHeightPx;
private final int overviewModeMaxIconZoneHeightPx;
@@ -71,7 +73,9 @@ public class DeviceProfile {
private final float overviewModeIconZoneRatio;
// Workspace
- private int desiredWorkspaceLeftRightMarginPx;
+ private final int desiredWorkspaceLeftRightMarginPx;
+ public final int cellLayoutPaddingLeftRightPx;
+ public final int cellLayoutBottomPaddingPx;
public final int edgeMarginPx;
public final Rect defaultWidgetPadding;
private final int defaultPageSpacingPx;
@@ -80,9 +84,9 @@ public class DeviceProfile {
public final int workspaceSpringLoadedBottomSpace;
// Page indicator
- private final int pageIndicatorHeightPx;
- private final int pageIndicatorLandGutterLeftNavBarPx;
- private final int pageIndicatorLandGutterRightNavBarPx;
+ private int pageIndicatorSizePx;
+ private final int pageIndicatorLandLeftNavBarGutterPx;
+ private final int pageIndicatorLandRightNavBarGutterPx;
private final int pageIndicatorLandWorkspaceOffsetPx;
// Workspace icons
@@ -93,6 +97,7 @@ public class DeviceProfile {
public int cellWidthPx;
public int cellHeightPx;
+ public int workspaceCellPaddingXPx;
// Folder
public int folderBackgroundOffset;
@@ -109,15 +114,20 @@ public class DeviceProfile {
public int folderChildDrawablePaddingPx;
// Hotseat
- public int hotseatCellWidthPx;
public int hotseatCellHeightPx;
- public int hotseatIconSizePx;
- public int hotseatBarHeightPx;
- private int hotseatBarTopPaddingPx;
- private int hotseatBarBottomPaddingPx;
- private int hotseatLandGutterPx;
+ // In portrait: size = height, in landscape: size = width
+ public int hotseatBarSizePx;
+ public int hotseatBarTopPaddingPx;
+ public int hotseatBarBottomPaddingPx;
+
+ public int hotseatBarLeftNavBarLeftPaddingPx;
+ public int hotseatBarLeftNavBarRightPaddingPx;
+
+ public int hotseatBarRightNavBarLeftPaddingPx;
+ public int hotseatBarRightNavBarRightPaddingPx;
// All apps
+ public int allAppsCellHeightPx;
public int allAppsNumCols;
public int allAppsNumPredictiveCols;
public int allAppsButtonVisualSize;
@@ -159,19 +169,29 @@ public class DeviceProfile {
transposeLayoutWithOrientation =
res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
+ context = getContext(context, isVerticalBarLayout()
+ ? Configuration.ORIENTATION_LANDSCAPE
+ : Configuration.ORIENTATION_PORTRAIT);
+ res = context.getResources();
+
+
ComponentName cn = new ComponentName(context.getPackageName(),
this.getClass().getName());
defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
- desiredWorkspaceLeftRightMarginPx = edgeMarginPx;
- pageIndicatorHeightPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
- pageIndicatorLandGutterLeftNavBarPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_page_indicator_gutter_width_left_nav_bar);
+ desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;
+ cellLayoutPaddingLeftRightPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
+ cellLayoutBottomPaddingPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding);
+ pageIndicatorSizePx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_min_page_indicator_size);
+ pageIndicatorLandLeftNavBarGutterPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_page_indicator_land_left_nav_bar_gutter_width);
+ pageIndicatorLandRightNavBarGutterPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_page_indicator_land_right_nav_bar_gutter_width);
pageIndicatorLandWorkspaceOffsetPx =
res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
- pageIndicatorLandGutterRightNavBarPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_page_indicator_gutter_width_right_nav_bar);
defaultPageSpacingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
topWorkspacePadding =
@@ -191,11 +211,25 @@ public class DeviceProfile {
dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
workspaceSpringLoadedBottomSpace =
res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
- hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
+
+ workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
+
hotseatBarTopPaddingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
- hotseatBarBottomPaddingPx = 0;
- hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
+ hotseatBarBottomPaddingPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+ hotseatBarLeftNavBarRightPaddingPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_land_left_nav_bar_right_padding);
+ hotseatBarRightNavBarRightPaddingPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_land_right_nav_bar_right_padding);
+ hotseatBarLeftNavBarLeftPaddingPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_land_left_nav_bar_left_padding);
+ hotseatBarRightNavBarLeftPaddingPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_land_right_nav_bar_left_padding);
+ hotseatBarSizePx = isVerticalBarLayout()
+ ? Utilities.pxFromDp(inv.iconSize, dm)
+ : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size)
+ + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
// Determine sizes.
widthPx = width;
@@ -208,8 +242,24 @@ public class DeviceProfile {
availableHeightPx = maxSize.y;
}
- // Calculate the remaining vars
+ // Calculate all of the remaining variables.
updateAvailableDimensions(dm, res);
+
+ // Now that we have all of the variables calculated, we can tune certain sizes.
+ float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
+ boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
+ if (!isVerticalBarLayout() && isPhone && isTallDevice) {
+ // We increase the hotseat size when there is extra space.
+ // ie. For a display with a large aspect ratio, we can keep the icons on the workspace
+ // in portrait mode closer together by adding more height to the hotseat.
+ // Note: This calculation was created after noticing a pattern in the design spec.
+ int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx;
+ hotseatBarSizePx += extraSpace - pageIndicatorSizePx;
+
+ // Recalculate the available dimensions using the new hotseat size.
+ updateAvailableDimensions(dm, res);
+ }
+
computeAllAppsButtonSize(context);
// This is done last, after iconSizePx is calculated above.
@@ -217,6 +267,10 @@ public class DeviceProfile {
}
DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
+ // We take the minimum sizes of this profile and it's multi-window variant to ensure that
+ // the system decor is always excluded.
+ mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
+
// 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.
@@ -224,12 +278,7 @@ public class DeviceProfile {
isLandscape);
// Hide labels on the workspace.
- profile.iconTextSizePx = 0;
- profile.cellHeightPx = profile.iconSizePx + profile.iconDrawablePaddingPx
- + Utilities.calculateTextHeight(profile.iconTextSizePx);
-
- // The nav bar is black so we add bottom padding to visually center hotseat icons.
- profile.hotseatBarBottomPaddingPx = profile.hotseatBarTopPaddingPx;
+ profile.adjustToHideWorkspaceLabels();
// We use these scales to measure and layout the widgets using their full invariant profile
// sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
@@ -252,6 +301,24 @@ public class DeviceProfile {
}
}
+ /**
+ * Adjusts the profile so that the labels on the Workspace are hidden.
+ * It is important to call this method after the All Apps variables have been set.
+ */
+ private void adjustToHideWorkspaceLabels() {
+ iconTextSizePx = 0;
+ iconDrawablePaddingPx = 0;
+ cellHeightPx = iconSizePx;
+
+ // In normal cases, All Apps cell height should equal the Workspace cell height.
+ // Since we are removing labels from the Workspace, we need to manually compute the
+ // All Apps cell height.
+ int topBottomPadding = allAppsIconDrawablePaddingPx * (isVerticalBarLayout() ? 2 : 1);
+ allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx
+ + Utilities.calculateTextHeight(allAppsIconTextSizePx)
+ + topBottomPadding * 2;
+ }
+
/**
* Determine the exact visual footprint of the all apps button, taking into account scaling
* and internal padding of the drawable.
@@ -259,45 +326,63 @@ public class DeviceProfile {
private void computeAllAppsButtonSize(Context context) {
Resources res = context.getResources();
float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
- allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources()
+ allAppsButtonVisualSize = (int) (iconSizePx * (1 - padding)) - context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
}
private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
- updateIconSize(1f, iconDrawablePaddingOriginalPx, res, dm);
+ updateIconSize(1f, res, dm);
// Check to see if the icons fit within the available height. If not, then scale down.
float usedHeight = (cellHeightPx * inv.numRows);
int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
if (usedHeight > maxHeight) {
float scale = maxHeight / usedHeight;
- updateIconSize(scale, 0, res, dm);
+ updateIconSize(scale, res, dm);
}
-
updateAvailableFolderCellDimensions(dm, res);
}
- private void updateIconSize(float scale, int drawablePadding, Resources res,
- DisplayMetrics dm) {
- iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
+ private void updateIconSize(float scale, Resources res, DisplayMetrics dm) {
+ // Workspace
+ float invIconSizePx = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+ iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
- iconDrawablePaddingPx = drawablePadding;
- hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
- allAppsIconSizePx = iconSizePx;
- allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
- allAppsIconTextSizePx = iconTextSizePx;
+ iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
- cellWidthPx = iconSizePx;
cellHeightPx = iconSizePx + iconDrawablePaddingPx
+ Utilities.calculateTextHeight(iconTextSizePx);
+ int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
+ if (iconDrawablePaddingPx > cellYPadding && !isVerticalBarLayout()
+ && !inMultiWindowMode()) {
+ // Ensures that the label is closer to its corresponding icon. This is not an issue
+ // with vertical bar layout or multi-window mode since the issue is handled separately
+ // with their calls to {@link #adjustToHideWorkspaceLabels}.
+ cellHeightPx -= (iconDrawablePaddingPx - cellYPadding);
+ iconDrawablePaddingPx = cellYPadding;
+ }
+ cellWidthPx = iconSizePx + iconDrawablePaddingPx;
+
+ // All apps
+ allAppsIconTextSizePx = iconTextSizePx;
+ allAppsIconSizePx = iconSizePx;
+ allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
+ allAppsCellHeightPx = getCellSize().y;
+
+ if (isVerticalBarLayout()) {
+ // Always hide the Workspace text with vertical bar layout.
+ adjustToHideWorkspaceLabels();
+ }
// Hotseat
- hotseatCellWidthPx = iconSizePx;
+ if (isVerticalBarLayout()) {
+ hotseatBarSizePx = iconSizePx;
+ }
hotseatCellHeightPx = iconSizePx;
if (!isVerticalBarLayout()) {
- int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
- - pageIndicatorHeightPx - topWorkspacePadding;
+ int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
+ - pageIndicatorSizePx - topWorkspacePadding;
float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
workspaceSpringLoadShrinkFactor = Math.min(
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -308,7 +393,7 @@ public class DeviceProfile {
}
// Folder icon
- folderBackgroundOffset = -edgeMarginPx;
+ folderBackgroundOffset = -iconDrawablePaddingPx;
folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
}
@@ -321,7 +406,7 @@ public class DeviceProfile {
updateFolderCellSize(1f, dm, res);
// Don't let the folder get too close to the edges of the screen.
- int folderMargin = 4 * edgeMarginPx;
+ int folderMargin = edgeMarginPx;
// Check if the icons fit within the available height.
float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
@@ -389,8 +474,10 @@ public class DeviceProfile {
// Since we are only concerned with the overall padding, layout direction does
// not matter.
Point padding = getTotalWorkspacePadding();
- result.x = calculateCellWidth(availableWidthPx - padding.x, inv.numColumns);
- result.y = calculateCellHeight(availableHeightPx - padding.y, inv.numRows);
+ result.x = calculateCellWidth(availableWidthPx - padding.x
+ - cellLayoutPaddingLeftRightPx * 2, inv.numColumns);
+ result.y = calculateCellHeight(availableHeightPx - padding.y
+ - cellLayoutBottomPaddingPx, inv.numRows);
return result;
}
@@ -401,22 +488,26 @@ public class DeviceProfile {
/**
* Returns the workspace padding in the specified orientation.
- * Note that it assumes that while in verticalBarLayout, the nav bar is on the right, as such
- * this value is not reliable.
- * Use {@link #getTotalWorkspacePadding()} instead.
*/
public Rect getWorkspacePadding(Rect recycle) {
Rect padding = recycle == null ? new Rect() : recycle;
if (isVerticalBarLayout()) {
if (mInsets.left > 0) {
- padding.set(mInsets.left + pageIndicatorLandGutterLeftNavBarPx, 0,
- hotseatBarHeightPx + hotseatLandGutterPx - mInsets.left, 2 * edgeMarginPx);
+ padding.set(mInsets.left + pageIndicatorLandLeftNavBarGutterPx,
+ 0,
+ hotseatBarSizePx + hotseatBarLeftNavBarRightPaddingPx
+ + hotseatBarLeftNavBarLeftPaddingPx
+ - mInsets.left,
+ edgeMarginPx);
} else {
- padding.set(pageIndicatorLandGutterRightNavBarPx, 0,
- hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx);
+ padding.set(pageIndicatorLandRightNavBarGutterPx,
+ 0,
+ hotseatBarSizePx + hotseatBarRightNavBarRightPaddingPx
+ + hotseatBarRightNavBarLeftPaddingPx,
+ edgeMarginPx);
}
} else {
- int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
+ int paddingBottom = hotseatBarSizePx + pageIndicatorSizePx;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
@@ -451,15 +542,15 @@ public class DeviceProfile {
// Folders should only appear right of the drop target bar and left of the hotseat
return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx,
mInsets.top,
- mInsets.left + availableWidthPx - hotseatBarHeightPx - edgeMarginPx,
+ mInsets.left + availableWidthPx - hotseatBarSizePx - edgeMarginPx,
mInsets.top + availableHeightPx);
} else {
// Folders should only appear below the drop target bar and above the hotseat
return new Rect(mInsets.left,
mInsets.top + dropTargetBarSizePx + edgeMarginPx,
mInsets.left + availableWidthPx,
- mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
- edgeMarginPx);
+ mInsets.top + availableHeightPx - hotseatBarSizePx
+ - pageIndicatorSizePx - edgeMarginPx);
}
}
@@ -477,9 +568,9 @@ public class DeviceProfile {
int getOverviewModeButtonBarHeight() {
int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
- zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
- Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
- return zoneHeight;
+ return Utilities.boundToRange(zoneHeight,
+ overviewModeMinIconZoneHeightPx,
+ overviewModeMaxIconZoneHeightPx);
}
public static int calculateCellWidth(int width, int countX) {
@@ -532,14 +623,6 @@ public class DeviceProfile {
workspacePadding.bottom);
workspace.setPageSpacing(getWorkspacePageSpacing());
- // Only display when enabled
- if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
- View qsbContainer = launcher.getQsbContainer();
- lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams();
- lp.topMargin = mInsets.top + workspacePadding.top;
- qsbContainer.setLayoutParams(lp);
- }
-
// Layout the hotseat
Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
@@ -553,28 +636,44 @@ public class DeviceProfile {
if (hasVerticalBarLayout) {
// Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
// screen regardless of RTL
+ int paddingRight = mInsets.left > 0
+ ? hotseatBarLeftNavBarRightPaddingPx
+ : hotseatBarRightNavBarRightPaddingPx;
+ int paddingLeft = mInsets.left > 0
+ ? hotseatBarLeftNavBarLeftPaddingPx
+ : hotseatBarRightNavBarLeftPaddingPx;
+
lp.gravity = Gravity.RIGHT;
- lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
+ lp.width = hotseatBarSizePx + mInsets.left + mInsets.right
+ + paddingLeft + paddingRight;
lp.height = LayoutParams.MATCH_PARENT;
- hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
- workspacePadding.bottom);
+
+ hotseat.getLayout().setPadding(mInsets.left + cellLayoutPaddingLeftRightPx
+ + paddingLeft,
+ mInsets.top,
+ mInsets.right + cellLayoutPaddingLeftRightPx + paddingRight,
+ workspacePadding.bottom + cellLayoutBottomPaddingPx);
} else if (isTablet) {
// Pad the hotseat with the workspace padding calculated above
lp.gravity = Gravity.BOTTOM;
lp.width = LayoutParams.MATCH_PARENT;
- lp.height = hotseatBarHeightPx + mInsets.bottom;
- hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
- hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
- hotseatBarBottomPaddingPx + mInsets.bottom);
+ lp.height = hotseatBarSizePx + mInsets.bottom;
+ hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left
+ + cellLayoutPaddingLeftRightPx,
+ hotseatBarTopPaddingPx,
+ hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
+ hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
} else {
// For phones, layout the hotseat without any bottom margin
// to ensure that we have space for the folders
lp.gravity = Gravity.BOTTOM;
lp.width = LayoutParams.MATCH_PARENT;
- lp.height = hotseatBarHeightPx + mInsets.bottom;
- hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
- hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
- hotseatBarBottomPaddingPx + mInsets.bottom);
+ lp.height = hotseatBarSizePx + mInsets.bottom;
+ hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left
+ + cellLayoutPaddingLeftRightPx,
+ hotseatBarTopPaddingPx,
+ hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
+ hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
}
hotseat.setLayoutParams(lp);
@@ -584,18 +683,16 @@ public class DeviceProfile {
lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
if (isVerticalBarLayout()) {
if (mInsets.left > 0) {
- lp.leftMargin = mInsets.left + pageIndicatorLandGutterLeftNavBarPx -
- lp.width - pageIndicatorLandWorkspaceOffsetPx;
- } else if (mInsets.right > 0) {
- lp.leftMargin = pageIndicatorLandGutterRightNavBarPx - lp.width -
- pageIndicatorLandWorkspaceOffsetPx;
+ lp.leftMargin = mInsets.left;
+ } else {
+ lp.leftMargin = pageIndicatorLandWorkspaceOffsetPx;
}
lp.bottomMargin = workspacePadding.bottom;
} else {
// Put the page indicators above the hotseat
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.height = pageIndicatorHeightPx;
- lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom;
+ lp.height = pageIndicatorSizePx;
+ lp.bottomMargin = hotseatBarSizePx + mInsets.bottom;
}
pageIndicator.setLayoutParams(lp);
}
@@ -609,10 +706,17 @@ public class DeviceProfile {
lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
lp.width = Math.min(availableWidthPx, maxWidth);
- lp.height = getOverviewModeButtonBarHeight() + mInsets.bottom;
+ lp.height = getOverviewModeButtonBarHeight();
+ lp.bottomMargin = mInsets.bottom;
overviewMode.setLayoutParams(lp);
}
+ // Layout the AllAppsRecyclerView
+ View view = launcher.findViewById(R.id.apps_list_view);
+ int paddingLeftRight = desiredWorkspaceLeftRightMarginPx + cellLayoutPaddingLeftRightPx;
+ view.setPadding(paddingLeftRight, view.getPaddingTop(), paddingLeftRight,
+ view.getPaddingBottom());
+
if (notifyListeners) {
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onLauncherLayoutChanged();
@@ -656,15 +760,24 @@ public class DeviceProfile {
}
// In landscape, we match the width of the workspace
- int padding = (pageIndicatorLandGutterRightNavBarPx +
- hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2;
- return new int[]{ padding, padding };
+ Rect padding = getWorkspacePadding(null);
+ return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
+ }
+
+ public boolean inMultiWindowMode() {
+ return this != inv.landscapeProfile && this != inv.portraitProfile;
}
public boolean shouldIgnoreLongPressToOverview(float touchX) {
- boolean inMultiWindowMode = this != inv.landscapeProfile && this != inv.portraitProfile;
boolean touchedLhsEdge = mInsets.left == 0 && touchX < edgeMarginPx;
boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
- return !inMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
+ return !inMultiWindowMode() && (touchedLhsEdge || touchedRhsEdge);
+ }
+
+ private static Context getContext(Context c, int orientation) {
+ Configuration context = new Configuration(c.getResources().getConfiguration());
+ context.orientation = orientation;
+ return c.createConfigurationContext(context);
+
}
}
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 0840b70150..29a1349d5c 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -78,6 +78,58 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi
setupButtonDropTarget(this, dragController);
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ boolean hideText = hideTextHelper(false /* shouldUpdateText */, false /* no-op */);
+ if (hideTextHelper(true /* shouldUpdateText */, hideText)) {
+ // Text has changed, so we need to re-measure.
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ /**
+ * Helper method that iterates through the children and returns whether any of the visible
+ * {@link ButtonDropTarget} has truncated text.
+ *
+ * @param shouldUpdateText If True, updates the text of all children.
+ * @param hideText If True and {@param shouldUpdateText} is True, clears the text of all
+ * children; otherwise it sets the original text value.
+ *
+ *
+ * @return If shouldUpdateText is True, returns whether any of the children updated their text.
+ * Else, returns whether any of the children have truncated their text.
+ */
+ private boolean hideTextHelper(boolean shouldUpdateText, boolean hideText) {
+ boolean result = false;
+ View visibleView;
+ ButtonDropTarget dropTarget;
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ if (getChildAt(i) instanceof ButtonDropTarget) {
+ visibleView = dropTarget = (ButtonDropTarget) getChildAt(i);
+ } else if (getChildAt(i) instanceof ViewGroup) {
+ // The Drop Target is wrapped in a FrameLayout.
+ visibleView = getChildAt(i);
+ dropTarget = (ButtonDropTarget) ((ViewGroup) visibleView).getChildAt(0);
+ } else {
+ // Ignore other views.
+ continue;
+ }
+
+ if (visibleView.getVisibility() == View.VISIBLE) {
+ if (shouldUpdateText) {
+ result |= dropTarget.updateText(hideText);
+ } else if (dropTarget.isTextTruncated()) {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
private void setupButtonDropTarget(View view, DragController dragController) {
if (view instanceof ButtonDropTarget) {
ButtonDropTarget bdt = (ButtonDropTarget) view;
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 199baaf588..1272e0ade9 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -36,8 +36,6 @@ import com.android.launcher3.graphics.IconPalette;
public class FastBitmapDrawable extends Drawable {
- private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
-
private static final float PRESSED_BRIGHTNESS = 100f / 255f;
private static final float DISABLED_DESATURATION = 1f;
private static final float DISABLED_BRIGHTNESS = 0.5f;
@@ -107,17 +105,6 @@ public class FastBitmapDrawable extends Drawable {
@Override
public void draw(Canvas canvas) {
- drawInternal(canvas);
- }
-
- public void drawWithBrightness(Canvas canvas, float brightness) {
- float oldBrightness = getBrightness();
- setBrightness(brightness);
- drawInternal(canvas);
- setBrightness(oldBrightness);
- }
-
- protected void drawInternal(Canvas canvas) {
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
}
@@ -184,6 +171,11 @@ public class FastBitmapDrawable extends Drawable {
return true;
}
+ @Override
+ public ColorFilter getColorFilter() {
+ return mPaint.getColorFilter();
+ }
+
@Override
protected boolean onStateChange(int[] state) {
boolean isPressed = false;
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index a51ddd4b88..3cbc989eb3 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -23,7 +23,6 @@ import android.util.Log;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
-
import com.android.launcher3.util.Thunk;
/*
@@ -33,10 +32,11 @@ import com.android.launcher3.util.Thunk;
*/
public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
implements ValueAnimator.AnimatorUpdateListener {
+ private static final String TAG = "FirstFrameAnimatorHlpr";
private static final boolean DEBUG = false;
private static final int MAX_DELAY = 1000;
private static final int IDEAL_FRAME_DURATION = 16;
- private View mTarget;
+ private final View mTarget;
private long mStartFrame;
private long mStartTime = -1;
private boolean mHandlingOnAnimationUpdate;
@@ -77,7 +77,7 @@ public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
sGlobalFrameCounter++;
if (DEBUG) {
long newTime = System.currentTimeMillis();
- Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+ Log.d(TAG, "TICK " + (newTime - mTime));
mTime = newTime;
}
}
@@ -139,7 +139,7 @@ public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
public void print(ValueAnimator animation) {
float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
- Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
+ Log.d(TAG, sGlobalFrameCounter +
"(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index b36734bab4..fe7acda17b 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -22,7 +22,7 @@ import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderPagedView;
import com.android.launcher3.util.FocusLogic;
@@ -93,7 +93,7 @@ public class FocusHelper {
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 0041bb4d63..21254ab292 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -65,9 +65,17 @@ public class FolderInfo extends ItemInfo {
* @param item
*/
public void add(ShortcutInfo item, boolean animate) {
- contents.add(item);
+ add(item, contents.size(), animate);
+ }
+
+ /**
+ * Add an app or shortcut for a specified rank.
+ */
+ public void add(ShortcutInfo item, int rank, boolean animate) {
+ rank = Utilities.boundToRange(rank, 0, contents.size());
+ contents.add(rank, item);
for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onAdd(item);
+ listeners.get(i).onAdd(item, rank);
}
itemsChanged(animate);
}
@@ -121,7 +129,7 @@ public class FolderInfo extends ItemInfo {
}
public interface FolderListener {
- public void onAdd(ShortcutInfo item);
+ public void onAdd(ShortcutInfo item, int rank);
public void onRemove(ShortcutInfo item);
public void onTitleChanged(CharSequence title);
public void onItemsChanged(boolean animate);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 47052a77e2..a6d80e336e 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -72,7 +72,9 @@ public class Hotseat extends FrameLayout
mBackgroundColor = ColorUtils.setAlphaComponent(
Themes.getAttrColor(context, android.R.attr.colorPrimary), 0);
mBackground = new ColorDrawable(mBackgroundColor);
- setBackground(mBackground);
+ if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ setBackground(mBackground);
+ }
}
public CellLayout getLayout() {
@@ -147,9 +149,7 @@ public class Hotseat extends FrameLayout
allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
if (mLauncher != null) {
mLauncher.setAllAppsButton(allAppsButton);
- allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
allAppsButton.setOnClickListener(mLauncher);
- allAppsButton.setOnLongClickListener(mLauncher);
allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
}
@@ -179,8 +179,12 @@ public class Hotseat extends FrameLayout
}
public void updateColor(ExtractedColors extractedColors, boolean animate) {
+ if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ // not hotseat visible
+ return;
+ }
if (!mHasVerticalHotseat) {
- int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+ int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
if (mBackgroundColorAnimator != null) {
mBackgroundColorAnimator.cancel();
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 12170309b9..573e8a2563 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -32,10 +32,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
@@ -45,19 +41,17 @@ import android.os.UserHandle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
-
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -75,7 +69,7 @@ public class IconCache {
private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
// Empty class name is used for storing package default entry.
- private static final String EMPTY_CLASS_NAME = ".";
+ public static final String EMPTY_CLASS_NAME = ".";
private static final boolean DEBUG = false;
private static final boolean DEBUG_IGNORE_CACHE = false;
@@ -96,45 +90,32 @@ public class IconCache {
private final Context mContext;
private final PackageManager mPackageManager;
- private IconProvider mIconProvider;
+ private final IconProvider mIconProvider;
@Thunk final UserManagerCompat mUserManager;
private final LauncherAppsCompat mLauncherApps;
private final HashMap mCache =
- new HashMap(INITIAL_ICON_CACHE_CAPACITY);
+ new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
+ private final InstantAppResolver mInstantAppResolver;
private final int mIconDpi;
@Thunk final IconDB mIconDb;
@Thunk final Handler mWorkerHandler;
- // The background color used for activity icons. Since these icons are displayed in all-apps
- // and folders, this would be same as the light quantum panel background. This color
- // is used to convert icons to RGB_565.
- private final int mActivityBgColor;
- // The background color used for package icons. These are displayed in widget tray, which
- // has a dark quantum panel background.
- private final int mPackageBgColor;
private final BitmapFactory.Options mLowResOptions;
- private Canvas mLowResCanvas;
- private Paint mLowResPaint;
-
public IconCache(Context context, InvariantDeviceProfile inv) {
mContext = context;
mPackageManager = context.getPackageManager();
mUserManager = UserManagerCompat.getInstance(mContext);
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+ mInstantAppResolver = InstantAppResolver.newInstance(mContext);
mIconDpi = inv.fillResIconDpi;
mIconDb = new IconDB(context, inv.iconBitmapSize);
- mLowResCanvas = new Canvas();
- mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
mIconProvider = Utilities.getOverrideObject(
IconProvider.class, context, R.string.icon_provider_class);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
- mActivityBgColor = Themes.getColorPrimary(context, R.style.LauncherTheme);
- mPackageBgColor = Themes.getColorPrimary(context, R.style.WidgetContainerTheme);
-
mLowResOptions = new BitmapFactory.Options();
// Always prefer RGB_565 config for low res. If the bitmap has transparency, it will
// automatically be loaded as ALPHA_8888.
@@ -142,7 +123,8 @@ public class IconCache {
}
private Drawable getFullResDefaultActivityIcon() {
- return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon);
+ return getFullResIcon(Resources.getSystem(), Utilities.ATLEAST_OREO ?
+ android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon);
}
private Drawable getFullResIcon(Resources resources, int iconId) {
@@ -190,7 +172,11 @@ public class IconCache {
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
- return mIconProvider.getIcon(info, mIconDpi);
+ return getFullResIcon(info, true);
+ }
+
+ public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
+ return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
protected Bitmap makeDefaultIcon(UserHandle user) {
@@ -209,7 +195,7 @@ public class IconCache {
* Remove any records for the supplied package name from memory.
*/
private void removeFromMemCacheLocked(String packageName, UserHandle user) {
- HashSet forDeletion = new HashSet();
+ HashSet forDeletion = new HashSet<>();
for (ComponentKey key: mCache.keySet()) {
if (key.componentName.getPackageName().equals(packageName)
&& key.user.equals(user)) {
@@ -235,7 +221,6 @@ public class IconCache {
}
} catch (NameNotFoundException e) {
Log.d(TAG, "Package not found", e);
- return;
}
}
@@ -280,7 +265,7 @@ public class IconCache {
Set ignorePackages) {
long userSerial = mUserManager.getSerialNumberForUser(user);
PackageManager pm = mContext.getPackageManager();
- HashMap pkgInfoMap = new HashMap();
+ HashMap pkgInfoMap = new HashMap<>();
for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
pkgInfoMap.put(info.packageName, info);
}
@@ -290,7 +275,7 @@ public class IconCache {
componentMap.put(app.getComponentName(), app);
}
- HashSet itemsToRemove = new HashSet();
+ HashSet itemsToRemove = new HashSet<>();
Stack appsToUpdate = new Stack<>();
Cursor c = null;
@@ -387,7 +372,7 @@ public class IconCache {
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(key, entry);
- Bitmap lowResIcon = generateLowResIcon(entry.icon, mActivityBgColor);
+ Bitmap lowResIcon = generateLowResIcon(entry.icon);
ContentValues values = newContentValues(entry.icon, lowResIcon, entry.title.toString(),
app.getApplicationInfo().packageName);
addIconToDB(values, app.getComponentName(), info, userSerial);
@@ -593,7 +578,6 @@ public class IconCache {
// For icon caching, do not go through DB. Just update the in-memory entry.
if (entry == null) {
entry = new CacheEntry();
- mCache.put(cacheKey, entry);
}
if (!TextUtils.isEmpty(title)) {
entry.title = title;
@@ -601,6 +585,9 @@ public class IconCache {
if (icon != null) {
entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
}
+ if (!TextUtils.isEmpty(title) && entry.icon != null) {
+ mCache.put(cacheKey, entry);
+ }
}
private static ComponentKey getPackageKey(String packageName, UserHandle user) {
@@ -637,7 +624,11 @@ public class IconCache {
// only keep the low resolution icon instead of the larger full-sized icon
Bitmap icon = LauncherIcons.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
- Bitmap lowResIcon = generateLowResIcon(icon, mPackageBgColor);
+ if (mInstantAppResolver.isInstantApp(appInfo)) {
+ icon = LauncherIcons.badgeWithDrawable(icon,
+ mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
+ }
+ Bitmap lowResIcon = generateLowResIcon(icon);
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
entry.icon = useLowResIcon ? lowResIcon : icon;
@@ -720,7 +711,7 @@ public class IconCache {
private final HashMap mPkgInfoMap;
private final Stack mAppsToAdd;
private final Stack mAppsToUpdate;
- private final HashSet mUpdatedPackages = new HashSet();
+ private final HashSet mUpdatedPackages = new HashSet<>();
@Thunk SerializedIconUpdateTask(long userSerial, HashMap pkgInfoMap,
Stack appsToAdd,
@@ -769,7 +760,7 @@ public class IconCache {
}
private static final class IconDB extends SQLiteCacheHelper {
- private final static int DB_VERSION = 13;
+ private final static int DB_VERSION = 17;
private final static int RELEASE_VERSION = DB_VERSION +
(FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -822,24 +813,10 @@ public class IconCache {
/**
* Generates a new low-res icon given a high-res icon.
*/
- private Bitmap generateLowResIcon(Bitmap icon, int lowResBackgroundColor) {
- if (lowResBackgroundColor == Color.TRANSPARENT) {
- return Bitmap.createScaledBitmap(icon,
- icon.getWidth() / LOW_RES_SCALE_FACTOR,
- icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
- } else {
- Bitmap lowResIcon = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
- icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
- synchronized (this) {
- mLowResCanvas.setBitmap(lowResIcon);
- mLowResCanvas.drawColor(lowResBackgroundColor);
- mLowResCanvas.drawBitmap(icon, new Rect(0, 0, icon.getWidth(), icon.getHeight()),
- new Rect(0, 0, lowResIcon.getWidth(), lowResIcon.getHeight()),
- mLowResPaint);
- mLowResCanvas.setBitmap(null);
- }
- return lowResIcon;
- }
+ private Bitmap generateLowResIcon(Bitmap icon) {
+ return Bitmap.createScaledBitmap(icon,
+ icon.getWidth() / LOW_RES_SCALE_FACTOR,
+ icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
}
private static Bitmap loadIconNoResize(Cursor c, int iconIndex, BitmapFactory.Options options) {
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index a5d399013e..4dee2b57db 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -2,6 +2,7 @@ package com.android.launcher3;
import android.content.pm.LauncherActivityInfo;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import java.util.Locale;
@@ -17,15 +18,18 @@ public class IconProvider {
}
public void updateSystemStateString() {
- mSystemState = Locale.getDefault().toString();
+ mSystemState = Locale.getDefault().toString() + "," + Build.VERSION.SDK_INT;
}
public String getIconSystemState(String packageName) {
return mSystemState;
}
-
- public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
+ /**
+ * @param flattenDrawable true if the caller does not care about the specification of the
+ * original icon as long as the flattened version looks the same.
+ */
+ public Drawable getIcon(LauncherActivityInfo info, int iconDpi, boolean flattenDrawable) {
return info.getIcon(iconDpi);
}
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 0608fdd2e4..f919dd052c 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -42,12 +42,10 @@ public class InfoDropTarget extends UninstallDropTarget {
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
+ protected void setupUi() {
// Get the hover color
mHoverColor = Themes.getColorAccent(getContext());
-
- setDrawable(R.drawable.ic_info_launcher);
+ setDrawable(R.drawable.ic_info_shadow);
}
@Override
@@ -67,12 +65,17 @@ public class InfoDropTarget extends UninstallDropTarget {
public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher,
DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) {
+ if (info instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+ launcher.startActivity(promiseAppInfo.getMarketIntent());
+ return true;
+ }
boolean result = false;
ComponentName componentName = null;
if (info instanceof AppInfo) {
componentName = ((AppInfo) info).componentName;
} else if (info instanceof ShortcutInfo) {
- componentName = ((ShortcutInfo) info).intent.getComponent();
+ componentName = info.getTargetComponent();
} else if (info instanceof PendingAddItemInfo) {
componentName = ((PendingAddItemInfo) info).componentName;
} else if (info instanceof LauncherAppWidgetInfo) {
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 154641cabe..be7649013c 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -9,9 +9,6 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.config.FeatureFlags;
-
public class InsettableFrameLayout extends FrameLayout implements
ViewGroup.OnHierarchyChangeListener, Insettable {
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index ce8557065e..0370777d17 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -34,6 +34,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
+import android.util.Pair;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
@@ -59,6 +60,16 @@ import java.util.List;
import java.util.Set;
public class InstallShortcutReceiver extends BroadcastReceiver {
+
+ public static final int FLAG_ACTIVITY_PAUSED = 1;
+ public static final int FLAG_LOADER_RUNNING = 2;
+ public static final int FLAG_DRAG_AND_DROP = 4;
+ public static final int FLAG_BULK_ADD = 4;
+
+ // Determines whether to defer installing shortcuts immediately until
+ // processAllPendingInstalls() is called.
+ private static int sInstallQueueDisabledFlags = 0;
+
private static final String TAG = "InstallShortcutReceiver";
private static final boolean DBG = false;
@@ -151,10 +162,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
}
- // Determines whether to defer installing shortcuts immediately until
- // processAllPendingInstalls() is called.
- private static boolean mUseInstallQueue = false;
-
public void onReceive(Context context, Intent data) {
if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
return;
@@ -207,7 +214,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
PendingInstallShortcutInfo info = createPendingInfo(context, data);
- return info == null ? null : (ShortcutInfo) info.getItemInfo();
+ return info == null ? null : (ShortcutInfo) info.getItemInfo().first;
+ }
+
+ public static ShortcutInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
+ return (ShortcutInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
}
public static void queueShortcut(ShortcutInfoCompat info, Context context) {
@@ -245,27 +256,28 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
- LauncherAppState app = LauncherAppState.getInstance(context);
- boolean launcherNotLoaded = app.getModel().getCallback() == null;
-
addToInstallQueue(Utilities.getPrefs(context), info);
- if (!mUseInstallQueue && !launcherNotLoaded) {
- flushInstallQueue(context);
- }
+ flushInstallQueue(context);
}
- static void enableInstallQueue() {
- mUseInstallQueue = true;
+ public static void enableInstallQueue(int flag) {
+ sInstallQueueDisabledFlags |= flag;
}
- static void disableAndFlushInstallQueue(Context context) {
- mUseInstallQueue = false;
+ public static void disableAndFlushInstallQueue(int flag, Context context) {
+ sInstallQueueDisabledFlags &= ~flag;
flushInstallQueue(context);
}
static void flushInstallQueue(Context context) {
+ LauncherModel model = LauncherAppState.getInstance(context).getModel();
+ boolean launcherNotLoaded = model.getCallback() == null;
+ if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) {
+ return;
+ }
+
ArrayList items = getAndClearInstallQueue(context);
if (!items.isEmpty()) {
- LauncherAppState.getInstance(context).getModel().addAndBindAddedWorkspaceItems(
+ model.addAndBindAddedWorkspaceItems(
new LazyShortcutsProvider(context.getApplicationContext(), items));
}
}
@@ -439,7 +451,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
}
- public ItemInfo getItemInfo() {
+ public Pair getItemInfo() {
if (activityInfo != null) {
AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
final LauncherAppState app = LauncherAppState.getInstance(mContext);
@@ -459,11 +471,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
});
}
- return si;
+ return Pair.create((ItemInfo) si, (Object) activityInfo);
} else if (shortcutInfo != null) {
ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
- return si;
+ return Pair.create((ItemInfo) si, (Object) shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
.fromProviderInfo(mContext, providerInfo);
@@ -475,9 +487,10 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
widgetInfo.minSpanY = info.minSpanY;
widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
- return widgetInfo;
+ return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
} else {
- return createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+ ShortcutInfo si = createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+ return Pair.create((ItemInfo) si, null);
}
}
@@ -588,7 +601,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
return new PendingInstallShortcutInfo(info, original.mContext);
}
- private static class LazyShortcutsProvider extends Provider> {
+ private static class LazyShortcutsProvider extends Provider>> {
private final Context mContext;
private final ArrayList mPendingItems;
@@ -603,9 +616,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
* packageManager and icon cache.
*/
@Override
- public ArrayList get() {
+ public ArrayList> get() {
Preconditions.assertNonUiThread();
- ArrayList installQueue = new ArrayList<>();
+ ArrayList> installQueue = new ArrayList<>();
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
// If the intent specifies a package, make sure the package exists
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 9e214d1ec2..7a431986da 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -28,7 +28,6 @@ import android.view.Display;
import android.view.WindowManager;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -77,6 +76,7 @@ public class InvariantDeviceProfile {
public int numFolderRows;
public int numFolderColumns;
public float iconSize;
+ public float landscapeIconSize;
public int iconBitmapSize;
public int fillResIconDpi;
public float iconTextSize;
@@ -85,9 +85,9 @@ public class InvariantDeviceProfile {
* Number of icons inside the hotseat area.
*/
public int numHotseatIcons;
- float hotseatIconSize;
- public float hotseatScale;
+
int defaultLayoutId;
+ int demoModeLayoutId;
public DeviceProfile landscapeProfile;
public DeviceProfile portraitProfile;
@@ -100,12 +100,12 @@ public class InvariantDeviceProfile {
public InvariantDeviceProfile(InvariantDeviceProfile p) {
this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
- p.iconSize, p.iconTextSize, p.numHotseatIcons, p.hotseatIconSize,
- p.defaultLayoutId);
+ p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
+ p.defaultLayoutId, p.demoModeLayoutId);
}
InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
- float is, float its, int hs, float his, int dlId) {
+ float is, float lis, float its, int hs, int dlId, int dmlId) {
name = n;
minWidthDps = w;
minHeightDps = h;
@@ -115,12 +115,11 @@ public class InvariantDeviceProfile {
numFolderColumns = fc;
minAllAppsPredictionColumns = maapc;
iconSize = is;
+ landscapeIconSize = lis;
iconTextSize = its;
numHotseatIcons = hs;
- hotseatIconSize = his;
defaultLayoutId = dlId;
-
- hotseatScale = hotseatIconSize / iconSize;
+ demoModeLayoutId = dmlId;
}
@TargetApi(23)
@@ -148,22 +147,21 @@ public class InvariantDeviceProfile {
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
defaultLayoutId = closestProfile.defaultLayoutId;
+ demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
iconSize = interpolatedDeviceProfileOut.iconSize;
+ landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
- hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, dm);
- hotseatScale = hotseatIconSize / iconSize;
-
Point realSize = new Point();
display.getRealSize(realSize);
// The real size never changes. smallSide and largeSide will remain the
@@ -211,10 +209,11 @@ public class InvariantDeviceProfile {
a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
a.getInt(R.styleable.InvariantDeviceProfile_minAllAppsPredictionColumns, numColumns),
iconSize,
+ a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
- a.getFloat(R.styleable.InvariantDeviceProfile_hotseatIconSize, iconSize),
- a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0)));
+ a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0),
+ a.getResourceId(R.styleable.InvariantDeviceProfile_demoModeLayoutId, 0)));
a.recycle();
}
}
@@ -305,19 +304,19 @@ public class InvariantDeviceProfile {
private void add(InvariantDeviceProfile p) {
iconSize += p.iconSize;
+ landscapeIconSize += p.landscapeIconSize;
iconTextSize += p.iconTextSize;
- hotseatIconSize += p.hotseatIconSize;
}
private InvariantDeviceProfile multiply(float w) {
iconSize *= w;
+ landscapeIconSize *= w;
iconTextSize *= w;
- hotseatIconSize *= w;
return this;
}
public int getAllAppsButtonRank() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
throw new IllegalAccessError("Accessing all apps rank when all-apps is disabled");
}
return numHotseatIcons / 2;
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 0779a3d206..fa3253c67a 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -39,8 +39,10 @@ public class ItemInfo {
/**
* One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
* {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER}, or
- * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER},
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET} or
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}.
*/
public int itemType;
@@ -53,7 +55,9 @@ public class ItemInfo {
public long container = NO_ID;
/**
- * Indicates the screen in which the shortcut appears.
+ * Indicates the screen in which the shortcut appears if the container types is
+ * {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is
+ * {@link LauncherSettings.Favorites#CONTAINER_HOTSEAT})
*/
public long screenId = -1;
@@ -133,7 +137,12 @@ public class ItemInfo {
}
public ComponentName getTargetComponent() {
- return getIntent() == null ? null : getIntent().getComponent();
+ Intent intent = getIntent();
+ if (intent != null) {
+ return intent.getComponent();
+ } else {
+ return null;
+ }
}
public void writeToValues(ContentWriter writer) {
@@ -178,15 +187,12 @@ public class ItemInfo {
protected String dumpProperties() {
return "id=" + id
- + " type=" + itemType
- + " container=" + container
+ + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
+ + " container=" + LauncherSettings.Favorites.containerToString((int)container)
+ " screen=" + screenId
- + " cellX=" + cellX
- + " cellY=" + cellY
- + " spanX=" + spanX
- + " spanY=" + spanY
- + " minSpanX=" + minSpanX
- + " minSpanY=" + minSpanY
+ + " cell(" + cellX + "," + cellY + ")"
+ + " span(" + spanX + "," + spanY + ")"
+ + " minSpan(" + minSpanX + "," + minSpanY + ")"
+ " rank=" + rank
+ " user=" + user
+ " title=" + title;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2de10030d9..32af059008 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,7 +18,9 @@ package com.android.launcher3;
import android.Manifest;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -65,6 +67,7 @@ import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
@@ -76,27 +79,27 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.OvershootInterpolator;
import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.compat.LauncherAppsCompatVO;
+import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.PinItemDragListener;
import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -111,19 +114,22 @@ import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.ActivityResultInfo;
+import com.android.launcher3.util.RunnableWithId;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.TestingUtils;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -132,6 +138,7 @@ import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetsContainerView;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -140,6 +147,10 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
+
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
/**
* Default launcher application.
@@ -147,7 +158,8 @@ import java.util.Set;
public class Launcher extends BaseActivity
implements LauncherExterns, View.OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
- AccessibilityManager.AccessibilityStateChangeListener {
+ AccessibilityManager.AccessibilityStateChangeListener,
+ WallpaperColorInfo.OnThemeChangeListener {
public static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -157,14 +169,15 @@ public class Launcher extends BaseActivity
private static final int REQUEST_CREATE_SHORTCUT = 1;
private static final int REQUEST_CREATE_APPWIDGET = 5;
+
private static final int REQUEST_PICK_APPWIDGET = 9;
private static final int REQUEST_PICK_WALLPAPER = 10;
private static final int REQUEST_BIND_APPWIDGET = 11;
- private static final int REQUEST_BIND_PENDING_APPWIDGET = 14;
- private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
+ private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
+ private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
- private static final int REQUEST_PERMISSION_CALL_PHONE = 13;
+ private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
@@ -203,28 +216,27 @@ public class Launcher extends BaseActivity
private boolean mIsSafeModeEnabled;
- public static final int APPWIDGET_HOST_ID = 1024;
public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
- private static final int ACTIVITY_START_DELAY = 1000;
// How long to wait before the new-shortcut animation automatically pans the workspace
- private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
- private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
- @Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
+ private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+ private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
+ @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
+
+ private final ExtractedColors mExtractedColors = new ExtractedColors();
@Thunk Workspace mWorkspace;
private View mLauncherView;
@Thunk DragLayer mDragLayer;
private DragController mDragController;
- private View mQsbContainer;
public View mWeightWatcher;
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
- private int[] mTmpAddItemCellCoordinates = new int[2];
+ private final int[] mTmpAddItemCellCoordinates = new int[2];
@Thunk Hotseat mHotseat;
private ViewGroup mOverviewPanel;
@@ -240,7 +252,10 @@ public class Launcher extends BaseActivity
// Main container view and the model for the widget tray screen.
@Thunk WidgetsContainerView mWidgetsView;
- @Thunk MultiHashMap mAllWidgets;
+
+ // We need to store the orientation Launcher was created with, due to a bug (b/64916689)
+ // that results in widgets being inflated in the wrong orientation.
+ private int mOrientation;
// We set the state in both onCreate and then onNewIntent in some cases, which causes both
// scroll issues (because the workspace may not have been measured yet) and extra work.
@@ -254,29 +269,27 @@ public class Launcher extends BaseActivity
private boolean mPaused = true;
private boolean mOnResumeNeedsLoad;
- private ArrayList mBindOnResumeCallbacks = new ArrayList();
- private ArrayList mOnResumeCallbacks = new ArrayList();
+ private final ArrayList mBindOnResumeCallbacks = new ArrayList<>();
+ private final ArrayList mOnResumeCallbacks = new ArrayList<>();
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
private ModelWriter mModelWriter;
private IconCache mIconCache;
- private ExtractedColors mExtractedColors;
private LauncherAccessibilityDelegate mAccessibilityDelegate;
- private Handler mHandler = new Handler();
- private boolean mIsResumeFromActionScreenOff;
+ private final Handler mHandler = new Handler();
private boolean mHasFocus = false;
- private boolean mAttached = false;
+
+ private ObjectAnimator mScrimAnimator;
+ private boolean mShouldFadeInScrim;
private PopupDataProvider mPopupDataProvider;
- private View.OnTouchListener mHapticFeedbackTouchListener;
-
// Determines how long to wait after a rotation before restoring the screen orientation to
// match the sensor state.
private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
- private final ArrayList mSynchronouslyBoundPages = new ArrayList();
+ private final ArrayList mSynchronouslyBoundPages = new ArrayList<>();
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
@@ -289,8 +302,8 @@ public class Launcher extends BaseActivity
// the press state and keep this reference to reset the press state when we return to launcher.
private BubbleTextView mWaitingForResume;
- protected static HashMap sCustomAppWidgets =
- new HashMap();
+ protected static final HashMap sCustomAppWidgets =
+ new HashMap<>();
static {
if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
@@ -303,7 +316,7 @@ public class Launcher extends BaseActivity
// simply unregister this runnable.
private Runnable mExitSpringLoadedModeRunnable;
- @Thunk Runnable mBuildLayersRunnable = new Runnable() {
+ @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
public void run() {
if (mWorkspace != null) {
mWorkspace.buildPageHardwareLayers();
@@ -359,6 +372,10 @@ public class Launcher extends BaseActivity
mLauncherCallbacks.preOnCreate();
}
+ WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
+ wallpaperColorInfo.setOnThemeChangeListener(this);
+ overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
+
super.onCreate(savedInstanceState);
LauncherAppState app = LauncherAppState.getInstance(this);
@@ -372,6 +389,7 @@ public class Launcher extends BaseActivity
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
+ mOrientation = getResources().getConfiguration().orientation;
mSharedPrefs = Utilities.getPrefs(this);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);
@@ -385,7 +403,10 @@ public class Launcher extends BaseActivity
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
- mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
+ mAppWidgetHost = new LauncherAppWidgetHost(this);
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ mAppWidgetHost.addProviderChangeListener(this);
+ }
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
@@ -393,11 +414,10 @@ public class Launcher extends BaseActivity
// LauncherModel load.
mPaused = false;
- mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
+ mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
setupViews();
mDeviceProfile.layout(this, false /* notifyListeners */);
- mExtractedColors = new ExtractedColors();
loadExtractedColorsAndColorItems();
mPopupDataProvider = new PopupDataProvider(this);
@@ -453,19 +473,48 @@ public class Launcher extends BaseActivity
setOrientation();
setContentView(mLauncherView);
+
+ // Listen for broadcasts
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
+ registerReceiver(mReceiver, filter);
+ mShouldFadeInScrim = true;
+
+ getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+ Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
}
@Override
- public View findViewById(int id) {
+ public void onThemeChanged() {
+ recreate();
+ }
+
+ protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
+ if (isDark) {
+ setTheme(R.style.LauncherThemeDark);
+ } else if (supportsDarkText) {
+ setTheme(R.style.LauncherThemeDarkText);
+ }
+ }
+
+ @Override
+ public T findViewById(int id) {
return mLauncherView.findViewById(id);
}
@Override
public void onExtractedColorsChanged() {
loadExtractedColorsAndColorItems();
+ mExtractedColors.notifyChange();
+ }
+
+ public ExtractedColors getExtractedColors() {
+ return mExtractedColors;
}
@Override
@@ -481,46 +530,6 @@ public class Launcher extends BaseActivity
mExtractedColors.load(this);
mHotseat.updateColor(mExtractedColors, !mPaused);
mWorkspace.getPageIndicator().updateColor(mExtractedColors);
- boolean lightStatusBar = (FeatureFlags.LIGHT_STATUS_BAR
- && mExtractedColors.getColor(ExtractedColors.STATUS_BAR_INDEX,
- ExtractedColors.DEFAULT_DARK) == ExtractedColors.DEFAULT_LIGHT);
- // It's possible that All Apps is visible when this is run,
- // so always use light status bar in that case. Only change nav bar color to status bar
- // color when All Apps is visible.
- activateLightSystemBars(lightStatusBar || isAllAppsVisible(), true, isAllAppsVisible());
- }
- }
-
- // TODO: use platform flag on API >= 26
- private static final int SYSTEM_UI_FLAG_LIGHT_NAV_BAR = 0x10;
-
- /**
- * Sets the status and/or nav bar to be light or not. Light status bar means dark icons.
- * @param isLight make sure the system bar is light.
- * @param statusBar if true, make the status bar theme match the isLight param.
- * @param navBar if true, make the nav bar theme match the isLight param.
- */
- public void activateLightSystemBars(boolean isLight, boolean statusBar, boolean navBar) {
- int oldSystemUiFlags = getWindow().getDecorView().getSystemUiVisibility();
- int newSystemUiFlags = oldSystemUiFlags;
- if (isLight) {
- if (statusBar) {
- newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- }
- if (navBar && Utilities.isAtLeastO()) {
- newSystemUiFlags |= SYSTEM_UI_FLAG_LIGHT_NAV_BAR;
- }
- } else {
- if (statusBar) {
- newSystemUiFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- }
- if (navBar && Utilities.isAtLeastO()) {
- newSystemUiFlags &= ~(SYSTEM_UI_FLAG_LIGHT_NAV_BAR);
- }
- }
-
- if (newSystemUiFlags != oldSystemUiFlags) {
- getWindow().getDecorView().setSystemUiVisibility(newSystemUiFlags);
}
}
@@ -550,47 +559,6 @@ public class Launcher extends BaseActivity
public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
mLauncherCallbacks = callbacks;
- mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() {
- private boolean mWorkspaceImportanceStored = false;
- private boolean mHotseatImportanceStored = false;
- private int mWorkspaceImportanceForAccessibility =
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
- private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-
- @Override
- public void onSearchOverlayOpened() {
- if (mWorkspaceImportanceStored || mHotseatImportanceStored) {
- return;
- }
- // The underlying workspace and hotseat are temporarily suppressed by the search
- // overlay. So they shouldn't be accessible.
- if (mWorkspace != null) {
- mWorkspaceImportanceForAccessibility =
- mWorkspace.getImportantForAccessibility();
- mWorkspace.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- mWorkspaceImportanceStored = true;
- }
- if (mHotseat != null) {
- mHotseatImportanceForAccessibility = mHotseat.getImportantForAccessibility();
- mHotseat.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- mHotseatImportanceStored = true;
- }
- }
-
- @Override
- public void onSearchOverlayClosed() {
- if (mWorkspaceImportanceStored && mWorkspace != null) {
- mWorkspace.setImportantForAccessibility(mWorkspaceImportanceForAccessibility);
- }
- if (mHotseatImportanceStored && mHotseat != null) {
- mHotseat.setImportantForAccessibility(mHotseatImportanceForAccessibility);
- }
- mWorkspaceImportanceStored = false;
- mHotseatImportanceStored = false;
- }
- });
return true;
}
@@ -830,7 +798,7 @@ public class Launcher extends BaseActivity
}
@Override
- protected void onActivityResult(
+ public void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
handleActivityResult(requestCode, resultCode, data);
if (mLauncherCallbacks != null) {
@@ -952,6 +920,25 @@ public class Launcher extends BaseActivity
if (!isWorkspaceLoading()) {
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
}
+
+ if (mShouldFadeInScrim && mDragLayer.getBackground() != null) {
+ if (mScrimAnimator != null) {
+ mScrimAnimator.cancel();
+ }
+ mDragLayer.getBackground().setAlpha(0);
+ mScrimAnimator = ObjectAnimator.ofInt(mDragLayer.getBackground(),
+ LauncherAnimUtils.DRAWABLE_ALPHA, 0, 255);
+ mScrimAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrimAnimator = null;
+ }
+ });
+ mScrimAnimator.setDuration(600);
+ mScrimAnimator.setStartDelay(getWindow().getTransitionBackgroundFadeDuration());
+ mScrimAnimator.start();
+ }
+ mShouldFadeInScrim = false;
}
@Override
@@ -977,11 +964,13 @@ public class Launcher extends BaseActivity
// Don't update the predicted apps if the user is returning to launcher in the apps
// view after launching an app, as they may be depending on the UI to be static to
// switch to another app, otherwise, if it was
- showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
- mAppsView.shouldRestoreImeState() /* focusSearchBar */);
+ showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
+ if (mOnResumeState != State.APPS) {
+ tryAndUpdatePredictedApps();
+ }
mOnResumeState = State.NONE;
mPaused = false;
@@ -1051,18 +1040,16 @@ public class Launcher extends BaseActivity
updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
mWorkspace.onResume();
- if (!isWorkspaceLoading()) {
- // Process any items that were added while Launcher was away.
- InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+ // Process any items that were added while Launcher was away.
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
- // Refresh shortcuts if the permission changed.
- mModel.refreshShortcutsIfRequired();
- }
+ // Refresh shortcuts if the permission changed.
+ mModel.refreshShortcutsIfRequired();
if (shouldShowDiscoveryBounce()) {
mAllAppsController.showDiscoveryBounce();
}
- mIsResumeFromActionScreenOff = false;
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onResume();
}
@@ -1072,7 +1059,7 @@ public class Launcher extends BaseActivity
@Override
protected void onPause() {
// Ensure that items added to Launcher are queued until Launcher returns
- InstallShortcutReceiver.enableInstallQueue();
+ InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
super.onPause();
mPaused = true;
@@ -1093,13 +1080,13 @@ public class Launcher extends BaseActivity
public interface CustomContentCallbacks {
// Custom content is completely shown. {@code fromResume} indicates whether this was caused
// by a onResume or by scrolling otherwise.
- public void onShow(boolean fromResume);
+ void onShow(boolean fromResume);
// Custom content is completely hidden
- public void onHide();
+ void onHide();
// Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
- public void onScrollProgressChanged(float progress);
+ void onScrollProgressChanged(float progress);
// Indicates whether the user is allowed to scroll away from the custom content.
boolean isScrollingAllowed();
@@ -1110,40 +1097,28 @@ public class Launcher extends BaseActivity
/**
* Touch interaction leading to overscroll has begun
*/
- public void onScrollInteractionBegin();
+ void onScrollInteractionBegin();
/**
* Touch interaction related to overscroll has ended
*/
- public void onScrollInteractionEnd();
+ void onScrollInteractionEnd();
/**
* Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
* screen (or in the case of RTL, the rightmost screen).
*/
- public void onScrollChange(float progress, boolean rtl);
+ void onScrollChange(float progress, boolean rtl);
/**
* Called when the launcher is ready to use the overlay
* @param callbacks A set of callbacks provided by Launcher in relation to the overlay
*/
- public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
- }
-
- public interface LauncherSearchCallbacks {
- /**
- * Called when the search overlay is shown.
- */
- public void onSearchOverlayOpened();
-
- /**
- * Called when the search overlay is dismissed.
- */
- public void onSearchOverlayClosed();
+ void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
}
public interface LauncherOverlayCallbacks {
- public void onScrollChanged(float progress);
+ void onScrollChanged(float progress);
}
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1162,7 +1137,7 @@ public class Launcher extends BaseActivity
// On O and above we there is always some setting present settings (add icon to
// home screen or icon badging). On earlier APIs we will have the allow rotation
// setting, on devices with a locked orientation,
- return Utilities.isAtLeastO() || !getResources().getBoolean(R.bool.allow_rotation);
+ return Utilities.ATLEAST_OREO || !getResources().getBoolean(R.bool.allow_rotation);
}
}
@@ -1297,9 +1272,7 @@ public class Launcher extends BaseActivity
private void setupViews() {
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
- mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
- mQsbContainer = mDragLayer.findViewById(mDeviceProfile.isVerticalBarLayout()
- ? R.id.workspace_blocked_row : R.id.qsb_container);
+ mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1329,25 +1302,18 @@ public class Launcher extends BaseActivity
mDragController.addDragListener(mWorkspace);
// Get the search/delete/uninstall bar
- mDropTargetBar = (DropTargetBar) mDragLayer.findViewById(R.id.drop_target_bar);
+ mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
// Setup Apps and Widgets
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
- if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
- mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
- } else {
- mAppsView.setSearchBarController(new DefaultAppSearchController());
- }
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDragController.setMoveTarget(mWorkspace);
mDragController.addDropTarget(mWorkspace);
mDropTargetBar.setup(mDragController);
- if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
- mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
- }
+ mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
if (TestingUtils.MEMORY_DUMP_ENABLED) {
TestingUtils.addWeightWatcher(this);
@@ -1365,7 +1331,6 @@ public class Launcher extends BaseActivity
onClickWallpaperPicker(view);
}
}.attachTo(wallpaperButton);
- wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
// Bind widget button actions
mWidgetsButton = findViewById(R.id.widget_button);
@@ -1375,7 +1340,6 @@ public class Launcher extends BaseActivity
onClickAddWidgetButton(view);
}
}.attachTo(mWidgetsButton);
- mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
// Bind settings actions
View settingsButton = findViewById(R.id.settings_button);
@@ -1387,7 +1351,6 @@ public class Launcher extends BaseActivity
onClickSettingsButton(view);
}
}.attachTo(settingsButton);
- settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
} else {
settingsButton.setVisibility(View.GONE);
}
@@ -1429,34 +1392,33 @@ public class Launcher extends BaseActivity
* @return A View inflated from layoutResId.
*/
public View createShortcut(ViewGroup parent, ShortcutInfo info) {
- BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon,
- parent, false);
+ BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.app_icon, parent, false);
favorite.applyFromShortcutInfo(info);
- favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
favorite.setOnClickListener(this);
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
/**
- * Add a shortcut to the workspace.
+ * Add a shortcut to the workspace or to a Folder.
*
* @param data The intent describing the shortcut.
*/
private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
int cellY, PendingRequestArgs args) {
- int[] cellXY = mTmpAddItemCellCoordinates;
- CellLayout layout = getCellLayout(container, screenId);
-
- if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
- args.getPendingIntent().getComponent() == null) {
+ if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
+ || args.getPendingIntent().getComponent() == null) {
return;
}
+ int[] cellXY = mTmpAddItemCellCoordinates;
+ CellLayout layout = getCellLayout(container, screenId);
+
ShortcutInfo info = null;
- if (Utilities.isAtLeastO()) {
- info = LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
- this, PinItemRequestCompat.getPinItemRequest(data), 0);
+ if (Utilities.ATLEAST_OREO) {
+ info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+ this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
}
if (info == null) {
@@ -1475,36 +1437,57 @@ public class Launcher extends BaseActivity
}
}
- final View view = createShortcut(info);
- boolean foundCellSpan = false;
- // First we check if we already know the exact location where we want to add this item.
- if (cellX >= 0 && cellY >= 0) {
- cellXY[0] = cellX;
- cellXY[1] = cellY;
- foundCellSpan = true;
+ if (container < 0) {
+ // Adding a shortcut to the Workspace.
+ final View view = createShortcut(info);
+ boolean foundCellSpan = false;
+ // First we check if we already know the exact location where we want to add this item.
+ if (cellX >= 0 && cellY >= 0) {
+ cellXY[0] = cellX;
+ cellXY[1] = cellY;
+ foundCellSpan = true;
- // If appropriate, either create a folder or add to an existing folder
- if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
- true, null,null)) {
- return;
- }
- DragObject dragObject = new DragObject();
- dragObject.dragInfo = info;
- if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
- true)) {
+ // If appropriate, either create a folder or add to an existing folder
+ if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
+ true, null, null)) {
+ return;
+ }
+ DragObject dragObject = new DragObject();
+ dragObject.dragInfo = info;
+ if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
+ true)) {
+ return;
+ }
+ } else {
+ foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
+ }
+
+ if (!foundCellSpan) {
+ mWorkspace.onNoCellFound(layout);
return;
}
+
+ getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
+ mWorkspace.addInScreen(view, info);
} else {
- foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
+ // Adding a shortcut to a Folder.
+ FolderIcon folderIcon = findFolderIcon(container);
+ if (folderIcon != null) {
+ FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
+ folderInfo.add(info, args.rank, false);
+ } else {
+ Log.e(TAG, "Could not find folder with id " + container + " to add shortcut.");
+ }
}
+ }
- if (!foundCellSpan) {
- mWorkspace.onNoCellFound(layout);
- return;
- }
-
- getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
- mWorkspace.addInScreen(view, info);
+ public FolderIcon findFolderIcon(final long folderIconId) {
+ return (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View view) {
+ return info != null && info.id == folderIconId;
+ }
+ });
}
/**
@@ -1565,7 +1548,11 @@ public class Launcher extends BaseActivity
mAppsView.reset();
}
}
- mIsResumeFromActionScreenOff = true;
+ mShouldFadeInScrim = true;
+ } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
+ // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
+ // the user unlocked and the Launcher is not in the foreground.
+ mShouldFadeInScrim = false;
}
}
};
@@ -1592,13 +1579,7 @@ public class Launcher extends BaseActivity
public void onAttachedToWindow() {
super.onAttachedToWindow();
- // Listen for broadcasts related to user-presence
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
- mAttached = true;
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onAttachedToWindow();
}
@@ -1607,10 +1588,6 @@ public class Launcher extends BaseActivity
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mAttached) {
- unregisterReceiver(mReceiver);
- mAttached = false;
- }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onDetachedFromWindow();
@@ -1648,7 +1625,6 @@ public class Launcher extends BaseActivity
}
}
});
- return;
}
});
}
@@ -1672,10 +1648,6 @@ public class Launcher extends BaseActivity
return mWorkspace;
}
- public View getQsbContainer() {
- return mQsbContainer;
- }
-
public Hotseat getHotseat() {
return mHotseat;
}
@@ -1704,6 +1676,8 @@ public class Launcher extends BaseActivity
return mSharedPrefs;
}
+ public int getOrientation() { return mOrientation; }
+
@Override
protected void onNewIntent(Intent intent) {
long startTime = 0;
@@ -1767,7 +1741,7 @@ public class Launcher extends BaseActivity
// Reset the apps view
if (!alreadyOnHome && mAppsView != null) {
- mAppsView.scrollToTop();
+ mAppsView.reset();
}
// Reset the widgets view
@@ -1789,8 +1763,9 @@ public class Launcher extends BaseActivity
// as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
// animation.
if (isActionMain) {
- boolean callbackAllowsMoveToDefaultScreen = mLauncherCallbacks != null ?
- mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
+ boolean callbackAllowsMoveToDefaultScreen =
+ mLauncherCallbacks == null || mLauncherCallbacks
+ .shouldMoveToDefaultScreenOnHomeIntent();
if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
&& callbackAllowsMoveToDefaultScreen) {
@@ -1851,6 +1826,7 @@ public class Launcher extends BaseActivity
public void onDestroy() {
super.onDestroy();
+ unregisterReceiver(mReceiver);
mWorkspace.removeCallbacks(mBuildLayersRunnable);
mWorkspace.removeFolderListeners();
@@ -1878,8 +1854,12 @@ public class Launcher extends BaseActivity
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.removeAccessibilityStateChangeListener(this);
+ WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
+
LauncherAnimUtils.onDestroyActivity();
+ clearPendingBinds();
+
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onDestroy();
}
@@ -2299,8 +2279,8 @@ public class Launcher extends BaseActivity
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
- } else if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
- (v == mAllAppsButton && mAllAppsButton != null)) {
+ } else if ((v instanceof PageIndicator) ||
+ (v == mAllAppsButton && mAllAppsButton != null)) {
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
startAppShortcutOrInfoActivity(v);
@@ -2351,8 +2331,9 @@ public class Launcher extends BaseActivity
}
/**
- * Event handler for the "grid" button that appears on the home screen, which
- * enters all apps mode.
+ * Event handler for the "grid" button or "caret" that appears on the home screen, which
+ * enters all apps mode. In verticalBarLayout the caret can be seen when all apps is open, and
+ * so in that case reverses the action.
*
* @param v The view that was clicked.
*/
@@ -2361,18 +2342,9 @@ public class Launcher extends BaseActivity
if (!isAppsViewVisible()) {
getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.ALL_APPS_BUTTON);
- showAppsView(true /* animated */, true /* updatePredictedApps */,
- false /* focusSearchBar */);
- }
- }
-
- protected void onLongClickAllAppsButton(View v) {
- if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
- if (!isAppsViewVisible()) {
- getUserEventDispatcher().logActionOnControl(Action.Touch.LONGPRESS,
- ControlType.ALL_APPS_BUTTON);
- showAppsView(true /* animated */,
- true /* updatePredictedApps */, true /* focusSearchBar */);
+ showAppsView(true /* animated */, true /* updatePredictedApps */);
+ } else {
+ showWorkspace(true);
}
}
@@ -2453,7 +2425,7 @@ public class Launcher extends BaseActivity
}
// Check for abandoned promise
- if ((v instanceof BubbleTextView) && shortcut.isPromise()) {
+ if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
String packageName = shortcut.intent.getComponent() != null ?
shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
if (!TextUtils.isEmpty(packageName)) {
@@ -2469,7 +2441,13 @@ public class Launcher extends BaseActivity
private void startAppShortcutOrInfoActivity(View v) {
ItemInfo item = (ItemInfo) v.getTag();
- Intent intent = item.getIntent();
+ Intent intent;
+ if (item instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
+ intent = promiseAppInfo.getMarketIntent();
+ } else {
+ intent = item.getIntent();
+ }
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
@@ -2530,8 +2508,8 @@ public class Launcher extends BaseActivity
.putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
String pickerPackage = getString(R.string.wallpaper_picker_package);
- boolean hasTargetPackage = TextUtils.isEmpty(pickerPackage);
- if (!hasTargetPackage) {
+ boolean hasTargetPackage = !TextUtils.isEmpty(pickerPackage);
+ if (hasTargetPackage) {
intent.setPackage(pickerPackage);
}
@@ -2559,22 +2537,6 @@ public class Launcher extends BaseActivity
startActivity(intent, getActivityLaunchOptions(v));
}
- public View.OnTouchListener getHapticFeedbackTouchListener() {
- if (mHapticFeedbackTouchListener == null) {
- mHapticFeedbackTouchListener = new View.OnTouchListener() {
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
- v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- }
- return false;
- }
- };
- }
- return mHapticFeedbackTouchListener;
- }
-
@Override
public void onAccessibilityStateChanged(boolean enabled) {
mDragLayer.onAccessibilityStateChanged(enabled);
@@ -2673,9 +2635,9 @@ public class Launcher extends BaseActivity
if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
- if (v instanceof TextView) {
+ if (v instanceof BubbleTextView) {
// Launch from center of icon, not entire view
- Drawable icon = Workspace.getTextViewIcon((TextView) v);
+ Drawable icon = ((BubbleTextView) v).getIcon();
if (icon != null) {
Rect bounds = icon.getBounds();
left = (width - bounds.width()) / 2;
@@ -2754,13 +2716,6 @@ public class Launcher extends BaseActivity
if (isWorkspaceLocked()) return false;
if (mState != State.WORKSPACE) return false;
- if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
- (v == mAllAppsButton && mAllAppsButton != null)) {
- onLongClickAllAppsButton(v);
- return true;
- }
-
-
boolean ignoreLongPressToOverview =
mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
@@ -2960,13 +2915,12 @@ public class Launcher extends BaseActivity
/**
* Shows the apps view.
*/
- public void showAppsView(boolean animated, boolean updatePredictedApps,
- boolean focusSearchBar) {
+ public void showAppsView(boolean animated, boolean updatePredictedApps) {
markAppsViewShown();
if (updatePredictedApps) {
tryAndUpdatePredictedApps();
}
- showAppsOrWidgets(State.APPS, animated, focusSearchBar);
+ showAppsOrWidgets(State.APPS, animated);
}
/**
@@ -2977,7 +2931,7 @@ public class Launcher extends BaseActivity
if (resetPageToZero) {
mWidgetsView.scrollToTop();
}
- showAppsOrWidgets(State.WIDGETS, animated, false);
+ showAppsOrWidgets(State.WIDGETS, animated);
mWidgetsView.post(new Runnable() {
@Override
@@ -2994,7 +2948,7 @@ public class Launcher extends BaseActivity
*/
// TODO: calling method should use the return value so that when {@code false} is returned
// the workspace transition doesn't fall into invalid state.
- private boolean showAppsOrWidgets(State toState, boolean animated, boolean focusSearchBar) {
+ private boolean showAppsOrWidgets(State toState, boolean animated) {
if (!(mState == State.WORKSPACE ||
mState == State.APPS_SPRING_LOADED ||
mState == State.WIDGETS_SPRING_LOADED ||
@@ -3012,7 +2966,7 @@ public class Launcher extends BaseActivity
}
if (toState == State.APPS) {
- mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
+ mStateTransitionAnimation.startAnimationToAllApps(animated);
} else {
mStateTransitionAnimation.startAnimationToWidgets(animated);
}
@@ -3085,8 +3039,7 @@ public class Launcher extends BaseActivity
public void exitSpringLoadedDragMode() {
if (mState == State.APPS_SPRING_LOADED) {
- showAppsView(true /* animated */,
- false /* updatePredictedApps */, false /* focusSearchBar */);
+ showAppsView(true /* animated */, false /* updatePredictedApps */);
} else if (mState == State.WIDGETS_SPRING_LOADED) {
showWidgetsView(true, false);
} else if (mState == State.WORKSPACE_SPRING_LOADED) {
@@ -3100,10 +3053,9 @@ public class Launcher extends BaseActivity
*/
public void tryAndUpdatePredictedApps() {
if (mLauncherCallbacks != null) {
- List apps = mLauncherCallbacks.getPredictedApps();
+ List> apps = mLauncherCallbacks.getPredictedApps();
if (apps != null) {
mAppsView.setPredictedApps(apps);
- getUserEventDispatcher().setPredictedApps(apps);
}
}
}
@@ -3147,12 +3099,12 @@ public class Launcher extends BaseActivity
*
* @return {@code true} if we are currently paused. The caller might be able to skip some work
*/
- @Thunk boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
+ @Thunk boolean waitUntilResume(Runnable run) {
if (mPaused) {
if (LOGD) Log.d(TAG, "Deferring update until onResume");
- if (deletePreviousRunnables) {
- while (mBindOnResumeCallbacks.remove(run)) {
- }
+ if (run instanceof RunnableWithId) {
+ // Remove any runnables which have the same id
+ while (mBindOnResumeCallbacks.remove(run)) { }
}
mBindOnResumeCallbacks.add(run);
return true;
@@ -3161,10 +3113,6 @@ public class Launcher extends BaseActivity
}
}
- private boolean waitUntilResume(Runnable run) {
- return waitUntilResume(run, false);
- }
-
public void addOnResumeCallback(Runnable run) {
mOnResumeCallbacks.add(run);
}
@@ -3252,7 +3200,7 @@ public class Launcher extends BaseActivity
orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
- mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
+ LauncherModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
} else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
// If there are no screens, we need to have an empty screen
mWorkspace.addExtraEmptyScreen();
@@ -3283,13 +3231,13 @@ public class Launcher extends BaseActivity
}
}
+ @Override
public void bindAppsAdded(final ArrayList newScreens,
final ArrayList addNotAnimated,
- final ArrayList addAnimated,
- final ArrayList addedApps) {
+ final ArrayList addAnimated) {
Runnable r = new Runnable() {
public void run() {
- bindAppsAdded(newScreens, addNotAnimated, addAnimated, addedApps);
+ bindAppsAdded(newScreens, addNotAnimated, addAnimated);
}
};
if (waitUntilResume(r)) {
@@ -3304,20 +3252,14 @@ public class Launcher extends BaseActivity
// We add the items without animation on non-visible pages, and with
// animations on the new page (which we will try and snap to).
if (addNotAnimated != null && !addNotAnimated.isEmpty()) {
- bindItems(addNotAnimated, 0,
- addNotAnimated.size(), false);
+ bindItems(addNotAnimated, false);
}
if (addAnimated != null && !addAnimated.isEmpty()) {
- bindItems(addAnimated, 0,
- addAnimated.size(), true);
+ bindItems(addAnimated, true);
}
// Remove the extra empty screen
mWorkspace.removeExtraEmptyScreen(false, false);
-
- if (addedApps != null && mAppsView != null) {
- mAppsView.addApps(addedApps);
- }
}
/**
@@ -3326,11 +3268,10 @@ public class Launcher extends BaseActivity
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
- public void bindItems(final ArrayList items, final int start, final int end,
- final boolean forceAnimateIcons) {
+ public void bindItems(final List items, final boolean forceAnimateIcons) {
Runnable r = new Runnable() {
public void run() {
- bindItems(items, start, end, forceAnimateIcons);
+ bindItems(items, forceAnimateIcons);
}
};
if (waitUntilResume(r)) {
@@ -3339,11 +3280,12 @@ public class Launcher extends BaseActivity
// Get the list of added items and intersect them with the set of items here
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
- final Collection bounceAnims = new ArrayList();
+ final Collection bounceAnims = new ArrayList<>();
final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
Workspace workspace = mWorkspace;
long newItemsScreenId = -1;
- for (int i = start; i < end; i++) {
+ int end = items.size();
+ for (int i = 0; i < end; i++) {
final ItemInfo item = items.get(i);
// Short circuit if we are loading dock items for a configuration which has no dock
@@ -3368,19 +3310,10 @@ public class Launcher extends BaseActivity
break;
}
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
- LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
- if (mIsSafeModeEnabled) {
- view = new PendingAppWidgetHostView(this, info, mIconCache, true);
- } else {
- LauncherAppWidgetProviderInfo providerInfo =
- mAppWidgetManager.getLauncherAppWidgetInfo(info.appWidgetId);
- if (providerInfo == null) {
- deleteWidgetInfo(info);
- continue;
- }
- view = mAppWidgetHost.createView(this, info.appWidgetId, providerInfo);
+ view = inflateAppWidget((LauncherAppWidgetInfo) item);
+ if (view == null) {
+ continue;
}
- prepareAppWidget((AppWidgetHostView) view, info);
break;
}
default:
@@ -3397,7 +3330,7 @@ public class Launcher extends BaseActivity
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
@@ -3450,26 +3383,21 @@ public class Launcher extends BaseActivity
/**
* Add the views for a widget to the workspace.
- *
- * Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAppWidget(final LauncherAppWidgetInfo item) {
- Runnable r = new Runnable() {
- public void run() {
- bindAppWidget(item);
- }
- };
- if (waitUntilResume(r)) {
- return;
+ public void bindAppWidget(LauncherAppWidgetInfo item) {
+ View view = inflateAppWidget(item);
+ if (view != null) {
+ mWorkspace.addInScreen(view, item);
+ mWorkspace.requestLayout();
}
+ }
+ private View inflateAppWidget(LauncherAppWidgetInfo item) {
if (mIsSafeModeEnabled) {
PendingAppWidgetHostView view =
new PendingAppWidgetHostView(this, item, mIconCache, true);
prepareAppWidget(view, item);
- mWorkspace.addInScreen(view, item);
- mWorkspace.requestLayout();
- return;
+ return view;
}
final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
@@ -3499,7 +3427,7 @@ public class Launcher extends BaseActivity
+ ", as the provider is null");
}
getModelWriter().deleteItemFromDatabase(item);
- return;
+ return null;
}
// If we do not have a valid id, try to bind an id.
@@ -3567,7 +3495,7 @@ public class Launcher extends BaseActivity
if (appWidgetInfo == null) {
FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
deleteWidgetInfo(item);
- return;
+ return null;
}
item.minSpanX = appWidgetInfo.minSpanX;
@@ -3577,13 +3505,12 @@ public class Launcher extends BaseActivity
view = new PendingAppWidgetHostView(this, item, mIconCache, false);
}
prepareAppWidget(view, item);
- mWorkspace.addInScreen(view, item);
- mWorkspace.requestLayout();
if (DEBUG_WIDGETS) {
Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
+ (SystemClock.uptimeMillis()-start) + "ms");
}
+ return view;
}
/**
@@ -3600,6 +3527,9 @@ public class Launcher extends BaseActivity
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
info.restoreStatus = finalRestoreFlag;
+ if (info.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ info.pendingItemInfo = null;
+ }
mWorkspace.reinflateWidgetsIfNecessary();
getModelWriter().updateItemInDatabase(info);
@@ -3678,7 +3608,8 @@ public class Launcher extends BaseActivity
mPendingActivityResult = null;
}
- InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
@@ -3714,30 +3645,29 @@ public class Launcher extends BaseActivity
return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL;
}
- /**
- * A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent
- * multiple calls to bind the same list.)
- */
- @Thunk ArrayList mTmpAppsList;
- private Runnable mBindAllApplicationsRunnable = new Runnable() {
- public void run() {
- bindAllApplications(mTmpAppsList);
- mTmpAppsList = null;
- }
- };
-
/**
* Add the icons for all apps.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAllApplications(final ArrayList apps) {
- if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
- mTmpAppsList = apps;
+ Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) {
+ public void run() {
+ bindAllApplications(apps);
+ }
+ };
+ if (waitUntilResume(r)) {
return;
}
if (mAppsView != null) {
+ Executor pendingExecutor = getPendingExecutor();
+ if (pendingExecutor != null && mState != State.APPS) {
+ // Wait until the fade in animation has finished before setting all apps list.
+ pendingExecutor.execute(r);
+ return;
+ }
+
mAppsView.setApps(apps);
}
if (mLauncherCallbacks != null) {
@@ -3745,6 +3675,14 @@ public class Launcher extends BaseActivity
}
}
+ /**
+ * Returns an Executor that will run after the launcher is first drawn (including after the
+ * initial fade in animation). Returns null if the first draw has already occurred.
+ */
+ public @Nullable Executor getPendingExecutor() {
+ return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null;
+ }
+
/**
* Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
* because LauncherModel's map is updated in the background, while Launcher runs on the UI.
@@ -3759,10 +3697,10 @@ public class Launcher extends BaseActivity
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAppsUpdated(final ArrayList apps) {
+ public void bindAppsAddedOrUpdated(final ArrayList apps) {
Runnable r = new Runnable() {
public void run() {
- bindAppsUpdated(apps);
+ bindAppsAddedOrUpdated(apps);
}
};
if (waitUntilResume(r)) {
@@ -3770,7 +3708,23 @@ public class Launcher extends BaseActivity
}
if (mAppsView != null) {
- mAppsView.updateApps(apps);
+ mAppsView.addOrUpdateApps(apps);
+ }
+ }
+
+ @Override
+ public void bindPromiseAppProgressUpdated(final PromiseAppInfo app) {
+ Runnable r = new Runnable() {
+ public void run() {
+ bindPromiseAppProgressUpdated(app);
+ }
+ };
+ if (waitUntilResume(r)) {
+ return;
+ }
+
+ if (mAppsView != null) {
+ mAppsView.updatePromiseAppProgress(app);
}
}
@@ -3792,16 +3746,12 @@ public class Launcher extends BaseActivity
* Implementation of the method from LauncherModel.Callbacks.
*
* @param updated list of shortcuts which have changed.
- * @param removed list of shortcuts which were deleted in the background. This can happen when
- * an app gets removed from the system or some of its components are no longer
- * available.
*/
@Override
- public void bindShortcutsChanged(final ArrayList updated,
- final ArrayList removed, final UserHandle user) {
+ public void bindShortcutsChanged(final ArrayList updated, final UserHandle user) {
Runnable r = new Runnable() {
public void run() {
- bindShortcutsChanged(updated, removed, user);
+ bindShortcutsChanged(updated, user);
}
};
if (waitUntilResume(r)) {
@@ -3811,31 +3761,6 @@ public class Launcher extends BaseActivity
if (!updated.isEmpty()) {
mWorkspace.updateShortcuts(updated);
}
-
- if (!removed.isEmpty()) {
- HashSet removedComponents = new HashSet<>();
- HashSet removedDeepShortcuts = new HashSet<>();
-
- for (ShortcutInfo si : removed) {
- if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- removedDeepShortcuts.add(ShortcutKey.fromItemInfo(si));
- } else {
- removedComponents.add(si.getTargetComponent());
- }
- }
-
- if (!removedComponents.isEmpty()) {
- ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(removedComponents, user);
- mWorkspace.removeItemsByMatcher(matcher);
- mDragController.onAppsRemoved(matcher);
- }
-
- if (!removedDeepShortcuts.isEmpty()) {
- ItemInfoMatcher matcher = ItemInfoMatcher.ofShortcutKeys(removedDeepShortcuts);
- mWorkspace.removeItemsByMatcher(matcher);
- mDragController.onAppsRemoved(matcher);
- }
- }
}
/**
@@ -3865,28 +3790,17 @@ public class Launcher extends BaseActivity
* package-removal should clear all items by package name.
*/
@Override
- public void bindWorkspaceComponentsRemoved(
- final HashSet packageNames, final HashSet components,
- final UserHandle user) {
+ public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
Runnable r = new Runnable() {
public void run() {
- bindWorkspaceComponentsRemoved(packageNames, components, user);
+ bindWorkspaceComponentsRemoved(matcher);
}
};
if (waitUntilResume(r)) {
return;
}
- if (!packageNames.isEmpty()) {
- ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageNames, user);
- mWorkspace.removeItemsByMatcher(matcher);
- mDragController.onAppsRemoved(matcher);
-
- }
- if (!components.isEmpty()) {
- ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(components, user);
- mWorkspace.removeItemsByMatcher(matcher);
- mDragController.onAppsRemoved(matcher);
- }
+ mWorkspace.removeItemsByMatcher(matcher);
+ mDragController.onAppsRemoved(matcher);
}
@Override
@@ -3907,22 +3821,25 @@ public class Launcher extends BaseActivity
}
}
- private Runnable mBindAllWidgetsRunnable = new Runnable() {
+ @Override
+ public void bindAllWidgets(final MultiHashMap allWidgets) {
+ Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
+ @Override
public void run() {
- bindAllWidgets(mAllWidgets);
+ bindAllWidgets(allWidgets);
}
};
-
- @Override
- public void bindAllWidgets(MultiHashMap allWidgets) {
- if (waitUntilResume(mBindAllWidgetsRunnable, true)) {
- mAllWidgets = allWidgets;
+ if (waitUntilResume(r)) {
return;
}
if (mWidgetsView != null && allWidgets != null) {
+ Executor pendingExecutor = getPendingExecutor();
+ if (pendingExecutor != null && mState != State.WIDGETS) {
+ pendingExecutor.execute(r);
+ return;
+ }
mWidgetsView.setWidgets(allWidgets);
- mAllWidgets = null;
}
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -3947,7 +3864,7 @@ public class Launcher extends BaseActivity
* refreshes the widgets and shortcuts associated with the given package/user
*/
public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) {
- mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty(), packageUser);
+ mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
}
public void lockScreenOrientation() {
@@ -3978,19 +3895,8 @@ public class Launcher extends BaseActivity
}
private boolean shouldShowDiscoveryBounce() {
- if (mState != mState.WORKSPACE) {
- return false;
- }
- if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
- return true;
- }
- if (!mIsResumeFromActionScreenOff) {
- return false;
- }
- if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
- return false;
- }
- return true;
+ UserManagerCompat um = UserManagerCompat.getInstance(this);
+ return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) && !um.isDemoUser();
}
protected void moveWorkspaceToDefaultScreen() {
@@ -4079,7 +3985,7 @@ public class Launcher extends BaseActivity
switch (keyCode) {
case KeyEvent.KEYCODE_A:
if (mState == State.WORKSPACE) {
- showAppsView(true, true, false);
+ showAppsView(true, true);
return true;
}
break;
@@ -4126,9 +4032,8 @@ public class Launcher extends BaseActivity
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
- // Finish this instance of the activity. When the activity is recreated,
- // it will initialize the rotation preference again.
- finish();
+ // Recreate the activity so that it initializes the rotation preference again.
+ recreate();
}
}
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index aa7f5ee5fe..cfb9b570b0 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -21,11 +21,10 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
+import android.graphics.drawable.Drawable;
import android.util.Property;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.widget.ViewAnimator;
import java.util.HashSet;
import java.util.WeakHashMap;
@@ -130,4 +129,16 @@ public class LauncherAnimUtils {
return anim;
}
+ public static final Property DRAWABLE_ALPHA =
+ new Property(Integer.TYPE, "drawableAlpha") {
+ @Override
+ public Integer get(Drawable drawable) {
+ return drawable.getAlpha();
+ }
+
+ @Override
+ public void set(Drawable drawable, Integer alpha) {
+ drawable.setAlpha(alpha);
+ }
+ };
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 180c202fa4..1ffe41bc6c 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
@@ -26,18 +27,22 @@ import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.SettingsObserver;
import com.android.launcher3.util.TestingUtils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
+import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+
public class LauncherAppState {
- public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
+ public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD;
// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
@@ -47,7 +52,7 @@ public class LauncherAppState {
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
-
+ private final SettingsObserver mNotificationBadgingObserver;
public static LauncherAppState getInstance(final Context context) {
if (INSTANCE == null) {
@@ -93,9 +98,7 @@ public class LauncherAppState {
mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
-
- mModel = new LauncherModel(this, mIconCache,
- Utilities.getOverrideObject(AppFilter.class, mContext, R.string.app_filter_class));
+ mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
@@ -119,6 +122,23 @@ public class LauncherAppState {
new ConfigMonitor(mContext).register();
ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
+
+ if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
+ mNotificationBadgingObserver = null;
+ } else {
+ // Register an observer to rebind the notification listener when badging is re-enabled.
+ mNotificationBadgingObserver = new SettingsObserver.Secure(
+ mContext.getContentResolver()) {
+ @Override
+ public void onSettingChanged(boolean isNotificationBadgingEnabled) {
+ if (isNotificationBadgingEnabled) {
+ NotificationListener.requestRebind(new ComponentName(
+ mContext, NotificationListener.class));
+ }
+ }
+ };
+ mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+ }
}
/**
@@ -129,6 +149,9 @@ public class LauncherAppState {
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
launcherApps.removeOnAppsChangedCallback(mModel);
PackageInstallerCompat.getInstance(mContext).onStop();
+ if (mNotificationBadgingObserver != null) {
+ mNotificationBadgingObserver.unregister();
+ }
}
LauncherModel setLauncher(Launcher launcher) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 6e8c59b66c..5573c5c151 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -16,12 +16,20 @@
package com.android.launcher3;
+import android.app.Activity;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
import android.util.SparseArray;
import android.view.LayoutInflater;
+import android.widget.Toast;
+
+import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
@@ -33,14 +41,16 @@ import java.util.ArrayList;
*/
public class LauncherAppWidgetHost extends AppWidgetHost {
- private final ArrayList mProviderChangeListeners = new ArrayList();
+ public static final int APPWIDGET_HOST_ID = 1024;
+
+ private final ArrayList mProviderChangeListeners = new ArrayList<>();
private final SparseArray mViews = new SparseArray<>();
- private Launcher mLauncher;
+ private final Context mContext;
- public LauncherAppWidgetHost(Launcher launcher, int hostId) {
- super(launcher, hostId);
- mLauncher = launcher;
+ public LauncherAppWidgetHost(Context context) {
+ super(context, APPWIDGET_HOST_ID);
+ mContext = context;
}
@Override
@@ -53,6 +63,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
@Override
public void startListening() {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return;
+ }
+
try {
super.startListening();
} catch (Exception e) {
@@ -66,24 +80,38 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
}
}
- public void addProviderChangeListener(Runnable callback) {
+ @Override
+ public void stopListening() {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return;
+ }
+
+ super.stopListening();
+ }
+
+ @Override
+ public int allocateAppWidgetId() {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ return AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+
+ return super.allocateAppWidgetId();
+ }
+
+ public void addProviderChangeListener(ProviderChangedListener callback) {
mProviderChangeListeners.add(callback);
}
- public void removeProviderChangeListener(Runnable callback) {
+ public void removeProviderChangeListener(ProviderChangedListener callback) {
mProviderChangeListeners.remove(callback);
}
protected void onProvidersChanged() {
if (!mProviderChangeListeners.isEmpty()) {
- for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) {
- callback.run();
+ for (ProviderChangedListener callback : new ArrayList<>(mProviderChangeListeners)) {
+ callback.notifyWidgetProvidersChanged();
}
}
-
- if (Utilities.ATLEAST_MARSHMALLOW) {
- mLauncher.notifyWidgetProvidersChanged();
- }
}
public AppWidgetHostView createView(Context context, int appWidgetId,
@@ -109,7 +137,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
// will update.
LauncherAppWidgetHostView view = mViews.get(appWidgetId);
if (view == null) {
- view = onCreateView(mLauncher, appWidgetId, appWidget);
+ view = onCreateView(mContext, appWidgetId, appWidget);
}
view.setAppWidget(appWidgetId, appWidget);
view.switchToErrorView();
@@ -124,11 +152,11 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
@Override
protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
- mLauncher, appWidget);
+ mContext, appWidget);
super.onProviderChanged(appWidgetId, info);
// The super method updates the dimensions of the providerInfo. Update the
// launcher spans accordingly.
- info.initSpans(mLauncher);
+ info.initSpans(mContext);
}
@Override
@@ -142,4 +170,53 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
super.clearViews();
mViews.clear();
}
+
+ public void startBindFlow(BaseActivity activity,
+ int appWidgetId, AppWidgetProviderInfo info, int requestCode) {
+
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ sendActionCancelled(activity, requestCode);
+ return;
+ }
+
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider)
+ .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.getProfile());
+ // TODO: we need to make sure that this accounts for the options bundle.
+ // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+
+ public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) {
+ if (FeatureFlags.GO_DISABLE_WIDGETS) {
+ sendActionCancelled(activity, requestCode);
+ return;
+ }
+
+ try {
+ startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
+ } catch (ActivityNotFoundException | SecurityException e) {
+ Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ sendActionCancelled(activity, requestCode);
+ }
+ }
+
+ private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ activity.onActivityResult(requestCode, Activity.RESULT_CANCELED, null);
+ }
+ });
+ }
+
+ /**
+ * Listener for getting notifications on provider changes.
+ */
+ public interface ProviderChangedListener {
+
+ void notifyWidgetProvidersChanged();
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 13cc7ba077..7fe1308764 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -23,7 +23,6 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
-import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -40,9 +39,7 @@ import android.widget.RemoteViews;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
-import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
/**
* {@inheritDoc}
@@ -50,8 +47,6 @@ import java.util.concurrent.Executor;
public class LauncherAppWidgetHostView extends AppWidgetHostView
implements TouchCompleteListener, View.OnLongClickListener {
- private static final String TAG = "LauncherWidgetHostView";
-
// Related to the auto-advancing of widgets
private static final long ADVANCE_INTERVAL = 20000;
private static final long ADVANCE_STAGGER = 250;
@@ -97,14 +92,8 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView
setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
- if (Utilities.isAtLeastO()) {
- try {
- Method asyncMethod = AppWidgetHostView.class
- .getMethod("setExecutor", Executor.class);
- asyncMethod.invoke(this, Utilities.THREAD_POOL_EXECUTOR);
- } catch (Exception e) {
- Log.e(TAG, "Unable to set async executor", e);
- }
+ if (Utilities.ATLEAST_OREO) {
+ setExecutor(Utilities.THREAD_POOL_EXECUTOR);
}
}
@@ -153,9 +142,8 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView
return false;
}
- public boolean isReinflateRequired() {
+ public boolean isReinflateRequired(int orientation) {
// Re-inflate is required if the orientation has changed since last inflated.
- int orientation = mContext.getResources().getConfiguration().orientation;
if (mPreviousOrientation != orientation) {
return true;
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 1e0f28546b..6f23e56b34 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.os.Process;
+import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ContentWriter;
/**
@@ -95,6 +96,11 @@ public class LauncherAppWidgetInfo extends ItemInfo {
*/
public Intent bindOptions;
+ /**
+ * Nonnull for pending widgets. We use this to get the icon and title for the widget.
+ */
+ public PackageItemInfo pendingItemInfo;
+
private boolean mHasNotifiedInitialWidgetSizeChanged;
public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 2bac11f975..66da046ef8 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -17,14 +17,11 @@
package com.android.launcher3;
import android.content.Intent;
-import android.graphics.Rect;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
-import com.android.launcher3.allapps.AllAppsSearchBarController;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,69 +41,58 @@ public interface LauncherCallbacks {
* Activity life-cycle methods. These methods are triggered after
* the code in the corresponding Launcher method is executed.
*/
- public void preOnCreate();
- public void onCreate(Bundle savedInstanceState);
- public void preOnResume();
- public void onResume();
- public void onStart();
- public void onStop();
- public void onPause();
- public void onDestroy();
- public void onSaveInstanceState(Bundle outState);
- public void onPostCreate(Bundle savedInstanceState);
- public void onNewIntent(Intent intent);
- public void onActivityResult(int requestCode, int resultCode, Intent data);
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ void preOnCreate();
+ void onCreate(Bundle savedInstanceState);
+ void preOnResume();
+ void onResume();
+ void onStart();
+ void onStop();
+ void onPause();
+ void onDestroy();
+ void onSaveInstanceState(Bundle outState);
+ void onPostCreate(Bundle savedInstanceState);
+ void onNewIntent(Intent intent);
+ void onActivityResult(int requestCode, int resultCode, Intent data);
+ void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults);
- public void onWindowFocusChanged(boolean hasFocus);
- public void onAttachedToWindow();
- public void onDetachedFromWindow();
- public boolean onPrepareOptionsMenu(Menu menu);
- public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
- public void onHomeIntent();
- public boolean handleBackPressed();
- public void onTrimMemory(int level);
+ void onWindowFocusChanged(boolean hasFocus);
+ void onAttachedToWindow();
+ void onDetachedFromWindow();
+ boolean onPrepareOptionsMenu(Menu menu);
+ void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
+ void onHomeIntent();
+ boolean handleBackPressed();
+ void onTrimMemory(int level);
/*
* Extension points for providing custom behavior on certain user interactions.
*/
- public void onLauncherProviderChange();
- public void finishBindingItems(final boolean upgradePath);
- public void bindAllApplications(ArrayList apps);
- public void onInteractionBegin();
- public void onInteractionEnd();
+ void onLauncherProviderChange();
+ void finishBindingItems(final boolean upgradePath);
+ void bindAllApplications(ArrayList apps);
+ void onInteractionBegin();
+ void onInteractionEnd();
@Deprecated
- public void onWorkspaceLockedChanged();
+ void onWorkspaceLockedChanged();
/**
* Starts a search with {@param initialQuery}. Return false if search was not started.
*/
- public boolean startSearch(
+ boolean startSearch(
String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
- public boolean hasCustomContentToLeft();
- public void populateCustomContentContainer();
- public View getQsbBar();
- public Bundle getAdditionalSearchWidgetOptions();
+ boolean hasCustomContentToLeft();
+ void populateCustomContentContainer();
+ View getQsbBar();
+ Bundle getAdditionalSearchWidgetOptions();
/*
* Extensions points for adding / replacing some other aspects of the Launcher experience.
*/
- public boolean shouldMoveToDefaultScreenOnHomeIntent();
- public boolean hasSettings();
- public AllAppsSearchBarController getAllAppsSearchBarController();
- public List getPredictedApps();
- public static final int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
+ boolean shouldMoveToDefaultScreenOnHomeIntent();
+ boolean hasSettings();
+ List> getPredictedApps();
+ int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
/** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */
- public int getSearchBarHeight();
-
- /**
- * Sets the callbacks to allow reacting the actions of search overlays of the launcher.
- *
- * @param callbacks A set of callbacks to the Launcher, is actually a LauncherSearchCallback,
- * but for implementation purposes is passed around as an object.
- */
- public void setLauncherSearchCallback(Object callbacks);
-
- public boolean shouldShowDiscoveryBounce();
+ int getSearchBarHeight();
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b2f628417e..a906b00f1f 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderOperation;
@@ -24,57 +23,41 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.LauncherActivityInfo;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
-import android.os.SystemClock;
-import android.os.Trace;
import android.os.UserHandle;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.MutableInt;
+import android.util.Pair;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.CacheDataUpdatedTask;
-import com.android.launcher3.model.ExtendedModelTask;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.model.LoaderCursor;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.PackageInstallStateChangedTask;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.PackageUpdatedTask;
-import com.android.launcher3.model.SdCardAvailableReceiver;
import com.android.launcher3.model.ShortcutsChangedTask;
import com.android.launcher3.model.UserLockStateChangedTask;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.provider.ImportDataTask;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
@@ -85,14 +68,9 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
@@ -103,21 +81,16 @@ import java.util.concurrent.Executor;
*/
public class LauncherModel extends BroadcastReceiver
implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
- static final boolean DEBUG_LOADERS = false;
private static final boolean DEBUG_RECEIVER = false;
static final String TAG = "Launcher.Model";
- private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
- private static final long INVALID_SCREEN_ID = -1L;
-
+ private final MainThreadExecutor mUiExecutor = new MainThreadExecutor();
@Thunk final LauncherAppState mApp;
@Thunk final Object mLock = new Object();
- @Thunk DeferredHandler mHandler = new DeferredHandler();
- @Thunk LoaderTask mLoaderTask;
+ @Thunk
+ LoaderTask mLoaderTask;
@Thunk boolean mIsLoaderTaskRunning;
- @Thunk boolean mHasLoaderCompletedOnce;
- @Thunk boolean mIsManagedHeuristicAppsUpdated;
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
@@ -136,33 +109,10 @@ public class LauncherModel extends BroadcastReceiver
}
}
- /**
- * Set of runnables to be called on the background thread after the workspace binding
- * is complete.
- */
- static final ArrayList mBindCompleteRunnables = new ArrayList();
-
@Thunk WeakReference mCallbacks;
// < only access in worker thread >
private final AllAppsList mBgAllAppsList;
- // Entire list of widgets.
- private final WidgetsModel mBgWidgetsModel;
-
- private boolean mHasShortcutHostPermission;
- // Runnable to check if the shortcuts permission has changed.
- private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
- @Override
- public void run() {
- if (mModelLoaded) {
- boolean hasShortcutHostPermission =
- DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
- if (hasShortcutHostPermission != mHasShortcutHostPermission) {
- forceReload();
- }
- }
- }
- };
/**
* All the static data should be accessed on the background thread, A lock should be acquired
@@ -170,39 +120,40 @@ public class LauncherModel extends BroadcastReceiver
*/
static final BgDataModel sBgDataModel = new BgDataModel();
- // only access in worker thread >
+ // Runnable to check if the shortcuts permission has changed.
+ private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mModelLoaded) {
+ boolean hasShortcutHostPermission =
+ DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
+ if (hasShortcutHostPermission != sBgDataModel.hasShortcutHostPermission) {
+ forceReload();
+ }
+ }
+ }
+ };
- private final IconCache mIconCache;
-
- private final LauncherAppsCompat mLauncherApps;
- private final UserManagerCompat mUserManager;
-
- public interface Callbacks {
+ public interface Callbacks extends LauncherAppWidgetHost.ProviderChangedListener {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
public void startBinding();
- public void bindItems(ArrayList shortcuts, int start, int end,
- boolean forceAnimateIcons);
+ public void bindItems(List shortcuts, boolean forceAnimateIcons);
public void bindScreens(ArrayList orderedScreenIds);
public void finishFirstPageBind(ViewOnDrawExecutor executor);
public void finishBindingItems();
- public void bindAppWidget(LauncherAppWidgetInfo info);
public void bindAllApplications(ArrayList apps);
+ public void bindAppsAddedOrUpdated(ArrayList apps);
public void bindAppsAdded(ArrayList newScreens,
ArrayList addNotAnimated,
- ArrayList addAnimated,
- ArrayList addedApps);
- public void bindAppsUpdated(ArrayList apps);
- public void bindShortcutsChanged(ArrayList updated,
- ArrayList removed, UserHandle user);
+ ArrayList addAnimated);
+ public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
+ public void bindShortcutsChanged(ArrayList updated, UserHandle user);
public void bindWidgetsRestored(ArrayList widgets);
public void bindRestoreItemsChange(HashSet updates);
- public void bindWorkspaceComponentsRemoved(
- HashSet packageNames, HashSet components,
- UserHandle user);
+ public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
public void bindAppInfosRemoved(ArrayList appInfos);
- public void notifyWidgetProvidersChanged();
public void bindAllWidgets(MultiHashMap widgets);
public void onPageBoundSynchronously(int page);
public void executeOnNextDraw(ViewOnDrawExecutor executor);
@@ -210,25 +161,8 @@ public class LauncherModel extends BroadcastReceiver
}
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
- Context context = app.getContext();
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
- mBgWidgetsModel = new WidgetsModel(iconCache, appFilter);
- mIconCache = iconCache;
-
- mLauncherApps = LauncherAppsCompat.getInstance(context);
- mUserManager = UserManagerCompat.getInstance(context);
- }
-
- /** Runs the specified runnable immediately if called from the main thread, otherwise it is
- * posted on the main thread handler. */
- private void runOnMainThread(Runnable r) {
- if (sWorkerThread.getThreadId() == Process.myTid()) {
- // If we are on the worker thread, post onto the main handler
- mHandler.post(r);
- } else {
- r.run();
- }
}
/** Runs the specified runnable immediately if called from the worker thread, otherwise it is
@@ -256,18 +190,11 @@ public class LauncherModel extends BroadcastReceiver
CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages));
}
- /**
- * Adds the provided items to the workspace.
- */
- public void addAndBindAddedWorkspaceItems(List workspaceApps) {
- addAndBindAddedWorkspaceItems(Provider.of(workspaceApps));
- }
-
/**
* Adds the provided items to the workspace.
*/
public void addAndBindAddedWorkspaceItems(
- Provider> appsProvider) {
+ Provider>> appsProvider) {
enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
}
@@ -380,8 +307,6 @@ public class LauncherModel extends BroadcastReceiver
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
Preconditions.assertUIThread();
- // Remove any queued UI runnables
- mHandler.cancelAll();
mCallbacks = new WeakReference<>(callbacks);
}
}
@@ -493,7 +418,7 @@ public class LauncherModel extends BroadcastReceiver
public void forceReload() {
synchronized (mLock) {
// Stop any existing loaders first, so they don't set mModelLoaded to true later
- stopLoaderLocked();
+ stopLoader();
mModelLoaded = false;
}
@@ -519,16 +444,6 @@ public class LauncherModel extends BroadcastReceiver
}
}
- /**
- * If there is already a loader task running, tell it to stop.
- */
- private void stopLoaderLocked() {
- LoaderTask oldTask = mLoaderTask;
- if (oldTask != null) {
- oldTask.stopLocked();
- }
- }
-
public boolean isCurrentCallbacks(Callbacks callbacks) {
return (mCallbacks != null && mCallbacks.get() == callbacks);
}
@@ -539,42 +454,61 @@ public class LauncherModel extends BroadcastReceiver
*/
public boolean startLoader(int synchronousBindPage) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
- InstallShortcutReceiver.enableInstallQueue();
+ InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
synchronized (mLock) {
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
final Callbacks oldCallbacks = mCallbacks.get();
// Clear any pending bind-runnables from the synchronized load process.
- runOnMainThread(new Runnable() {
- public void run() {
- oldCallbacks.clearPendingBinds();
- }
- });
+ mUiExecutor.execute(new Runnable() {
+ public void run() {
+ oldCallbacks.clearPendingBinds();
+ }
+ });
// If there is already one running, tell it to stop.
- stopLoaderLocked();
- mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
- if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
- && mModelLoaded && !mIsLoaderTaskRunning) {
- mLoaderTask.runBindSynchronousPage(synchronousBindPage);
+ stopLoader();
+ LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
+ mBgAllAppsList, synchronousBindPage, mCallbacks);
+ if (mModelLoaded && !mIsLoaderTaskRunning) {
+ // Divide the set of loaded items into those that we are binding synchronously,
+ // and everything else that is to be bound normally (asynchronously).
+ loaderResults.bindWorkspace();
+ // For now, continue posting the binding of AllApps as there are other
+ // issues that arise from that.
+ loaderResults.bindAllApps();
+ loaderResults.bindDeepShortcuts();
+ loaderResults.bindWidgets();
return true;
} else {
- sWorkerThread.setPriority(Thread.NORM_PRIORITY);
- sWorker.post(mLoaderTask);
+ startLoaderForResults(loaderResults);
}
}
}
return false;
}
+ /**
+ * If there is already a loader task running, tell it to stop.
+ */
public void stopLoader() {
synchronized (mLock) {
- if (mLoaderTask != null) {
- mLoaderTask.stopLocked();
+ LoaderTask oldTask = mLoaderTask;
+ mLoaderTask = null;
+ if (oldTask != null) {
+ oldTask.stopLocked();
}
}
}
+ public void startLoaderForResults(LoaderResults results) {
+ synchronized (mLock) {
+ stopLoader();
+ mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
+ runOnWorkerThread(mLoaderTask);
+ }
+ }
+
/**
* Loads the workspace screen ids in an ordered list.
*/
@@ -587,1230 +521,61 @@ public class LauncherModel extends BroadcastReceiver
screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
}
- /**
- * Runnable for the thread that loads the contents of the launcher:
- * - workspace icons
- * - widgets
- * - all apps icons
- * - deep shortcuts within apps
- */
- private class LoaderTask implements Runnable {
- private Context mContext;
- private int mPageToBindFirst;
-
- @Thunk boolean mIsLoadingAndBindingWorkspace;
- private boolean mStopped;
- @Thunk boolean mLoadAndBindStepFinished;
-
- LoaderTask(Context context, int pageToBindFirst) {
- mContext = context;
- mPageToBindFirst = pageToBindFirst;
- }
-
- private void waitForIdle() {
- // Wait until the either we're stopped or the other threads are done.
- // This way we don't start loading all apps until the workspace has settled
- // down.
- synchronized (LoaderTask.this) {
- final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
- mHandler.postIdle(new Runnable() {
- public void run() {
- synchronized (LoaderTask.this) {
- mLoadAndBindStepFinished = true;
- if (DEBUG_LOADERS) {
- Log.d(TAG, "done with previous binding step");
- }
- LoaderTask.this.notify();
- }
+ public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ apps.addPromiseApp(app.getContext(), sessionInfo);
+ if (!apps.added.isEmpty()) {
+ final ArrayList arrayList = new ArrayList<>(apps.added);
+ apps.added.clear();
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindAppsAddedOrUpdated(arrayList);
}
});
-
- while (!mStopped && !mLoadAndBindStepFinished) {
- try {
- // Just in case mFlushingWorkerThread changes but we aren't woken up,
- // wait no longer than 1sec at a time
- this.wait(1000);
- } catch (InterruptedException ex) {
- // Ignore
- }
- }
- if (DEBUG_LOADERS) {
- Log.d(TAG, "waited "
- + (SystemClock.uptimeMillis()-workspaceWaitTime)
- + "ms for previous step to finish binding");
}
}
- }
+ });
+ }
- void runBindSynchronousPage(int synchronousBindPage) {
- if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
- // Ensure that we have a valid page index to load synchronously
- throw new RuntimeException("Should not call runBindSynchronousPage() without " +
- "valid page index");
- }
- if (!mModelLoaded) {
- // Ensure that we don't try and bind a specified page when the pages have not been
- // loaded already (we should load everything asynchronously in that case)
- throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
- }
+ public class LoaderTransaction implements AutoCloseable {
+
+ private final LoaderTask mTask;
+
+ private LoaderTransaction(LoaderTask task) throws CancellationException {
synchronized (mLock) {
- if (mIsLoaderTaskRunning) {
- // Ensure that we are never running the background loading at this point since
- // we also touch the background collections
- throw new RuntimeException("Error! Background loading is already running");
- }
- }
-
- // XXX: Throw an exception if we are already loading (since we touch the worker thread
- // data structures, we can't allow any other thread to touch that data, but because
- // this call is synchronous, we can get away with not locking).
-
- // The LauncherModel is static in the LauncherAppState and mHandler may have queued
- // operations from the previous activity. We need to ensure that all queued operations
- // are executed before any synchronous binding work is done.
- mHandler.flush();
-
- // Divide the set of loaded items into those that we are binding synchronously, and
- // everything else that is to be bound normally (asynchronously).
- bindWorkspace(synchronousBindPage);
- // XXX: For now, continue posting the binding of AllApps as there are other issues that
- // arise from that.
- onlyBindAllApps();
-
- bindDeepShortcuts();
- }
-
- private void verifyNotStopped() throws CancellationException {
- synchronized (LoaderTask.this) {
- if (mStopped) {
- throw new CancellationException("Loader stopped");
- }
- }
- }
-
- public void run() {
- synchronized (mLock) {
- if (mStopped) {
- return;
+ if (mLoaderTask != task) {
+ throw new CancellationException("Loader already stopped");
}
+ mTask = task;
mIsLoaderTaskRunning = true;
- }
-
- try {
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
- // Set to false in bindWorkspace()
- mIsLoadingAndBindingWorkspace = true;
- loadWorkspace();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
- bindWorkspace(mPageToBindFirst);
-
- // Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
- waitForIdle();
- verifyNotStopped();
-
- // second step
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
- loadAllApps();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Update icon cache");
- updateIconCache();
-
- // Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle");
- waitForIdle();
- verifyNotStopped();
-
- // third step
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
- loadDeepShortcuts();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
- bindDeepShortcuts();
-
- // Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
- waitForIdle();
- verifyNotStopped();
-
- // fourth step
- if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
- refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */,
- null /* packageUser */);
-
- synchronized (mLock) {
- // Everything loaded bind the data.
- mModelLoaded = true;
- mHasLoaderCompletedOnce = true;
- }
- } catch (CancellationException e) {
- // Loader stopped, ignore
- } finally {
- // Clear out this reference, otherwise we end up holding it until all of the
- // callback runnables are done.
- mContext = null;
-
- synchronized (mLock) {
- // If we are still the last one to be scheduled, remove ourselves.
- if (mLoaderTask == this) {
- mLoaderTask = null;
- }
- mIsLoaderTaskRunning = false;
- }
+ mModelLoaded = false;
}
}
- public void stopLocked() {
- synchronized (LoaderTask.this) {
- mStopped = true;
- this.notify();
- }
- }
-
- /**
- * Gets the callbacks object. If we've been stopped, or if the launcher object
- * has somehow been garbage collected, return null instead. Pass in the Callbacks
- * object that was around when the deferred message was scheduled, and if there's
- * a new Callbacks object around then also return null. This will save us from
- * calling onto it with data that will be ignored.
- */
- Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
+ public void commit() {
synchronized (mLock) {
- if (mStopped) {
- return null;
- }
-
- if (mCallbacks == null) {
- return null;
- }
-
- final Callbacks callbacks = mCallbacks.get();
- if (callbacks != oldCallbacks) {
- return null;
- }
- if (callbacks == null) {
- Log.w(TAG, "no mCallbacks");
- return null;
- }
-
- return callbacks;
+ // Everything loaded bind the data.
+ mModelLoaded = true;
}
}
- private void loadWorkspace() {
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Loading Workspace");
- }
-
- final Context context = mContext;
- final ContentResolver contentResolver = context.getContentResolver();
- final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
- final boolean isSafeMode = pmHelper.isSafeMode();
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
- final DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(context);
- final boolean isSdCardReady = Utilities.isBootCompleted();
- final MultiHashMap pendingPackages = new MultiHashMap<>();
-
- boolean clearDb = false;
- try {
- ImportDataTask.performImportIfPossible(context);
- } catch (Exception e) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (!clearDb && GridSizeMigrationTask.ENABLED &&
- !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (clearDb) {
- Log.d(TAG, "loadWorkspace: resetting launcher database");
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- }
-
- Log.d(TAG, "loadWorkspace: loading default favorites");
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
-
- synchronized (sBgDataModel) {
- sBgDataModel.clear();
-
- final HashMap installingPkgs = PackageInstallerCompat
- .getInstance(mContext).updateAndGetActiveSessionCache();
- sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
-
- Map shortcutKeyToPinnedShortcuts = new HashMap<>();
- final LoaderCursor c = new LoaderCursor(contentResolver.query(
- LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
-
- HashMap widgetProvidersMap = null;
-
- try {
- final int appWidgetIdIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.APPWIDGET_ID);
- final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.APPWIDGET_PROVIDER);
- final int spanXIndex = c.getColumnIndexOrThrow
- (LauncherSettings.Favorites.SPANX);
- final int spanYIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.SPANY);
- final int rankIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.RANK);
- final int optionsIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.OPTIONS);
-
- final LongSparseArray allUsers = c.allUsers;
- final LongSparseArray quietMode = new LongSparseArray<>();
- final LongSparseArray unlockedUsers = new LongSparseArray<>();
- for (UserHandle user : mUserManager.getUserProfiles()) {
- long serialNo = mUserManager.getSerialNumberForUser(user);
- allUsers.put(serialNo, user);
- quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
-
- boolean userUnlocked = mUserManager.isUserUnlocked(user);
-
- // We can only query for shortcuts when the user is unlocked.
- if (userUnlocked) {
- List pinnedShortcuts =
- shortcutManager.queryForPinnedShortcuts(null, user);
- if (shortcutManager.wasLastCallSuccess()) {
- for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
- shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
- shortcut);
- }
- } else {
- // Shortcut manager can fail due to some race condition when the
- // lock state changes too frequently. For the purpose of the loading
- // shortcuts, consider the user is still locked.
- userUnlocked = false;
- }
- }
- unlockedUsers.put(serialNo, userUnlocked);
- }
-
- ShortcutInfo info;
- LauncherAppWidgetInfo appWidgetInfo;
- Intent intent;
- String targetPkg;
-
- while (!mStopped && c.moveToNext()) {
- try {
- if (c.user == null) {
- // User has been deleted, remove the item.
- c.markDeleted("User has been deleted");
- continue;
- }
-
- boolean allowMissingTarget = false;
- switch (c.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- intent = c.parseIntent();
- if (intent == null) {
- c.markDeleted("Invalid or null intent");
- continue;
- }
-
- int disabledState = quietMode.get(c.serialNumber) ?
- ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
- ComponentName cn = intent.getComponent();
- targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
-
- if (!Process.myUserHandle().equals(c.user)) {
- if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Legacy shortcuts are only allowed for default user");
- continue;
- } else if (c.restoreFlag != 0) {
- // Don't restore items for other profiles.
- c.markDeleted("Restore from managed profile not supported");
- continue;
- }
- }
- if (TextUtils.isEmpty(targetPkg) &&
- c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Only legacy shortcuts can have null package");
- continue;
- }
-
- // If there is no target package, its an implicit intent
- // (legacy shortcut) which is always valid
- boolean validTarget = TextUtils.isEmpty(targetPkg) ||
- launcherApps.isPackageEnabledForProfile(targetPkg, c.user);
-
- if (cn != null && validTarget) {
- // If the apk is present and the shortcut points to a specific
- // component.
-
- // If the component is already present
- if (launcherApps.isActivityEnabledForProfile(cn, c.user)) {
- // no special handling necessary for this item
- c.markRestored();
- } else {
- if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
- // We allow auto install apps to have their intent
- // updated after an install.
- intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
- if (intent != null) {
- c.restoreFlag = 0;
- c.updater().put(
- LauncherSettings.Favorites.INTENT,
- intent.toUri(0)).commit();
- cn = intent.getComponent();
- } else {
- c.markDeleted("Unable to find a launch target");
- continue;
- }
- } else {
- // The app is installed but the component is no
- // longer available.
- c.markDeleted("Invalid component removed: " + cn);
- continue;
- }
- }
- }
- // else if cn == null => can't infer much, leave it
- // else if !validPkg => could be restored icon or missing sd-card
-
- if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
- // Points to a valid app (superset of cn != null) but the apk
- // is not available.
-
- if (c.restoreFlag != 0) {
- // Package is not yet available but might be
- // installed later.
- FileLog.d(TAG, "package not yet restored: " + targetPkg);
-
- if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
- // Restore has started once.
- } else if (installingPkgs.containsKey(targetPkg)) {
- // App restore has started. Update the flag
- c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
- c.updater().commit();
- } else {
- c.markDeleted("Unrestored app removed: " + targetPkg);
- continue;
- }
- } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
- // Package is present but not available.
- disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
- // Add the icon on the workspace anyway.
- allowMissingTarget = true;
- } else if (!isSdCardReady) {
- // SdCard is not ready yet. Package might get available,
- // once it is ready.
- Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
- pendingPackages.addToList(c.user, targetPkg);
- // Add the icon on the workspace anyway.
- allowMissingTarget = true;
- } else {
- // Do not wait for external media load anymore.
- c.markDeleted("Invalid package removed: " + targetPkg);
- continue;
- }
- }
-
- if (validTarget) {
- // The shortcut points to a valid target (either no target
- // or something which is ready to be used)
- c.markRestored();
- }
-
- boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
- c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
-
- if (c.restoreFlag != 0) {
- // Already verified above that user is same as default user
- info = c.getRestoredItemInfo(intent);
- } else if (c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- info = c.getAppShortcutInfo(
- intent, allowMissingTarget, useLowResIcon);
- } else if (c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-
- ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
- if (unlockedUsers.get(c.serialNumber)) {
- ShortcutInfoCompat pinnedShortcut =
- shortcutKeyToPinnedShortcuts.get(key);
- if (pinnedShortcut == null) {
- // The shortcut is no longer valid.
- c.markDeleted("Pinned shortcut not found");
- continue;
- }
- info = new ShortcutInfo(pinnedShortcut, context);
- info.iconBitmap = LauncherIcons
- .createShortcutIcon(pinnedShortcut, context);
- if (pmHelper.isAppSuspended(
- pinnedShortcut.getPackage(), info.user)) {
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
- }
- intent = info.intent;
- } else {
- // Create a shortcut info in disabled mode for now.
- info = c.loadSimpleShortcut();
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
- }
- } else { // item type == ITEM_TYPE_SHORTCUT
- info = c.loadSimpleShortcut();
-
- // Shortcuts are only available on the primary profile
- if (!TextUtils.isEmpty(targetPkg)
- && pmHelper.isAppSuspended(targetPkg, c.user)) {
- disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
- }
-
- // App shortcuts that used to be automatically added to Launcher
- // didn't always have the correct intent flags set, so do that
- // here
- if (intent.getAction() != null &&
- intent.getCategories() != null &&
- intent.getAction().equals(Intent.ACTION_MAIN) &&
- intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- }
- }
-
- if (info != null) {
- c.applyCommonProperties(info);
-
- info.intent = intent;
- info.rank = c.getInt(rankIndex);
- info.spanX = 1;
- info.spanY = 1;
- info.isDisabled |= disabledState;
- if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
- }
-
- if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
- Integer progress = installingPkgs.get(targetPkg);
- if (progress != null) {
- info.setInstallProgress(progress);
- } else {
- info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
- }
- }
-
- c.checkAndAddItem(info, sBgDataModel);
- } else {
- throw new RuntimeException("Unexpected null ShortcutInfo");
- }
- break;
-
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
- c.applyCommonProperties(folderInfo);
-
- // Do not trim the folder label, as is was set by the user.
- folderInfo.title = c.getString(c.titleIndex);
- folderInfo.spanX = 1;
- folderInfo.spanY = 1;
- folderInfo.options = c.getInt(optionsIndex);
-
- // no special handling required for restored folders
- c.markRestored();
-
- c.checkAndAddItem(folderInfo, sBgDataModel);
- break;
-
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
- // Read all Launcher-specific widget details
- boolean customWidget = c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
-
- int appWidgetId = c.getInt(appWidgetIdIndex);
- String savedProvider = c.getString(appWidgetProviderIndex);
-
- final ComponentName component =
- ComponentName.unflattenFromString(savedProvider);
-
- final boolean isIdValid = !c.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
- final boolean wasProviderReady = !c.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
-
- if (widgetProvidersMap == null) {
- widgetProvidersMap = AppWidgetManagerCompat
- .getInstance(mContext).getAllProvidersMap();
- }
- final AppWidgetProviderInfo provider = widgetProvidersMap.get(
- new ComponentKey(
- ComponentName.unflattenFromString(savedProvider),
- c.user));
-
- final boolean isProviderReady = isValidProvider(provider);
- if (!isSafeMode && !customWidget &&
- wasProviderReady && !isProviderReady) {
- c.markDeleted(
- "Deleting widget that isn't installed anymore: "
- + provider);
- } else {
- if (isProviderReady) {
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- provider.provider);
-
- // The provider is available. So the widget is either
- // available or not available. We do not need to track
- // any future restore updates.
- int status = c.restoreFlag &
- ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- if (!wasProviderReady) {
- // If provider was not previously ready, update the
- // status and UI flag.
-
- // Id would be valid only if the widget restore broadcast was received.
- if (isIdValid) {
- status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- } else {
- status &= ~LauncherAppWidgetInfo
- .FLAG_PROVIDER_NOT_READY;
- }
- }
- appWidgetInfo.restoreStatus = status;
- } else {
- Log.v(TAG, "Widget restore pending id=" + c.id
- + " appWidgetId=" + appWidgetId
- + " status =" + c.restoreFlag);
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- component);
- appWidgetInfo.restoreStatus = c.restoreFlag;
- Integer installProgress = installingPkgs.get(component.getPackageName());
-
- if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
- // Restore has started once.
- } else if (installProgress != null) {
- // App restore has started. Update the flag
- appWidgetInfo.restoreStatus |=
- LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- } else if (!isSafeMode) {
- c.markDeleted("Unrestored widget removed: " + component);
- continue;
- }
-
- appWidgetInfo.installProgress =
- installProgress == null ? 0 : installProgress;
- }
- if (appWidgetInfo.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
- appWidgetInfo.bindOptions = c.parseIntent();
- }
-
- c.applyCommonProperties(appWidgetInfo);
- appWidgetInfo.spanX = c.getInt(spanXIndex);
- appWidgetInfo.spanY = c.getInt(spanYIndex);
- appWidgetInfo.user = c.user;
-
- if (!c.isOnWorkspaceOrHotseat()) {
- c.markDeleted("Widget found where container != " +
- "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
- continue;
- }
-
- if (!customWidget) {
- String providerName =
- appWidgetInfo.providerName.flattenToString();
- if (!providerName.equals(savedProvider) ||
- (appWidgetInfo.restoreStatus != c.restoreFlag)) {
- c.updater()
- .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
- providerName)
- .put(LauncherSettings.Favorites.RESTORED,
- appWidgetInfo.restoreStatus)
- .commit();
- }
- }
- c.checkAndAddItem(appWidgetInfo, sBgDataModel);
- }
- break;
- }
- } catch (Exception e) {
- Log.e(TAG, "Desktop items loading interrupted", e);
- }
- }
- } finally {
- Utilities.closeSilently(c);
- }
-
- // Break early if we've stopped loading
- if (mStopped) {
- sBgDataModel.clear();
- return;
- }
-
- // Remove dead items
- if (c.commitDeleted()) {
- // Remove any empty folder
- ArrayList deletedFolderIds = (ArrayList) LauncherSettings.Settings
- .call(contentResolver,
- LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
- .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
- for (long folderId : deletedFolderIds) {
- sBgDataModel.workspaceItems.remove(sBgDataModel.folders.get(folderId));
- sBgDataModel.folders.remove(folderId);
- sBgDataModel.itemsIdMap.remove(folderId);
- }
-
- // Remove any ghost widgets
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
- }
-
- // Unpin shortcuts that don't exist on the workspace.
- HashSet pendingShortcuts =
- InstallShortcutReceiver.getPendingShortcuts(context);
- for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
- MutableInt numTimesPinned = sBgDataModel.pinnedShortcutCounts.get(key);
- if ((numTimesPinned == null || numTimesPinned.value == 0)
- && !pendingShortcuts.contains(key)) {
- // Shortcut is pinned but doesn't exist on the workspace; unpin it.
- shortcutManager.unpinShortcut(key);
- }
- }
-
- // Sort all the folder items and make sure the first 3 items are high resolution.
- for (FolderInfo folder : sBgDataModel.folders) {
- Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
- int pos = 0;
- for (ShortcutInfo info : folder.contents) {
- if (info.usingLowResIcon &&
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- mIconCache.getTitleAndIcon(info, false);
- }
- pos ++;
- if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
- break;
- }
- }
- }
-
- c.commitRestoredItems();
- if (!isSdCardReady && !pendingPackages.isEmpty()) {
- context.registerReceiver(
- new SdCardAvailableReceiver(
- LauncherModel.this, mContext, pendingPackages),
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
- null,
- sWorker);
- }
-
- // Remove any empty screens
- ArrayList unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
- for (ItemInfo item: sBgDataModel.itemsIdMap) {
- long screenId = item.screenId;
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- unusedScreens.contains(screenId)) {
- unusedScreens.remove(screenId);
- }
- }
-
- // If there are any empty screens remove them, and update.
- if (unusedScreens.size() != 0) {
- sBgDataModel.workspaceScreens.removeAll(unusedScreens);
- updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
- }
- }
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
- }
-
- /** Filters the set of items who are directly or indirectly (via another container) on the
- * specified screen. */
- private void filterCurrentWorkspaceItems(long currentScreenId,
- ArrayList allWorkspaceItems,
- ArrayList currentScreenItems,
- ArrayList otherScreenItems) {
- // Purge any null ItemInfos
- Iterator iter = allWorkspaceItems.iterator();
- while (iter.hasNext()) {
- ItemInfo i = iter.next();
- if (i == null) {
- iter.remove();
- }
- }
-
- // Order the set of items by their containers first, this allows use to walk through the
- // list sequentially, build up a list of containers that are in the specified screen,
- // as well as all items in those containers.
- Set itemsOnScreen = new HashSet();
- Collections.sort(allWorkspaceItems, new Comparator() {
- @Override
- public int compare(ItemInfo lhs, ItemInfo rhs) {
- return Utilities.longCompare(lhs.container, rhs.container);
- }
- });
- for (ItemInfo info : allWorkspaceItems) {
- if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (info.screenId == currentScreenId) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- if (itemsOnScreen.contains(info.container)) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- }
- }
- }
-
- /** Filters the set of widgets which are on the specified screen. */
- private void filterCurrentAppWidgets(long currentScreenId,
- ArrayList appWidgets,
- ArrayList currentScreenWidgets,
- ArrayList otherScreenWidgets) {
-
- for (LauncherAppWidgetInfo widget : appWidgets) {
- if (widget == null) continue;
- if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- widget.screenId == currentScreenId) {
- currentScreenWidgets.add(widget);
- } else {
- otherScreenWidgets.add(widget);
- }
- }
- }
-
- /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
- * right) */
- private void sortWorkspaceItemsSpatially(ArrayList workspaceItems) {
- final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
- final int screenCols = profile.numColumns;
- final int screenCellCount = profile.numColumns * profile.numRows;
- Collections.sort(workspaceItems, new Comparator() {
- @Override
- public int compare(ItemInfo lhs, ItemInfo rhs) {
- if (lhs.container == rhs.container) {
- // Within containers, order by their spatial position in that container
- switch ((int) lhs.container) {
- case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
- long lr = (lhs.screenId * screenCellCount +
- lhs.cellY * screenCols + lhs.cellX);
- long rr = (rhs.screenId * screenCellCount +
- rhs.cellY * screenCols + rhs.cellX);
- return Utilities.longCompare(lr, rr);
- }
- case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
- // We currently use the screen id as the rank
- return Utilities.longCompare(lhs.screenId, rhs.screenId);
- }
- default:
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- throw new RuntimeException("Unexpected container type when " +
- "sorting workspace items.");
- }
- return 0;
- }
- } else {
- // Between containers, order by hotseat, desktop
- return Utilities.longCompare(lhs.container, rhs.container);
- }
- }
- });
- }
-
- private void bindWorkspaceScreens(final Callbacks oldCallbacks,
- final ArrayList orderedScreens) {
- final Runnable r = new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindScreens(orderedScreens);
- }
- }
- };
- runOnMainThread(r);
- }
-
- private void bindWorkspaceItems(final Callbacks oldCallbacks,
- final ArrayList workspaceItems,
- final ArrayList appWidgets,
- final Executor executor) {
-
- // Bind the workspace items
- int N = workspaceItems.size();
- for (int i = 0; i < N; i += ITEMS_CHUNK) {
- final int start = i;
- final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
- final Runnable r = new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindItems(workspaceItems, start, start+chunkSize,
- false);
- }
- }
- };
- executor.execute(r);
- }
-
- // Bind the widgets, one at a time
- N = appWidgets.size();
- for (int i = 0; i < N; i++) {
- final LauncherAppWidgetInfo widget = appWidgets.get(i);
- final Runnable r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindAppWidget(widget);
- }
- }
- };
- executor.execute(r);
- }
- }
-
- /**
- * Binds all loaded data to actual views on the main thread.
- */
- private void bindWorkspace(int synchronizeBindPage) {
- final long t = SystemClock.uptimeMillis();
- Runnable r;
-
- // Don't use these two variables in any of the callback runnables.
- // Otherwise we hold a reference to them.
- final Callbacks oldCallbacks = mCallbacks.get();
- if (oldCallbacks == null) {
- // This launcher has exited and nobody bothered to tell us. Just bail.
- Log.w(TAG, "LoaderTask running with no launcher");
- return;
- }
-
- // Save a copy of all the bg-thread collections
- ArrayList workspaceItems = new ArrayList<>();
- ArrayList appWidgets = new ArrayList<>();
- ArrayList orderedScreenIds = new ArrayList<>();
-
- synchronized (sBgDataModel) {
- workspaceItems.addAll(sBgDataModel.workspaceItems);
- appWidgets.addAll(sBgDataModel.appWidgets);
- orderedScreenIds.addAll(sBgDataModel.workspaceScreens);
- }
-
- final int currentScreen;
- {
- int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
- ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
- if (currScreen >= orderedScreenIds.size()) {
- // There may be no workspace screens (just hotseat items and an empty page).
- currScreen = PagedView.INVALID_RESTORE_PAGE;
- }
- currentScreen = currScreen;
- }
- final boolean validFirstPage = currentScreen >= 0;
- final long currentScreenId =
- validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
-
- // Separate the items that are on the current screen, and all the other remaining items
- ArrayList currentWorkspaceItems = new ArrayList<>();
- ArrayList otherWorkspaceItems = new ArrayList<>();
- ArrayList currentAppWidgets = new ArrayList<>();
- ArrayList otherAppWidgets = new ArrayList<>();
-
- filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
- otherWorkspaceItems);
- filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
- otherAppWidgets);
- sortWorkspaceItemsSpatially(currentWorkspaceItems);
- sortWorkspaceItemsSpatially(otherWorkspaceItems);
-
- // Tell the workspace that we're about to start binding items
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.clearPendingBinds();
- callbacks.startBinding();
- }
- }
- };
- runOnMainThread(r);
-
- bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
-
- Executor mainExecutor = new DeferredMainThreadExecutor();
- // Load items on the current page.
- bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
-
- // In case of validFirstPage, only bind the first screen, and defer binding the
- // remaining screens after first onDraw (and an optional the fade animation whichever
- // happens later).
- // This ensures that the first screen is immediately visible (eg. during rotation)
- // In case of !validFirstPage, bind all pages one after other.
- final Executor deferredExecutor =
- validFirstPage ? new ViewOnDrawExecutor(mHandler) : mainExecutor;
-
- mainExecutor.execute(new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.finishFirstPageBind(
- validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
- }
- }
- });
-
- bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
-
- // Tell the workspace that we're done binding items
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.finishBindingItems();
- }
-
- mIsLoadingAndBindingWorkspace = false;
-
- // Run all the bind complete runnables after workspace is bound.
- if (!mBindCompleteRunnables.isEmpty()) {
- synchronized (mBindCompleteRunnables) {
- for (final Runnable r : mBindCompleteRunnables) {
- runOnWorkerThread(r);
- }
- mBindCompleteRunnables.clear();
- }
- }
-
- // If we're profiling, ensure this is the last thing in the queue.
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound workspace in "
- + (SystemClock.uptimeMillis()-t) + "ms");
- }
-
- }
- };
- deferredExecutor.execute(r);
-
- if (validFirstPage) {
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- // We are loading synchronously, which means, some of the pages will be
- // bound after first draw. Inform the callbacks that page binding is
- // not complete, and schedule the remaining pages.
- if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
- callbacks.onPageBoundSynchronously(currentScreen);
- }
- callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
- }
- }
- };
- runOnMainThread(r);
- }
- }
-
- private void updateIconCache() {
- // Ignore packages which have a promise icon.
- HashSet packagesToIgnore = new HashSet<>();
- synchronized (sBgDataModel) {
- for (ItemInfo info : sBgDataModel.itemsIdMap) {
- if (info instanceof ShortcutInfo) {
- ShortcutInfo si = (ShortcutInfo) info;
- if (si.isPromise() && si.getTargetComponent() != null) {
- packagesToIgnore.add(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());
- }
- }
- }
- }
- mIconCache.updateDbIcons(packagesToIgnore);
- }
-
- private void onlyBindAllApps() {
- final Callbacks oldCallbacks = mCallbacks.get();
- if (oldCallbacks == null) {
- // This launcher has exited and nobody bothered to tell us. Just bail.
- Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
- return;
- }
-
- // shallow copy
- @SuppressWarnings("unchecked")
- final ArrayList list
- = (ArrayList) mBgAllAppsList.data.clone();
- Runnable r = new Runnable() {
- public void run() {
- final long t = SystemClock.uptimeMillis();
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindAllApplications(list);
- }
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound all " + list.size() + " apps from cache in "
- + (SystemClock.uptimeMillis() - t) + "ms");
- }
- }
- };
- runOnMainThread(r);
- }
-
- private void scheduleManagedHeuristicRunnable(final ManagedProfileHeuristic heuristic,
- final UserHandle user, final List apps) {
- if (heuristic != null) {
- // Assume the app lists now is updated.
- mIsManagedHeuristicAppsUpdated = false;
- final Runnable managedHeuristicRunnable = new Runnable() {
- @Override
- public void run() {
- if (mIsManagedHeuristicAppsUpdated) {
- // If app list is updated, we need to reschedule it otherwise old app
- // list will override everything in processUserApps().
- sWorker.post(new Runnable() {
- public void run() {
- final List updatedApps =
- mLauncherApps.getActivityList(null, user);
- scheduleManagedHeuristicRunnable(heuristic, user,
- updatedApps);
- }
- });
- } else {
- heuristic.processUserApps(apps);
- }
- }
- };
- runOnMainThread(new Runnable() {
- @Override
- public void run() {
- // Check isLoadingWorkspace on the UI thread, as it is updated on the UI
- // thread.
- if (mIsLoadingAndBindingWorkspace) {
- synchronized (mBindCompleteRunnables) {
- mBindCompleteRunnables.add(managedHeuristicRunnable);
- }
- } else {
- runOnWorkerThread(managedHeuristicRunnable);
- }
- }
- });
- }
- }
-
- private void loadAllApps() {
- final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
- final Callbacks oldCallbacks = mCallbacks.get();
- if (oldCallbacks == null) {
- // This launcher has exited and nobody bothered to tell us. Just bail.
- Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
- return;
- }
-
- final List profiles = mUserManager.getUserProfiles();
-
- // Clear the list of apps
- mBgAllAppsList.clear();
- for (UserHandle user : profiles) {
- // Query for the set of apps
- final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
- final List apps = mLauncherApps.getActivityList(null, user);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "getActivityList took "
- + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
- Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
- }
- // Fail if we don't have any apps
- // TODO: Fix this. Only fail for the current user.
- if (apps == null || apps.isEmpty()) {
- return;
- }
- boolean quietMode = mUserManager.isQuietModeEnabled(user);
- // Create the ApplicationInfos
- for (int i = 0; i < apps.size(); i++) {
- LauncherActivityInfo app = apps.get(i);
- // This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
- }
- final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
- if (heuristic != null) {
- scheduleManagedHeuristicRunnable(heuristic, user, apps);
- }
- }
- // Huh? Shouldn't this be inside the Runnable below?
- final ArrayList added = mBgAllAppsList.added;
- mBgAllAppsList.added = new ArrayList();
-
- // Post callback on main thread
- mHandler.post(new Runnable() {
- public void run() {
-
- final long bindTime = SystemClock.uptimeMillis();
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindAllApplications(added);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound " + added.size() + " apps in "
- + (SystemClock.uptimeMillis() - bindTime) + "ms");
- }
- } else {
- Log.i(TAG, "not binding apps: no Launcher activity");
- }
- }
- });
- // Cleanup any data stored for a deleted user.
- ManagedProfileHeuristic.processAllUsers(profiles, mContext);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "Icons processed in "
- + (SystemClock.uptimeMillis() - loadTime) + "ms");
- }
- }
-
- private void loadDeepShortcuts() {
- sBgDataModel.deepShortcutMap.clear();
- DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
- mHasShortcutHostPermission = shortcutManager.hasHostPermission();
- if (mHasShortcutHostPermission) {
- for (UserHandle user : mUserManager.getUserProfiles()) {
- if (mUserManager.isUserUnlocked(user)) {
- List shortcuts =
- shortcutManager.queryForAllShortcuts(user);
- sBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
- }
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ // If we are still the last one to be scheduled, remove ourselves.
+ if (mLoaderTask == mTask) {
+ mLoaderTask = null;
}
+ mIsLoaderTaskRunning = false;
}
}
}
- public void bindDeepShortcuts() {
- final MultiHashMap shortcutMapCopy =
- sBgDataModel.deepShortcutMap.clone();
- Runnable r = new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = getCallback();
- if (callbacks != null) {
- callbacks.bindDeepShortcutMap(shortcutMapCopy);
- }
- }
- };
- runOnMainThread(r);
+ public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
+ return new LoaderTransaction(task);
}
/**
@@ -1835,14 +600,8 @@ public class LauncherModel extends BroadcastReceiver
CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
}
- void enqueueModelUpdateTask(BaseModelUpdateTask task) {
- if (!mModelLoaded && mLoaderTask == null) {
- if (DEBUG_LOADERS) {
- Log.d(TAG, "enqueueModelUpdateTask Ignoring task since loader is pending=" + task);
- }
- return;
- }
- task.init(this);
+ public void enqueueModelUpdateTask(ModelUpdateTask task) {
+ task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor);
runOnWorkerThread(task);
}
@@ -1858,51 +617,14 @@ public class LauncherModel extends BroadcastReceiver
/**
* A runnable which changes/updates the data model of the launcher based on certain events.
*/
- public static abstract class BaseModelUpdateTask implements Runnable {
-
- private LauncherModel mModel;
- private DeferredHandler mUiHandler;
-
- /* package private */
- void init(LauncherModel model) {
- mModel = model;
- mUiHandler = mModel.mHandler;
- }
-
- @Override
- public void run() {
- if (!mModel.mHasLoaderCompletedOnce) {
- // Loader has not yet run.
- return;
- }
- execute(mModel.mApp, sBgDataModel, mModel.mBgAllAppsList);
- }
+ public interface ModelUpdateTask extends Runnable {
/**
- * Execute the actual task. Called on the worker thread.
+ * Called before the task is posted to initialize the internal state.
*/
- public abstract void execute(
- LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
+ void init(LauncherAppState app, LauncherModel model,
+ BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor);
- /**
- * Schedules a {@param task} to be executed on the current callbacks.
- */
- public final void scheduleCallbackTask(final CallbackTask task) {
- final Callbacks callbacks = mModel.getCallback();
- mUiHandler.post(new Runnable() {
- public void run() {
- Callbacks cb = mModel.getCallback();
- if (callbacks == cb && cb != null) {
- task.execute(callbacks);
- }
- }
- });
- }
-
- public ModelWriter getModelWriter() {
- // Updates from model task, do not deal with icon position in hotseat.
- return mModel.getWriter(false /* hasVerticalHotseat */);
- }
}
public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
@@ -1920,7 +642,7 @@ public class LauncherModel extends BroadcastReceiver
* Utility method to update a shortcut on the background thread.
*/
public void updateAndBindShortcutInfo(final Provider shortcutProvider) {
- enqueueModelUpdateTask(new ExtendedModelTask() {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
ShortcutInfo info = shortcutProvider.get();
@@ -1931,43 +653,16 @@ public class LauncherModel extends BroadcastReceiver
});
}
- private void bindWidgetsModel(final Callbacks callbacks) {
- final MultiHashMap widgets
- = mBgWidgetsModel.getWidgetsMap().clone();
- mHandler.post(new Runnable() {
+ public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.bindAllWidgets(widgets);
- }
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ dataModel.widgetsModel.update(app, packageUser);
+ bindUpdatedWidgets(dataModel);
}
});
}
- public void refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks,
- final boolean bindFirst, @Nullable final PackageUserKey packageUser) {
- runOnWorkerThread(new Runnable() {
- @Override
- public void run() {
- if (bindFirst && !mBgWidgetsModel.isEmpty()) {
- bindWidgetsModel(callbacks);
- }
- ArrayList widgets = mBgWidgetsModel.update(
- mApp.getContext(), packageUser);
- bindWidgetsModel(callbacks);
-
- // update the Widget entries inside DB on the worker thread.
- mApp.getWidgetCache().removeObsoletePreviews(widgets, packageUser);
- }
- });
- }
-
- static boolean isValidProvider(AppWidgetProviderInfo provider) {
- return (provider != null) && (provider.provider != null)
- && (provider.provider.getPackageName() != null);
- }
-
public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
@@ -1983,27 +678,14 @@ public class LauncherModel extends BroadcastReceiver
return mCallbacks != null ? mCallbacks.get() : null;
}
- /**
- * @return {@link FolderInfo} if its already loaded.
- */
- public FolderInfo findFolderById(Long folderId) {
- synchronized (sBgDataModel) {
- return sBgDataModel.folders.get(folderId);
- }
- }
-
- @Thunk class DeferredMainThreadExecutor implements Executor {
-
- @Override
- public void execute(Runnable command) {
- runOnMainThread(command);
- }
- }
-
/**
* @return the looper for the worker thread which can be used to start background tasks.
*/
public static Looper getWorkerLooper() {
return sWorkerThread.getLooper();
}
+
+ public static void setWorkerPriority(final int priority) {
+ Process.setThreadPriority(sWorkerThread.getThreadId(), priority);
+ }
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 3150d5b0e3..b31df98947 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.annotation.TargetApi;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
@@ -38,6 +39,7 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -53,43 +55,39 @@ import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.DbDowngradeHelper;
import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.NoLocaleSqliteContext;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
private static final boolean LOGD = false;
+ private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
+
/**
* Represents the schema of the database. Changes in scheme need not be backwards compatible.
*/
- private static final int SCHEMA_VERSION = 27;
- /**
- * Represents the actual data. It could include additional validations and normalizations added
- * overtime. These must be backwards compatible, else we risk breaking old devices during
- * restore or binary version downgrade.
- */
- private static final int DATA_VERSION = 3;
+ public static final int SCHEMA_VERSION = 27;
- private static final String PREF_KEY_DATA_VERISON = "provider_data_version";
-
- public static final String AUTHORITY = ProviderConfig.AUTHORITY;
+ public static final String AUTHORITY = (BuildConfig.APPLICATION_ID + ".settings").intern();
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
@@ -114,7 +112,7 @@ public class LauncherProvider extends ContentProvider {
@Override
public boolean onCreate() {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.d(TAG, "Launcher process started");
}
mListenerHandler = new Handler(mListenerWrapper);
@@ -305,8 +303,7 @@ public class LauncherProvider extends ContentProvider {
SqlArguments args = new SqlArguments(uri);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
int numValues = values.length;
for (int i = 0; i < numValues; i++) {
addModifiedTime(values[i]);
@@ -314,9 +311,7 @@ public class LauncherProvider extends ContentProvider {
return 0;
}
}
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
+ t.commit();
}
notifyListeners();
@@ -328,15 +323,11 @@ public class LauncherProvider extends ContentProvider {
public ContentProviderResult[] applyBatch(ArrayList operations)
throws OperationApplicationException {
createDbIfNotExists();
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) {
ContentProviderResult[] result = super.applyBatch(operations);
- db.setTransactionSuccessful();
+ t.commit();
reloadLauncherIfExternal();
return result;
- } finally {
- db.endTransaction();
}
}
@@ -442,31 +433,26 @@ public class LauncherProvider extends ContentProvider {
private ArrayList deleteEmptyFolders() {
ArrayList folderIds = new ArrayList<>();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
// Select folders whose id do not match any container value.
String selection = LauncherSettings.Favorites.ITEM_TYPE + " = "
+ LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND "
+ LauncherSettings.Favorites._ID + " NOT IN (SELECT " +
LauncherSettings.Favorites.CONTAINER + " FROM "
+ Favorites.TABLE_NAME + ")";
- Cursor c = db.query(Favorites.TABLE_NAME,
+ try (Cursor c = db.query(Favorites.TABLE_NAME,
new String[] {LauncherSettings.Favorites._ID},
- selection, null, null, null, null);
- while (c.moveToNext()) {
- folderIds.add(c.getLong(0));
+ selection, null, null, null, null)) {
+ LauncherDbUtils.iterateCursor(c, 0, folderIds);
}
- c.close();
if (!folderIds.isEmpty()) {
db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, folderIds), null);
}
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
folderIds.clear();
- } finally {
- db.endTransaction();
}
return folderIds;
}
@@ -566,7 +552,14 @@ public class LauncherProvider extends ContentProvider {
}
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
- int defaultLayout = LauncherAppState.getIDP(getContext()).defaultLayoutId;
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
+ int defaultLayout = idp.defaultLayoutId;
+
+ UserManagerCompat um = UserManagerCompat.getInstance(getContext());
+ if (um.isDemoUser() && idp.demoModeLayoutId != 0) {
+ defaultLayout = idp.demoModeLayoutId;
+ }
+
return new DefaultLayoutParser(getContext(), widgetHost,
mOpenHelper, getContext().getResources(), defaultLayout);
}
@@ -714,50 +707,30 @@ public class LauncherProvider extends ContentProvider {
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
- SharedPreferences prefs = mContext
- .getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0);
- int oldVersion = prefs.getInt(PREF_KEY_DATA_VERISON, 0);
- if (oldVersion != DATA_VERSION) {
- // Only run the data upgrade path for an existing db.
- if (!Utilities.getPrefs(mContext).getBoolean(EMPTY_DATABASE_CREATED, false)) {
- db.beginTransaction();
- try {
- onDataUpgrade(db, oldVersion);
- db.setTransactionSuccessful();
- } catch (Exception e) {
- Log.d(TAG, "Error updating data version, ignoring", e);
- return;
- } finally {
- db.endTransaction();
- }
- }
- prefs.edit().putInt(PREF_KEY_DATA_VERISON, DATA_VERSION).apply();
+
+ File schemaFile = mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE);
+ if (!schemaFile.exists()) {
+ handleOneTimeDataUpgrade(db);
}
+ DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext,
+ R.raw.downgrade_schema);
}
/**
- * Called when the data is updated as part of app update. It can be called multiple times
- * with old version, even though it had been run before. The changes made here must be
- * backwards compatible, else we risk breaking old devices during restore or binary
- * version downgrade.
+ * One-time data updated before support of onDowngrade was added. This update is backwards
+ * compatible and can safely be run multiple times.
+ * Note: No new logic should be added here after release, as the new logic might not get
+ * executed on an existing device.
+ * TODO: Move this to db upgrade path, once the downgrade path is released.
*/
- protected void onDataUpgrade(SQLiteDatabase db, int oldVersion) {
- switch (oldVersion) {
- case 0:
- case 1: {
- // Remove "profile extra"
- UserManagerCompat um = UserManagerCompat.getInstance(mContext);
- for (UserHandle user : um.getUserProfiles()) {
- long serial = um.getSerialNumberForUser(user);
- String sql = "update favorites set intent = replace(intent, "
- + "';l.profile=" + serial + ";', ';') where itemType = 0;";
- db.execSQL(sql);
- }
- }
- case 2:
- case 3:
- // data updated
- return;
+ protected void handleOneTimeDataUpgrade(SQLiteDatabase db) {
+ // Remove "profile extra"
+ UserManagerCompat um = UserManagerCompat.getInstance(mContext);
+ for (UserHandle user : um.getUserProfiles()) {
+ long serial = um.getSerialNumberForUser(user);
+ String sql = "update favorites set intent = replace(intent, "
+ + "';l.profile=" + serial + ";', ';') where itemType = 0;";
+ db.execSQL(sql);
}
}
@@ -774,35 +747,29 @@ public class LauncherProvider extends ContentProvider {
addWorkspacesTable(db, false);
}
case 13: {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
// Insert new column for holding widget provider name
db.execSQL("ALTER TABLE favorites " +
"ADD COLUMN appWidgetProvider TEXT;");
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
// Old version remains, which means we wipe old data
break;
- } finally {
- db.endTransaction();
}
}
case 14: {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
// Insert new column for holding update timestamp
db.execSQL("ALTER TABLE favorites " +
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
db.execSQL("ALTER TABLE workspaceScreens " +
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
// Old version remains, which means we wipe old data
break;
- } finally {
- db.endTransaction();
}
}
case 15: {
@@ -870,29 +837,25 @@ public class LauncherProvider extends ContentProvider {
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion == 28 && newVersion == 27) {
- // TODO: remove this check. This is only applicable for internal development/testing
- // and for any released version of Launcher.
- return;
+ try {
+ DbDowngradeHelper.parse(mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE))
+ .onDowngrade(db, oldVersion, newVersion);
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to downgrade from: " + oldVersion + " to " + newVersion +
+ ". Wiping databse.", e);
+ createEmptyDB(db);
}
- // This shouldn't happen -- throw our hands up in the air and start over.
- Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
- ". Wiping databse.");
- createEmptyDB(db);
}
/**
* Clears all the data for a fresh start.
*/
public void createEmptyDB(SQLiteDatabase db) {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
onCreate(db);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
+ t.commit();
}
}
@@ -900,40 +863,39 @@ public class LauncherProvider extends ContentProvider {
* Removes widgets which are registered to the Launcher's host, but are not present
* in our model.
*/
+ @TargetApi(Build.VERSION_CODES.O)
public void removeGhostWidgets(SQLiteDatabase db) {
// Get all existing widget ids.
final AppWidgetHost host = newLauncherWidgetHost();
final int[] allWidgets;
try {
- Method getter = AppWidgetHost.class.getDeclaredMethod("getAppWidgetIds");
- getter.setAccessible(true);
- allWidgets = (int[]) getter.invoke(host);
- } catch (Exception e) {
+ // Although the method was defined in O, it has existed since the beginning of time,
+ // so it might work on older platforms as well.
+ allWidgets = host.getAppWidgetIds();
+ } catch (IncompatibleClassChangeError e) {
Log.e(TAG, "getAppWidgetIds not supported", e);
return;
}
- try {
- Cursor c = db.query(Favorites.TABLE_NAME,
- new String[] {Favorites.APPWIDGET_ID },
- "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null);
- HashSet validWidgets = new HashSet<>();
+ final HashSet