diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 5c4eb9d92f..dcc3b052c9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -21,6 +21,7 @@ import android.app.PendingIntent;
import android.app.Person;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherUserInfo;
@@ -156,6 +157,13 @@ public class ApiWrapper {
}
}
+ /**
+ * Checks if an activity is flagged as non-resizeable.
+ */
+ public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
+ return lai.getActivityInfo().resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ }
+
private static class NoopDrawable extends ColorDrawable {
@Override
public int getIntrinsicHeight() {
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 379cdda8db..fa7057f19d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -46,6 +46,10 @@
Save app pair
%1$s | %2$s
+
+ This pair isn\'t supported at this screen size
+
+ Unfold your device to use this pair
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index 9b85a655e0..48d0fbd485 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -17,6 +17,7 @@
package com.android.launcher3.apppairs;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
@@ -33,11 +34,13 @@ import com.android.launcher3.R;
import com.android.launcher3.Reorderable;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.views.ActivityContext;
import java.util.Collections;
import java.util.Comparator;
+import java.util.function.Predicate;
/**
* A {@link android.widget.FrameLayout} used to represent an app pair icon on the workspace.
@@ -48,6 +51,11 @@ import java.util.Comparator;
public class AppPairIcon extends FrameLayout implements DraggableView, Reorderable {
private static final String TAG = "AppPairIcon";
+ /**
+ * Indicates that the app pair is currently launchable on the current screen.
+ */
+ private boolean mIsLaunchableAtScreenSize = true;
+
// A view that holds the app pair icon graphic.
private AppPairIconGraphic mIconGraphic;
// A view that holds the app pair's title.
@@ -86,6 +94,13 @@ public class AppPairIcon extends FrameLayout implements DraggableView, Reorderab
icon.setOnClickListener(activity.getItemOnClickListener());
icon.mInfo = appPairInfo;
+ if (icon.mInfo.contents.size() != 2) {
+ Log.wtf(TAG, "AppPair contents not 2, size: " + icon.mInfo.contents.size());
+ return icon;
+ }
+
+ icon.checkScreenSize();
+
// Set up icon drawable area
icon.mIconGraphic = icon.findViewById(R.id.app_pair_icon_graphic);
icon.mIconGraphic.init(activity.getDeviceProfile(), icon);
@@ -109,11 +124,6 @@ public class AppPairIcon extends FrameLayout implements DraggableView, Reorderab
* Returns a formatted accessibility title for app pairs.
*/
public String getAccessibilityTitle(FolderInfo appPairInfo) {
- if (appPairInfo.contents.size() != 2) {
- Log.wtf(TAG, "AppPair contents not 2, size: " + appPairInfo.contents.size());
- return "";
- }
-
CharSequence app1 = appPairInfo.contents.get(0).title;
CharSequence app2 = appPairInfo.contents.get(1).title;
return getContext().getString(R.string.app_pair_name_format, app1, app2);
@@ -167,4 +177,40 @@ public class AppPairIcon extends FrameLayout implements DraggableView, Reorderab
public View getIconDrawableArea() {
return mIconGraphic;
}
+
+ public boolean isLaunchableAtScreenSize() {
+ return mIsLaunchableAtScreenSize;
+ }
+
+ /**
+ * Checks if the app pair is launchable in the current device configuration.
+ *
+ * App pairs can be "disabled" in two ways:
+ * 1) One of the member WorkspaceItemInfos is disabled (i.e. the app software itself is paused
+ * by the user or can't be launched).
+ * 2) This specific instance of an app pair can't be launched due to screen size requirements.
+ *
+ * This method checks and updates #2. Both #1 and #2 are checked when app pairs are drawn
+ * {@link AppPairIconGraphic#dispatchDraw(Canvas)} or clicked on
+ * {@link com.android.launcher3.touch.ItemClickHandler#onClickAppPairIcon(View)}
+ */
+ public void checkScreenSize() {
+ DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
+ // If user is on a small screen, we can't launch if either of the apps is non-resizeable
+ mIsLaunchableAtScreenSize =
+ dp.isTablet || getInfo().contents.stream().noneMatch(
+ wii -> wii.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE));
+ }
+
+ /**
+ * Called when WorkspaceItemInfos get updated, and the app pair icon may need to be redrawn.
+ */
+ public void maybeRedrawForWorkspaceUpdate(Predicate itemCheck) {
+ // If either of the app pair icons return true on the predicate (i.e. in the list of
+ // updated apps), redraw the icon graphic (icon background and both icons).
+ if (getInfo().contents.stream().anyMatch(itemCheck)) {
+ checkScreenSize();
+ mIconGraphic.invalidate();
+ }
+ }
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconBackground.java b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
index 4e60ece170..b5011f126c 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconBackground.java
+++ b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
@@ -157,7 +157,7 @@ class AppPairIconBackground extends Drawable {
@Override
public void setAlpha(int i) {
- // Required by Drawable but not used.
+ mBackgroundPaint.setAlpha(i);
}
@Override
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index ab5ba5499b..365edf8125 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -46,6 +46,9 @@ class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: Attr
private const val CENTER_CHANNEL_SCALE = 1 / 30f
private const val BIG_RADIUS_SCALE = 1 / 5f
private const val SMALL_RADIUS_SCALE = 1 / 15f
+ // Disabled alpha is 38%, or 97/255
+ private const val DISABLED_ALPHA = 97
+ private const val ENABLED_ALPHA = 255
}
// App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
@@ -133,7 +136,13 @@ class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: Attr
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
+
+ val drawAlpha =
+ if (!parentIcon.isLaunchableAtScreenSize || parentIcon.info.isDisabled) DISABLED_ALPHA
+ else ENABLED_ALPHA
+
// Draw background
+ appPairBackground.alpha = drawAlpha
appPairBackground.draw(canvas)
// Make sure icons are loaded and fresh
@@ -147,6 +156,7 @@ class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: Attr
} else {
canvas.translate(width / 2f - memberIconSize / 2f, innerPadding)
}
+ appIcon1?.alpha = drawAlpha
appIcon1?.draw(canvas)
canvas.restore()
@@ -164,6 +174,7 @@ class AppPairIconGraphic @JvmOverloads constructor(context: Context, attrs: Attr
height - (innerPadding + memberIconSize)
)
}
+ appIcon2?.alpha = drawAlpha
appIcon2?.draw(canvas)
canvas.restore()
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 4d06c2e079..0ba468d5f0 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -48,6 +48,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -284,6 +285,12 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
}
}
if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
+ if (activities != null && !activities.isEmpty()) {
+ si.status = ApiWrapper
+ .isNonResizeableActivity(activities.get(0))
+ ? si.status | WorkspaceItemInfo.FLAG_NON_RESIZEABLE
+ : si.status & ~WorkspaceItemInfo.FLAG_NON_RESIZEABLE;
+ }
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
infoUpdated = true;
}
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index e38af0184d..59f56dfaa8 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -38,6 +38,7 @@ import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.PackageInstallInfo
import com.android.launcher3.shortcuts.ShortcutKey
+import com.android.launcher3.uioverrides.ApiWrapper
import com.android.launcher3.util.ComponentKey
import com.android.launcher3.util.PackageManagerHelper
import com.android.launcher3.util.PackageUserKey
@@ -321,6 +322,9 @@ class WorkspaceItemProcessor(
}
val activityInfo = c.launcherActivityInfo
if (activityInfo != null) {
+ if (ApiWrapper.isNonResizeableActivity(activityInfo)) {
+ info.status = info.status or WorkspaceItemInfo.FLAG_NON_RESIZEABLE
+ }
info.setProgressLevel(
PackageManagerHelper.getLoadingProgress(activityInfo),
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 435d223af8..9917ad7a64 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -74,6 +74,12 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
*/
public static final int FLAG_START_FOR_RESULT = 1 << 4;
+ /**
+ * The app is flagged non-resizeable, meaning that it does not support multi-window on small
+ * screens.
+ */
+ public static final int FLAG_NON_RESIZEABLE = 1 << 5;
+
/**
* The intent used to start the application.
*/
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 4abefc7a3c..111931eeeb 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -41,6 +41,7 @@ import android.widget.Toast;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.BuildConfig;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
@@ -149,7 +150,15 @@ public class ItemClickHandler {
private static void onClickAppPairIcon(View v) {
Launcher launcher = Launcher.getLauncher(v.getContext());
AppPairIcon appPairIcon = (AppPairIcon) v;
- if (appPairIcon.getInfo().isDisabled()) {
+ if (!appPairIcon.isLaunchableAtScreenSize()) {
+ // Display a message for app pairs that are disabled due to screen size
+ boolean isFoldable = InvariantDeviceProfile.INSTANCE.get(launcher)
+ .supportedProfiles.stream().anyMatch(dp -> dp.isTwoPanels);
+ Toast.makeText(launcher, isFoldable
+ ? R.string.app_pair_needs_unfold
+ : R.string.app_pair_unlaunchable_at_screen_size,
+ Toast.LENGTH_SHORT).show();
+ } else if (appPairIcon.getInfo().isDisabled()) {
WorkspaceItemInfo app1 = appPairIcon.getInfo().contents.get(0);
WorkspaceItemInfo app2 = appPairIcon.getInfo().contents.get(1);
// Show the user why the app pair is disabled.
diff --git a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
index f73940bfee..69786bb5e5 100644
--- a/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
+++ b/src/com/android/launcher3/util/LauncherBindableItemsContainer.java
@@ -19,6 +19,7 @@ import android.graphics.drawable.Drawable;
import android.view.View;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.PreloadIconDrawable;
@@ -58,6 +59,8 @@ public interface LauncherBindableItemsContainer {
: null);
} else if (info instanceof FolderInfo && v instanceof FolderIcon) {
((FolderIcon) v).updatePreviewItems(updates::contains);
+ } else if (info instanceof FolderInfo && v instanceof AppPairIcon appPairIcon) {
+ appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains);
}
// Iterate all items
@@ -86,6 +89,8 @@ public interface LauncherBindableItemsContainer {
((PendingAppWidgetHostView) v).applyState();
} else if (v instanceof FolderIcon && info instanceof FolderInfo) {
((FolderIcon) v).updatePreviewItems(updates::contains);
+ } else if (info instanceof FolderInfo && v instanceof AppPairIcon appPairIcon) {
+ appPairIcon.maybeRedrawForWorkspaceUpdate(updates::contains);
}
// process all the shortcuts
return false;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index b9f9ac57da..efde7d863a 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -107,6 +107,15 @@ public class ApiWrapper {
.authority(context.getPackageName()).build());
}
+ /**
+ * Checks if an activity is flagged as non-resizeable.
+ */
+ public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
+ // Overridden in quickstep
+ return false;
+ }
+
+
private static class NoopDrawable extends ColorDrawable {
@Override
public int getIntrinsicHeight() {