diff --git a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java index 70a143ee6f..7c97b93b03 100644 --- a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java +++ b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java @@ -36,6 +36,7 @@ import android.os.UserHandle; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.ComponentWithLabel; import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.BgDataModel.FixedContainerItems; @@ -44,6 +45,7 @@ import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.shadows.ShadowDeviceFlag; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.ItemInfoMatcher; @@ -60,6 +62,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowAppWidgetManager; import org.robolectric.shadows.ShadowPackageManager; import org.robolectric.util.ReflectionHelpers; @@ -174,6 +177,41 @@ public final class WidgetsPredicationUpdateTaskTest { assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1); } + @Test + public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder() + throws Exception { + ShadowDeviceFlag shadowDeviceFlag = Shadow.extract( + FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER); + shadowDeviceFlag.setValue(false); + + // WHEN newPredicationTask is executed with 5 predicated widgets. + AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1", + mUserHandle); + AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2", + mUserHandle); + // Not installed app + AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1", + mUserHandle); + // Not installed widget + AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3", + mUserHandle); + AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1", + mUserHandle); + mModelHelper.executeTaskForTest( + newWidgetsPredicationTask(List.of(widget5, widget3, widget2, widget4, widget1))) + .forEach(Runnable::run); + + // THEN only 3 widgets are returned because the launcher only filters out non-exist widgets. + List recommendedWidgets = mCallback.mRecommendedWidgets.items + .stream() + .map(itemInfo -> (PendingAddWidgetInfo) itemInfo) + .collect(Collectors.toList()); + assertThat(recommendedWidgets).hasSize(3); + assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1); + assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2); + assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1); + } + private void assertWidgetInfo( LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) { assertThat(actual.provider).isEqualTo(expected.provider); diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index c6c6c01542..cd22196686 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -19,7 +19,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.util.DisplayController.CHANGE_SIZE; +import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; @@ -240,7 +240,7 @@ public abstract class BaseQuickstepLauncher extends Launcher public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) { super.onDisplayInfoChanged(context, info, flags); - if ((flags & CHANGE_SIZE) != 0) { + if ((flags & CHANGE_ACTIVE_SCREEN) != 0) { addTaskbarIfNecessary(); } } diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index cc3ccea4d0..6d5975f5fa 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -33,7 +33,6 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderView; @@ -80,9 +79,9 @@ public class PredictionRowView extends LinearLayout implements setOrientation(LinearLayout.HORIZONTAL); mFocusHelper = new SimpleFocusIndicatorHelper(this); - mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns; mLauncher = Launcher.getLauncher(context); mLauncher.addOnDeviceProfileChangeListener(this); + mNumPredictedAppsPerRow = mLauncher.getDeviceProfile().numShownAllAppsColumns; updateVisibility(); } @@ -174,7 +173,7 @@ public class PredictionRowView extends LinearLayout implements @Override public void onDeviceProfileChanged(DeviceProfile dp) { - mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns; + mNumPredictedAppsPerRow = dp.numShownAllAppsColumns; removeAllViews(); applyPredictionApps(); } diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 8c6887250e..b0c13f99c0 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -104,8 +104,8 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange // TODO: Implement caching and preloading super.loadItems(ums, pinnedShortcuts); - WorkspaceItemFactory allAppsFactory = - new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numAllAppsColumns); + WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory( + mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns); mAllAppsState.items.setItems( mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get)); mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items); @@ -204,7 +204,7 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange registerPredictor(mAllAppsState, apm.createAppPredictionSession( new AppPredictionContext.Builder(context) .setUiSurface("home") - .setPredictedTargetCount(mIDP.numAllAppsColumns) + .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns) .build())); // TODO: get bundle diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java index a29ac1a7de..22a8c9b1d0 100644 --- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java +++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java @@ -16,16 +16,17 @@ package com.android.launcher3.model; import android.app.prediction.AppTarget; +import android.content.ComponentName; +import android.text.TextUtils; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.QuickstepModelDelegate.PredictorState; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.widget.PendingAddWidgetInfo; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -56,25 +57,43 @@ public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask { Map> allWidgets = dataModel.widgetsModel.getAllWidgetsWithoutShortcuts(); - ArrayList recommendedWidgetsInDescendingOrder = new ArrayList<>(); - for (AppTarget app : mTargets) { - PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser()); - if (allWidgets.containsKey(packageUserKey)) { - List notAddedWidgets = allWidgets.get(packageUserKey).stream() - .filter(item -> - !widgetsInWorkspace.contains( - new ComponentKey(item.componentName, item.user))) - .collect(Collectors.toList()); - if (notAddedWidgets.size() > 0) { - // Even an apps have more than one widgets, we only include one widget. - recommendedWidgetsInDescendingOrder.add( - new PendingAddWidgetInfo(notAddedWidgets.get(0).widgetInfo)); + FixedContainerItems fixedContainerItems = mPredictorState.items; + fixedContainerItems.items.clear(); + + if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) { + for (AppTarget app : mTargets) { + PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), + app.getUser()); + if (allWidgets.containsKey(packageUserKey)) { + List notAddedWidgets = allWidgets.get(packageUserKey).stream() + .filter(item -> + !widgetsInWorkspace.contains( + new ComponentKey(item.componentName, item.user))) + .collect(Collectors.toList()); + if (notAddedWidgets.size() > 0) { + // Even an apps have more than one widgets, we only include one widget. + fixedContainerItems.items.add( + new PendingAddWidgetInfo(notAddedWidgets.get(0).widgetInfo)); + } + } + } + } else { + Map widgetItems = + allWidgets.values().stream().flatMap(List::stream) + .collect(Collectors.toMap(widget -> (ComponentKey) widget, + widget -> widget)); + for (AppTarget app : mTargets) { + if (TextUtils.isEmpty(app.getClassName())) { + continue; + } + ComponentKey targetWidget = new ComponentKey( + new ComponentName(app.getPackageName(), app.getClassName()), app.getUser()); + if (widgetItems.containsKey(targetWidget)) { + fixedContainerItems.items.add( + new PendingAddWidgetInfo(widgetItems.get(targetWidget).widgetInfo)); } } } - FixedContainerItems fixedContainerItems = mPredictorState.items; - fixedContainerItems.items.clear(); - fixedContainerItems.items.addAll(recommendedWidgetsInDescendingOrder); bindExtraContainerItems(fixedContainerItems); // Don't store widgets prediction to disk because it is not used frequently. diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java index 1e03b05b5e..76a5782d11 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java @@ -18,13 +18,23 @@ package com.android.launcher3.uioverrides; import android.app.Person; import android.content.pm.ShortcutInfo; +import android.view.Display; import com.android.launcher3.Utilities; public class ApiWrapper { + public static final boolean TASKBAR_DRAWN_IN_PROCESS = true; + public static Person[] getPersons(ShortcutInfo si) { Person[] persons = si.getPersons(); return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons; } + + /** + * Returns true if the display is an internal displays + */ + public static boolean isInternalDisplay(Display display) { + return display.getType() == Display.TYPE_INTERNAL; + } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java index bb1f6fcc00..c115bbb9fb 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java +++ b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java @@ -68,6 +68,12 @@ public class DeviceFlag extends DebugFlag { mListeners.remove(r); } + @Override + public boolean get() { + // Override this method in order to let Robolectric ShadowDeviceFlag to stub it. + return super.get(); + } + private void registerDeviceConfigChangedListener(Context context) { DeviceConfig.addOnPropertiesChangedListener( NAMESPACE_LAUNCHER, diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java index 5c19ab8bf4..a54f963100 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java @@ -50,6 +50,7 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler { Pair options = remoteResponse.getLaunchOptions(hostView); ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager() .getActivityLaunchOptions(mLauncher, hostView); + activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK); Object itemInfo = hostView.getTag(); if (itemInfo instanceof ItemInfo) { mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options); diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index f09d9e087e..d07cc35ecc 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -26,6 +26,7 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_WIDGET_APP_START; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL; @@ -36,7 +37,6 @@ import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SY import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.os.SystemProperties; import android.view.HapticFeedbackConstants; import android.view.View; @@ -90,9 +90,6 @@ import java.util.stream.Stream; public class QuickstepLauncher extends BaseQuickstepLauncher { - private static final boolean ENABLE_APP_WIDGET_LAUNCH_ANIMATION = - SystemProperties.getBoolean("persist.debug.quickstep_app_widget_launch", false); - public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false; /** * Reusable command for applying the shelf height on the background thread. @@ -327,7 +324,7 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { protected LauncherAppWidgetHost createAppWidgetHost() { LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost(); - if (ENABLE_APP_WIDGET_LAUNCH_ANIMATION) { + if (ENABLE_QUICKSTEP_WIDGET_APP_START.get()) { appWidgetHost.setInteractionHandler(new QuickstepInteractionHandler(this)); } return appWidgetHost; diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index ba6192314e..3ac78663f7 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -77,6 +77,7 @@ public class QuickstepAtomicAnimationFactory extends @Override public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState, StateAnimationConfig config) { + RecentsView overview = mActivity.getOverviewPanel(); if (toState == NORMAL && fromState == OVERVIEW) { config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL); config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); @@ -85,7 +86,12 @@ public class QuickstepAtomicAnimationFactory extends config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL); if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) { - config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME); + // Scrolling in tasks, so make visible straight away + if (overview.getTaskViewCount() > 0) { + config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME); + } else { + config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); + } } else { config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); } @@ -122,13 +128,18 @@ public class QuickstepAtomicAnimationFactory extends config.setInterpolator(ANIM_WORKSPACE_SCALE, fromState == NORMAL ? ACCEL : OVERSHOOT_1_2); config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); - config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); + + // Scrolling in tasks, so show straight away + if (overview.getTaskViewCount() > 0) { + config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); + } else { + config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); + } } else { config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); // Scale up the recents, if it is not coming from the side - RecentsView overview = mActivity.getOverviewPanel(); if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 2d95ce2b98..729e2cb5c6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -18,20 +18,11 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR; import static com.android.launcher3.LauncherAnimUtils.newCancelListener; -import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; -import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL_3; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; @@ -45,7 +36,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.SystemUiProxy; @@ -269,39 +259,4 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch private float dpiFromPx(float pixels) { return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics().densityDpi); } - - @Override - protected StateAnimationConfig getConfigForStates( - LauncherState fromState, LauncherState toState) { - if (fromState == NORMAL && toState == ALL_APPS) { - StateAnimationConfig builder = new StateAnimationConfig(); - builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress( - ACCEL, - 0, - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD)); - builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress( - ACCEL, - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD, - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD)); - - // Get workspace out of the way quickly, to prepare for potential pause. - builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3); - builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3); - builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3); - return builder; - } else if (fromState == ALL_APPS && toState == NORMAL) { - StateAnimationConfig builder = new StateAnimationConfig(); - - builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress( - DEACCEL, - 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, - 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD)); - builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress( - DEACCEL, - 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, - 1)); - return builder; - } - return super.getConfigForStates(fromState, toState); - } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index b8f9452f36..3c83d25b71 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -24,6 +24,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE; import android.view.MotionEvent; @@ -56,13 +57,17 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr /** * Minimum clamping progress for fading in all apps content */ - protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.4f; - + protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f; /** - * The progress at which recents will begin fading out when swiping up from overview. + * Minimum clamping progress for fading in all apps scrim */ - private static final float RECENTS_FADE_THRESHOLD = 0.88f; + protected static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f; + + /** + * Maximum clamping progress for opaque all apps scrim + */ + protected static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f; private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper; @@ -122,14 +127,22 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr private StateAnimationConfig getNormalToAllAppsAnimation() { StateAnimationConfig builder = new StateAnimationConfig(); builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL, - 0, ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD)); + ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD, + ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD)); + builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(ACCEL, + ALL_APPS_SCRIM_VISIBLE_THRESHOLD, + ALL_APPS_SCRIM_OPAQUE_THRESHOLD)); return builder; } private StateAnimationConfig getAllAppsToNormalAnimation() { StateAnimationConfig builder = new StateAnimationConfig(); builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL, - 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, 1)); + 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, + 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD)); + builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(DEACCEL, + 1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD, + 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD)); return builder; } diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index c47300cd5f..62b821c41d 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -191,7 +191,7 @@ class OrientationTouchTransformer { * @see #enableMultipleRegions(boolean, Info) */ void createOrAddTouchRegion(Info info) { - mCurrentDisplay = new CurrentDisplay(info.realSize, info.rotation); + mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation); if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED && mCurrentDisplay.rotation == mQuickStepStartingRotation) { @@ -256,7 +256,7 @@ class OrientationTouchTransformer { Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation); } - mCurrentDisplay = new CurrentDisplay(region.realSize, region.rotation); + mCurrentDisplay = new CurrentDisplay(region.currentSize, region.rotation); OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay); if (DEBUG) { Log.d(TestProtocol.NO_SWIPE_TO_HOME, "cached region: " + regionToKeep @@ -289,7 +289,7 @@ class OrientationTouchTransformer { + " with mode: " + mMode + " displayRotation: " + display.rotation); } - Point size = display.realSize; + Point size = display.currentSize; int rotation = display.rotation; int touchHeight = mNavBarGesturalHeight; OrientationRectF orientationRectF = diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index ef09957c9d..e271203d18 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -18,7 +18,7 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; import static com.android.launcher3.util.DisplayController.CHANGE_ALL; -import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY; +import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION; import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED; import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; @@ -271,15 +271,9 @@ public class RecentsAnimationDeviceState implements @Override public void onDisplayInfoChanged(Context context, Info info, int flags) { - if (info.id != getDisplayId() || flags == CHANGE_FRAME_DELAY) { - // ignore displays that aren't running launcher and frame refresh rate changes - return; + if ((flags & CHANGE_ROTATION) != 0) { + mNavBarPosition = new NavBarPosition(mMode, info); } - - if (!mMode.hasGestures) { - return; - } - mNavBarPosition = new NavBarPosition(mMode, info); } @Override diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java index fd0de4212f..76298624d3 100644 --- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java +++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java @@ -17,8 +17,9 @@ package com.android.quickstep; import static android.view.Surface.ROTATION_0; +import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.DisplayController.CHANGE_ALL; -import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY; +import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; @@ -274,8 +275,7 @@ public class RotationTouchHelper implements @Override public void onDisplayInfoChanged(Context context, Info info, int flags) { - if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) { - // ignore displays that aren't running launcher and frame refresh rate changes + if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) { return; } diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java index 7067dbcc0d..e4d148cb76 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -20,7 +20,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MO import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCRIM_FADE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; @@ -98,6 +98,6 @@ public class FallbackRecentsStateController implements StateHandler mPendingCallbacks = + Collections.synchronizedMap(new WeakHashMap<>()); + public AssistContentRequester(Context context) { mActivityTaskManager = ActivityTaskManager.getService(); mPackageName = context.getApplicationContext().getPackageName(); @@ -66,20 +74,28 @@ public class AssistContentRequester { public void requestAssistContent(int taskId, Callback callback) { try { mActivityTaskManager.requestAssistDataForTask( - new AssistDataReceiver(callback, mCallbackExecutor), taskId, mPackageName); + new AssistDataReceiver(callback, this), taskId, mPackageName); } catch (RemoteException e) { Log.e(TAG, "Requesting assist content failed for task: " + taskId, e); } } + private void executeOnMainExecutor(Runnable callback) { + mCallbackExecutor.execute(callback); + } + private static final class AssistDataReceiver extends IAssistDataReceiver.Stub { - private final Executor mExecutor; - private final Callback mCallback; + // The AssistDataReceiver binder callback object is passed to a system server, that may + // keep hold of it for longer than the lifetime of the AssistContentRequester object, + // potentially causing a memory leak. In the callback passed to the system server, only + // keep a weak reference to the parent object and lookup its callback if it still exists. + private final WeakReference mParentRef; + private final Object mCallbackKey = new Object(); - AssistDataReceiver(Callback callback, Executor callbackExecutor) { - mCallback = callback; - mExecutor = callbackExecutor; + AssistDataReceiver(Callback callback, AssistContentRequester parent) { + parent.mPendingCallbacks.put(mCallbackKey, callback); + mParentRef = new WeakReference<>(parent); } @Override @@ -94,7 +110,18 @@ public class AssistContentRequester { return; } - mExecutor.execute(() -> mCallback.onAssistContentAvailable(content)); + AssistContentRequester requester = mParentRef.get(); + if (requester != null) { + Callback callback = requester.mPendingCallbacks.get(mCallbackKey); + if (callback != null) { + requester.executeOnMainExecutor( + () -> callback.onAssistContentAvailable(content)); + } else { + Log.d(TAG, "Callback received after calling UI was disposed of"); + } + } else { + Log.d(TAG, "Callback received after Requester was collected"); + } } @Override diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index 188efadd45..d164c8ce3e 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -33,6 +33,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.util.Log; @@ -49,6 +50,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.WindowBounds; import com.android.quickstep.BaseActivityInterface; @@ -561,11 +563,27 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre */ public DeviceProfile getLauncherDeviceProfile() { InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext); - // TODO also check the natural orientation is landscape or portrait - return (mRecentsActivityRotation == ROTATION_90 - || mRecentsActivityRotation == ROTATION_270) - ? idp.landscapeProfile - : idp.portraitProfile; + Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize; + + int width, height; + if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) { + width = Math.max(currentSize.x, currentSize.y); + height = Math.min(currentSize.x, currentSize.y); + } else { + width = Math.min(currentSize.x, currentSize.y); + height = Math.max(currentSize.x, currentSize.y); + } + + DeviceProfile bestMatch = idp.supportedProfiles.get(0); + float minDiff = Float.MAX_VALUE; + for (DeviceProfile profile : idp.supportedProfiles) { + float diff = Math.abs(profile.widthPx - width) + Math.abs(profile.heightPx - height); + if (diff < minDiff) { + minDiff = diff; + bestMatch = profile; + } + } + return bestMatch; } private static String nameAndAddress(Object obj) { diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java index 88cc650276..483a1c652a 100644 --- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java +++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java @@ -20,10 +20,7 @@ import static android.view.Surface.ROTATION_180; import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Insets; -import android.graphics.Rect; import android.os.Build; -import android.view.WindowInsets.Type; import android.view.WindowManager; import android.view.WindowMetrics; @@ -73,10 +70,8 @@ public class SplitScreenBounds { */ private static WindowBounds createDefaultWindowBounds(Context context) { WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); - Insets insets = wm.getWindowInsets().getInsets(Type.systemBars()); + WindowBounds bounds = WindowBounds.fromWindowMetrics(wm); - WindowBounds bounds = new WindowBounds(wm.getBounds(), - new Rect(insets.left, insets.top, insets.right, insets.bottom)); int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation; int halfDividerSize = context.getResources() .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2; diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 53f880fbb3..c9e7a73c72 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2481,15 +2481,6 @@ public abstract class RecentsView finishRecentsAnimation(true, this::onConfigurationChangedInternal)); - } else { - onConfigurationChangedInternal(); - } - } - - private void onConfigurationChangedInternal() { final int rotation = mActivity.getDisplay().getRotation(); if (mOrientationState.setRecentsRotation(rotation)) { updateOrientationHandler(); @@ -3442,7 +3433,7 @@ public abstract class RecentsView"Dubbeltik en hou om \'n legstuk te skuif of gebruik gepasmaakte handelinge." "%1$d × %2$d" "%1$d breed by %2$d hoog" - - - - + "Raak en hou die legstuk om dit op die Tuisskerm rond te beweeg" + "Voeg by Tuisskerm" %1$d legstukke %1$d legstuk diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 5241acd0e1..7123a8bb2a 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -32,10 +32,8 @@ "ምግብርን ለማንቀሳቀስ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ ያድርጉ እና ይያዙ።" "%1$d × %2$d" "%1$d ስፋት በ%2$d ከፍታ" - - - - + "በመነሻ ገጽ አካባቢ ላይ ለማንቀሳቀስ ነክተው ይያዙት" + "ወደ መነሻ ገጽ አክል" %1$d ንዑስ ፕሮግራሞች %1$d ንዑስ ፕሮግራሞች diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 669aa91ac2..c6ab52a9e6 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -32,10 +32,8 @@ "انقر مرتين مع تثبيت إصبعك لنقل أداة أو استخدام الإجراءات المخصّصة." "%1$d × %2$d" "‏العرض %1$d الطول %2$d" - - - - + "يمكنك النقر على الأداة مع الاستمرار لتحريكها على الشاشة الرئيسية." + "إضافة إلى الشاشة الرئيسية" %1$d أداة أداتان (%1$d) diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index 931b3ab2e4..5e80e652b4 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -32,10 +32,8 @@ "কোনো ৱিজেট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক অথবা কাষ্টম কাৰ্য ব্যৱহাৰ কৰক।" "%1$d × %2$d" "%1$d বহল x %2$d ওখ" - - - - + "ৱিজেটটো গৃহ স্ক্ৰীনৰ আশে-পাশে নিবলৈ সেইটোত স্পৰ্শ কৰি ধৰি ৰাখক" + "গৃহ স্ক্ৰীনত যোগ কৰক" %1$d টা ৱিজেট %1$d টা ৱিজেট diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 8b525f29b5..17f600c887 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -32,10 +32,8 @@ "Vidceti daşımaq üçün iki dəfə toxunub saxlayın və ya fərdi əməliyyatlardan istifadə edin." "%1$d × %2$d" "%2$d hündürlük %1$d enində" - - - - + "Əsas ekranda hərəkət etdirmək üçün vidcetə toxunub saxlayın" + "Əsas ekrana əlavə edin" %1$d vidcet %1$d vidcet diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index d4f013a5e0..dc04fedda3 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -32,10 +32,8 @@ "Dvaput dodirnite i zadržite da biste pomerali vidžet ili koristite prilagođene radnje." "%1$d×%2$d" "širina od %1$d i visina od %2$d" - - - - + "Dodirnite i zadržite vidžet da biste ga pomerali po početnom ekranu" + "Dodaj na početni ekran" %1$d vidžet %1$d vidžeta diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index cf02d485c8..9bb2c00f74 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -32,10 +32,8 @@ "Дакраніцеся двойчы і ўтрымлівайце, каб перамясціць віджэт або выкарыстоўваць спецыяльныя дзеянні." "%1$d × %2$d" "Шырына: %1$d, вышыня: %2$d" - - - - + "Утрымліваючы віджэт націснутым, перамяшчайце яго па Галоўным экране" + "Дадаць на Галоўны экран" %1$d віджэт %1$d віджэты diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index b3c5ca4028..0d398ee657 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -32,10 +32,8 @@ "Докоснете двукратно и задръжте за преместване на приспособление или използвайте персонал. действия." "%1$d × %2$d" "Ширина %1$d и височина %2$d" - - - - + "Докоснете приспособлението и го задръжте, за да го местите на началния екран" + "Добавяне към началния екран" %1$d приспособления %1$d приспособление diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index fcd6f3faed..50aff809c4 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -32,10 +32,8 @@ "Dvaput dodirnite i zadržite da pomjerite vidžet ili da koristite prilagođene radnje." "%1$d × %2$d" "Širina %1$d, visina %2$d" - - - - + "Dodirnite i držite vidžet da ga pomjerate po Početnom ekranu" + "Dodaj na početni ekran" %1$d vidžet %1$d vidžeta diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index d54670b4ac..9ea5fc71ce 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -32,10 +32,8 @@ "Fes doble toc i mantén premut per moure un widget o per utilitzar accions personalitzades." "%1$d × %2$d" "%1$d d\'amplada per %2$d d\'alçada" - - - - + "Mantén premut el widget per moure\'l per la pantalla d\'inici" + "Afegeix a la pantalla d\'inici" %1$d widgets %1$d widget diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index a9ea5878b3..9ae37cc096 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -32,10 +32,8 @@ "Dvojitým klepnutím a podržením přesunete widget, případně použijte vlastní akce." "%1$d × %2$d" "šířka %1$d, výška %2$d" - - - - + "Pokud widgetem chcete pohybovat po ploše, dotkněte se ho a podržte ho" + "Přidat na plochu" %1$d widgety %1$d widgetu diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index e5dacac7da..3b4e07b752 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -32,10 +32,8 @@ "Tryk to gange, og hold en widget nede for at flytte den eller bruge tilpassede handlinger." "%1$d × %2$d" "%1$d i bredden og %2$d i højden" - - - - + "Hold widgetten nede for at flytte den rundt på startskærmen" + "Føj til startskærm" %1$d widget %1$d widgets diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index a9deedb951..1d93bfcb52 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -32,10 +32,8 @@ "Πατήστε δύο φορές παρατεταμένα για μετακίνηση γραφικού στοιχείου ή χρήση προσαρμοσμένων ενεργειών." "%1$d × %2$d" "Πλάτος %1$d επί ύψος %2$d" - - - - + "Αγγίξτε παρατεταμένα το γραφικό στοιχείο για να το μετακινήσετε στην Αρχική οθόνη" + "Προσθήκη στην Αρχική οθόνη" %1$d γραφικά στοιχεία %1$d γραφικό στοιχείο diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 0330795e8c..79d33fd597 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -32,10 +32,8 @@ "Presiona dos veces y mantén presionado para mover un widget o usar acciones personalizadas." "%1$d × %2$d" "%1$d de ancho por %2$d de alto" - - - - + "Mantén presionado el widget para moverlo por la pantalla principal" + "Agregar a pantalla principal" %1$d widgets %1$d widget diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 888436fa2b..1bc4c603b4 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -32,10 +32,8 @@ "Toca dos veces y mantén pulsado un widget para moverlo o usar acciones personalizadas." "%1$d × %2$d" "%1$d de ancho por %2$d de alto" - - - - + "Mantén pulsado el widget para moverlo por la pantalla de inicio" + "Añadir a la pantalla de inicio" Widgets: %1$d Widget: %1$d diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index 15b6177adf..916aafc9ca 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -32,10 +32,8 @@ "Vidina teisaldamiseks või kohandatud toimingute kasutamiseks topeltpuudutage ja hoidke all." "%1$d × %2$d" "%1$d lai ja %2$d kõrge" - - - - + "Vidina avakuval liigutamiseks puudutage vidinat ja hoidke all" + "Lisa avakuvale" %1$d vidinat %1$d vidin diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 53090527d8..901d3cbe18 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -32,10 +32,8 @@ "Sakatu birritan eta eduki sakatuta widget bat mugitzeko edo ekintza pertsonalizatuak erabiltzeko." "%1$d × %2$d" "%1$d zabal eta %2$d luze" - - - - + "Eduki sakatuta widgeta hasierako pantailan zehar mugitzeko" + "Gehitu hasierako pantailan" %1$d widget %1$d widget diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index b0c300c76d..194ea0df7a 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -32,10 +32,8 @@ "برای جابه‌جا کردن ابزارک یا استفاده از کنش‌های سفارشی، دوضربه بزنید و نگه دارید." "%1$d × %2$d" "‏%1$d عرض در %2$d طول" - - - - + "ابزارک را لمس و کنید و نگه دارید تا آن را در صفحه اصلی حرکت دهید" + "افزودن به صفحه اصلی" %1$d ابزارک %1$d ابزارک diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 47466cfeba..6cffafc48c 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -32,10 +32,8 @@ "Kaksoisnapauta ja paina pitkään, niin voit siirtää widgetiä tai käyttää muokattuja toimintoja." "%1$d × %2$d" "Leveys: %1$d, korkeus: %2$d" - - - - + "Voit siirtää widgetiä aloitusnäytöllä koskettamalla sitä pitkään" + "Lisää aloitusnäytölle" %1$d widgetiä %1$d widget diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index f50d837c2b..3ebd61a218 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -32,10 +32,8 @@ "Touchez 2x un widget et maintenez le doigt dessus pour le déplacer ou utiliser des actions personnalisées." "%1$d × %2$d" "%1$d de largeur sur %2$d de hauteur" - - - - + "Maintenez le doigt sur le widget pour le déplacer sur l\'écran d\'accueil" + "Ajouter à l\'écran d\'accueil" %1$d widget %1$d widgets diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index f51c3d9e00..bb56bac166 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -32,10 +32,8 @@ "Appuyez deux fois et maintenez la pression pour déplacer widget ou utiliser actions personnalisées." "%1$d x %2$d" "%1$d de largeur et %2$d de hauteur" - - - - + "Appuyez de manière prolongée sur le widget pour le déplacer sur l\'écran d\'accueil" + "Ajouter à l\'écran d\'accueil" %1$d widget %1$d widgets diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index c3e3596ee5..d3f3f0b017 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -32,10 +32,8 @@ "Toca dúas veces un widget e manteno premido para movelo ou utiliza accións personalizadas." "%1$d × %2$d" "%1$d de largo por %2$d de alto" - - - - + "Mantén premido o widget para movelo pola pantalla de inicio" + "Engadir á pantalla de inicio" %1$d widgets %1$d widget diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 61776d6f64..9a3c56ce85 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -32,10 +32,8 @@ "किसी विजेट को एक से दूसरी जगह ले जाने के लिए, उस पर दो बार टैप करके दबाकर रखें या पसंद के मुताबिक कार्रवाइयां इस्तेमाल करें." "%1$d × %2$d" "%1$d चौड़ाई गुणा %2$d ऊंचाई" - - - - + "होम स्क्रीन पर यहां-वहां ले जाने के लिए विजेट को दबाकर रखें" + "होम स्क्रीन पर जोड़ें" %1$d विजेट %1$d विजेट diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 171f5d07e6..d1cb5bfd2d 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -32,10 +32,8 @@ "Dvaput dodirnite i zadržite pritisak da biste premjestili widget ili upotrijebite prilagođene radnje" "%1$d × %2$d" "%1$d širine i %2$d visine" - - - - + "Dodirnite i zadržite widget da biste ga pomicali po početnom zaslonu" + "Dodaj na početni zaslon" %1$d widget %1$d widgeta diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index bbb24e9d00..9e2c358f99 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -32,10 +32,8 @@ "Modul áthelyezéséhez koppintson duplán, tartsa nyomva az ujját, vagy használjon egyéni műveleteket." "%1$d × %2$d" "%1$d széles és %2$d magas" - - - - + "Tartsa lenyomva a modult a kezdőképernyőn való mozgatáshoz" + "Hozzáadás a kezdőképernyőhöz" %1$d modul %1$d modul diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index c1889ff367..d42e4f9d53 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -32,10 +32,8 @@ "Ketuk dua kali & tahan untuk memindahkan widget atau gunakan tindakan khusus." "%1$d × %2$d" "lebar %1$d x tinggi %2$d" - - - - + "Sentuh lama widget untuk memindahkannya di sekitar Layar utama" + "Tambahkan ke Layar utama" %1$d widget %1$d widget diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 1dce7c7396..30e73b4cf3 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -32,10 +32,8 @@ "Ýttu tvisvar og haltu fingri á græju til að færa hana eða notaðu sérsniðnar aðgerðir." "%1$d × %2$d" "%1$d á breidd og %2$d á hæð" - - - - + "Haltu fingri á græjunni til að hreyfa hana um heimaskjáinn" + "Bæta á heimaskjá" %1$d græja %1$d græjur diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 3d1cb5bc86..1d420a9006 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -32,10 +32,8 @@ "Tocca due volte e tieni premuto per spostare un widget o per usare le azioni personalizzate." "%1$d × %2$d" "%1$d di larghezza per %2$d di altezza" - - - - + "Tocca e tieni premuto il widget per spostarlo nella schermata Home" + "Aggiungi a schermata Home" %1$d widget %1$d widget diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 4cf0eb2218..7cd53fa481 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -32,10 +32,8 @@ "כדי להעביר ווידג\'ט למקום אחר או להשתמש בפעולות מותאמות אישית, יש ללחוץ פעמיים ולא להרפות." "%1$d × %2$d" "‏רוחב %1$d על גובה %2$d" - - - - + "יש ללחוץ לחיצה ארוכה על הווידג\'ט כדי להזיז אותו ברחבי מסך הבית" + "הוספה למסך הבית" %1$d ווידג\'טים %1$d ווידג\'טים diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 77efbadfe3..c1136a42de 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -32,10 +32,8 @@ "ウィジェットをダブルタップして長押ししながら移動するか、カスタム操作を使用してください。" "%1$dx%2$d" "幅 %1$d、高さ %2$d" - - - - + "ウィジェットを押し続けた状態で、ホーム画面上に移動させます。" + "ホーム画面に追加" %1$d 件のウィジェット %1$d 件のウィジェット diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index e3be57fe60..c70179592a 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -32,10 +32,8 @@ "ორმაგი შეხებით აირჩიეთ და გეჭიროთ ვიჯეტის გადასაადგილებლად ან მორგებული მოქმედებების გამოსაყენებლად." "%1$d × %2$d" "სიგრძე: %1$d, სიგანე: %2$d" - - - - + "ხანგრძლივად შეეხეთ ვიჯეტს მთავარ ეკრანზე მის გადასაადგილებლად" + "მთავარ ეკრანზე დამატება" %1$d ვიჯეტი %1$d ვიჯეტი diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 392dc8bfd4..8f92db0332 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -32,10 +32,8 @@ "Виджетті жылжыту үшін екі рет түртіңіз де, ұстап тұрыңыз немесе арнаулы әрекеттерді пайдаланыңыз." "%1$d × %2$d" "Ені: %1$d, биіктігі: %2$d" - - - - + "Негізгі экран бойымен қозғалту үшін виджетті басып, ұстап тұрыңыз." + "Негізгі экранға қосу" %1$d виджет %1$d виджет diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index dd04c01b5f..8a82bdaec7 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -32,10 +32,8 @@ "ចុចពីរដង រួចសង្កត់ឱ្យជាប់ ដើម្បីផ្លាស់ទី​ធាតុក្រាហ្វិក ឬប្រើ​សកម្មភាព​តាមបំណង​។" "%1$d × %2$d" "ទទឺង %1$d គុណនឹងកម្ពស់ %2$d" - - - - + "ចុចធាតុក្រាហ្វិក​ឱ្យជាប់ ដើម្បីផ្លាស់ទីវា​ជុំវិញអេក្រង់ដើម" + "បញ្ចូល​ទៅអេក្រង់ដើម" ធាតុ​ក្រាហ្វិក %1$d ធាតុ​ក្រាហ្វិក %1$d diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 7e24dc3b47..b98ee5c0e5 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -32,10 +32,8 @@ "두 번 탭한 다음 길게 터치하여 위젯을 이동하거나 맞춤 작업을 사용하세요." "%1$d×%2$d" "너비 %1$d, 높이 %2$d" - - - - + "홈 화면에서 위젯을 이동하려면 길게 터치하세요." + "홈 화면에 추가" 위젯 %1$d 위젯 %1$d diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index a4f54bf376..62fb75c4fc 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -32,10 +32,8 @@ "Виджетти жылдыруу үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз." "%1$d × %2$d" "Туурасы: %1$d, бийиктиги: %2$d" - - - - + "Башкы экранга жылдыруу үчүн виджетти коё бербей басып туруңуз" + "Башкы экранга кошуу" %1$d виджет %1$d виджет diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 43055a523a..0677e88ffa 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -32,10 +32,8 @@ "Dukart palieskite ir palaikykite, kad perkeltumėte valdiklį ar naudotumėte tinkintus veiksmus." "%1$d × %2$d" "%1$d plotis ir %2$d aukštis" - - - - + "Paliesdami ir palaikydami valdiklį galite judėti pagrindiniame ekrane" + "Pridėti prie pagrindinio ekrano" %1$d valdiklis %1$d valdikliai diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 8bcf250186..6310375f8a 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -32,10 +32,8 @@ "Lai pārvietotu logrīku, uz tā veiciet dubultskārienu un turiet. Varat arī veikt pielāgotas darbības." "%1$d × %2$d" "%1$d plats un %2$d augsts" - - - - + "Pieskarieties logrīkam un turiet to, lai to pārvietotu pa sākuma ekrānu." + "Pievienot sākuma ekrānam" %1$d logrīku %1$d logrīks diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index b512dc23ab..3e99d6767e 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -32,10 +32,8 @@ "Жижиг хэрэгслийг зөөх эсвэл захиалгат үйлдлийг ашиглахын тулд хоёр товшоод, удаан дарна уу." "%1$d × %2$d" "%1$d өргөн %2$d өндөр" - - - - + "Жижиг хэрэгслийг Үндсэн нүүрний эргэн тойронд зөөхийн тулд түүнд хүрээд, удаан дарна уу" + "Үндсэн нүүрэнд нэмэх" %1$d жижиг хэрэгсэл %1$d жижиг хэрэгсэл diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 91f2a4d667..9401040f70 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -32,10 +32,8 @@ "Ketik dua kali & tahan untuk menggerakkan widget atau menggunakan tindakan tersuai." "%1$d × %2$d" "Lebar %1$d kali tinggi %2$d" - - - - + "Sentuh & tahan widget untuk menggerakkan widget di sekitar Skrin utama" + "Tambah pada Skrin utama" %1$d widget %1$d widget diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index f042629bef..4dcd6c12fc 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -32,10 +32,8 @@ "ဝိဂျက်ကို ရွှေ့ရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။" "%1$d × %2$d" "အလျား %1$d နှင့် အမြင့် %2$d" - - - - + "ပင်မစာမျက်နှာအနီးတွင် ဝိဂျက်ကိုရွှေ့ရန် ၎င်းကို တို့ထိ၍ဖိထားပါ" + "ပင်မစာမျက်နှာသို့ ထည့်ရန်" ဝိဂျက် %1$d ခု ဝိဂျက် %1$d ခု diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 540d7266a3..ad5affb2c2 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -32,10 +32,8 @@ "Dobbelttrykk og hold inne for å flytte en modul eller bruke tilpassede handlinger." "%1$d × %2$d" "%1$d bredde x %2$d høyde" - - - - + "Trykk og hold på modulen for å bevege den rundt på startskjermen" + "Legg til på startskjermen" %1$d moduler %1$d modul diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index d4478b71f0..cc16a89835 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -32,10 +32,8 @@ "Dubbeltik en houd vast om een widget te verplaatsen of aangepaste acties te gebruiken." "%1$d × %2$d" "%1$d breed en %2$d hoog" - - - - + "Houd de widget ingedrukt om deze te verplaatsen op het startscherm" + "Toevoegen aan startscherm" %1$d widgets %1$d widget diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index 3595f333a8..7f3495a122 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -32,10 +32,8 @@ "ଏକ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଦୁଇଥର-ଟାପ୍ କରି ଧରି ରଖନ୍ତୁ କିମ୍ବା କଷ୍ଟମ୍ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ବ୍ୟବହାର କରନ୍ତୁ।" "%1$d × %2$d" "%1$d ଓସାର ଓ %2$d ଉଚ୍ଚ" - - - - + "ମୂଳସ୍କ୍ରିନର ଆଖପାଖରେ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଏହାକୁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ" + "ମୂଳସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ" %1$dଟି ୱିଜେଟ୍ %1$dଟି ୱିଜେଟ୍ diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 306a9a0fb9..ceb804668c 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -32,10 +32,8 @@ "Naciśnij dwukrotnie i przytrzymaj, aby przenieść widżet lub użyć działań niestandardowych." "%1$d × %2$d" "Szerokość %1$d, wysokość %2$d" - - - - + "Kliknij i przytrzymaj widżet, by poruszać nim po ekranie głównym." + "Dodaj do ekranu głównego" %1$d widżety %1$d widżetów diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index a1049315e2..e40accfeae 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -32,10 +32,8 @@ "Toque duas vezes sem soltar para mover um widget ou utilizar ações personalizadas." "%1$d × %2$d" "%1$d de largura por %2$d de altura" - - - - + "Toque sem soltar no widget para o mover à volta do ecrã principal" + "Adicionar ao ecrã principal" %1$d widgets %1$d widget diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 35d1e5d3a7..8b18329f6a 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -32,10 +32,8 @@ "Toque duas vezes e mantenha a tela pressionada para mover um widget ou usar ações personalizadas." "%1$d × %2$d" "%1$d de largura por %2$d de altura" - - - - + "Toque no widget e mantenha-o pressionado para movê-lo pela tela inicial" + "Adicionar à tela inicial" %1$d widget %1$d widgets diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index f614b18468..a71f999cee 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -32,10 +32,8 @@ "Atingeți de două ori și țineți apăsat pentru a muta un widget sau folosiți acțiuni personalizate." "%1$d × %2$d" "%1$d lățime și %2$d înălțime" - - - - + "Atingeți lung widgetul pentru a-l muta pe ecranul de pornire" + "Adăugați pe ecranul de pornire" %1$d widgeturi %1$d de widgeturi diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 3336b0bd13..0c1acbc658 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -32,10 +32,8 @@ "Чтобы использовать специальные действия или перенести виджет, нажмите на него дважды и удерживайте." "%1$d x %2$d" "Ширина %1$d, высота %2$d" - - - - + "Нажмите на виджет и удерживайте его, чтобы переместить в нужное место на главном экране." + "Добавить на главный экран" %1$d виджет %1$d виджета diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index fa3287d7fd..6b2716853e 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -32,10 +32,8 @@ "විජට් එකක් ගෙන යාමට හෝ අභිරුචි ක්‍රියා භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න." "%1$d × %2$d" "පළල %1$d උස %2$d" - - - - + "එය මුල් පිටු තිරය වටා ගෙන යාමට විජට් එක ස්පර්ශ කර අල්ලා ගන්න" + "මුල් පිටු තිරය වෙත එක් කරන්න" විජට් %1$d විජට් %1$d diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index bd90155468..7ea59d64b2 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -32,10 +32,8 @@ "Dvojitým klepnutím a pridržaním presuňte miniaplikáciu alebo použite vlastné akcie." "%1$d × %2$d" "šírka %1$d, výška %2$d" - - - - + "Po pridržaní môžete miniaplikáciu môžete posúvať po ploche" + "Pridať na plochu" %1$d miniaplikácie %1$d widgets diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index df1db7e604..f74bdcc534 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -32,10 +32,8 @@ "Dvakrat se dotaknite pripomočka in ga pridržite, da ga premaknete, ali pa uporabite dejanja po meri." "%1$d × %2$d" "Širina %1$d, višina %2$d" - - - - + "Pridržite pripomoček, če ga želite premikati po začetnem zaslonu." + "Dodaj na začetni zaslon" %1$d pripomoček %1$d pripomočka diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 4d97676952..9a53936f56 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -32,10 +32,8 @@ "Trokit dy herë dhe mbaje shtypur një miniapliikacion për ta zhvendosur atë ose për të përdorur veprimet e personalizuara." "%1$d × %2$d" "%1$d i gjerë me %2$d i lartë" - - - - + "Prek dhe mbaj miniaplikacionin për ta lëvizur nëpër \"Ekranin bazë\"" + "Shto në \"Ekranin bazë\"" %1$d miniaplikacione %1$d miniaplikacion diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 4103b0084d..4f67b23815 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -32,10 +32,8 @@ "Двапут додирните и задржите да бисте померали виџет или користите прилагођене радње." "%1$d×%2$d" "ширина од %1$d и висина од %2$d" - - - - + "Додирните и задржите виџет да бисте га померали по почетном екрану" + "Додај на почетни екран" %1$d виџет %1$d виџета diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index fb3e8c1f08..91b9b8866d 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -32,10 +32,8 @@ "Tryck snabbt två gånger och håll kvar för att flytta en widget eller använda anpassade åtgärder." "%1$d × %2$d" "%1$d bred gånger %2$d hög" - - - - + "Tryck länge på widgeten om du vill flytta den på startskärmen" + "Lägg till på startskärmen" %1$d widgetar %1$d widget diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 6caadaa368..6227239529 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -32,10 +32,8 @@ "Gusa mara mbili na ushikilie ili usogeze wijeti au utumie vitendo maalum." "%1$d × %2$d" "Upana wa %1$d na kimo cha %2$d" - - - - + "Gusa na ushikilie wijeti ili uisogeze kwenye Skrini ya kwanza" + "Weka kwenye Skrini ya kwanza" Wijeti %1$d Wijeti %1$d diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml index d50115b20d..09bdaafc1d 100644 --- a/res/values-sw600dp/config.xml +++ b/res/values-sw600dp/config.xml @@ -1,6 +1,3 @@ true - - - false diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index a2bc2c57d5..cae7e249dc 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -32,10 +32,8 @@ "விட்ஜெட்டை நகர்த்த இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேகச் செயல்களைப் பயன்படுத்தவும்." "%1$d × %2$d" "%1$d அகலத்திற்கு %2$d உயரம்" - - - - + "முகப்புத் திரைக்கு விட்ஜெட்டை நகர்த்த அதைத் தொட்டுப் பிடிக்கவும்" + "முகப்புத் திரையில் சேர்" %1$d விட்ஜெட்டுகள் %1$d விட்ஜெட் diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 1b79b9cbca..4f98cd2ad9 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -32,10 +32,8 @@ "แตะสองครั้งค้างไว้เพื่อย้ายวิดเจ็ตหรือใช้การดำเนินการที่กำหนดเอง" "%1$d × %2$d" "กว้าง %1$d x สูง %2$d" - - - - + "แตะวิดเจ็ตค้างไว้เพื่อย้ายไปรอบๆ หน้าจอหลัก" + "เพิ่มลงในหน้าจอหลัก" วิดเจ็ต %1$d รายการ วิดเจ็ต %1$d รายการ diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 58e8c8ae0b..a3504fe07e 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -32,10 +32,8 @@ "I-double tap at pindutin nang matagal para ilipat ang widget o gumamit ng mga custom na pagkilos." "%1$d × %2$d" "%1$d ang lapad at %2$d ang taas" - - - - + "Pindutin nang matagal ang widget para ilipat-lipat ito sa Home screen" + "Idagdag sa Home screen" %1$d widget %1$d na widget diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index bc6ad4466e..8029aeb302 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -32,10 +32,8 @@ "Widget\'ı taşımak veya özel işlemleri kullanmak için iki kez dokunup basılı tutun." "%1$d × %2$d" "genişlik: %1$d, yükseklik: %2$d" - - - - + "Ana ekranda taşımak için widget\'a dokunup basılı tutun" + "Ana ekrana ekle" %1$d widget %1$d widget diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index fdba5e615b..5e179aa82b 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -32,10 +32,8 @@ "Двічі натисніть і втримуйте віджет, щоб перемістити його або виконати інші дії." "%1$d × %2$d" "Ширина – %1$d, висота – %2$d" - - - - + "Натисніть і втримуйте віджет, щоб переміщувати його головним екраном" + "Додати на головний екран" %1$d віджет %1$d віджети diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index 1ab449dd3f..9c8f7ed460 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -32,10 +32,8 @@ "Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning." "%1$d × %2$d" "Eni %1$d, bo‘yi %2$d" - - - - + "Bosh ekranda surish uchun vidjet ustiga bosib turing" + "Bosh ekranga chiqarish" %1$d ta vidjet %1$d ta vidjet diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index acb101b7d4..a026dbc0ff 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -32,10 +32,8 @@ "Nhấn đúp và giữ để di chuyển một tiện ích hoặc sử dụng các thao tác tùy chỉnh." "%1$d × %2$d" "Rộng %1$d x cao %2$d" - - - - + "Chạm và giữ để di chuyển tiện ích xung quanh Màn hình chính" + "Thêm vào Màn hình chính" %1$d tiện ích %1$d tiện ích diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 5b06896707..d32611d575 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -32,10 +32,8 @@ "㩒兩下之後㩒住,就可以郁小工具或者用自訂操作。" "%1$d × %2$d" "%1$d 闊,%2$d 高" - - - - + "按住小工具即可將其移至主畫面上任何位置" + "新增至主畫面" %1$d 個小工具 %1$d 個小工具 diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 9566a8cf5c..58e8f19a07 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -32,10 +32,8 @@ "輕觸兩下並按住即可移動小工具或使用自訂操作。" "%1$d × %2$d" "寬度為 %1$d,高度為 %2$d" - - - - + "按住小工具即可將小工具移到主畫面的任一位置" + "新增到主畫面" %1$d 項小工具 %1$d 項小工具 diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 0da9ed7631..3892f6b10c 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -32,10 +32,8 @@ "Thepha kabili uphinde ubambe ukuze uhambise iwijethi noma usebenzise izindlela ezingokwezifiso." "%1$d × %2$d" "%1$d ububanzi ngokungu-%2$d ukuya phezulu" - - - - + "Thinta uphinde ubambe iwijethi ukuyihambisa Kusikrini sasekhaya" + "Engeza kusikrini sasekhaya" Amawijethi angu-%1$d Amawijethi angu-%1$d diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 93d88c2e94..a81802d87a 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -127,17 +127,24 @@ + + + + + + + + - - - @@ -167,8 +174,12 @@ - - + + + + + + @@ -176,11 +187,6 @@ - - - - - diff --git a/res/values/config.xml b/res/values/config.xml index 1e7837353f..9ad12245be 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -55,9 +55,6 @@ 200 - - true - diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java index 344f53206f..b58e4b7074 100644 --- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java +++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java @@ -18,11 +18,15 @@ package com.android.launcher3.shadows; import android.content.Context; +import androidx.annotation.Nullable; + import com.android.launcher3.uioverrides.DeviceFlag; import com.android.launcher3.util.LooperExecutor; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.RealObject; +import org.robolectric.shadow.api.Shadow; /** * Shadow for {@link LooperExecutor} to provide reset functionality for static executors. @@ -30,6 +34,9 @@ import org.robolectric.annotation.Implements; @Implements(value = DeviceFlag.class, isInAndroidSdk = false) public class ShadowDeviceFlag { + @RealObject private DeviceFlag mRealObject; + @Nullable private Boolean mValue; + /** * Mock change listener as it uses internal system classes not available to robolectric */ @@ -40,4 +47,16 @@ public class ShadowDeviceFlag { protected static boolean getDeviceValue(String key, boolean defaultValue) { return defaultValue; } + + @Implementation + public boolean get() { + if (mValue != null) { + return mValue; + } + return Shadow.directlyOn(mRealObject, DeviceFlag.class, "get"); + } + + public void setValue(boolean value) { + mValue = new Boolean(value); + } } diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java index 4d151f1948..ea755484c1 100644 --- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java +++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java @@ -161,7 +161,7 @@ public class LauncherUIScrollTest { private static MotionEvent createScrollEvent(int scroll) { DeviceProfile dp = InvariantDeviceProfile.INSTANCE - .get(RuntimeEnvironment.application).portraitProfile; + .get(RuntimeEnvironment.application).supportedProfiles.get(0); final PointerProperties[] pointerProperties = new PointerProperties[1]; pointerProperties[0] = new PointerProperties(); diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java index 92f77f2bbd..d97701148b 100644 --- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java +++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java @@ -17,6 +17,9 @@ package com.android.launcher3.widget; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; + import android.content.Context; import android.graphics.Point; @@ -109,10 +112,14 @@ public final class LauncherAppWidgetProviderInfoTest { private InvariantDeviceProfile createIDP() { DeviceProfile profile = Mockito.mock(DeviceProfile.class); + doAnswer(i -> { + ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE); + return null; + }).when(profile).getCellSize(any(Point.class)); Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE)); InvariantDeviceProfile idp = new InvariantDeviceProfile(); - idp.landscapeProfile = idp.portraitProfile = profile; + idp.supportedProfiles.add(profile); return idp; } diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 5d41bb5c3e..20b4631dec 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -1,5 +1,7 @@ package com.android.launcher3; +import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget; + import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT; import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH; import static com.android.launcher3.Utilities.ATLEAST_S; @@ -10,7 +12,9 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; @@ -25,16 +29,14 @@ import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; -import androidx.annotation.Nullable; - import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.dragndrop.DragLayer; -import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener { private static final int SNAP_DURATION = 150; @@ -43,22 +45,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O private static final Rect sTmpRect = new Rect(); - // Represents the cell size on the grid in the two orientations. - public static final MainThreadInitializedObject CELL_SIZE = - new MainThreadInitializedObject<>(c -> { - InvariantDeviceProfile inv = LauncherAppState.getIDP(c); - return new Point[] {inv.landscapeProfile.getCellSize(), - inv.portraitProfile.getCellSize()}; - }); - - // Represents the border spacing size on the grid in the two orientations. - public static final MainThreadInitializedObject BORDER_SPACING_SIZE = - new MainThreadInitializedObject<>(c -> { - InvariantDeviceProfile inv = LauncherAppState.getIDP(c); - return new int[] {inv.landscapeProfile.cellLayoutBorderSpacingPx, - inv.portraitProfile.cellLayoutBorderSpacingPx}; - }); - private static final int HANDLE_COUNT = 4; private static final int INDEX_LEFT = 0; private static final int INDEX_TOP = 1; @@ -202,7 +188,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mMaxHSpan = info.maxSpanX; mMaxVSpan = info.maxSpanY; - mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(), + mWidgetPadding = getDefaultPaddingForWidget(getContext(), widgetView.getAppWidgetInfo().provider, null); if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) { @@ -392,81 +378,82 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mWidgetView.requestLayout(); } - public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher, - int spanX, int spanY) { - List sizes = getWidgetSizes(launcher, spanX, spanY); + public static void updateWidgetSizeRanges( + AppWidgetHostView widgetView, Context context, int spanX, int spanY) { + List sizes = getWidgetSizes(context, spanX, spanY); if (ATLEAST_S) { widgetView.updateAppWidgetSize(new Bundle(), sizes); } else { - Rect bounds = getMinMaxSizes(sizes, null /* outRect */); + Rect bounds = getMinMaxSizes(sizes); widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right, bounds.bottom); } } - private static SizeF getWidgetSize(Context context, Point cellSize, int spanX, int spanY, - int borderSpacing) { - final float density = context.getResources().getDisplayMetrics().density; - final float hBorderSpacing = (spanX - 1) * borderSpacing; - final float vBorderSpacing = (spanY - 1) * borderSpacing; - - return new SizeF(((spanX * cellSize.x) + hBorderSpacing) / density, - ((spanY * cellSize.y) + vBorderSpacing) / density); - } - /** Returns the list of sizes for a widget of given span, in dp. */ public static ArrayList getWidgetSizes(Context context, int spanX, int spanY) { - final Point[] cellSize = CELL_SIZE.get(context); - final int[] borderSpacing = BORDER_SPACING_SIZE.get(context); - - SizeF landSize = getWidgetSize(context, cellSize[0], spanX, spanY, borderSpacing[0]); - SizeF portSize = getWidgetSize(context, cellSize[1], spanX, spanY, borderSpacing[1]); - ArrayList sizes = new ArrayList<>(2); - sizes.add(landSize); - sizes.add(portSize); + final float density = context.getResources().getDisplayMetrics().density; + Point cellSize = new Point(); + + for (DeviceProfile profile : LauncherAppState.getIDP(context).supportedProfiles) { + final float hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacingPx; + final float vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacingPx; + profile.getCellSize(cellSize); + sizes.add(new SizeF( + ((spanX * cellSize.x) + hBorderSpacing) / density, + ((spanY * cellSize.y) + vBorderSpacing) / density)); + } return sizes; } + /** + * Returns the bundle to be used as the default options for a widget with provided size + */ + public static Bundle getWidgetSizeOptions( + Context context, ComponentName provider, int spanX, int spanY) { + ArrayList sizes = getWidgetSizes(context, spanX, spanY); + Rect padding = getDefaultPaddingForWidget(context, provider, null); + float density = context.getResources().getDisplayMetrics().density; + float xPaddingDips = (padding.left + padding.right) / density; + float yPaddingDips = (padding.top + padding.bottom) / density; + + ArrayList paddedSizes = sizes.stream() + .map(size -> new SizeF( + Math.max(0.f, size.getWidth() - xPaddingDips), + Math.max(0.f, size.getHeight() - yPaddingDips))) + .collect(Collectors.toCollection(ArrayList::new)); + + Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes); + Bundle options = new Bundle(); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom); + options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes); + return options; + } + /** * Returns the min and max widths and heights given a list of sizes, in dp. * * @param sizes List of sizes to get the min/max from. - * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If - * null, a new rectangle will be allocated. * @return A rectangle with the left (resp. top) is used for the min width (resp. height) and * the right (resp. bottom) for the max. The returned rectangle is set with 0s if the list is * empty. */ - public static Rect getMinMaxSizes(List sizes, @Nullable Rect outRect) { - if (outRect == null) { - outRect = new Rect(); - } + private static Rect getMinMaxSizes(List sizes) { if (sizes.isEmpty()) { - outRect.set(0, 0, 0, 0); + return new Rect(); } else { SizeF first = sizes.get(0); - outRect.set((int) first.getWidth(), (int) first.getHeight(), (int) first.getWidth(), - (int) first.getHeight()); + Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(), + (int) first.getWidth(), (int) first.getHeight()); for (int i = 1; i < sizes.size(); i++) { - outRect.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight()); + result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight()); } + return result; } - return outRect; - } - - /** - * Returns the range of sizes a widget may be displayed, given its span. - * - * @param context Context in which the View is rendered. - * @param spanX Width of the widget, in cells. - * @param spanY Height of the widget, in cells. - * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If - * null, a new rectangle will be allocated. - */ - public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, - @Nullable Rect outRect) { - return getMinMaxSizes(getWidgetSizes(context, spanX, spanY), outRect); } @Override diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 2ace796ca0..3d044d68ce 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -317,7 +317,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @UiThread protected void applyIconAndLabel(ItemInfoWithIcon info) { - FastBitmapDrawable iconDrawable = info.newIcon(getContext()); + boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER; + FastBitmapDrawable iconDrawable = info.newIcon(getContext(), useTheme); mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f); setIcon(iconDrawable); diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index fd9793625e..d5860dc57d 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -31,7 +31,6 @@ import android.graphics.PointF; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.util.DisplayMetrics; -import android.util.Log; import android.view.Surface; import android.view.WindowInsets; import android.view.WindowManager; @@ -42,7 +41,6 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.GraphicsUtils; import com.android.launcher3.icons.IconNormalizer; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.WindowBounds; @@ -52,9 +50,6 @@ import java.io.PrintWriter; @SuppressLint("NewApi") public class DeviceProfile { - private static final float TABLET_MIN_DPS = 600; - private static final float LARGE_TABLET_MIN_DPS = 720; - private static final int DEFAULT_DOT_SIZE = 100; public final InvariantDeviceProfile inv; @@ -63,9 +58,9 @@ public class DeviceProfile { // Device properties public final boolean isTablet; - public final boolean isLargeTablet; public final boolean isPhone; public final boolean transposeLayoutWithOrientation; + public final boolean isTwoPanels; // Device properties in current orientation public final boolean isLandscape; @@ -164,6 +159,7 @@ public class DeviceProfile { public int allAppsCellWidthPx; public int allAppsIconSizePx; public int allAppsIconDrawablePaddingPx; + public final int numShownAllAppsColumns; public float allAppsIconTextSizePx; // Overview @@ -194,42 +190,30 @@ public class DeviceProfile { // How much of the bottom inset is due to Taskbar rather than other system elements. public int nonOverlappingTaskbarInset; - DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, - Point minSize, Point maxSize, int width, int height, boolean isLandscape, + DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds, boolean isMultiWindowMode, boolean transposeLayoutWithOrientation, - Point windowPosition) { + boolean useTwoPanels) { this.inv = inv; - this.isLandscape = isLandscape; + this.isLandscape = windowBounds.isLandscape(); this.isMultiWindowMode = isMultiWindowMode; this.transposeLayoutWithOrientation = transposeLayoutWithOrientation; - windowX = windowPosition.x; - windowY = windowPosition.y; + windowX = windowBounds.bounds.left; + windowY = windowBounds.bounds.top; isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode; // Determine sizes. - widthPx = width; - heightPx = height; - int nonFinalAvailableHeightPx; - if (isLandscape) { - availableWidthPx = maxSize.x; - nonFinalAvailableHeightPx = minSize.y; - } else { - availableWidthPx = minSize.x; - nonFinalAvailableHeightPx = maxSize.y; - } + widthPx = windowBounds.bounds.width(); + heightPx = windowBounds.bounds.height(); + availableWidthPx = windowBounds.availableSize.x; + int nonFinalAvailableHeightPx = windowBounds.availableSize.y; mInfo = info; + isTablet = info.isTablet(windowBounds); + isPhone = !isTablet; + isTwoPanels = isTablet && useTwoPanels; - // Constants from resources - float swDPs = dpiFromPx(Math.min(info.smallestSize.x, info.smallestSize.y), - info.densityDpi); - boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation); - // Tablet UI is built with assumption that simulated landscape is disabled. - isTablet = allowRotation && swDPs >= TABLET_MIN_DPS; - isLargeTablet = isTablet && swDPs >= LARGE_TABLET_MIN_DPS; - isPhone = !isTablet && !isLargeTablet; aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; @@ -284,7 +268,7 @@ public class DeviceProfile { ? 0 : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding); - if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get() && isTablet) { + if (isTwoPanels) { cellLayoutPaddingLeftRightPx = res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding); cellLayoutBottomPaddingPx = 0; @@ -309,7 +293,10 @@ public class DeviceProfile { workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x); - numShownHotseatIcons = inv.numShownHotseatIcons; + numShownHotseatIcons = + isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons; + numShownAllAppsColumns = + isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns; hotseatBarTopPaddingPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding); hotseatBarBottomPaddingPx = (isTallDevice ? 0 @@ -393,11 +380,12 @@ public class DeviceProfile { } public Builder toBuilder(Context context) { - Point size = new Point(availableWidthPx, availableHeightPx); + WindowBounds bounds = + new WindowBounds(widthPx, heightPx, availableWidthPx, availableHeightPx); + bounds.bounds.offsetTo(windowX, windowY); return new Builder(context, inv, mInfo) - .setSizeRange(size, size) - .setSize(widthPx, heightPx) - .setWindowPosition(windowX, windowY) + .setWindowBounds(bounds) + .setUseTwoPanels(isTwoPanels) .setMultiWindowMode(isMultiWindowMode); } @@ -409,15 +397,8 @@ public class DeviceProfile { * TODO: Move this to the builder as part of setMultiWindowMode */ public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowBounds) { - // We take the minimum sizes of this profile and it's multi-window variant to ensure that - // the system decor is always excluded. - Point mwSize = new Point(Math.min(availableWidthPx, windowBounds.availableSize.x), - Math.min(availableHeightPx, windowBounds.availableSize.y)); - DeviceProfile profile = toBuilder(context) - .setSizeRange(mwSize, mwSize) - .setSize(windowBounds.bounds.width(), windowBounds.bounds.height()) - .setWindowPosition(windowBounds.bounds.left, windowBounds.bounds.top) + .setWindowBounds(windowBounds) .setMultiWindowMode(true) .build(); @@ -433,14 +414,6 @@ public class DeviceProfile { return profile; } - /** - * Inverse of {@link #getMultiWindowProfile(Context, WindowBounds)} - * @return device profile corresponding to the current orientation in non multi-window mode. - */ - public DeviceProfile getFullScreenProfile() { - return isLandscape ? inv.landscapeProfile : inv.portraitProfile; - } - /** * Checks if there is enough space for labels on the workspace. * If there is not, labels on the Workspace are hidden. @@ -553,7 +526,7 @@ public class DeviceProfile { } // All apps - if (allAppsHasDifferentNumColumns()) { + if (numShownAllAppsColumns != inv.numColumns) { allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics); allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mMetrics); allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx; @@ -667,18 +640,20 @@ public class DeviceProfile { } public Point getCellSize() { - return getCellSize(inv.numColumns, inv.numRows); + return getCellSize(null); } - private Point getCellSize(int numColumns, int numRows) { - Point result = new Point(); + public Point getCellSize(Point result) { + if (result == null) { + result = new Point(); + } // Since we are only concerned with the overall padding, layout direction does // not matter. Point padding = getTotalWorkspacePadding(); result.x = calculateCellWidth(availableWidthPx - padding.x - - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, numColumns); + - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, inv.numColumns); result.y = calculateCellHeight(availableHeightPx - padding.y - - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, numRows); + - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows); return result; } @@ -723,7 +698,7 @@ public class DeviceProfile { padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2, availablePaddingX / 2, paddingBottom + availablePaddingY / 2); - if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get()) { + if (isTwoPanels) { padding.set(0, padding.top, 0, padding.bottom); } } else { @@ -751,7 +726,7 @@ public class DeviceProfile { // for this, we pad the left and right of the hotseat with half of the difference of a // workspace cell vs a hotseat cell. float workspaceCellWidth = (float) widthPx / inv.numColumns; - float hotseatCellWidth = (float) widthPx / inv.numShownHotseatIcons; + float hotseatCellWidth = (float) widthPx / numShownHotseatIcons; int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2); mHotseatPadding.set( hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx @@ -801,13 +776,6 @@ public class DeviceProfile { return isLandscape && transposeLayoutWithOrientation; } - /** - * Returns true when the number of workspace columns and all apps columns differs. - */ - private boolean allAppsHasDifferentNumColumns() { - return inv.numAllAppsColumns != inv.numColumns; - } - /** * Updates orientation information and returns true if it has changed from the previous value. */ @@ -828,7 +796,7 @@ public class DeviceProfile { } public boolean shouldFadeAdjacentWorkspaceScreens() { - return isVerticalBarLayout() || isLargeTablet; + return isVerticalBarLayout(); } public int getCellHeight(@ContainerType int containerType) { @@ -854,13 +822,13 @@ public class DeviceProfile { writer.println(prefix + "\t1 dp = " + mMetrics.density + " px"); writer.println(prefix + "\tisTablet:" + isTablet); - writer.println(prefix + "\tisLargeTablet:" + isLargeTablet); writer.println(prefix + "\tisPhone:" + isPhone); writer.println(prefix + "\ttransposeLayoutWithOrientation:" + transposeLayoutWithOrientation); writer.println(prefix + "\tisLandscape:" + isLandscape); writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode); + writer.println(prefix + "\tisTwoPanels:" + isTwoPanels); writer.println(prefix + pxToDpStr("windowX", windowX)); writer.println(prefix + pxToDpStr("windowY", windowY)); @@ -907,6 +875,7 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx", allAppsIconDrawablePaddingPx)); writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx)); + writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns); writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx)); writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx)); @@ -916,6 +885,7 @@ public class DeviceProfile { hotseatBarSidePaddingStartPx)); writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx", hotseatBarSidePaddingEndPx)); + writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons); writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent); @@ -967,41 +937,16 @@ public class DeviceProfile { private InvariantDeviceProfile mInv; private Info mInfo; - private final Point mWindowPosition = new Point(); - private Point mMinSize, mMaxSize; - private int mWidth, mHeight; + private WindowBounds mWindowBounds; + private boolean mUseTwoPanels; - private boolean mIsLandscape; private boolean mIsMultiWindowMode = false; - private boolean mTransposeLayoutWithOrientation; + private Boolean mTransposeLayoutWithOrientation; public Builder(Context context, InvariantDeviceProfile inv, Info info) { mContext = context; mInv = inv; mInfo = info; - mTransposeLayoutWithOrientation = context.getResources() - .getBoolean(R.bool.hotseat_transpose_layout_with_orientation); - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, - "transposeLayout=" + mTransposeLayoutWithOrientation); - } - } - - public Builder setSizeRange(Point minSize, Point maxSize) { - mMinSize = minSize; - mMaxSize = maxSize; - return this; - } - - public Builder setSize(int width, int height) { - mWidth = width; - mHeight = height; - mIsLandscape = mWidth > mHeight; - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, - "isLandscape=" + mIsLandscape + " w=" + mWidth + " h=" + mHeight); - } - return this; } public Builder setMultiWindowMode(boolean isMultiWindowMode) { @@ -1009,11 +954,14 @@ public class DeviceProfile { return this; } - /** - * Sets the window position if not full-screen - */ - public Builder setWindowPosition(int x, int y) { - mWindowPosition.set(x, y); + public Builder setUseTwoPanels(boolean useTwoPanels) { + mUseTwoPanels = useTwoPanels; + return this; + } + + + public Builder setWindowBounds(WindowBounds bounds) { + mWindowBounds = bounds; return this; } @@ -1023,9 +971,14 @@ public class DeviceProfile { } public DeviceProfile build() { - return new DeviceProfile(mContext, mInv, mInfo, mMinSize, mMaxSize, - mWidth, mHeight, mIsLandscape, mIsMultiWindowMode, - mTransposeLayoutWithOrientation, mWindowPosition); + if (mWindowBounds == null) { + throw new IllegalArgumentException("Window bounds not set"); + } + if (mTransposeLayoutWithOrientation == null) { + mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds); + } + return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, + mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels); } } diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 8496fd5d9c..b2a9e75e50 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -90,11 +90,11 @@ public class Hotseat extends CellLayout implements Insettable { public void resetLayout(boolean hasVerticalHotseat) { removeAllViewsInLayout(); mHasVerticalHotseat = hasVerticalHotseat; - InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv; + DeviceProfile dp = mActivity.getDeviceProfile(); if (hasVerticalHotseat) { - setGridSize(1, idp.numShownHotseatIcons); + setGridSize(1, dp.numShownHotseatIcons); } else { - setGridSize(idp.numShownHotseatIcons, 1); + setGridSize(dp.numShownHotseatIcons, 1); } } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 1332e14af0..b263d3830e 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -16,11 +16,12 @@ package com.android.launcher3; +import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.Utilities.getDevicePrefs; import static com.android.launcher3.Utilities.getPointString; import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME; import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; -import static com.android.launcher3.util.DisplayController.CHANGE_SIZE; +import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter; @@ -55,6 +56,7 @@ import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Themes; +import com.android.launcher3.util.WindowBounds; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -62,6 +64,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.List; public class InvariantDeviceProfile { @@ -73,6 +76,9 @@ public class InvariantDeviceProfile { public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size"; public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count"; + private static final int DEFAULT_TRUE = -1; + private static final int DEFAULT_SPLIT_DISPLAY = 2; + private static final String KEY_IDP_GRID_NAME = "idp_grid_name"; private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; @@ -136,6 +142,7 @@ public class InvariantDeviceProfile { * Number of columns in the all apps list. */ public int numAllAppsColumns; + public int numDatabaseAllAppsColumns; /** * Do not query directly. see {@link DeviceProfile#isScalableGrid}. @@ -147,8 +154,7 @@ public class InvariantDeviceProfile { public int defaultLayoutId; int demoModeLayoutId; - public DeviceProfile landscapeProfile; - public DeviceProfile portraitProfile; + public final List supportedProfiles = new ArrayList<>(); @Nullable public DevicePaddings devicePaddings; @@ -175,6 +181,7 @@ public class InvariantDeviceProfile { numShownHotseatIcons = p.numShownHotseatIcons; numDatabaseHotseatIcons = p.numDatabaseHotseatIcons; numAllAppsColumns = p.numAllAppsColumns; + numDatabaseAllAppsColumns = p.numDatabaseAllAppsColumns; isScalable = p.isScalable; devicePaddingId = p.devicePaddingId; minCellHeight = p.minCellHeight; @@ -204,7 +211,7 @@ public class InvariantDeviceProfile { DisplayController.INSTANCE.get(context).addChangeListener( (displayContext, info, flags) -> { - if ((flags & (CHANGE_SIZE | CHANGE_DENSITY)) != 0) { + if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) { onConfigChanged(displayContext); } }); @@ -232,35 +239,29 @@ public class InvariantDeviceProfile { // Get the display info based on default display and interpolate it to existing display DisplayOption defaultDisplayOption = invDistWeightedInterpolate( DisplayController.INSTANCE.get(context).getInfo(), - getPredefinedDeviceProfiles(context, gridName)); + getPredefinedDeviceProfiles(context, gridName, false), false); Info myInfo = new Info(context, display); DisplayOption myDisplayOption = invDistWeightedInterpolate( - myInfo, getPredefinedDeviceProfiles(context, gridName)); + myInfo, getPredefinedDeviceProfiles(context, gridName, false), false); DisplayOption result = new DisplayOption(defaultDisplayOption.grid) .add(myDisplayOption); result.iconSize = defaultDisplayOption.iconSize; result.landscapeIconSize = defaultDisplayOption.landscapeIconSize; - result.numShownHotseatIcons = myDisplayOption.numShownHotseatIcons; if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) { result.allAppsIconSize = defaultDisplayOption.allAppsIconSize; - result.numAllAppsColumns = defaultDisplayOption.numAllAppsColumns; } else { result.allAppsIconSize = myDisplayOption.allAppsIconSize; - result.numAllAppsColumns = myDisplayOption.numAllAppsColumns; } result.minCellHeight = defaultDisplayOption.minCellHeight; result.minCellWidth = defaultDisplayOption.minCellWidth; result.borderSpacing = defaultDisplayOption.borderSpacing; - initGrid(context, myInfo, result); + initGrid(context, myInfo, result, false); } public static String getCurrentGridName(Context context) { - if (ENABLE_TWO_PANEL_HOME.get()) { - return ENABLE_TWO_PANEL_HOME.key; - } return Utilities.isGridOptionsEnabled(context) ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null; } @@ -278,20 +279,33 @@ public class InvariantDeviceProfile { private String initGrid(Context context, String gridName) { Info displayInfo = DisplayController.INSTANCE.get(context).getInfo(); - ArrayList allOptions = getPredefinedDeviceProfiles(context, gridName); + // Determine if we have split display - DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions); - initGrid(context, displayInfo, displayOption); + boolean isTablet = false, isPhone = false; + for (WindowBounds bounds : displayInfo.supportedBounds) { + if (displayInfo.isTablet(bounds)) { + isTablet = true; + } else { + isPhone = true; + } + } + boolean isSplitDisplay = isPhone && isTablet && ENABLE_TWO_PANEL_HOME.get(); + + ArrayList allOptions = + getPredefinedDeviceProfiles(context, gridName, isSplitDisplay); + DisplayOption displayOption = + invDistWeightedInterpolate(displayInfo, allOptions, isSplitDisplay); + initGrid(context, displayInfo, displayOption, isSplitDisplay); return displayOption.grid.name; } private void initGrid( - Context context, Info displayInfo, DisplayOption displayOption) { + Context context, Info displayInfo, DisplayOption displayOption, + boolean isSplitDisplay) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); GridOption closestProfile = displayOption.grid; numRows = closestProfile.numRows; numColumns = closestProfile.numColumns; - numDatabaseHotseatIcons = closestProfile.numDatabaseHotseatIcons; dbFile = closestProfile.dbFile; defaultLayoutId = closestProfile.defaultLayoutId; demoModeLayoutId = closestProfile.demoModeLayoutId; @@ -313,8 +327,14 @@ public class InvariantDeviceProfile { minCellHeight = displayOption.minCellHeight; minCellWidth = displayOption.minCellWidth; borderSpacing = displayOption.borderSpacing; - numShownHotseatIcons = Math.round(displayOption.numShownHotseatIcons); - numAllAppsColumns = Math.round(displayOption.numAllAppsColumns); + + numShownHotseatIcons = closestProfile.numHotseatIcons; + numDatabaseHotseatIcons = isSplitDisplay + ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons; + + numAllAppsColumns = closestProfile.numAllAppsColumns; + numDatabaseAllAppsColumns = isSplitDisplay + ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns; if (Utilities.isGridOptionsEnabled(context)) { allAppsIconSize = displayOption.allAppsIconSize; @@ -332,31 +352,26 @@ public class InvariantDeviceProfile { // Supported overrides: numRows, numColumns, iconSize applyPartnerDeviceProfileOverrides(context, metrics); - Point realSize = new Point(displayInfo.realSize); - // The real size never changes. smallSide and largeSide will remain the - // same in any orientation. - int smallSide = Math.min(realSize.x, realSize.y); - int largeSide = Math.max(realSize.x, realSize.y); + supportedProfiles.clear(); + defaultWallpaperSize = new Point(displayInfo.currentSize); + for (WindowBounds bounds : displayInfo.supportedBounds) { + supportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo) + .setUseTwoPanels(isSplitDisplay) + .setWindowBounds(bounds).build()); - DeviceProfile.Builder builder = new DeviceProfile.Builder(context, this, displayInfo) - .setSizeRange(new Point(displayInfo.smallestSize), - new Point(displayInfo.largestSize)); - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, - "largeSide=" + largeSide + " smallSide=" + smallSide); - } + // Wallpaper size should be the maximum of the all possible sizes Launcher expects + int displayWidth = bounds.bounds.width(); + int displayHeight = bounds.bounds.height(); + defaultWallpaperSize.y = Math.max(defaultWallpaperSize.y, displayHeight); - landscapeProfile = builder.setSize(largeSide, smallSide).build(); - portraitProfile = builder.setSize(smallSide, largeSide).build(); - - // We need to ensure that there is enough extra space in the wallpaper - // for the intended parallax effects - if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) { - defaultWallpaperSize = new Point( - (int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)), - largeSide); - } else { - defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide); + // We need to ensure that there is enough extra space in the wallpaper + // for the intended parallax effects + float parallaxFactor = + dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720 + ? 2 + : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight); + defaultWallpaperSize.x = + Math.max(defaultWallpaperSize.x, Math.round(parallaxFactor * displayWidth)); } ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName()); @@ -385,7 +400,7 @@ public class InvariantDeviceProfile { } else if (!savedIconMaskPath.equals(getIconShapePath(context))) { getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context)) .apply(); - apply(context, CHANGE_FLAG_ICON_PARAMS); + apply(CHANGE_FLAG_ICON_PARAMS); } } @@ -423,16 +438,17 @@ public class InvariantDeviceProfile { IconShape.init(context); } - apply(context, changeFlags); + apply(changeFlags); } - private void apply(Context context, int changeFlags) { + private void apply(int changeFlags) { for (OnIDPChangeListener listener : mChangeListeners) { listener.onIdpChanged(changeFlags, this); } } - static ArrayList getPredefinedDeviceProfiles(Context context, String gridName) { + private static ArrayList getPredefinedDeviceProfiles( + Context context, String gridName, boolean isSplitDisplay) { ArrayList profiles = new ArrayList<>(); try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) { final int depth = parser.getDepth(); @@ -449,8 +465,9 @@ public class InvariantDeviceProfile { && type != XmlPullParser.END_DOCUMENT) { if ((type == XmlPullParser.START_TAG) && "display-option".equals( parser.getName())) { - profiles.add(new DisplayOption( - gridOption, context, Xml.asAttributeSet(parser))); + profiles.add(new DisplayOption(gridOption, context, + Xml.asAttributeSet(parser), + isSplitDisplay ? DEFAULT_SPLIT_DISPLAY : DEFAULT_TRUE)); } } } @@ -521,17 +538,29 @@ public class InvariantDeviceProfile { return (float) Math.hypot(x1 - x0, y1 - y0); } - @VisibleForTesting - static DisplayOption invDistWeightedInterpolate( - Info displayInfo, ArrayList points) { - Point smallestSize = new Point(displayInfo.smallestSize); - Point largestSize = new Point(displayInfo.largestSize); + private static DisplayOption invDistWeightedInterpolate( + Info displayInfo, ArrayList points, boolean isSplitDisplay) { + int minWidthPx = Integer.MAX_VALUE; + int minHeightPx = Integer.MAX_VALUE; + for (WindowBounds bounds : displayInfo.supportedBounds) { + boolean isTablet = displayInfo.isTablet(bounds); + if (isTablet && isSplitDisplay) { + // For split displays, take half width per page + minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2); + minHeightPx = Math.min(minHeightPx, bounds.availableSize.y); - // This guarantees that width < height - float width = Utilities.dpiFromPx((float) Math.min(smallestSize.x, smallestSize.y), - displayInfo.densityDpi); - float height = Utilities.dpiFromPx((float) Math.min(largestSize.x, largestSize.y), - displayInfo.densityDpi); + } else if (!isTablet && bounds.isLandscape()) { + // We will use transposed layout in this case + minWidthPx = Math.min(minWidthPx, bounds.availableSize.y); + minHeightPx = Math.min(minHeightPx, bounds.availableSize.x); + } else { + minWidthPx = Math.min(minWidthPx, bounds.availableSize.x); + minHeightPx = Math.min(minHeightPx, bounds.availableSize.y); + } + } + + float width = dpiFromPx(minWidthPx, displayInfo.densityDpi); + float height = dpiFromPx(minHeightPx, displayInfo.densityDpi); // Sort the profiles based on the closeness to the device size Collections.sort(points, (a, b) -> @@ -556,33 +585,30 @@ public class InvariantDeviceProfile { return out.multiply(1.0f / weights); } - @VisibleForTesting - static DisplayOption invDistWeightedInterpolate(float width, float height, - ArrayList points) { - float weights = 0; - - DisplayOption p = points.get(0); - if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) { - return p; - } - - DisplayOption out = new DisplayOption(); - for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) { - p = points.get(i); - float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER); - weights += w; - out.add(new DisplayOption().add(p).multiply(w)); - } - return out.multiply(1.0f / weights); - } - public DeviceProfile getDeviceProfile(Context context) { + Resources res = context.getResources(); + Configuration config = context.getResources().getConfiguration(); + + float availableWidth = config.screenWidthDp * res.getDisplayMetrics().density; + float availableHeight = config.screenHeightDp * res.getDisplayMetrics().density; + if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "getDeviceProfile: orientation=" - + context.getResources().getConfiguration().orientation); + Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, + "getDeviceProfile: orientation=" + config.orientation + + " size=" + availableWidth + "x" + availableHeight); } - return context.getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE ? landscapeProfile : portraitProfile; + DeviceProfile bestMatch = supportedProfiles.get(0); + float minDiff = Float.MAX_VALUE; + + for (DeviceProfile profile : supportedProfiles) { + float diff = Math.abs(profile.availableWidthPx - availableWidth) + + Math.abs(profile.availableHeightPx - availableHeight); + if (diff < minDiff) { + minDiff = diff; + bestMatch = profile; + } + } + return bestMatch; } private static float weight(float x0, float y0, float x1, float y1, float pow) { @@ -639,6 +665,9 @@ public class InvariantDeviceProfile { private final int numFolderRows; private final int numFolderColumns; + private final int numAllAppsColumns; + private final int numDatabaseAllAppsColumns; + private final int numHotseatIcons; private final int numDatabaseHotseatIcons; private final String dbFile; @@ -649,8 +678,6 @@ public class InvariantDeviceProfile { private final boolean isScalable; private final int devicePaddingId; - public final boolean visible; - private final SparseArray extraAttrs; public GridOption(Context context, AttributeSet attrs) { @@ -665,8 +692,17 @@ public class InvariantDeviceProfile { R.styleable.GridDisplayOption_defaultLayoutId, 0); demoModeLayoutId = a.getResourceId( R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId); - numDatabaseHotseatIcons = a.getInt( + + numAllAppsColumns = a.getInt( + R.styleable.GridDisplayOption_numAllAppsColumns, numColumns); + numDatabaseAllAppsColumns = a.getInt( + R.styleable.GridDisplayOption_numExtendedAllAppsColumns, 2 * numAllAppsColumns); + + numHotseatIcons = a.getInt( R.styleable.GridDisplayOption_numHotseatIcons, numColumns); + numDatabaseHotseatIcons = a.getInt( + R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons); + numFolderRows = a.getInt( R.styleable.GridDisplayOption_numFolderRows, numRows); numFolderColumns = a.getInt( @@ -677,24 +713,21 @@ public class InvariantDeviceProfile { devicePaddingId = a.getResourceId( R.styleable.GridDisplayOption_devicePaddingId, 0); - visible = a.getBoolean(R.styleable.GridDisplayOption_visible, true); - a.recycle(); - extraAttrs = Themes.createValueMap(context, attrs, IntArray.wrap(R.styleable.GridDisplayOption)); } } - private static final class DisplayOption { - private final GridOption grid; + @VisibleForTesting + static final class DisplayOption { + + public final GridOption grid; private final float minWidthDps; private final float minHeightDps; private final boolean canBeDefault; - private float numShownHotseatIcons; - private float numAllAppsColumns; private float minCellHeight; private float minCellWidth; private float borderSpacing; @@ -706,7 +739,7 @@ public class InvariantDeviceProfile { private float allAppsIconSize; private float allAppsIconTextSize; - DisplayOption(GridOption grid, Context context, AttributeSet attrs) { + DisplayOption(GridOption grid, Context context, AttributeSet attrs, int defaultFlagValue) { this.grid = grid; TypedArray a = context.obtainStyledAttributes( @@ -714,12 +747,9 @@ public class InvariantDeviceProfile { minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0); minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0); - canBeDefault = a.getBoolean( - R.styleable.ProfileDisplayOption_canBeDefault, false); - numShownHotseatIcons = a.getInt(R.styleable.ProfileDisplayOption_numShownHotseatIcons, - grid.numDatabaseHotseatIcons); - numAllAppsColumns = a.getInt(R.styleable.ProfileDisplayOption_numAllAppsColumns, - grid.numColumns); + + canBeDefault = a.getInt(R.styleable.ProfileDisplayOption_canBeDefault, 0) + == defaultFlagValue; minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0); minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0); @@ -748,16 +778,12 @@ public class InvariantDeviceProfile { minWidthDps = 0; minHeightDps = 0; canBeDefault = false; - numShownHotseatIcons = 0; - numAllAppsColumns = 0; minCellHeight = 0; minCellWidth = 0; borderSpacing = 0; } private DisplayOption multiply(float w) { - numShownHotseatIcons *= w; - numAllAppsColumns *= w; iconSize *= w; landscapeIconSize *= w; allAppsIconSize *= w; @@ -771,8 +797,6 @@ public class InvariantDeviceProfile { } private DisplayOption add(DisplayOption p) { - numShownHotseatIcons += p.numShownHotseatIcons; - numAllAppsColumns += p.numAllAppsColumns; iconSize += p.iconSize; landscapeIconSize += p.landscapeIconSize; allAppsIconSize += p.allAppsIconSize; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index f640118552..09c7b7aff0 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -189,7 +189,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.WidgetAddFlowHandler; -import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.custom.CustomWidgetManager; import com.android.launcher3.widget.model.WidgetsListBaseEntry; @@ -2332,8 +2331,7 @@ public class Launcher extends StatefulActivity implements Launche pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; - Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, - pendingInfo); + Bundle options = pendingInfo.getDefaultSizeOptions(this); boolean isDirectConfig = item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index a799b4a33a..be5463ec2c 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -74,9 +74,9 @@ import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.graphics.GridCustomizationsProvider; import com.android.launcher3.graphics.TintedDrawableSpan; import com.android.launcher3.icons.FastBitmapDrawable; -import com.android.launcher3.icons.IconProvider; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.icons.ShortcutCachingLogic; +import com.android.launcher3.icons.ThemedIconDrawable.ThemedAdaptiveIcon; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.pm.ShortcutConfigActivityInfo; @@ -596,13 +596,23 @@ public final class Utilities { */ public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height, Object[] outObj) { + Drawable icon = loadFullDrawableWithoutTheme(launcher, info, width, height, outObj); + if (icon instanceof ThemedAdaptiveIcon) { + icon = ((ThemedAdaptiveIcon) icon).getThemedDrawable(launcher); + } + return icon; + } + + private static Drawable loadFullDrawableWithoutTheme(Launcher launcher, ItemInfo info, + int width, int height, Object[] outObj) { LauncherAppState appState = LauncherAppState.getInstance(launcher); if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { LauncherActivityInfo activityInfo = launcher.getSystemService(LauncherApps.class) .resolveActivity(info.getIntent(), info.user); outObj[0] = activityInfo; - return activityInfo == null ? null : new IconProvider(launcher).getIcon( - activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi); + return activityInfo == null ? null : LauncherAppState.getInstance(launcher) + .getIconCache().getIconProvider().getIcon( + activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi); } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { if (info instanceof PendingAddShortcutInfo) { ShortcutConfigActivityInfo activityInfo = diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 05d6e045a6..7c5f99e07b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -456,7 +456,7 @@ public class Workspace extends PagedView } private boolean isTwoPanelEnabled() { - return mLauncher.mDeviceProfile.isTablet && FeatureFlags.ENABLE_TWO_PANEL_HOME.get(); + return mLauncher.mDeviceProfile.isTwoPanels; } @Override diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index aa99d5240a..c771e3e8d6 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -34,9 +34,9 @@ import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCRIM_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM; @@ -166,10 +166,9 @@ public class WorkspaceStateTransitionAnimation { propertySetter.setFloat(sysUiScrim, SYSUI_PROGRESS, state.hasFlag(FLAG_HAS_SYS_UI_SCRIM) ? 1 : 0, LINEAR); - propertySetter.setViewBackgroundColor(mLauncher.getScrimView(), state.getWorkspaceScrimColor(mLauncher), - config.getInterpolator(ANIM_WORKSPACE_SCRIM_FADE, LINEAR)); + config.getInterpolator(ANIM_SCRIM_FADE, LINEAR)); } public void applyChildState(LauncherState state, CellLayout cl, int childIndex) { diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 40f7ab167c..119a91ffba 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -157,7 +157,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo @Override public void onDeviceProfileChanged(DeviceProfile dp) { for (AdapterHolder holder : mAH) { - holder.adapter.setAppsPerRow(dp.inv.numAllAppsColumns); + holder.adapter.setAppsPerRow(dp.numShownAllAppsColumns); if (holder.recyclerView != null) { // Remove all views and clear the pool, while keeping the data same. After this // call, all the viewHolders will be recreated. diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 5b4c4c5c36..70588ea317 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -283,7 +283,7 @@ public class AllAppsGridAdapter extends mOnIconClickListener = launcher.getItemOnClickListener(); mSearchAdapterProvider = searchAdapterProvider; - setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns); + setAppsPerRow(mLauncher.getDeviceProfile().numShownAllAppsColumns); } public void setAppsPerRow(int appsPerRow) { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index bad87043ef..3b88a0b2af 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -87,6 +87,10 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag( "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview"); + public static final BooleanFlag ENABLE_QUICKSTEP_WIDGET_APP_START = getDebugFlag( + "ENABLE_QUICKSTEP_WIDGET_APP_START", false, + "Enable Quickstep animation when launching activities from an app widget"); + // Keep as DeviceFlag to allow remote disable in emergency. public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag( "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", true, "Show chip hints on the overview screen"); @@ -137,8 +141,8 @@ public final class FeatureFlags { public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag( "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm"); - public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag( - "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker"); + public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag( + "ENABLE_THEMED_ICONS", false, "Enable themed icons on workspace"); // Keep as DeviceFlag for remote disable in emergency. public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag( @@ -226,6 +230,10 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag( "ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets"); + public static final BooleanFlag ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER = new DeviceFlag( + "ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER", true, + "Enables a local filter for recommended widgets."); + public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false, "Sends a notification whenever launcher encounters an uncaught exception."); diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index d5a04a647d..5ba36f21e3 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -64,7 +64,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetCell; import com.android.launcher3.widget.WidgetCellPreview; -import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetImageView; import com.android.launcher3.widget.WidgetManagerHelper; @@ -234,7 +233,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo); pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX); pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY); - mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo); + mWidgetOptions = pendingInfo.getDefaultSizeOptions(this); mWidgetCell.getWidgetView().setTag(pendingInfo); applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache())); diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index d7f6cdb8bb..575b8fdd99 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -22,7 +22,6 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.Utilities.ATLEAST_Q; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import android.animation.ValueAnimator; import android.content.ComponentName; import android.content.res.Resources; import android.graphics.Point; @@ -307,17 +306,12 @@ public class DragController implements DragDriver.EventListener, TouchController mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/); } mIsInPreDrag = false; + mDragObject.dragView.onDragStart(); for (DragListener listener : new ArrayList<>(mListeners)) { listener.onDragStart(mDragObject, mOptions); } } - public void addFirstFrameAnimationHelper(ValueAnimator anim) { - if (mDragObject != null && mDragObject.dragView != null) { - mDragObject.dragView.mFirstFrameAnimatorHelper.addTo(anim); - } - } - public Optional getLogInstanceId() { return Optional.ofNullable(mDragObject) .map(dragObject -> dragObject.logInstanceId); diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 68a8af238e..30ee9ecddd 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -51,7 +51,6 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; -import com.android.launcher3.FirstFrameAnimatorHelper; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; @@ -62,6 +61,7 @@ import com.android.launcher3.icons.FastBitmapDrawable; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statemanager.StateManager.StateListener; +import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.BaseDragLayer; @@ -85,12 +85,13 @@ public class DragView extends FrameLayout implements StateListener { + new Handler(Looper.getMainLooper()).post(() -> mOnDragStartCallback.add(() -> { + // TODO: Consider fade-in animation // Assign the variable on the UI thread to avoid race conditions. mScaledMaskPath = mask; // Avoid relayout as we do not care about children affecting layout @@ -290,11 +291,18 @@ public class DragView extends FrameLayout implements StateListener depth) && type != XmlPullParser.END_DOCUMENT) { if ((type == XmlPullParser.START_TAG) && GridOption.TAG_NAME.equals(parser.getName())) { - GridOption option = new GridOption(getContext(), Xml.asAttributeSet(parser)); - if (option.visible) { - result.add(option); - } + result.add(new GridOption(getContext(), Xml.asAttributeSet(parser))); } } } catch (IOException | XmlPullParserException e) { diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 31764c57f5..f5b6890867 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -20,7 +20,6 @@ import static android.view.View.MeasureSpec.makeMeasureSpec; import static android.view.View.VISIBLE; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; -import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER; import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks; import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially; @@ -118,7 +117,7 @@ import java.util.concurrent.TimeoutException; * 4) Measure and draw the view on a canvas */ @TargetApi(Build.VERSION_CODES.O) -public class LauncherPreviewRenderer extends ContextThemeWrapper +public class LauncherPreviewRenderer extends ContextWrapper implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 { private static final String TAG = "LauncherPreviewRenderer"; @@ -220,11 +219,11 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper private final CellLayout mWorkspace; public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) { - super(context, R.style.AppTheme); + super(context); mUiHandler = new Handler(Looper.getMainLooper()); mContext = context; mIdp = idp; - mDp = idp.portraitProfile.copy(context); + mDp = idp.getDeviceProfile(context).copy(context); mMigrated = migrated; // TODO: get correct insets once display cutout API is available. @@ -271,14 +270,6 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper return mRootView; } - public boolean shouldShowRealLauncherPreview() { - return ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get(); - } - - public boolean shouldShowQsb() { - return FeatureFlags.QSB_ON_FIRST_SCREEN; - } - @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { if ("TextClock".equals(name)) { @@ -402,107 +393,88 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper } private void populate() { - if (shouldShowRealLauncherPreview()) { - WorkspaceFetcher fetcher; - PreviewContext previewContext = null; - if (mMigrated) { - previewContext = new PreviewContext(mContext, mIdp); - LauncherAppState appForPreview = new LauncherAppState( - previewContext, null /* iconCacheFileName */); - fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview); - MODEL_EXECUTOR.execute(fetcher); - } else { - fetcher = new WorkspaceItemsInfoFetcher(); - LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask( - (LauncherModel.ModelUpdateTask) fetcher); - } - WorkspaceResult workspaceResult = fetcher.get(); - if (previewContext != null) { - previewContext.onDestroy(); - } - - if (workspaceResult == null) { - return; - } - - // Separate the items that are on the current screen, and the other remaining items. - ArrayList currentWorkspaceItems = new ArrayList<>(); - ArrayList otherWorkspaceItems = new ArrayList<>(); - ArrayList currentAppWidgets = new ArrayList<>(); - ArrayList otherAppWidgets = new ArrayList<>(); - filterCurrentWorkspaceItems(0 /* currentScreenId */, - workspaceResult.mWorkspaceItems, currentWorkspaceItems, - otherWorkspaceItems); - filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets, - currentAppWidgets, otherAppWidgets); - sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems); - for (ItemInfo itemInfo : currentWorkspaceItems) { - switch (itemInfo.itemType) { - case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_SHORTCUT: - case Favorites.ITEM_TYPE_DEEP_SHORTCUT: - inflateAndAddIcon((WorkspaceItemInfo) itemInfo); - break; - case Favorites.ITEM_TYPE_FOLDER: - inflateAndAddFolder((FolderInfo) itemInfo); - break; - default: - break; - } - } - for (ItemInfo itemInfo : currentAppWidgets) { - switch (itemInfo.itemType) { - case Favorites.ITEM_TYPE_APPWIDGET: - case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: - if (mMigrated) { - inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo, - workspaceResult.mWidgetProvidersMap); - } else { - inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo, - workspaceResult.mWidgetsModel); - } - break; - default: - break; - } - } - IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems, - mDp.numShownHotseatIcons); - List predictions = workspaceResult.mHotseatPredictions == null - ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items; - int count = Math.min(ranks.size(), predictions.size()); - for (int i = 0; i < count; i++) { - int rank = ranks.get(i); - WorkspaceItemInfo itemInfo = - new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i)); - itemInfo.container = CONTAINER_HOTSEAT_PREDICTION; - itemInfo.rank = rank; - itemInfo.cellX = mHotseat.getCellXFromOrder(rank); - itemInfo.cellY = mHotseat.getCellYFromOrder(rank); - itemInfo.screenId = rank; - inflateAndAddPredictedIcon(itemInfo); - } + WorkspaceFetcher fetcher; + PreviewContext previewContext = null; + if (mMigrated) { + previewContext = new PreviewContext(mContext, mIdp); + LauncherAppState appForPreview = new LauncherAppState( + previewContext, null /* iconCacheFileName */); + fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview); + MODEL_EXECUTOR.execute(fetcher); } else { - // Add hotseat icons - for (int i = 0; i < mDp.numShownHotseatIcons; i++) { - WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo); - info.container = Favorites.CONTAINER_HOTSEAT; - info.screenId = i; - inflateAndAddIcon(info); + fetcher = new WorkspaceItemsInfoFetcher(); + LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask( + (LauncherModel.ModelUpdateTask) fetcher); + } + WorkspaceResult workspaceResult = fetcher.get(); + if (previewContext != null) { + previewContext.onDestroy(); + } + + if (workspaceResult == null) { + return; + } + + // Separate the items that are on the current screen, and the other remaining items. + ArrayList currentWorkspaceItems = new ArrayList<>(); + ArrayList otherWorkspaceItems = new ArrayList<>(); + ArrayList currentAppWidgets = new ArrayList<>(); + ArrayList otherAppWidgets = new ArrayList<>(); + filterCurrentWorkspaceItems(0 /* currentScreenId */, + workspaceResult.mWorkspaceItems, currentWorkspaceItems, + otherWorkspaceItems); + filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets, + currentAppWidgets, otherAppWidgets); + sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems); + for (ItemInfo itemInfo : currentWorkspaceItems) { + switch (itemInfo.itemType) { + case Favorites.ITEM_TYPE_APPLICATION: + case Favorites.ITEM_TYPE_SHORTCUT: + case Favorites.ITEM_TYPE_DEEP_SHORTCUT: + inflateAndAddIcon((WorkspaceItemInfo) itemInfo); + break; + case Favorites.ITEM_TYPE_FOLDER: + inflateAndAddFolder((FolderInfo) itemInfo); + break; + default: + break; } - // Add workspace icons - for (int i = 0; i < mIdp.numColumns; i++) { - WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo); - info.container = Favorites.CONTAINER_DESKTOP; - info.screenId = 0; - info.cellX = i; - info.cellY = mIdp.numRows - 1; - inflateAndAddIcon(info); + } + for (ItemInfo itemInfo : currentAppWidgets) { + switch (itemInfo.itemType) { + case Favorites.ITEM_TYPE_APPWIDGET: + case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: + if (mMigrated) { + inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo, + workspaceResult.mWidgetProvidersMap); + } else { + inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo, + workspaceResult.mWidgetsModel); + } + break; + default: + break; } } + IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems, + mDp.numShownHotseatIcons); + List predictions = workspaceResult.mHotseatPredictions == null + ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items; + int count = Math.min(ranks.size(), predictions.size()); + for (int i = 0; i < count; i++) { + int rank = ranks.get(i); + WorkspaceItemInfo itemInfo = + new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i)); + itemInfo.container = CONTAINER_HOTSEAT_PREDICTION; + itemInfo.rank = rank; + itemInfo.cellX = mHotseat.getCellXFromOrder(rank); + itemInfo.cellY = mHotseat.getCellYFromOrder(rank); + itemInfo.screenId = rank; + inflateAndAddPredictedIcon(itemInfo); + } // Add first page QSB - if (shouldShowQsb()) { + if (FeatureFlags.QSB_ON_FIRST_SCREEN) { View qsb = mHomeElementInflater.inflate( R.layout.search_container_workspace, mWorkspace, false); CellLayout.LayoutParams lp = diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java index fdc3a947a3..6193570e1f 100644 --- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java +++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java @@ -20,6 +20,7 @@ import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_A import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import android.app.WallpaperColors; import android.content.Context; import android.hardware.display.DisplayManager; import android.os.Bundle; @@ -28,18 +29,23 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.view.ContextThemeWrapper; import android.view.Display; import android.view.SurfaceControlViewHost; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.R; import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.model.GridSizeMigrationTaskV2; +import com.android.launcher3.util.Themes; +import com.android.launcher3.widget.LocalColorExtractor; import java.util.concurrent.TimeUnit; /** Render preview using surface view. */ +@SuppressWarnings("NewApi") public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { private static final int FADE_IN_ANIMATION_DURATION = 200; @@ -50,6 +56,7 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { private static final String KEY_DISPLAY_ID = "display_id"; private static final String KEY_SURFACE_PACKAGE = "surface_package"; private static final String KEY_CALLBACK = "callback"; + private static final String KEY_COLORS = "wallpaper_colors"; private final Context mContext; private final InvariantDeviceProfile mIdp; @@ -57,6 +64,7 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { private final int mWidth; private final int mHeight; private final Display mDisplay; + private final WallpaperColors mWallpaperColors; private SurfaceControlViewHost mSurfaceControlViewHost; @@ -68,6 +76,8 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { if (gridName == null) { gridName = InvariantDeviceProfile.getCurrentGridName(context); } + mWallpaperColors = bundle.getParcelable(KEY_COLORS); + mIdp = new InvariantDeviceProfile(context, gridName); mHostToken = bundle.getBinder(KEY_HOST_TOKEN); @@ -100,6 +110,19 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { MODEL_EXECUTOR.post(() -> { final boolean success = doGridMigrationIfNecessary(); + final Context inflationContext; + if (mWallpaperColors != null) { + // Workaround to create a themed context + Context context = mContext.createDisplayContext(mDisplay); + LocalColorExtractor.newInstance(mContext) + .applyColorsOverride(context, mWallpaperColors); + + inflationContext = new ContextThemeWrapper(context, + Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints())); + } else { + inflationContext = new ContextThemeWrapper(mContext, R.style.AppTheme); + } + MAIN_EXECUTOR.post(() -> { // If mSurfaceControlViewHost is null due to any reason (e.g. binder died, // happening when user leaves the preview screen before preview rendering finishes), @@ -109,7 +132,8 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { return; } - View view = new LauncherPreviewRenderer(mContext, mIdp, success).getRenderedView(); + View view = new LauncherPreviewRenderer(inflationContext, mIdp, success) + .getRenderedView(); // This aspect scales the view to fit in the surface and centers it final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(), mHeight / (float) view.getMeasuredHeight()); diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index a2c0f5cdf4..bc93a1e3d9 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -52,7 +52,6 @@ import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutKey; -import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; @@ -94,7 +93,7 @@ public class IconCache extends BaseIconCache { mLauncherApps = mContext.getSystemService(LauncherApps.class); mUserManager = UserCache.INSTANCE.get(mContext); mInstantAppResolver = InstantAppResolver.newInstance(mContext); - mIconProvider = new IconProvider(context); + mIconProvider = new IconProvider(context, true /* supportsIconTheme */); } @Override @@ -107,8 +106,12 @@ public class IconCache extends BaseIconCache { return mInstantAppResolver.isInstantApp(info); } + public IconProvider getIconProvider() { + return mIconProvider; + } + @Override - protected BaseIconFactory getIconFactory() { + public BaseIconFactory getIconFactory() { return LauncherIcons.obtain(mContext); } @@ -333,15 +336,6 @@ public class IconCache extends BaseIconCache { + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get(); } - @Override - protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) { - if (mIconProvider.isClockIcon(cacheKey)) { - // For clock icon, we always load the dynamic icon - return false; - } - return super.getEntryFromDB(cacheKey, entry, lowRes); - } - /** * Interface for receiving itemInfo with high-res icon. */ diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java index 93de35a88a..8fc397738e 100644 --- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java +++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.pm.LauncherActivityInfo; import android.os.UserHandle; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.icons.cache.CachingLogic; import com.android.launcher3.util.ResourceBasedOverride; @@ -56,8 +57,8 @@ public class LauncherActivityCachingLogic @Override public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) { try (LauncherIcons li = LauncherIcons.obtain(context)) { - return li.createBadgedIconBitmap(new IconProvider(context) - .getIcon(object, li.mFillResIconDpi), + return li.createBadgedIconBitmap(LauncherAppState.getInstance(context) + .getIconCache().getIconProvider().getIcon(object, li.mFillResIconDpi), object.getUser(), object.getApplicationInfo().targetSdkVersion); } } diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java index 76b2ab0e85..0754c293b6 100644 --- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java +++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java @@ -16,6 +16,8 @@ package com.android.launcher3.model.data; +import static com.android.launcher3.config.FeatureFlags.ENABLE_THEMED_ICONS; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -223,7 +225,15 @@ public abstract class ItemInfoWithIcon extends ItemInfo { * Returns a FastBitmapDrawable with the icon. */ public FastBitmapDrawable newIcon(Context context) { - FastBitmapDrawable drawable = bitmap.newIcon(context); + return newIcon(context, false); + } + + /** + * Returns a FastBitmapDrawable with the icon and context theme applied + */ + public FastBitmapDrawable newIcon(Context context, boolean applyTheme) { + FastBitmapDrawable drawable = applyTheme && ENABLE_THEMED_ICONS.get() + ? bitmap.newThemedIcon(context) : bitmap.newIcon(context); drawable.setIsDisabled(isDisabled()); return drawable; } diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java index a191df4b38..22c3f58574 100644 --- a/src/com/android/launcher3/qsb/QsbContainerView.java +++ b/src/com/android/launcher3/qsb/QsbContainerView.java @@ -20,7 +20,7 @@ import static android.appwidget.AppWidgetManager.ACTION_APPWIDGET_BIND; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER; -import static com.android.launcher3.Utilities.ATLEAST_S; +import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions; import android.app.Activity; import android.app.Fragment; @@ -32,11 +32,9 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.graphics.Rect; import android.os.Bundle; import android.provider.Settings; import android.util.AttributeSet; -import android.util.SizeF; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -45,7 +43,6 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; @@ -53,8 +50,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.FragmentWithPreview; -import java.util.ArrayList; - /** * A frame layout which contains a QSB. This internally uses fragment to bind the view, which * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}. @@ -163,7 +158,7 @@ public class QsbContainerView extends FrameLayout { protected String mKeyWidgetId = "qsb_widget_id"; private QsbWidgetHost mQsbWidgetHost; - private AppWidgetProviderInfo mWidgetInfo; + protected AppWidgetProviderInfo mWidgetInfo; private QsbWidgetHostView mQsb; // We need to store the orientation here, due to a bug (b/64916689) that results in widgets @@ -297,19 +292,7 @@ public class QsbContainerView extends FrameLayout { protected Bundle createBindOptions() { InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext()); - - Bundle opts = new Bundle(); - ArrayList sizes = AppWidgetResizeFrame - .getWidgetSizes(getContext(), idp.numColumns, 1); - Rect size = AppWidgetResizeFrame.getMinMaxSizes(sizes, null /* outRect */); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom); - if (ATLEAST_S) { - opts.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizes); - } - return opts; + return getWidgetSizeOptions(getContext(), mWidgetInfo.provider, idp.numColumns, 1); } protected View getDefaultView(ViewGroup container, boolean showSetupIcon) { diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java index f5e74b7168..5999091786 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -87,8 +87,8 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity if (mDragLayer != null) { return; } - InvariantDeviceProfile currentDisplayIdp = - new InvariantDeviceProfile(this, getWindow().getDecorView().getDisplay()); + InvariantDeviceProfile currentDisplayIdp = new InvariantDeviceProfile( + this, getWindow().getDecorView().getDisplay()); // Disable transpose layout and use multi-window mode so that the icons are scaled properly mDeviceProfile = currentDisplayIdp.getDeviceProfile(this) diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java index 40630d3c2e..f78f6dd40c 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java @@ -29,7 +29,6 @@ import android.widget.GridView; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.model.data.ItemInfo; @@ -109,8 +108,6 @@ public class SecondaryDragLayer extends BaseDragLayer setMeasuredDimension(width, height); DeviceProfile grid = mActivity.getDeviceProfile(); - InvariantDeviceProfile idp = grid.inv; - int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); @@ -118,10 +115,10 @@ public class SecondaryDragLayer extends BaseDragLayer int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx + grid.cellLayoutPaddingLeftRightPx); - int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding; + int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + padding; int appsWidth = Math.min(width, maxWidth); - int maxHeight = grid.allAppsCellHeightPx * idp.numAllAppsColumns + padding; + int maxHeight = grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + padding; int appsHeight = Math.min(height, maxHeight); mAppsView.measure( diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java index 2f26b4f70f..bd6f7d3a74 100644 --- a/src/com/android/launcher3/states/StateAnimationConfig.java +++ b/src/com/android/launcher3/states/StateAnimationConfig.java @@ -58,8 +58,7 @@ public class StateAnimationConfig { ANIM_OVERVIEW_TRANSLATE_Y, ANIM_OVERVIEW_FADE, ANIM_ALL_APPS_FADE, - ANIM_WORKSPACE_SCRIM_FADE, - ANIM_ALL_APPS_HEADER_FADE, + ANIM_SCRIM_FADE, ANIM_OVERVIEW_MODAL, ANIM_DEPTH, ANIM_OVERVIEW_ACTIONS_FADE, @@ -77,13 +76,12 @@ public class StateAnimationConfig { public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8; public static final int ANIM_OVERVIEW_FADE = 9; public static final int ANIM_ALL_APPS_FADE = 10; - public static final int ANIM_WORKSPACE_SCRIM_FADE = 11; - public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions - public static final int ANIM_OVERVIEW_MODAL = 13; - public static final int ANIM_DEPTH = 14; - public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15; + public static final int ANIM_SCRIM_FADE = 11; + public static final int ANIM_OVERVIEW_MODAL = 12; + public static final int ANIM_DEPTH = 13; + public static final int ANIM_OVERVIEW_ACTIONS_FADE = 14; - private static final int ANIM_TYPES_COUNT = 16; + private static final int ANIM_TYPES_COUNT = 15; protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT]; diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index 75c089e754..b7512077a9 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -18,8 +18,10 @@ package com.android.launcher3.util; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -32,16 +34,22 @@ import android.graphics.Point; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.os.Build; +import android.util.ArraySet; import android.util.Log; import android.view.Display; +import android.view.WindowMetrics; import androidx.annotation.AnyThread; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import com.android.launcher3.Utilities; +import com.android.launcher3.uioverrides.ApiWrapper; import java.util.ArrayList; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; /** * Utility class to cache properties of default display to avoid a system RPC on every call. @@ -54,13 +62,14 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(DisplayController::new); - public static final int CHANGE_SIZE = 1 << 0; + public static final int CHANGE_ACTIVE_SCREEN = 1 << 0; public static final int CHANGE_ROTATION = 1 << 1; public static final int CHANGE_FRAME_DELAY = 1 << 2; public static final int CHANGE_DENSITY = 1 << 3; + public static final int CHANGE_SUPPORTED_BOUNDS = 1 << 4; - public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION - | CHANGE_FRAME_DELAY | CHANGE_DENSITY; + public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION + | CHANGE_FRAME_DELAY | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS; private final Context mContext; private final DisplayManager mDM; @@ -87,7 +96,22 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); } - mInfo = new Info(getContext(display), display); + // Create a single holder for all internal displays. External display holders created + // lazily. + Set extraInternalDisplays = new ArraySet<>(); + for (Display d : mDM.getDisplays()) { + if (ApiWrapper.isInternalDisplay(display) && d.getDisplayId() != DEFAULT_DISPLAY) { + Point size = new Point(); + d.getRealSize(size); + extraInternalDisplays.add(new PortraitSize(size.x, size.y)); + } + } + + if (extraInternalDisplays.isEmpty() || !Utilities.ATLEAST_S) { + mInfo = new Info(createDisplayInfoContext(display), display, Collections.emptySet()); + } else { + mInfo = new Info(mWindowContext, display, extraInternalDisplays); + } mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler()); } @@ -139,7 +163,7 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { */ private void onConfigChanged(Intent intent) { Configuration config = mContext.getResources().getConfiguration(); - if (config.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) { + if (mInfo.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) { Log.d(TAG, "Configuration changed, notifying listeners"); Display display = mDM.getDisplay(DEFAULT_DISPLAY); if (display != null) { @@ -157,8 +181,7 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { || config.fontScale != mInfo.fontScale || display.getRotation() != mInfo.rotation || !mInfo.mScreenSizeDp.equals( - Math.min(config.screenHeightDp, config.screenWidthDp), - Math.max(config.screenHeightDp, config.screenWidthDp))) { + new PortraitSize(config.screenHeightDp, config.screenWidthDp))) { handleInfoChange(display); } } @@ -178,18 +201,23 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { return mInfo; } - private Context getContext(Display display) { - return Utilities.ATLEAST_S ? mWindowContext : mContext.createDisplayContext(display); + private Context createDisplayInfoContext(Display display) { + return Utilities.ATLEAST_S + ? mContext.createWindowContext(display, TYPE_APPLICATION, null) + : mContext.createDisplayContext(display); } @AnyThread private void handleInfoChange(Display display) { Info oldInfo = mInfo; - Context context = getContext(display); - Info newInfo = new Info(context, display); + Set extraDisplaysSizes = oldInfo.mAllSizes.size() > 1 + ? oldInfo.mAllSizes : Collections.emptySet(); + + Context displayContext = createDisplayInfoContext(display); + Info newInfo = new Info(displayContext, display, extraDisplaysSizes); int change = 0; - if (newInfo.hasDifferentSize(oldInfo)) { - change |= CHANGE_SIZE; + if (!newInfo.mScreenSizeDp.equals(oldInfo.mScreenSizeDp)) { + change |= CHANGE_ACTIVE_SCREEN; } if (newInfo.rotation != oldInfo.rotation) { change |= CHANGE_ROTATION; @@ -200,11 +228,14 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) { change |= CHANGE_DENSITY; } + if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) { + change |= CHANGE_SUPPORTED_BOUNDS; + } if (change != 0) { mInfo = newInfo; final int flags = change; - MAIN_EXECUTOR.execute(() -> notifyChange(context, flags)); + MAIN_EXECUTOR.execute(() -> notifyChange(displayContext, flags)); } } @@ -224,13 +255,18 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { public final float fontScale; public final int densityDpi; - private final Point mScreenSizeDp; + private final PortraitSize mScreenSizeDp; + private final Set mAllSizes; - public final Point realSize; - public final Point smallestSize; - public final Point largestSize; + public final Point currentSize; + + public final Set supportedBounds = new ArraySet<>(); public Info(Context context, Display display) { + this(context, display, Collections.emptySet()); + } + + private Info(Context context, Display display, Set extraDisplaysSizes) { id = display.getDisplayId(); rotation = display.getRotation(); @@ -238,35 +274,67 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { Configuration config = context.getResources().getConfiguration(); fontScale = config.fontScale; densityDpi = config.densityDpi; - mScreenSizeDp = new Point( - Math.min(config.screenHeightDp, config.screenWidthDp), - Math.max(config.screenHeightDp, config.screenWidthDp)); + mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp); singleFrameMs = getSingleFrameMs(display); + currentSize = new Point(); - realSize = new Point(); - smallestSize = new Point(); - largestSize = new Point(); + display.getRealSize(currentSize); - display.getRealSize(realSize); - display.getCurrentSizeRange(smallestSize, largestSize); + if (extraDisplaysSizes.isEmpty() || !Utilities.ATLEAST_S) { + Point smallestSize = new Point(); + Point largestSize = new Point(); + display.getCurrentSizeRange(smallestSize, largestSize); + + int portraitWidth = Math.min(currentSize.x, currentSize.y); + int portraitHeight = Math.max(currentSize.x, currentSize.y); + + supportedBounds.add(new WindowBounds(portraitWidth, portraitHeight, + smallestSize.x, largestSize.y)); + supportedBounds.add(new WindowBounds(portraitHeight, portraitWidth, + largestSize.x, smallestSize.y)); + mAllSizes = Collections.singleton(new PortraitSize(currentSize.x, currentSize.y)); + } else { + mAllSizes = new ArraySet<>(extraDisplaysSizes); + mAllSizes.add(new PortraitSize(currentSize.x, currentSize.y)); + Set metrics = WindowManagerCompat.getDisplayProfiles( + context, mAllSizes, densityDpi, + ApiWrapper.TASKBAR_DRAWN_IN_PROCESS); + metrics.forEach(wm -> supportedBounds.add(WindowBounds.fromWindowMetrics(wm))); + } } - private boolean hasDifferentSize(Info info) { - if (!realSize.equals(info.realSize) - && !realSize.equals(info.realSize.y, info.realSize.x)) { - Log.d(TAG, String.format("Display size changed from %s to %s", - info.realSize, realSize)); - return true; - } + /** + * Returns true if the bounds represent a tablet + */ + public boolean isTablet(WindowBounds bounds) { + return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()), + densityDpi) >= MIN_TABLET_WIDTH; + } + } - if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) { - Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]", - smallestSize, largestSize, info.smallestSize, info.largestSize)); - return true; - } + /** + * Utility class to hold a size information in an orientation independent way + */ + public static class PortraitSize { + public final int width, height; - return false; + public PortraitSize(int w, int h) { + width = Math.min(w, h); + height = Math.max(w, h); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PortraitSize that = (PortraitSize) o; + return width == that.width && height == that.height; + } + + @Override + public int hashCode() { + return Objects.hash(width, height); } } diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java index 3c2fb62c16..c92770e8fd 100644 --- a/src/com/android/launcher3/util/WindowBounds.java +++ b/src/com/android/launcher3/util/WindowBounds.java @@ -15,11 +15,16 @@ */ package com.android.launcher3.util; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; +import android.view.WindowInsets.Type; +import android.view.WindowMetrics; import androidx.annotation.Nullable; +import java.util.Objects; + /** * Utility class to hold information about window position and layout */ @@ -36,6 +41,18 @@ public class WindowBounds { bounds.height() - insets.top - insets.bottom); } + public WindowBounds(int width, int height, int availableWidth, int availableHeight) { + this.bounds = new Rect(0, 0, width, height); + this.availableSize = new Point(availableWidth, availableHeight); + // We don't care about insets in this case + this.insets = new Rect(0, 0, width - availableWidth, height - availableHeight); + } + + @Override + public int hashCode() { + return Objects.hash(bounds, insets); + } + @Override public boolean equals(@Nullable Object obj) { if (!(obj instanceof WindowBounds)) { @@ -44,4 +61,30 @@ public class WindowBounds { WindowBounds other = (WindowBounds) obj; return other.bounds.equals(bounds) && other.insets.equals(insets); } + + @Override + public String toString() { + return "WindowBounds{" + + "bounds=" + bounds + + ", insets=" + insets + + ", availableSize=" + availableSize + + '}'; + } + + /** + * Returns true if the device is in landscape orientation + */ + public final boolean isLandscape() { + return availableSize.x > availableSize.y; + } + + /** + * Returns the bounds corresponding to the provided WindowMetrics + */ + @SuppressWarnings("NewApi") + public static WindowBounds fromWindowMetrics(WindowMetrics wm) { + Insets insets = wm.getWindowInsets().getInsets(Type.systemBars()); + return new WindowBounds(wm.getBounds(), + new Rect(insets.left, insets.top, insets.right, insets.bottom)); + } } diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java new file mode 100644 index 0000000000..38a63de592 --- /dev/null +++ b/src/com/android/launcher3/util/WindowManagerCompat.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE; +import static com.android.launcher3.Utilities.dpiFromPx; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.Build; +import android.view.WindowInsets; +import android.view.WindowInsets.Type; +import android.view.WindowManager; +import android.view.WindowMetrics; + +import com.android.launcher3.R; +import com.android.launcher3.ResourceUtils; +import com.android.launcher3.util.DisplayController.PortraitSize; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Utility class to simulate window manager APIs until proper APIs are available + */ +@TargetApi(Build.VERSION_CODES.S) +public class WindowManagerCompat { + + public static final int MIN_TABLET_WIDTH = 600; + + /** + * Returns a set of supported render sizes for a set of internal displays. + * This is a temporary workaround which assumes only nav-bar insets change across displays + * @param consumeTaskBar if true, it assumes that task bar is part of the app window + * and ignores any insets because of task bar. + */ + public static Set getDisplayProfiles( + Context windowContext, Collection allDisplaySizes, + int densityDpi, boolean consumeTaskBar) { + WindowInsets metrics = windowContext.getSystemService(WindowManager.class) + .getMaximumWindowMetrics().getWindowInsets(); + boolean hasNavbar = ResourceUtils.getIntegerByName( + "config_navBarInteractionMode", + windowContext.getResources(), + INVALID_RESOURCE_HANDLE) != 0; + + WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(metrics); + + Set result = new HashSet<>(); + for (PortraitSize size : allDisplaySizes) { + int swDP = (int) dpiFromPx(size.width, densityDpi); + boolean isTablet = swDP >= MIN_TABLET_WIDTH; + + final Insets portraitNav, landscapeNav; + if (isTablet && !consumeTaskBar) { + portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources() + .getDimensionPixelSize(R.dimen.taskbar_size)); + } else if (hasNavbar) { + portraitNav = Insets.of(0, 0, 0, + getSystemResource(windowContext, "navigation_bar_height", swDP)); + landscapeNav = isTablet + ? Insets.of(0, 0, 0, getSystemResource(windowContext, + "navigation_bar_height_landscape", swDP)) + : Insets.of(0, 0, getSystemResource(windowContext, + "navigation_bar_width", swDP), 0); + } else { + portraitNav = landscapeNav = Insets.of(0, 0, 0, 0); + } + + result.add(new WindowMetrics( + new Rect(0, 0, size.width, size.height), + insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build())); + result.add(new WindowMetrics( + new Rect(0, 0, size.height, size.width), + insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build())); + } + return result; + } + + private static int getSystemResource(Context context, String key, int swDp) { + int resourceId = context.getResources().getIdentifier(key, "dimen", "android"); + if (resourceId > 0) { + Configuration conf = new Configuration(); + conf.smallestScreenWidthDp = swDp; + return context.createConfigurationContext(conf) + .getResources().getDimensionPixelSize(resourceId); + } + return 0; + } +} diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java index 3a24c3d21a..149ac57bfb 100644 --- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java @@ -44,7 +44,7 @@ public class DeferredAppWidgetHostView extends LauncherAppWidgetHostView { mPaint = new TextPaint(); mPaint.setColor(Color.WHITE); mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, - mLauncher.getDeviceProfile().getFullScreenProfile().iconTextSizePx, + mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics())); setBackgroundResource(R.drawable.bg_deferred_app_widget); } diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java index ad61495de8..de511cd00b 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java @@ -13,6 +13,7 @@ import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.UserHandle; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; @@ -66,20 +67,25 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo } public void initSpans(Context context, InvariantDeviceProfile idp) { - Point landCellSize = idp.landscapeProfile.getCellSize(); - Point portCellSize = idp.portraitProfile.getCellSize(); - // Always assume we're working with the smallest span to make sure we // reserve enough space in both orientations. - float smallestCellWidth = Math.min(landCellSize.x, portCellSize.x); - float smallestCellHeight = Math.min(landCellSize.y, portCellSize.y); + float smallestCellWidth = Float.MAX_VALUE; + float smallestCellHeight = Float.MAX_VALUE; + + Point cellSize = new Point(); + boolean isWidgetPadded = false; + for (DeviceProfile dp : idp.supportedProfiles) { + dp.getCellSize(cellSize); + smallestCellWidth = Math.min(smallestCellWidth, cellSize.x); + smallestCellHeight = Math.min(smallestCellHeight, cellSize.y); + isWidgetPadded = isWidgetPadded || !dp.shouldInsetWidgets(); + } // We want to account for the extra amount of padding that we are adding to the widget // to ensure that it gets the full amount of space that it has requested. // If grids supports insetting widgets, we do not account for widget padding. Rect widgetPadding = new Rect(); - if (!idp.landscapeProfile.shouldInsetWidgets() - || !idp.portraitProfile.shouldInsetWidgets()) { + if (isWidgetPadded) { AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding); } diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java index 097158b072..be4faea68f 100644 --- a/src/com/android/launcher3/widget/LocalColorExtractor.java +++ b/src/com/android/launcher3/widget/LocalColorExtractor.java @@ -16,6 +16,7 @@ package com.android.launcher3.widget; +import android.app.WallpaperColors; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.graphics.RectF; @@ -43,7 +44,10 @@ public class LocalColorExtractor implements ResourceBasedOverride { void onColorsChanged(RectF rect, SparseIntArray extractedColors); } - static LocalColorExtractor newInstance(Context context) { + /** + * Creates a new instance of LocalColorExtractor + */ + public static LocalColorExtractor newInstance(Context context) { return Overrides.getObject(LocalColorExtractor.class, context.getApplicationContext(), R.string.local_colors_extraction_class); } @@ -62,4 +66,9 @@ public class LocalColorExtractor implements ResourceBasedOverride { public void removeLocations() { // no-op } + + /** + * Updates the base context to contain the colors override + */ + public void applyColorsOverride(Context base, WallpaperColors colors) { } } diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java index ee0b84e815..3377abbb7b 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -15,9 +15,11 @@ */ package com.android.launcher3.widget; +import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY; import android.appwidget.AppWidgetHostView; +import android.content.Context; import android.os.Bundle; import com.android.launcher3.LauncherSettings; @@ -57,4 +59,8 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { public WidgetAddFlowHandler getHandler() { return new WidgetAddFlowHandler(info); } + + public Bundle getDefaultSizeOptions(Context context) { + return getWidgetSizeOptions(context, componentName, spanX, spanY); + } } diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index 12e0d43bd1..46141e0bc3 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -1,16 +1,12 @@ package com.android.launcher3.widget; import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; import android.content.Context; -import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.util.Log; -import android.util.SizeF; import android.view.View; -import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.DropTarget; import com.android.launcher3.Launcher; import com.android.launcher3.dragndrop.DragController; @@ -18,9 +14,6 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.util.Thunk; -import java.util.ArrayList; -import java.util.stream.Collectors; - public class WidgetHostViewLoader implements DragController.DragListener { private static final String TAG = "WidgetHostViewLoader"; private static final boolean LOGD = false; @@ -90,7 +83,7 @@ public class WidgetHostViewLoader implements DragController.DragListener { if (pInfo.isCustomWidget()) { return false; } - final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo); + final Bundle options = mInfo.getDefaultSizeOptions(mLauncher); // If there is a configuration activity, do not follow thru bound and inflate. if (mInfo.getHandler().needsConfigure()) { @@ -154,29 +147,4 @@ public class WidgetHostViewLoader implements DragController.DragListener { return true; } - public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) { - ArrayList sizes = AppWidgetResizeFrame - .getWidgetSizes(context, info.spanX, info.spanY); - - Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, - info.componentName, null); - float density = context.getResources().getDisplayMetrics().density; - float xPaddingDips = (padding.left + padding.right) / density; - float yPaddingDips = (padding.top + padding.bottom) / density; - - ArrayList paddedSizes = sizes.stream().map( - size -> new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips), - Math.max(0.f, size.getHeight() - yPaddingDips))).collect( - Collectors.toCollection(ArrayList::new)); - - Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes, null /* outRect */); - - Bundle options = new Bundle(); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom); - options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes); - return options; - } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java index 6fd147a055..4407fe1149 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java @@ -18,12 +18,22 @@ package com.android.launcher3.uioverrides; import android.app.Person; import android.content.pm.ShortcutInfo; +import android.view.Display; import com.android.launcher3.Utilities; public class ApiWrapper { + public static final boolean TASKBAR_DRAWN_IN_PROCESS = false; + public static Person[] getPersons(ShortcutInfo si) { return Utilities.EMPTY_PERSON_ARRAY; } + + /** + * Returns true if the display is an internal displays + */ + public static boolean isInternalDisplay(Display display) { + return display.getDisplayId() == Display.DEFAULT_DISPLAY; + } } diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src_common/com/android/launcher3/common/WidgetUtils.java index ffad93fffc..5e17e0a5a7 100644 --- a/tests/src_common/com/android/launcher3/common/WidgetUtils.java +++ b/tests/src_common/com/android/launcher3/common/WidgetUtils.java @@ -16,7 +16,6 @@ package com.android.launcher3.common; import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; -import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget; import android.appwidget.AppWidgetHost; import android.content.ContentResolver; @@ -62,7 +61,7 @@ public class WidgetUtils { pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; - Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo); + Bundle options = pendingInfo.getDefaultSizeOptions(targetContext); AppWidgetHost host = new LauncherAppWidgetHost(targetContext); int widgetId = host.allocateAppWidgetId(); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index ebad15449d..e5b93b1890 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -695,6 +695,7 @@ public final class LauncherInstrumentation { waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID); // Swiping up can temporarily bring Nexus Launcher if the current // Launcher is a Launcher3 one. Wait for the current launcher to reappear. + SystemClock.sleep(5000); // b/187080582 waitForLauncherObject(getAnyObjectSelector()); } }