diff options
Diffstat (limited to 'src/com/android')
63 files changed, 722 insertions, 274 deletions
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index 9ec26e26a..3da199635 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -95,10 +95,14 @@ public class AppInfo extends ItemInfoWithIcon { } public static Intent makeLaunchIntent(LauncherActivityInfo info) { + return makeLaunchIntent(info.getComponentName()); + } + + public static Intent makeLaunchIntent(ComponentName cn) { return new Intent(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_LAUNCHER) - .setComponent(info.getComponentName()) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + .addCategory(Intent.CATEGORY_LAUNCHER) + .setComponent(cn) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); } @Override diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 1a405f934..a486a3aa3 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -55,7 +55,6 @@ public class AppWidgetResizeFrame extends FrameLayout private final int[] mDirectionVector = new int[2]; private final int[] mLastDirectionVector = new int[2]; - private final int[] mTmpPt = new int[2]; private final IntRange mTempRange1 = new IntRange(); private final IntRange mTempRange2 = new IntRange(); @@ -344,13 +343,12 @@ public class AppWidgetResizeFrame extends FrameLayout return rect; } - /** - * This is the final step of the resize. Here we save the new widget size and position - * to LauncherModel and animate the resize frame. - */ - public void commitResize() { + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + // We are done with resizing the widget. Save the widget size & position to LauncherModel resizeWidgetIfNeeded(true); - requestLayout(); } private void onTouchUp() { diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index a63767c5d..ac842f92e 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -184,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); } @@ -481,7 +481,7 @@ 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; diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index e4a322622..632e49059 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,6 +98,7 @@ public abstract class ButtonDropTarget extends TextView @Override protected void onFinishInflate() { super.onFinishInflate(); + mText = getText(); mOriginalTextColor = getTextColors(); } @@ -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/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 975675a6f..4dcb64f01 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -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 150bc5309..dec0a92a9 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -267,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. @@ -555,9 +559,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) { @@ -693,7 +697,8 @@ 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); } diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index 0840b7015..29a1349d5 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/Hotseat.java b/src/com/android/launcher3/Hotseat.java index af3abebb4..a6d80e336 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -149,7 +149,6 @@ public class Hotseat extends FrameLayout allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener()); if (mLauncher != null) { mLauncher.setAllAppsButton(allAppsButton); - allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener()); allAppsButton.setOnClickListener(mLauncher); allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler); } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 3bcd7afb4..573e8a256 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -47,6 +47,7 @@ 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; @@ -94,6 +95,7 @@ public class IconCache { private final LauncherAppsCompat mLauncherApps; private final HashMap<ComponentKey, CacheEntry> mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); + private final InstantAppResolver mInstantAppResolver; private final int mIconDpi; @Thunk final IconDB mIconDb; @@ -106,6 +108,7 @@ public class IconCache { mPackageManager = context.getPackageManager(); mUserManager = UserManagerCompat.getInstance(mContext); mLauncherApps = LauncherAppsCompat.getInstance(mContext); + mInstantAppResolver = InstantAppResolver.newInstance(mContext); mIconDpi = inv.fillResIconDpi; mIconDb = new IconDB(context, inv.iconBitmapSize); @@ -120,7 +123,7 @@ public class IconCache { } private Drawable getFullResDefaultActivityIcon() { - return getFullResIcon(Resources.getSystem(), Utilities.isAtLeastO() ? + return getFullResIcon(Resources.getSystem(), Utilities.ATLEAST_OREO ? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon); } @@ -575,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; @@ -583,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) { @@ -619,6 +624,10 @@ 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); + 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); diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java index 687251514..4dee2b57d 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,7 +18,7 @@ public class IconProvider { } public void updateSystemStateString() { - mSystemState = Locale.getDefault().toString(); + mSystemState = Locale.getDefault().toString() + "," + Build.VERSION.SDK_INT; } public String getIconSystemState(String packageName) { diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index f088d1176..f919dd052 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -75,7 +75,7 @@ public class InfoDropTarget extends UninstallDropTarget { 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/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index c5be096a2..fa3253c67 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -138,17 +138,11 @@ public class ItemInfo { public ComponentName getTargetComponent() { Intent intent = getIntent(); - if (intent == null) { + if (intent != null) { + return intent.getComponent(); + } else { return null; } - ComponentName cn = intent.getComponent(); - if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT && cn == null) { - // Legacy shortcuts may not have a componentName but just a packageName. In that case - // create a dummy componentName instead of adding additional check everywhere. - String pkg = intent.getPackage(); - return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME); - } - return cn; } public void writeToValues(ContentWriter writer) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8492a7985..1e12b423f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -113,7 +113,6 @@ 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; @@ -121,6 +120,7 @@ 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; @@ -272,7 +272,6 @@ public class Launcher extends BaseActivity private IconCache mIconCache; private LauncherAccessibilityDelegate mAccessibilityDelegate; private final Handler mHandler = new Handler(); - private boolean mIsResumeFromActionScreenOff; private boolean mHasFocus = false; private ObjectAnimator mScrimAnimator; @@ -280,8 +279,6 @@ public class Launcher extends BaseActivity 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; @@ -1046,7 +1043,6 @@ public class Launcher extends BaseActivity if (shouldShowDiscoveryBounce()) { mAllAppsController.showDiscoveryBounce(); } - mIsResumeFromActionScreenOff = false; if (mLauncherCallbacks != null) { mLauncherCallbacks.onResume(); } @@ -1134,7 +1130,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); } } @@ -1328,7 +1324,6 @@ public class Launcher extends BaseActivity onClickWallpaperPicker(view); } }.attachTo(wallpaperButton); - wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener()); // Bind widget button actions mWidgetsButton = findViewById(R.id.widget_button); @@ -1338,7 +1333,6 @@ public class Launcher extends BaseActivity onClickAddWidgetButton(view); } }.attachTo(mWidgetsButton); - mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener()); // Bind settings actions View settingsButton = findViewById(R.id.settings_button); @@ -1350,7 +1344,6 @@ public class Launcher extends BaseActivity onClickSettingsButton(view); } }.attachTo(settingsButton); - settingsButton.setOnTouchListener(getHapticFeedbackTouchListener()); } else { settingsButton.setVisibility(View.GONE); } @@ -1416,7 +1409,7 @@ public class Launcher extends BaseActivity CellLayout layout = getCellLayout(container, screenId); ShortcutInfo info = null; - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest( this, LauncherAppsCompatVO.getPinItemRequest(data), 0); } @@ -1548,7 +1541,6 @@ 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 @@ -2424,7 +2416,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)) { @@ -2536,22 +2528,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); @@ -3068,7 +3044,7 @@ public class Launcher extends BaseActivity */ public void tryAndUpdatePredictedApps() { if (mLauncherCallbacks != null) { - List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps(); + List<ComponentKeyMapper<AppInfo>> apps = mLauncherCallbacks.getPredictedApps(); if (apps != null) { mAppsView.setPredictedApps(apps); } @@ -3910,16 +3886,7 @@ public class Launcher extends BaseActivity } private boolean shouldShowDiscoveryBounce() { - if (mState != State.WORKSPACE) { - return false; - } - if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) { - return true; - } - if (!mIsResumeFromActionScreenOff) { - return false; - } - return !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false); + return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false); } protected void moveWorkspaceToDefaultScreen() { diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index c7b778252..b65b74ea0 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -92,7 +92,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate()); setBackgroundResource(R.drawable.widget_internal_focus_bg); - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { setExecutor(Utilities.THREAD_POOL_EXECUTOR); } } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index d66b14c7d..66da046ef 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -21,7 +21,7 @@ import android.os.Bundle; import android.view.Menu; import android.view.View; -import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ComponentKeyMapper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -91,10 +91,8 @@ public interface LauncherCallbacks { */ boolean shouldMoveToDefaultScreenOnHomeIntent(); boolean hasSettings(); - List<ComponentKey> getPredictedApps(); + List<ComponentKeyMapper<AppInfo>> 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} */ int getSearchBarHeight(); - - boolean shouldShowDiscoveryBounce(); } diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java index 8caba75cd..edb7ff533 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -59,7 +59,7 @@ public class SessionCommitReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - if (!isEnabled(context) || !Utilities.isAtLeastO()) { + if (!isEnabled(context) || !Utilities.ATLEAST_OREO) { // User has decided to not add icons on homescreen. return; } @@ -92,7 +92,7 @@ public class SessionCommitReceiver extends BroadcastReceiver { } public static void applyDefaultUserPrefs(final Context context) { - if (!Utilities.isAtLeastO()) { + if (!Utilities.ATLEAST_OREO) { return; } SharedPreferences prefs = Utilities.getPrefs(context); diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index 90463725f..d40ac8f97 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -94,10 +94,12 @@ public class SettingsActivity extends Activity { ButtonPreference iconBadgingPref = (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY); - if (!Utilities.isAtLeastO()) { + if (!Utilities.ATLEAST_OREO) { getPreferenceScreen().removePreference( findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY)); getPreferenceScreen().removePreference(iconBadgingPref); + } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) { + getPreferenceScreen().removePreference(iconBadgingPref); } else { // Listen to system notification badge settings while this UI is active. mIconBadgingObserver = new IconBadgingObserver( diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index f0d9367af..adf008bf4 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -17,6 +17,7 @@ package com.android.launcher3; import android.annotation.TargetApi; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -59,6 +60,11 @@ public class ShortcutInfo extends ItemInfoWithIcon { public static final int FLAG_RESTORE_STARTED = 8; //0B1000; /** + * Web UI supported. + */ + public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000; + + /** * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}. * Upto 15 different types supported. */ @@ -188,6 +194,10 @@ public class ShortcutInfo extends ItemInfoWithIcon { return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON); } + public boolean hasPromiseIconUi() { + return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI); + } + public int getInstallProgress() { return mInstallProgress; } @@ -226,4 +236,18 @@ public class ShortcutInfo extends ItemInfoWithIcon { public boolean isDisabled() { return isDisabled != 0; } + + @Override + public ComponentName getTargetComponent() { + ComponentName cn = super.getTargetComponent(); + if (cn == null && (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT + || hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) { + // Legacy shortcuts and promise icons with web UI may not have a componentName but just + // a packageName. In that case create a dummy componentName instead of adding additional + // check everywhere. + String pkg = intent.getPackage(); + return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME); + } + return cn; + } } diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index e15cf9f50..84d6a9b34 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -11,12 +11,17 @@ import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.util.AttributeSet; +import android.util.Log; import android.widget.Toast; import com.android.launcher3.compat.LauncherAppsCompat; +import java.net.URISyntaxException; + public class UninstallDropTarget extends ButtonDropTarget { + private static final String TAG = "UninstallDropTarget"; + public UninstallDropTarget(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -99,25 +104,28 @@ public class UninstallDropTarget extends ButtonDropTarget { final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) { final ComponentName cn = getUninstallTarget(launcher, info); - final boolean isUninstallable; + boolean canUninstall; if (cn == null) { // System applications cannot be installed. For now, show a toast explaining that. // We may give them the option of disabling apps this way. Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show(); - isUninstallable = false; + canUninstall = false; } else { - Intent intent = new Intent(Intent.ACTION_DELETE, - Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - intent.putExtra(Intent.EXTRA_USER, info.user); - launcher.startActivity(intent); - isUninstallable = true; + try { + Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0) + .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) + .putExtra(Intent.EXTRA_USER, info.user); + launcher.startActivity(i); + canUninstall = true; + } catch (URISyntaxException e) { + Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info); + canUninstall = false; + } } if (callback != null) { - sendUninstallResult(launcher, isUninstallable, cn, info.user, callback); + sendUninstallResult(launcher, canUninstall, cn, info.user, callback); } - return isUninstallable; + return canUninstall; } /** diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 3aa2db000..b6876f670 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -83,15 +83,17 @@ public final class Utilities { private static final Matrix sMatrix = new Matrix(); private static final Matrix sInverseMatrix = new Matrix(); - public static boolean isAtLeastO() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; - } + public static final boolean ATLEAST_OREO_MR1 = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1; + + public static final boolean ATLEAST_OREO = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; public static final boolean ATLEAST_NOUGAT_MR1 = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1; + Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1; public static final boolean ATLEAST_NOUGAT = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; + Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; public static final boolean ATLEAST_MARSHMALLOW = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 97a87c16c..4eba5c6df 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -50,6 +50,7 @@ import com.android.launcher3.folder.Folder; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ComponentKeyMapper; import com.android.launcher3.util.PackageUserKey; import java.util.List; @@ -116,7 +117,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc /** * Sets the current set of predicted apps. */ - public void setPredictedApps(List<ComponentKey> apps) { + public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) { mApps.setPredictedApps(apps); } @@ -349,8 +350,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams(); navBarBgLp.height = insets.bottom; navBarBg.setLayoutParams(navBarBgLp); - navBarBg.setVisibility(FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS - ? View.INVISIBLE : View.VISIBLE); } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index ba4fbe061..1f60fcc73 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -448,9 +448,20 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. row = Math.abs(numTotalRows - row); } - // We manipulate the stiffness, min, and max values based on the items distance to the - // first row and the items distance to the center column to create the ^-shaped motion - // effect. + calculateSpringValues(spring, row, col); + } + + @Override + public void setDefaultValues(SpringAnimation spring) { + calculateSpringValues(spring, 0, mAppsPerRow / 2); + } + + /** + * We manipulate the stiffness, min, and max values based on the items distance to the + * first row and the items distance to the center column to create the ^-shaped motion + * effect. + */ + private void calculateSpringValues(SpringAnimation spring, int row, int col) { float rowFactor = (1 + row) * 0.5f; float colFactor = getColumnFactor(col, mAppsPerRow); diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index edfe0c15e..743b16ef1 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -7,6 +7,7 @@ import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.graphics.Color; +import android.support.animation.SpringAnimation; import android.support.v4.graphics.ColorUtils; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.MotionEvent; @@ -98,9 +99,10 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect // Used in discovery bounce animation to provide the transition without workspace changing. private boolean mIsTranslateWithoutWorkspace = false; - private AnimatorSet mDiscoBounceAnimation; + private Animator mDiscoBounceAnimation; private GradientView mGradientView; + private SpringAnimation mSearchSpring; private SpringAnimationHandler mSpringAnimationHandler; public AllAppsTransitionController(Launcher l) { @@ -226,6 +228,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect } mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */); if (hasSpringAnimationHandler()) { + mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */); // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.) mSpringAnimationHandler.animateToFinalPosition(0 /* pos */, 1 /* startValue */); } @@ -418,8 +421,8 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect cancelDiscoveryAnimation(); // assumption is that this variable is always null - mDiscoBounceAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(mLauncher, - R.anim.discovery_bounce); + mDiscoBounceAnimation = AnimatorInflater.loadAnimator(mLauncher, + R.animator.discovery_bounce); mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animator) { @@ -499,6 +502,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect public void finishPullUp() { mHotseat.setVisibility(View.INVISIBLE); if (hasSpringAnimationHandler()) { + mSpringAnimationHandler.remove(mSearchSpring); mSpringAnimationHandler.reset(); } setProgress(0f); @@ -544,6 +548,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher); mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this); mSpringAnimationHandler = mAppsView.getSpringAnimationHandler(); + mSearchSpring = mAppsView.getSearchUiManager().getSpringForFling(); } private boolean hasSpringAnimationHandler() { diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 5e7a5cac5..6bbe3ea55 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -29,6 +29,7 @@ import com.android.launcher3.discovery.AppDiscoveryAppInfo; import com.android.launcher3.discovery.AppDiscoveryItem; import com.android.launcher3.discovery.AppDiscoveryUpdateState; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ComponentKeyMapper; import com.android.launcher3.util.LabelComparator; import java.util.ArrayList; @@ -173,7 +174,7 @@ public class AlphabeticalAppsList { // The set of sections that we allow fast-scrolling to (includes non-merged sections) private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>(); // The set of predicted app component names - private final List<ComponentKey> mPredictedAppComponents = new ArrayList<>(); + private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>(); // The set of predicted apps resolved from the component names and the current set of apps private final List<AppInfo> mPredictedApps = new ArrayList<>(); private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>(); @@ -298,20 +299,20 @@ public class AlphabeticalAppsList { updateAdapterItems(); } - private List<AppInfo> processPredictedAppComponents(List<ComponentKey> components) { + private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) { if (mComponentToAppMap.isEmpty()) { // Apps have not been bound yet. return Collections.emptyList(); } List<AppInfo> predictedApps = new ArrayList<>(); - for (ComponentKey ck : components) { - AppInfo info = mComponentToAppMap.get(ck); + for (ComponentKeyMapper<AppInfo> mapper : components) { + AppInfo info = mapper.getItem(mComponentToAppMap); if (info != null) { predictedApps.add(info); } else { if (FeatureFlags.IS_DOGFOOD_BUILD) { - Log.e(TAG, "Predicted app not found: " + ck); + Log.e(TAG, "Predicted app not found: " + mapper); } } // Stop at the number of predicted apps @@ -331,7 +332,7 @@ public class AlphabeticalAppsList { * If the number of predicted apps is the same as the previous list of predicted apps, * we can optimize by swapping them in place. */ - public void setPredictedApps(List<ComponentKey> apps) { + public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) { mPredictedAppComponents.clear(); mPredictedAppComponents.addAll(apps); @@ -472,14 +473,14 @@ public class AlphabeticalAppsList { if (DEBUG_PREDICTIONS) { if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) { - mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, - Process.myUserHandle())); - mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, - Process.myUserHandle())); - mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, - Process.myUserHandle())); - mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName, - Process.myUserHandle())); + mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName, + Process.myUserHandle()))); + mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName, + Process.myUserHandle()))); + mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName, + Process.myUserHandle()))); + mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName, + Process.myUserHandle()))); } } @@ -644,8 +645,8 @@ public class AlphabeticalAppsList { return result; } - public AppInfo findApp(ComponentKey key) { - return mComponentToAppMap.get(key); + public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) { + return mapper.getItem(mComponentToAppMap); } /** diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java index c0d78508f..34230e046 100644 --- a/src/com/android/launcher3/allapps/SearchUiManager.java +++ b/src/com/android/launcher3/allapps/SearchUiManager.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.allapps; +import android.support.animation.SpringAnimation; +import android.support.annotation.NonNull; import android.view.KeyEvent; /** @@ -28,6 +30,11 @@ public interface SearchUiManager { void initialize(AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView); /** + * A {@link SpringAnimation} that will be used when the user flings. + */ + @NonNull SpringAnimation getSpringForFling(); + + /** * Notifies the search manager that the apps-list has changed and the search UI should be * updated accordingly. */ diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index d50455171..ddf6e5849 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -17,6 +17,9 @@ package com.android.launcher3.allapps.search; import android.content.Context; import android.graphics.Rect; +import android.support.animation.FloatValueHolder; +import android.support.animation.SpringAnimation; +import android.support.animation.SpringForce; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.Selection; @@ -62,6 +65,8 @@ public class AppsSearchContainerLayout extends FrameLayout private View mDivider; private HeaderElevationController mElevationController; + private SpringAnimation mSpring; + public AppsSearchContainerLayout(Context context) { this(context, null); } @@ -81,6 +86,9 @@ public class AppsSearchContainerLayout extends FrameLayout mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); + + // Note: This spring does nothing. + mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0)); } @Override @@ -127,6 +135,11 @@ public class AppsSearchContainerLayout extends FrameLayout } @Override + public @NonNull SpringAnimation getSpringForFling() { + return mSpring; + } + + @Override public void refreshSearchResult() { mSearchBarController.refreshSearchResult(); } diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java index 3e58adc3f..eec3a48ee 100644 --- a/src/com/android/launcher3/anim/SpringAnimationHandler.java +++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java @@ -70,6 +70,20 @@ public class SpringAnimationHandler<T> { } /** + * Adds a spring to the list of springs handled by this class. + * @param spring The new spring to be added. + * @param setDefaultValues If True, sets the spring to the default + * {@link AnimationFactory} values. + */ + public void add(SpringAnimation spring, boolean setDefaultValues) { + if (setDefaultValues) { + mAnimationFactory.setDefaultValues(spring); + } + spring.setStartVelocity(mCurrentVelocity); + mAnimations.add(spring); + } + + /** * Adds a new or recycled animation to the list of springs handled by this class. * * @param view The view the spring is attached to. @@ -82,15 +96,17 @@ public class SpringAnimationHandler<T> { view.setTag(R.id.spring_animation_tag, spring); } mAnimationFactory.update(spring, object); - spring.setStartVelocity(mCurrentVelocity); - mAnimations.add(spring); + add(spring, false /* setDefaultValues */); } /** * Stops and removes the spring attached to {@param view}. */ public void remove(View view) { - SpringAnimation animation = (SpringAnimation) view.getTag(R.id.spring_animation_tag); + remove((SpringAnimation) view.getTag(R.id.spring_animation_tag)); + } + + public void remove(SpringAnimation animation) { if (animation.canSkipToEnd()) { animation.skipToEnd(); } @@ -226,6 +242,11 @@ public class SpringAnimationHandler<T> { * Updates the value of {@param spring} based on {@param object}. */ void update(SpringAnimation spring, T object); + + /** + * Sets the factory default values for the given {@param spring}. + */ + void setDefaultValues(SpringAnimation spring); } /** diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java index 4e00eae9d..a77a87f2c 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -40,7 +40,7 @@ public abstract class AppWidgetManagerCompat { public static AppWidgetManagerCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { sInstance = new AppWidgetManagerCompatVO(context.getApplicationContext()); } else { sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext()); diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java index 75a2a5d18..2cac536f6 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompat.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java @@ -53,7 +53,7 @@ public abstract class LauncherAppsCompat { public static LauncherAppsCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { sInstance = new LauncherAppsCompatVO(context.getApplicationContext()); } else { sInstance = new LauncherAppsCompatVL(context.getApplicationContext()); diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java index cbcabdf9b..00258c7da 100644 --- a/src/com/android/launcher3/compat/WallpaperManagerCompat.java +++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java @@ -31,10 +31,10 @@ public abstract class WallpaperManagerCompat { if (sInstance == null) { context = context.getApplicationContext(); - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { try { sInstance = new WallpaperManagerCompatVOMR1(context); - } catch (Exception e) { + } catch (Throwable e) { // The wallpaper APIs do not yet exist } } diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java index fe5ff2a8c..524f266fc 100644 --- a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java +++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java @@ -16,96 +16,70 @@ package com.android.launcher3.compat; import android.annotation.TargetApi; +import android.app.WallpaperColors; import android.app.WallpaperManager; +import android.app.WallpaperManager.OnColorsChangedListener; import android.content.Context; import android.graphics.Color; -import android.os.Build; -import android.os.Handler; import android.support.annotation.Nullable; import android.util.Log; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -@TargetApi(Build.VERSION_CODES.O) + +@TargetApi(27) public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat { private static final String TAG = "WMCompatVOMR1"; private final WallpaperManager mWm; + private Method mWCColorHintsMethod; - private final Class mOCLClass; - private final Method mAddOCLMethod; - - private final Method mWCGetMethod; - private final Method mWCGetPrimaryColorMethod; - private final Method mWCGetSecondaryColorMethod; - private final Method mWCGetTertiaryColorMethod; - private final Method mWCColorHintsMethod; - - WallpaperManagerCompatVOMR1(Context context) throws Exception { + WallpaperManagerCompatVOMR1(Context context) throws Throwable { mWm = context.getSystemService(WallpaperManager.class); - - mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener"); - mAddOCLMethod = WallpaperManager.class.getDeclaredMethod( - "addOnColorsChangedListener", mOCLClass, Handler.class); - mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class); - Class wallpaperColorsClass = mWCGetMethod.getReturnType(); - mWCGetPrimaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getPrimaryColor"); - mWCGetSecondaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getSecondaryColor"); - mWCGetTertiaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getTertiaryColor"); - mWCColorHintsMethod = wallpaperColorsClass.getDeclaredMethod("getColorHints"); + String className = WallpaperColors.class.getName(); + try { + mWCColorHintsMethod = WallpaperColors.class.getDeclaredMethod("getColorHints"); + } catch (Exception exc) { + Log.e(TAG, "getColorHints not available", exc); + } } @Nullable @Override public WallpaperColorsCompat getWallpaperColors(int which) { - try { - return convertColorsObject(mWCGetMethod.invoke(mWm, which)); - } catch (Exception e) { - Log.e(TAG, "Error calling wallpaper API", e); - return null; - } + return convertColorsObject(mWm.getWallpaperColors(which)); } @Override public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) { - Object onChangeListener = Proxy.newProxyInstance( - WallpaperManager.class.getClassLoader(), - new Class[]{mOCLClass}, - new InvocationHandler() { - @Override - public Object invoke(Object o, Method method, Object[] objects) - throws Throwable { - String methodName = method.getName(); - if ("onColorsChanged".equals(methodName)) { - listener.onColorsChanged( - convertColorsObject(objects[0]), (Integer) objects[1]); - } else if ("toString".equals(methodName)) { - return listener.toString(); - } - return null; - } - }); - try { - mAddOCLMethod.invoke(mWm, onChangeListener, null); - } catch (Exception e) { - Log.e(TAG, "Error calling wallpaper API", e); - } + OnColorsChangedListener onChangeListener = new OnColorsChangedListener() { + @Override + public void onColorsChanged(WallpaperColors colors, int which) { + listener.onColorsChanged(convertColorsObject(colors), which); + } + }; + mWm.addOnColorsChangedListener(onChangeListener, null); } - private WallpaperColorsCompat convertColorsObject(Object colors) throws Exception { + private WallpaperColorsCompat convertColorsObject(WallpaperColors colors) { if (colors == null) { return null; } - Color primary = (Color) mWCGetPrimaryColorMethod.invoke(colors); - Color secondary = (Color) mWCGetSecondaryColorMethod.invoke(colors); - Color tertiary = (Color) mWCGetTertiaryColorMethod.invoke(colors); + Color primary = colors.getPrimaryColor(); + Color secondary = colors.getSecondaryColor(); + Color tertiary = colors.getTertiaryColor(); int primaryVal = primary != null ? primary.toArgb() : 0; int secondaryVal = secondary != null ? secondary.toArgb() : 0; int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0; - int colorHints = (Integer) mWCColorHintsMethod.invoke(colors); + int colorHints = 0; + try { + if (mWCColorHintsMethod != null) { + colorHints = (Integer) mWCColorHintsMethod.invoke(colors); + } + } catch (Exception exc) { + Log.e(TAG, "error calling color hints", exc); + } return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints); } } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 456562e2b..6a4cbcbf0 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -56,8 +56,6 @@ abstract class BaseFlags { public static final boolean PULLDOWN_SEARCH = false; // When enabled the status bar may show dark icons based on the top of the wallpaper. public static final boolean LIGHT_STATUS_BAR = false; - // When enabled icons are badged with the number of notifications associated with that app. - public static final boolean BADGE_ICONS = true; // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}. public static final boolean LEGACY_ICON_TREATMENT = true; // When enabled, adaptive icons would have shadows baked when being stored to icon cache. diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index ee6a0e0b8..fde7995ce 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -544,7 +544,6 @@ public class DragLayer extends InsettableFrameLayout { public void clearResizeFrame() { if (mCurrentResizeFrame != null) { - mCurrentResizeFrame.commitResize(); removeView(mCurrentResizeFrame); mCurrentResizeFrame = null; } diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index e81e2a386..33d4fa628 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -193,7 +193,7 @@ public class DragView extends View { */ @TargetApi(Build.VERSION_CODES.O) public void setItemInfo(final ItemInfo info) { - if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.isAtLeastO())) { + if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.ATLEAST_OREO)) { return; } if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java index c8d3890ba..b9d97ac5a 100644 --- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java @@ -128,7 +128,7 @@ public class PinItemDragListener extends BaseItemDragListener implements Parcela } public static boolean handleDragRequest(Launcher launcher, Intent intent) { - if (!Utilities.isAtLeastO()) { + if (!Utilities.ATLEAST_OREO) { return false; } if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) { diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java index 52abbc766..a70a9bb1f 100644 --- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java +++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java @@ -25,7 +25,9 @@ import android.content.pm.LauncherApps.PinItemRequest; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.Process; +import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.IconCache; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; @@ -69,8 +71,12 @@ class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo { @Override public Drawable getFullResIcon(IconCache cache) { - return mContext.getSystemService(LauncherApps.class) + Drawable d = mContext.getSystemService(LauncherApps.class) .getShortcutIconDrawable(mInfo, LauncherAppState.getIDP(mContext).fillResIconDpi); + if (d == null) { + d = new FastBitmapDrawable(cache.getDefaultIcon(Process.myUserHandle())); + } + return d; } @Override diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 6533b0463..8339bc5b8 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -61,6 +61,7 @@ import com.android.launcher3.Workspace; import com.android.launcher3.badge.BadgeRenderer; import com.android.launcher3.badge.FolderBadgeInfo; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dragndrop.BaseItemDragListener; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.graphics.IconPalette; @@ -375,6 +376,9 @@ public class FolderIcon extends FrameLayout implements FolderListener { if (d.dragInfo instanceof AppInfo) { // Came from all apps -- make a copy item = ((AppInfo) d.dragInfo).makeShortcut(); + } else if (d.dragSource instanceof BaseItemDragListener){ + // Came from a different window -- make a copy + item = new ShortcutInfo((ShortcutInfo) d.dragInfo); } else { item = (ShortcutInfo) d.dragInfo; } diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java index 45344c020..371479b36 100644 --- a/src/com/android/launcher3/graphics/DrawableFactory.java +++ b/src/com/android/launcher3/graphics/DrawableFactory.java @@ -80,7 +80,7 @@ public class DrawableFactory { protected Path getPreloadProgressPath(Context context) { - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { try { // Try to load the path from Mask Icon Drawable icon = context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper); diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java index 8ed62bcdc..28fc42368 100644 --- a/src/com/android/launcher3/graphics/IconNormalizer.java +++ b/src/com/android/launcher3/graphics/IconNormalizer.java @@ -231,7 +231,7 @@ public class IconNormalizer { */ public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds, @Nullable Path path, @Nullable boolean[] outMaskShape) { - if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable && + if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable && mAdaptiveIconScale != SCALE_NOT_INITIALIZED) { if (outBounds != null) { outBounds.set(mAdaptiveIconBounds); @@ -347,7 +347,7 @@ public class IconNormalizer { float areaScale = area / (width * height); // Use sqrt of the final ratio as the images is scaled across both width and height. float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; - if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable && + if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable && mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { mAdaptiveIconScale = scale; mAdaptiveIconBounds.set(mBounds); diff --git a/src/com/android/launcher3/graphics/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java index 654fa98bc..223243b7a 100644 --- a/src/com/android/launcher3/graphics/IconShapeOverride.java +++ b/src/com/android/launcher3/graphics/IconShapeOverride.java @@ -59,7 +59,7 @@ public class IconShapeOverride { private static final int RESTART_REQUEST_CODE = 42; // the answer to everything public static boolean isSupported(Context context) { - if (!Utilities.isAtLeastO()) { + if (!Utilities.ATLEAST_OREO) { return false; } // Only supported when developer settings is enabled @@ -82,7 +82,7 @@ public class IconShapeOverride { } public static void apply(Context context) { - if (!Utilities.isAtLeastO()) { + if (!Utilities.ATLEAST_OREO) { return; } String path = getAppliedValue(context); diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java index d95567492..d55baf0f8 100644 --- a/src/com/android/launcher3/graphics/LauncherIcons.java +++ b/src/com/android/launcher3/graphics/LauncherIcons.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import android.support.annotation.Nullable; import com.android.launcher3.AppInfo; +import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; @@ -103,7 +104,7 @@ public class LauncherIcons { float scale = 1f; if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) { normalizer = IconNormalizer.getInstance(context); - if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) { + if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) { boolean[] outShape = new boolean[1]; AdaptiveIconDrawable dr = (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate(); @@ -122,7 +123,7 @@ public class LauncherIcons { } } Bitmap bitmap = createIconBitmap(icon, context, scale); - if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() && + if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap); } @@ -157,13 +158,13 @@ public class LauncherIcons { float scale = 1f; if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) { normalizer = IconNormalizer.getInstance(context); - if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) { + if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) { boolean[] outShape = new boolean[1]; AdaptiveIconDrawable dr = (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate(); dr.setBounds(0, 0, 1, 1); scale = normalizer.getScale(icon, iconBounds, dr.getIconMask(), outShape); - if (Utilities.isAtLeastO() && FeatureFlags.LEGACY_ICON_TREATMENT && + if (Utilities.ATLEAST_OREO && FeatureFlags.LEGACY_ICON_TREATMENT && !outShape[0]) { Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale); if (wrappedIcon != icon) { @@ -192,13 +193,16 @@ public class LauncherIcons { * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions. */ public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) { + return badgeWithDrawable(srcTgt, new FastBitmapDrawable(badge), context); + } + + public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) { int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size); synchronized (sCanvas) { sCanvas.setBitmap(srcTgt); - sCanvas.drawBitmap(badge, new Rect(0, 0, badge.getWidth(), badge.getHeight()), - new Rect(srcTgt.getWidth() - badgeSize, - srcTgt.getHeight() - badgeSize, srcTgt.getWidth(), srcTgt.getHeight()), - new Paint(Paint.FILTER_BITMAP_FLAG)); + int iconSize = srcTgt.getWidth(); + badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize); + badge.draw(sCanvas); sCanvas.setBitmap(null); } return srcTgt; @@ -209,12 +213,12 @@ public class LauncherIcons { */ public static Bitmap createIconBitmap(Drawable icon, Context context) { float scale = 1f; - if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() && + if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { scale = ShadowGenerator.getScaleForBounds(new RectF(0, 0, 0, 0)); } Bitmap bitmap = createIconBitmap(icon, context, scale); - if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() && + if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap); } @@ -267,7 +271,7 @@ public class LauncherIcons { final int top = (textureHeight-height) / 2; sOldBounds.set(icon.getBounds()); - if (Utilities.isAtLeastO() && icon instanceof AdaptiveIconDrawable) { + if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { int offset = Math.max((int)(ShadowGenerator.BLUR_FACTOR * iconBitmapSize), Math.min(left, top)); int size = Math.max(width, height); @@ -292,7 +296,7 @@ public class LauncherIcons { * create AdaptiveIconDrawable. */ static Drawable wrapToAdaptiveIconDrawable(Context context, Drawable drawable, float scale) { - if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.isAtLeastO())) { + if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) { return drawable; } diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java index ffcedb26a..b40bf7828 100644 --- a/src/com/android/launcher3/graphics/ShadowDrawable.java +++ b/src/com/android/launcher3/graphics/ShadowDrawable.java @@ -146,7 +146,7 @@ public class ShadowDrawable extends Drawable { d.draw(canvas); } - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false); } mState.mLastDrawnBitmap = bitmap; diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java index 7a27741c3..0139bd902 100644 --- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java +++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java @@ -88,7 +88,7 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask { case OP_CACHE_UPDATE: return true; case OP_SESSION_UPDATE: - return si.isPromise(); + return si.hasPromiseIconUi(); default: return false; } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index c56325ad5..4756edcc0 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -431,6 +431,10 @@ public class LoaderTask implements Runnable { } } + if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) { + validTarget = false; + } + if (validTarget) { // The shortcut points to a valid target (either no target // or something which is ready to be used) diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java index 1e0af6881..32dfe2537 100644 --- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java +++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java @@ -16,6 +16,9 @@ package com.android.launcher3.model; import android.content.ComponentName; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Process; import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; @@ -28,6 +31,7 @@ import com.android.launcher3.PromiseAppInfo; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; +import com.android.launcher3.util.InstantAppResolver; import java.util.ArrayList; import java.util.HashSet; @@ -46,6 +50,17 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask { @Override public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLED) { + try { + // For instant apps we do not get package-add. Use setting events to update + // any pinned icons. + ApplicationInfo ai = app.getContext() + .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0); + if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) { + app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle()); + } + } catch (PackageManager.NameNotFoundException e) { + // Ignore + } // Ignore install success events as they are handled by Package add events. return; } @@ -94,7 +109,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask { if (info instanceof ShortcutInfo) { ShortcutInfo si = (ShortcutInfo) info; ComponentName cn = si.getTargetComponent(); - if (si.isPromise() && (cn != null) + if (si.hasPromiseIconUi() && (cn != null) && mInstallInfo.packageName.equals(cn.getPackageName())) { si.setInstallProgress(mInstallInfo.progress); if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) { diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 6c78d5bfc..78ecbc621 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -23,6 +23,7 @@ import android.os.Process; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; + import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; import com.android.launcher3.IconCache; @@ -32,7 +33,6 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.LauncherModel.Callbacks; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.ShortcutInfo; @@ -46,6 +46,7 @@ import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -100,10 +101,11 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { appsList.addPackage(context, packages[i], mUser); // Automatically add homescreen icon for work profile apps for below O device. - if (!Utilities.isAtLeastO() && !Process.myUserHandle().equals(mUser)) { + if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) { SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser); } } + flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE); break; } case OP_UPDATE: @@ -170,12 +172,15 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { } } + final LongArrayMap<Boolean> removedShortcuts = new LongArrayMap<>(); + // Update shortcut infos if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) { final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>(); - final LongArrayMap<Boolean> removedShortcuts = new LongArrayMap<>(); final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>(); + // For system apps, package manager send OP_UPDATE when an app is enabled. + final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE; synchronized (dataModel) { for (ItemInfo info : dataModel.itemsIdMap) { if (info instanceof ShortcutInfo && mUser.equals(info.user)) { @@ -197,9 +202,14 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { if (cn != null && matcher.matches(si, cn)) { AppInfo appInfo = addedOrUpdatedApps.get(cn); - // For system apps, package manager send OP_UPDATE when an - // app is enabled. - if (si.isPromise() && (mOp == OP_ADD || mOp == OP_UPDATE)) { + if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) { + removedShortcuts.put(si.id, false); + if (mOp == OP_REMOVE) { + continue; + } + } + + if (si.isPromise() && isNewApkAvailable) { if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) { // Auto install icon LauncherAppsCompat launcherApps @@ -213,23 +223,23 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { appInfo = addedOrUpdatedApps.get(cn); } - if ((intent == null) || (appInfo == null)) { + if (intent != null && appInfo != null) { + si.intent = intent; + si.status = ShortcutInfo.DEFAULT; + infoUpdated = true; + } else if (si.hasPromiseIconUi()) { removedShortcuts.put(si.id, true); continue; } - si.intent = intent; } - } - - si.status = ShortcutInfo.DEFAULT; - infoUpdated = true; - if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) { - iconCache.getTitleAndIcon(si, si.usingLowResIcon); + } else { + si.status = ShortcutInfo.DEFAULT; + infoUpdated = true; } } - if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction()) - && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { + if (isNewApkAvailable && + si.itemType == Favorites.ITEM_TYPE_APPLICATION) { iconCache.getTitleAndIcon(si, si.usingLowResIcon); infoUpdated = true; } @@ -247,7 +257,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { if (infoUpdated) { getModelWriter().updateItemInDatabase(si); } - } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) { + } else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) { LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info; if (mUser.equals(widgetInfo.user) && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) @@ -308,7 +318,8 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) { ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser) - .or(ItemInfoMatcher.ofComponents(removedComponents, mUser)); + .or(ItemInfoMatcher.ofComponents(removedComponents, mUser)) + .and(ItemInfoMatcher.ofItemIds(removedShortcuts, true)); deleteAndBindComponentsRemoved(removeMatch); // Remove any queued items from the install queue @@ -335,8 +346,9 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { callbacks.notifyWidgetProvidersChanged(); } }); - } else if (Utilities.isAtLeastO() && mOp == OP_ADD) { - // Load widgets for the new package. + } else if (Utilities.ATLEAST_OREO && mOp == OP_ADD) { + // Load widgets for the new package. Changes due to app updates are handled through + // AppWidgetHost events, this is just to initialize the long-press options. for (int i = 0; i < N; i++) { dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser)); } diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java index 2455eabea..ad07d37bd 100644 --- a/src/com/android/launcher3/notification/NotificationFooterLayout.java +++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java @@ -200,7 +200,9 @@ public class NotificationFooterLayout extends FrameLayout { PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen( Launcher.getLauncher(getContext())); if (popup != null) { - Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight(), + final int newHeight = getResources().getDimensionPixelSize( + R.dimen.notification_empty_footer_height); + Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight() - newHeight, getResources().getInteger(R.integer.config_removeNotificationViewDuration)); collapseFooter.addListener(new AnimatorListenerAdapter() { @Override @@ -208,7 +210,7 @@ public class NotificationFooterLayout extends FrameLayout { ((ViewGroup) getParent()).findViewById(R.id.divider).setVisibility(GONE); // Keep view around because gutter is aligned to it, but remove height to // both hide the view and keep calculations correct for last dismissal. - getLayoutParams().height = 0; + getLayoutParams().height = newHeight; requestLayout(); } }); diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java index 1b7c87b22..6e36f4f51 100644 --- a/src/com/android/launcher3/notification/NotificationInfo.java +++ b/src/com/android/launcher3/notification/NotificationInfo.java @@ -94,6 +94,9 @@ public class NotificationInfo implements View.OnClickListener { @Override public void onClick(View view) { + if (intent == null) { + return; + } final Launcher launcher = Launcher.getLauncher(view.getContext()); Bundle activityOptions = ActivityOptions.makeClipRevealAnimation( view, 0, 0, view.getWidth(), view.getHeight()).toBundle(); diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java index 78c64d7da..ab94c32d0 100644 --- a/src/com/android/launcher3/notification/NotificationItemView.java +++ b/src/com/android/launcher3/notification/NotificationItemView.java @@ -90,9 +90,19 @@ public class NotificationItemView extends PopupItemView implements LogContainerP return mMainView; } + /** + * This method is used to calculate the height to remove when dismissing the last notification. + * We subtract the height of the footer in this case since the footer should be gone or in the + * process of being removed. + * @return The height of the entire notification item, minus the footer if it still exists. + */ public int getHeightMinusFooter() { - int footerHeight = mFooter.getParent() == null ? 0 : mFooter.getHeight(); - return getHeight() - footerHeight; + if (mFooter.getParent() == null) { + return getHeight(); + } + int excessFooterHeight = mFooter.getHeight() - getResources().getDimensionPixelSize( + R.dimen.notification_empty_footer_height); + return getHeight() - excessFooterHeight; } public Animator animateHeightRemoval(int heightToRemove, boolean shouldRemoveFromTop) { diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java index 6a7098915..91266263f 100644 --- a/src/com/android/launcher3/notification/NotificationListener.java +++ b/src/com/android/launcher3/notification/NotificationListener.java @@ -32,7 +32,6 @@ import android.util.Log; import android.util.Pair; import com.android.launcher3.LauncherModel; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.SettingsObserver; @@ -164,9 +163,6 @@ public class NotificationListener extends NotificationListenerService { } public static void setNotificationsChangedListener(NotificationsChangedListener listener) { - if (!FeatureFlags.BADGE_ICONS) { - return; - } sNotificationsChangedListener = listener; NotificationListener notificationListener = getInstanceIfConnected(); diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java index 682d5a967..911be93fc 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java @@ -48,7 +48,6 @@ public class PageIndicatorCaretLandscape extends PageIndicator { setCaretDrawable(caretDrawable); Launcher l = Launcher.getLauncher(context); - setOnTouchListener(l.getHapticFeedbackTouchListener()); setOnClickListener(l); setOnFocusChangeListener(l.mFocusHandler); } diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java index 29834d764..6281fec03 100644 --- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java +++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java @@ -141,7 +141,6 @@ public class PageIndicatorLineCaret extends PageIndicator { super.onFinishInflate(); mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle); mAllAppsHandle.setImageDrawable(getCaretDrawable()); - mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener()); mAllAppsHandle.setOnClickListener(mLauncher); mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler); mLauncher.setAllAppsButton(mAllAppsHandle); diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index c3e2d8b89..8441598cf 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -275,8 +275,9 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) { mNotificationItemView = (NotificationItemView) item; boolean notificationFooterHasIcons = numNotifications > 1; - int footerHeight = notificationFooterHasIcons ? - res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0; + int footerHeight = res.getDimensionPixelSize( + notificationFooterHasIcons ? R.dimen.notification_footer_height + : R.dimen.notification_empty_footer_height); item.findViewById(R.id.footer).getLayoutParams().height = footerHeight; if (notificationFooterHasIcons) { mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE); diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java index 5ce78dc3a..f44f5c8d9 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java @@ -66,7 +66,7 @@ public class DeepShortcutManager { public static boolean supportsShortcuts(ItemInfo info) { boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo - && ((com.android.launcher3.ShortcutInfo) info).isPromise(); + && ((com.android.launcher3.ShortcutInfo) info).hasPromiseIconUi(); return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && !info.isDisabled() && !isItemPromise; } diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index 8d4351884..5cb8c4c7d 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -11,6 +11,7 @@ import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherCallbacks; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.ComponentKeyMapper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -197,7 +198,7 @@ public class LauncherExtension extends Launcher { } @Override - public List<ComponentKey> getPredictedApps() { + public List<ComponentKeyMapper<AppInfo>> getPredictedApps() { // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS return new ArrayList<>(); } @@ -214,10 +215,5 @@ public class LauncherExtension extends Launcher { @Override public void onDetachedFromWindow() { } - - @Override - public boolean shouldShowDiscoveryBounce() { - return false; - } } } diff --git a/src/com/android/launcher3/util/ComponentKeyMapper.java b/src/com/android/launcher3/util/ComponentKeyMapper.java new file mode 100644 index 000000000..916176ac4 --- /dev/null +++ b/src/com/android/launcher3/util/ComponentKeyMapper.java @@ -0,0 +1,50 @@ +package com.android.launcher3.util; + +/** + * Copyright (C) 2017 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. + */ + +import android.support.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ComponentKeyMapper<T> { + + protected final ComponentKey mComponentKey; + + public ComponentKeyMapper(ComponentKey key) { + this.mComponentKey = key; + } + + public @Nullable T getItem(Map<ComponentKey, T> map) { + return map.get(mComponentKey); + } + + public String getPackage() { + return mComponentKey.componentName.getPackageName(); + } + + public String getComponentClass() { + return mComponentKey.componentName.getClassName(); + } + + @Override + public String toString() { + return mComponentKey.toString(); + } + +} diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java new file mode 100644 index 000000000..e60d76808 --- /dev/null +++ b/src/com/android/launcher3/util/InstantAppResolver.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.util; + +import android.content.Context; +import android.content.pm.ApplicationInfo; + +import com.android.launcher3.R; +import com.android.launcher3.Utilities; + +import java.util.Collections; +import java.util.List; + +/** + * A wrapper class to access instant app related APIs. + */ +public class InstantAppResolver { + + public static InstantAppResolver newInstance(Context context) { + return Utilities.getOverrideObject( + InstantAppResolver.class, context, R.string.instant_app_resolver_class); + } + + public boolean isInstantApp(ApplicationInfo info) { + return false; + } + + public List<ApplicationInfo> getInstantApps() { + return Collections.emptyList(); + } +} diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java index 091dd84bc..009aee775 100644 --- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java +++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java @@ -67,7 +67,7 @@ public class ManagedProfileHeuristic { return; } - if (Utilities.isAtLeastO() && !SessionCommitReceiver.isEnabled(context)) { + if (Utilities.ATLEAST_OREO && !SessionCommitReceiver.isEnabled(context)) { // Just mark the folder id preference to avoid new folder creation later. ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply(); return; diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java index d7a2625e9..edbf05a7c 100644 --- a/src/com/android/launcher3/util/SystemUiController.java +++ b/src/com/android/launcher3/util/SystemUiController.java @@ -59,7 +59,7 @@ public class SystemUiController { // Apply the state flags in priority order int newFlags = oldFlags; for (int stateFlag : mStates) { - if (Utilities.isAtLeastO()) { + if (Utilities.ATLEAST_OREO) { if ((stateFlag & FLAG_LIGHT_NAV) != 0) { newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; } else if ((stateFlag & FLAG_DARK_NAV) != 0) { diff --git a/src/com/android/launcher3/widget/WidgetListRowEntry.java b/src/com/android/launcher3/widget/WidgetListRowEntry.java index 3e89eeb9b..335b8c759 100644 --- a/src/com/android/launcher3/widget/WidgetListRowEntry.java +++ b/src/com/android/launcher3/widget/WidgetListRowEntry.java @@ -41,4 +41,8 @@ public class WidgetListRowEntry { this.widgets = items; } + @Override + public String toString() { + return pkgItem.packageName + ":" + widgets.size(); + } } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 14a9d17ed..acec3dd3b 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -17,10 +17,12 @@ package com.android.launcher3.widget; import android.content.Context; +import android.content.pm.LauncherApps; import android.graphics.Point; import android.support.v7.widget.LinearLayoutManager; import android.util.AttributeSet; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; @@ -31,8 +33,10 @@ import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.folder.Folder; import com.android.launcher3.model.PackageItemInfo; @@ -74,7 +78,11 @@ public class WidgetsContainerView extends BaseContainerView public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mLauncher = Launcher.getLauncher(context); - mAdapter = new WidgetsListAdapter(this, this, context); + LauncherAppState apps = LauncherAppState.getInstance(context); + mAdapter = new WidgetsListAdapter(context, LayoutInflater.from(context), + apps.getWidgetCache(), new AlphabeticIndexCompat(context), this, this, + new WidgetsDiffReporter(apps.getIconCache())); + mAdapter.setNotifyListener(); if (LOGD) { Log.d(TAG, "WidgetsContainerView constructor"); } @@ -232,7 +240,6 @@ public class WidgetsContainerView extends BaseContainerView */ public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> model) { mAdapter.setWidgets(model); - mAdapter.notifyDataSetChanged(); View loader = getContentView().findViewById(R.id.loader); if (loader != null) { diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java new file mode 100644 index 000000000..52deec32b --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 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.widget; + +import android.util.Log; + +import com.android.launcher3.IconCache; +import com.android.launcher3.model.PackageItemInfo; +import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * Do diff on widget's tray list items and call the {@link NotifyListener} methods accordingly. + */ +public class WidgetsDiffReporter { + private final boolean DEBUG = false; + private final String TAG = "WidgetsDiffReporter"; + private final IconCache mIconCache; + private NotifyListener mListener; + + public interface NotifyListener { + void notifyDataSetChanged(); + void notifyItemChanged(int index); + void notifyItemInserted(int index); + void notifyItemRemoved(int index); + } + + public WidgetsDiffReporter(IconCache iconCache) { + mIconCache = iconCache; + } + + public void setListener(NotifyListener listener) { + mListener = listener; + } + + public void process(ArrayList<WidgetListRowEntry> currentEntries, + ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) { + if (DEBUG) { + Log.d(TAG, "process oldEntries#=" + currentEntries.size() + + " newEntries#=" + newEntries.size()); + } + if (currentEntries.size() == 0 && newEntries.size() > 0) { + currentEntries.addAll(newEntries); + mListener.notifyDataSetChanged(); + return; + } + ArrayList<WidgetListRowEntry> orgEntries = + (ArrayList<WidgetListRowEntry>) currentEntries.clone(); + Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator(); + Iterator<WidgetListRowEntry> newIter = newEntries.iterator(); + + WidgetListRowEntry orgRowEntry = orgIter.next(); + WidgetListRowEntry newRowEntry = newIter.next(); + + do { + int diff = comparePackageName(orgRowEntry, newRowEntry, comparator); + if (DEBUG) { + Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)", + diff, orgRowEntry != null? orgRowEntry.toString() : null, + newRowEntry != null? newRowEntry.toString() : null)); + } + int index = -1; + if (diff < 0) { + index = currentEntries.indexOf(orgRowEntry); + mListener.notifyItemRemoved(index); + if (DEBUG) { + Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index, + orgRowEntry.titleSectionName)); + } + currentEntries.remove(index); + orgRowEntry = orgIter.hasNext() ? orgIter.next() : null; + } else if (diff > 0) { + index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry): + currentEntries.size(); + currentEntries.add(index, newRowEntry); + if (DEBUG) { + Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index, + newRowEntry.titleSectionName)); + } + newRowEntry = newIter.hasNext() ? newIter.next() : null; + mListener.notifyItemInserted(index); + + } else { + // same package name but, + // did the icon, title, etc, change? + // or did the widget size and desc, span, etc change? + if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) || + !orgRowEntry.widgets.equals(newRowEntry.widgets)) { + index = currentEntries.indexOf(orgRowEntry); + currentEntries.set(index, newRowEntry); + mListener.notifyItemChanged(index); + if (DEBUG) { + Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index, + newRowEntry.titleSectionName)); + } + } + orgRowEntry = orgIter.hasNext() ? orgIter.next() : null; + newRowEntry = newIter.hasNext() ? newIter.next() : null; + } + } while(orgRowEntry != null || newRowEntry != null); + } + + /** + * Compare package name using the same comparator as in {@link WidgetsListAdapter}. + * Also handle null row pointers. + */ + private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow, + WidgetListRowEntryComparator comparator) { + if (curRow == null && newRow == null) { + throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null."); + } + + if (curRow == null && newRow != null) { + return 1; // new row needs to be inserted + } else if (curRow != null && newRow == null) { + return -1; // old row needs to be deleted + } + return comparator.compare(curRow, newRow); + } + + private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) { + return curInfo.iconBitmap.equals(newInfo.iconBitmap) && + !mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user); + } +} diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index a1eb0ab12..6b1800c67 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -21,9 +21,10 @@ import android.support.v7.widget.RecyclerView.Adapter; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.compat.AlphabeticIndexCompat; @@ -55,40 +56,67 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { private final WidgetPreviewLoader mWidgetPreviewLoader; private final LayoutInflater mLayoutInflater; - - private final View.OnClickListener mIconClickListener; - private final View.OnLongClickListener mIconLongClickListener; - - private final ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>(); private final AlphabeticIndexCompat mIndexer; + private final OnClickListener mIconClickListener; + private final OnLongClickListener mIconLongClickListener; private final int mIndent; - - public WidgetsListAdapter(View.OnClickListener iconClickListener, - View.OnLongClickListener iconLongClickListener, - Context context) { - mLayoutInflater = LayoutInflater.from(context); - mWidgetPreviewLoader = LauncherAppState.getInstance(context).getWidgetCache(); - - mIndexer = new AlphabeticIndexCompat(context); - + private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>(); + private final WidgetsDiffReporter mDiffReporter; + + public WidgetsListAdapter(Context context, LayoutInflater layoutInflater, + WidgetPreviewLoader widgetPreviewLoader, AlphabeticIndexCompat indexCompat, + OnClickListener iconClickListener, OnLongClickListener iconLongClickListener, + WidgetsDiffReporter diffReporter) { + mLayoutInflater = layoutInflater; + mWidgetPreviewLoader = widgetPreviewLoader; + mIndexer = indexCompat; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent); + mDiffReporter = diffReporter; } + public void setNotifyListener() { + mDiffReporter.setListener(new WidgetsDiffReporter.NotifyListener() { + @Override + public void notifyDataSetChanged() { + WidgetsListAdapter.this.notifyDataSetChanged(); + } + + @Override + public void notifyItemChanged(int index) { + WidgetsListAdapter.this.notifyItemChanged(index); + } + + @Override + public void notifyItemInserted(int index) { + WidgetsListAdapter.this.notifyItemInserted(index); + } + + @Override + public void notifyItemRemoved(int index) { + WidgetsListAdapter.this.notifyItemRemoved(index); + } + }); + } + + /** + * Update the widget list. + */ public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) { - mEntries.clear(); - WidgetItemComparator widgetComparator = new WidgetItemComparator(); + ArrayList<WidgetListRowEntry> tempEntries = new ArrayList<>(); + WidgetItemComparator widgetComparator = new WidgetItemComparator(); for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : widgets.entrySet()) { WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue()); row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title); Collections.sort(row.widgets, widgetComparator); - mEntries.add(row); + tempEntries.add(row); } - - Collections.sort(mEntries, new WidgetListRowEntryComparator()); + WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator(); + Collections.sort(tempEntries, rowComparator); + mDiffReporter.process(mEntries, tempEntries, rowComparator); } @Override |