Merge "Ensure that the cancel and end listener are called when cancelling state animation" into sc-dev
diff --git a/go/quickstep/res/values/config.xml b/go/quickstep/res/values/config.xml
index 402bf9a..796d14d 100644
--- a/go/quickstep/res/values/config.xml
+++ b/go/quickstep/res/values/config.xml
@@ -16,8 +16,6 @@
<resources>
<!-- The component to receive app sharing Intents -->
<string name="app_sharing_component" translatable="false"/>
- <!-- The package to receive Listen, Translate, and Search Intents -->
- <string name="niu_actions_package" translatable="false"/>
<!-- Feature Flags -->
<bool name="enable_niu_actions">true</bool>
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index 65cdcf0..3a6e9b5 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -21,6 +21,7 @@
import android.annotation.SuppressLint;
import android.app.assist.AssistContent;
+import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -29,10 +30,10 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.R;
import com.android.quickstep.util.AssistContentRequester;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
@@ -90,9 +91,7 @@
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
boolean rotated) {
getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
- mNIUPackageName =
- mApplicationContext.getString(R.string.niu_actions_package);
-
+ checkSettings();
if (thumbnail == null || TextUtils.isEmpty(mNIUPackageName)) {
return;
}
@@ -105,7 +104,6 @@
getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
mTaskPackageName = task.key.getPackageName();
- checkPermissions();
if (!mAssistPermissionsEnabled) {
return;
}
@@ -137,7 +135,11 @@
mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent);
} else {
intent.putExtra(ACTIONS_ERROR_CODE, ERROR_PERMISSIONS);
- mApplicationContext.startActivity(intent);
+ try {
+ mApplicationContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found to receive permission error intent");
+ }
}
}
@@ -160,13 +162,17 @@
* Checks whether the Assistant has screen context permissions
*/
@VisibleForTesting
- public void checkPermissions() {
+ public void checkSettings() {
ContentResolver contentResolver = mApplicationContext.getContentResolver();
boolean structureEnabled = Settings.Secure.getInt(contentResolver,
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0;
boolean screenshotEnabled = Settings.Secure.getInt(contentResolver,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1) != 0;
mAssistPermissionsEnabled = structureEnabled && screenshotEnabled;
+
+ String assistantPackage =
+ Settings.Secure.getString(contentResolver, Settings.Secure.ASSISTANT);
+ mNIUPackageName = assistantPackage.split("/", 2)[0];
}
protected class OverlayUICallbacksGoImpl extends OverlayUICallbacksImpl
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index b2ff770..f22a9d7 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -37,5 +37,5 @@
<string name="wellbeing_provider_pkg" translatable="false"/>
- <integer name="max_depth_blur_radius">150</integer>
+ <integer name="max_depth_blur_radius">72</integer>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 36322ce..26d407f 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -214,8 +214,9 @@
}
};
+ // Pairs of window starting type and starting window background color for starting tasks
// Will never be larger than MAX_NUM_TASKS
- private LinkedHashMap<Integer, Integer> mTypeForTaskId;
+ private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
public QuickstepTransitionManager(Context context) {
mLauncher = Launcher.cast(Launcher.getLauncher(context));
@@ -232,9 +233,9 @@
mLauncher.addOnDeviceProfileChangeListener(this);
if (supportsSSplashScreen()) {
- mTypeForTaskId = new LinkedHashMap<Integer, Integer>(MAX_NUM_TASKS) {
+ mTaskStartParams = new LinkedHashMap<Integer, Pair<Integer, Integer>>(MAX_NUM_TASKS) {
@Override
- protected boolean removeEldestEntry(Entry<Integer, Integer> entry) {
+ protected boolean removeEldestEntry(Entry<Integer, Pair<Integer, Integer>> entry) {
return size() > MAX_NUM_TASKS;
}
};
@@ -420,15 +421,6 @@
return bounds;
}
- private int getOpeningTaskId(RemoteAnimationTargetCompat[] appTargets) {
- for (RemoteAnimationTargetCompat target : appTargets) {
- if (target.mode == MODE_OPENING) {
- return target.taskId;
- }
- }
- return -1;
- }
-
public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider,
CancellationSignal cancellationSignal) {
mRemoteAnimationProvider = animationProvider;
@@ -595,10 +587,12 @@
final boolean hasSplashScreen;
if (supportsSSplashScreen()) {
- int taskId = getOpeningTaskId(appTargets);
- int type = mTypeForTaskId.getOrDefault(taskId, STARTING_WINDOW_TYPE_NONE);
- mTypeForTaskId.remove(taskId);
- hasSplashScreen = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ int taskId = openingTargets.getFirstAppTargetTaskId();
+ Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0);
+ Pair<Integer, Integer> taskParams =
+ mTaskStartParams.getOrDefault(taskId, defaultParams);
+ mTaskStartParams.remove(taskId);
+ hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else {
hasSplashScreen = false;
}
@@ -799,18 +793,30 @@
final RectF widgetBackgroundBounds = new RectF();
final Rect appWindowCrop = new Rect();
final Matrix matrix = new Matrix();
+ RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
+ wallpaperTargets, nonAppTargets, MODE_OPENING);
+
+ RemoteAnimationTargetCompat openingTarget = openingTargets.getFirstAppTarget();
+ int fallbackBackgroundColor = 0;
+ if (openingTarget != null && supportsSSplashScreen()) {
+ fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
+ ? mTaskStartParams.get(openingTarget.taskId).second : 0;
+ mTaskStartParams.remove(openingTarget.taskId);
+ }
+ if (fallbackBackgroundColor == 0) {
+ fallbackBackgroundColor =
+ FloatingWidgetView.getDefaultBackgroundColor(mLauncher, openingTarget);
+ }
final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher,
v, widgetBackgroundBounds,
new Size(windowTargetBounds.width(), windowTargetBounds.height()),
- finalWindowRadius, appTargetsAreTranslucent);
+ finalWindowRadius, appTargetsAreTranslucent, fallbackBackgroundColor);
final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
? floatingView.getInitialCornerRadius() : 0;
- RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
- wallpaperTargets, nonAppTargets, MODE_OPENING);
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
openingTargets.addReleaseCheck(surfaceApplier);
@@ -1434,8 +1440,8 @@
}
@Override
- public void onTaskLaunching(int taskId, int supportedType) {
- mTransitionManager.mTypeForTaskId.put(taskId, supportedType);
+ public void onTaskLaunching(int taskId, int supportedType, int color) {
+ mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
}
}
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 88db274..8f42993 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -117,6 +117,7 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TaskInfoCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -1071,7 +1072,8 @@
}
protected abstract HomeAnimationFactory createHomeAnimationFactory(
- ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent);
+ ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent,
+ boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget);
private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
@Override
@@ -1082,7 +1084,7 @@
// Since this is an edge case, just cancel and relaunch with default activity
// options (since we don't know if there's an associated app icon to launch from)
endRunningWindowAnim(true /* cancel */);
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mActivityRestartListener);
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null);
}
@@ -1097,7 +1099,7 @@
// If we are transitioning to launcher, then listen for the activity to be restarted while
// the transition is in progress
if (mGestureState.getEndTarget().isLauncher) {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(
mActivityRestartListener);
mParallelRunningAnim = mActivityInterface.getParallelAnimationToLauncher(
@@ -1116,13 +1118,15 @@
? runningTaskTarget.taskInfo.launchCookies
: new ArrayList<>();
boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent;
- HomeAnimationFactory homeAnimFactory =
- createHomeAnimationFactory(cookies, duration, isTranslucent);
- mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome()
+ boolean appCanEnterPip = !mDeviceState.isPipActive()
&& runningTaskTarget != null
&& runningTaskTarget.taskInfo.pictureInPictureParams != null
&& TaskInfoCompat.isAutoEnterPipEnabled(
runningTaskTarget.taskInfo.pictureInPictureParams);
+ HomeAnimationFactory homeAnimFactory =
+ createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
+ runningTaskTarget);
+ mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
if (mIsSwipingPipToHome) {
mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
homeAnimFactory, runningTaskTarget, start);
@@ -1398,7 +1402,8 @@
}
mActivityInitListener.unregister();
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mActivityRestartListener);
mTaskSnapshot = null;
}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 2d81429..fd44e02 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -129,7 +129,8 @@
@Override
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
- long duration, boolean isTargetTranslucent) {
+ long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
+ RemoteAnimationTargetCompat runningTaskTarget) {
mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
Intent intent = new Intent(mGestureState.getHomeIntent());
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 1bae1c5..47d94ba 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -20,6 +20,9 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.Utilities.boundToRange;
import static com.android.launcher3.Utilities.dpToPx;
+import static com.android.launcher3.Utilities.mapBoundToRange;
+import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
@@ -65,6 +68,7 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.ArrayList;
@@ -84,7 +88,8 @@
@Override
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
- long duration, boolean isTargetTranslucent) {
+ long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
+ RemoteAnimationTargetCompat runningTaskTarget) {
if (mActivity == null) {
mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
isPresent -> mRecentsView.startHome());
@@ -103,12 +108,12 @@
mActivity.getRootView().setForceHideBackArrow(true);
mActivity.setHintUserWillBeActive();
- if (!canUseWorkspaceView) {
+ if (!canUseWorkspaceView || appCanEnterPip) {
return new LauncherHomeAnimationFactory();
}
if (workspaceView instanceof LauncherAppWidgetHostView) {
return createWidgetHomeAnimationFactory((LauncherAppWidgetHostView) workspaceView,
- isTargetTranslucent);
+ isTargetTranslucent, runningTaskTarget);
}
return createIconHomeAnimationFactory(workspaceView);
}
@@ -169,15 +174,19 @@
}
private HomeAnimationFactory createWidgetHomeAnimationFactory(
- LauncherAppWidgetHostView hostView, boolean isTargetTranslucent) {
-
+ LauncherAppWidgetHostView hostView, boolean isTargetTranslucent,
+ RemoteAnimationTargetCompat runningTaskTarget) {
+ final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
RectF backgroundLocation = new RectF();
Rect crop = new Rect();
mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
Size windowSize = new Size(crop.width(), crop.height());
+ int fallbackBackgroundColor =
+ FloatingWidgetView.getDefaultBackgroundColor(mContext, runningTaskTarget);
FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity,
hostView, backgroundLocation, windowSize,
- mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent);
+ mTaskViewSimulator.getCurrentCornerRadius(), isTargetTranslucent,
+ fallbackBackgroundColor);
return new FloatingViewHomeAnimationFactory(floatingWidgetView) {
@@ -207,12 +216,20 @@
}
@Override
- public void update(@Nullable AppCloseConfig config, RectF currentRect,
- float progress, float radius) {
+ public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
+ float radius) {
super.update(config, currentRect, progress, radius);
- floatingWidgetView.update(currentRect, 1 /* floatingWidgetAlpha */,
- config != null ? config.getFgAlpha() : 1f /* foregroundAlpha */,
- 0 /* fallbackBackgroundAlpha */, 1 - progress);
+ final float fallbackBackgroundAlpha =
+ 1 - mapBoundToRange(progress, 0.8f, 1, 0, 1, EXAGGERATED_EASE);
+ final float foregroundAlpha =
+ mapBoundToRange(progress, 0.5f, 1, 0, 1, EXAGGERATED_EASE);
+ floatingWidgetView.update(currentRect, floatingWidgetAlpha, foregroundAlpha,
+ fallbackBackgroundAlpha, 1 - progress);
+ }
+
+ @Override
+ protected float getWindowAlpha(float progress) {
+ return 1 - mapBoundToRange(progress, 0, 0.5f, 0, 1, LINEAR);
}
};
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 3302da0..3080f04 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -33,6 +33,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.util.ArrayList;
import java.util.Collections;
@@ -66,7 +67,7 @@
mKeyguardManager = keyguardManager;
mChangeId = 1;
mActivityManagerWrapper = activityManagerWrapper;
- mActivityManagerWrapper.registerTaskStackListener(this);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
}
@VisibleForTesting
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 6852642..0e85ec3 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -353,7 +353,8 @@
public void startHome() {
if (LIVE_TILE.get()) {
RecentsView recentsView = getOverviewPanel();
- recentsView.switchToScreenshotAndFinishAnimationToRecents(this::startHomeInternal);
+ recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
+ this::startHomeInternal));
} else {
startHomeInternal();
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 50d0569..0ebe13b 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.view.IRecentsAnimationController;
import android.view.SurfaceControl;
import android.window.PictureInPictureSurfaceTransaction;
@@ -163,6 +164,14 @@
}
/**
+ * @see IRecentsAnimationController#animateNavigationBarToApp(long)
+ */
+ @UiThread
+ public void animateNavigationBarToApp(long duration) {
+ UI_HELPER_EXECUTOR.execute(() -> mController.animateNavigationBarToApp(duration));
+ }
+
+ /**
* Sets the final surface transaction on a Task. This is used by Launcher to notify the system
* that animating Activity to PiP has completed and the associated task surface should be
* updated accordingly. This should be called before `finish`
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index fa37901..444d77a 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -41,6 +43,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -50,6 +53,7 @@
import android.graphics.Region;
import android.net.Uri;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
@@ -75,6 +79,8 @@
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -96,6 +102,8 @@
private final DisplayController mDisplayController;
private final int mDisplayId;
private final RotationTouchHelper mRotationTouchHelper;
+ private final TaskStackChangeListener mPipListener;
+ private final List<ComponentName> mGestureBlockedActivities;
private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
@@ -106,9 +114,11 @@
private final Region mDeferredGestureRegion = new Region();
private boolean mAssistantAvailable;
private float mAssistantVisibility;
+ private boolean mIsUserSetupComplete;
private boolean mIsOneHandedModeEnabled;
private boolean mIsSwipeToNotificationEnabled;
private final boolean mIsOneHandedModeSupported;
+ private boolean mPipIsActive;
private boolean mIsUserUnlocked;
private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
@@ -125,10 +135,6 @@
private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener;
- private final List<ComponentName> mGestureBlockedActivities;
-
- private boolean mIsUserSetupComplete;
-
public RecentsAnimationDeviceState(Context context) {
this(context, false);
}
@@ -204,7 +210,6 @@
mIsOneHandedModeEnabled = false;
}
-
Uri swipeBottomNotificationUri =
Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
SettingsCache.OnChangeListener onChangeListener =
@@ -220,6 +225,27 @@
settingsCache.register(setupCompleteUri, userSetupChangeListener);
runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
}
+
+ try {
+ mPipIsActive = ActivityTaskManager.getService().getRootTaskInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED) != null;
+ } catch (RemoteException e) {
+ // Do nothing
+ }
+ mPipListener = new TaskStackChangeListener() {
+ @Override
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+ mPipIsActive = true;
+ }
+
+ @Override
+ public void onActivityUnpinned() {
+ mPipIsActive = false;
+ }
+ };
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener);
+ runOnDestroy(() ->
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener));
}
private void runOnDestroy(Runnable action) {
@@ -579,6 +605,10 @@
return mIsSwipeToNotificationEnabled;
}
+ public boolean isPipActive() {
+ return mPipIsActive;
+ }
+
public RotationTouchHelper getRotationTouchHelper() {
return mRotationTouchHelper;
}
@@ -596,6 +626,7 @@
pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
pw.println(" deferredGestureRegion=" + mDeferredGestureRegion);
+ pw.println(" pipIsActive=" + mPipIsActive);
mRotationTouchHelper.dump(pw);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 2eb9dd8..1e82c8c 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -39,6 +39,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.util.ArrayList;
import java.util.List;
@@ -75,7 +76,7 @@
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
- ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
}
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index edc3ab2..c032889 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -85,6 +85,17 @@
return null;
}
+ /** Returns the first opening app target. */
+ public RemoteAnimationTargetCompat getFirstAppTarget() {
+ return apps.length > 0 ? apps[0] : null;
+ }
+
+ /** Returns the task id of the first opening app target, or -1 if none is found. */
+ public int getFirstAppTargetTaskId() {
+ RemoteAnimationTargetCompat target = getFirstAppTarget();
+ return target == null ? -1 : target.taskId;
+ }
+
public boolean isAnimatingHome() {
for (RemoteAnimationTargetCompat target : unfilteredApps) {
if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index fc7a3df..678b176 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -38,6 +38,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -178,14 +179,14 @@
}
private void setupOrientationSwipeHandler() {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
- mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mFrozenTaskListener);
+ mOnDestroyFrozenTaskRunnable = () -> TaskStackChangeListeners.getInstance()
.unregisterTaskStackListener(mFrozenTaskListener);
runOnDestroy(mOnDestroyFrozenTaskRunnable);
}
private void destroyOrientationSwipeHandlerCallback() {
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index b79e934..4495455 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -181,6 +181,24 @@
public boolean supportSwipePipToHome() {
return false;
}
+
+ /**
+ * @param progress The progress of the animation to the home screen.
+ * @return The current alpha to set on the animating app window.
+ */
+ protected float getWindowAlpha(float progress) {
+ // Alpha interpolates between [1, 0] between progress values [start, end]
+ final float start = 0f;
+ final float end = 0.85f;
+
+ if (progress <= start) {
+ return 1f;
+ }
+ if (progress >= end) {
+ return 0f;
+ }
+ return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+ }
}
/**
@@ -236,24 +254,6 @@
return anim;
}
- /**
- * @param progress The progress of the animation to the home screen.
- * @return The current alpha to set on the animating app window.
- */
- protected float getWindowAlpha(float progress) {
- // Alpha interpolates between [1, 0] between progress values [start, end]
- final float start = 0f;
- final float end = 0.85f;
-
- if (progress <= start) {
- return 1f;
- }
- if (progress >= end) {
- return 0f;
- }
- return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
- }
-
protected class SpringAnimationRunner extends AnimationSuccessListener
implements RectFSpringAnim.OnUpdateListener, BuilderProxy {
@@ -292,7 +292,7 @@
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
- float alpha = getWindowAlpha(progress);
+ float alpha = mAnimationFactory.getWindowAlpha(progress);
if (config != null && PROTOTYPE_APP_CLOSE.get()) {
alpha = config.getWindowAlpha();
cornerRadius = config.getCornerRadius();
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 29ddde0..9731bf1 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -39,6 +39,7 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
public static final boolean ENABLE_SHELL_TRANSITIONS =
@@ -58,7 +59,7 @@
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
if (mLastGestureState == null) {
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mLiveTileRestartListener);
return;
}
@@ -68,7 +69,7 @@
RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
recentsView.launchSideTaskInLiveTileModeForRestartedApp(task.taskId);
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mLiveTileRestartListener);
}
}
@@ -197,7 +198,7 @@
}
public void enableLiveTileRestartListener() {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mLiveTileRestartListener);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mLiveTileRestartListener);
}
/**
@@ -241,7 +242,7 @@
mLiveTileCleanUpHandler.run();
mLiveTileCleanUpHandler = null;
}
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);
// Release all the target leashes
if (mTargets != null) {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index a59ba51..59bd1ed 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -263,6 +263,14 @@
finalParams.applySurfaceParams(navBuilder.build());
}
});
+ } else if (inLiveTileMode) {
+ // There is no transition animation for app launch from recent in live tile mode so
+ // we have to trigger the navigation bar animation from system here.
+ final RecentsAnimationController controller =
+ recentsView.getRecentsAnimationController();
+ if (controller != null) {
+ controller.animateNavigationBarToApp(RECENTS_LAUNCH_DURATION);
+ }
}
topMostSimulator = tsv;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index c3b342b..cf523d0 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -29,8 +29,6 @@
import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType;
-import java.util.ArrayDeque;
-import java.util.Deque;
import java.util.List;
/** Shows the gesture interactive sandbox in full screen mode. */
@@ -39,8 +37,9 @@
private static final String LOG_TAG = "GestureSandboxActivity";
private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
+ private static final String KEY_CURRENT_STEP = "current_step";
- private Deque<TutorialType> mTutorialSteps;
+ private TutorialType[] mTutorialSteps;
private TutorialType mCurrentTutorialStep;
private TutorialFragment mFragment;
@@ -55,9 +54,7 @@
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
mTutorialSteps = getTutorialSteps(args);
- mCurrentStep = 1;
- mNumSteps = mTutorialSteps.size();
- mCurrentTutorialStep = mTutorialSteps.pop();
+ mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
@@ -88,12 +85,13 @@
@Override
protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
+ savedInstanceState.putInt(KEY_CURRENT_STEP, mCurrentStep);
super.onSaveInstanceState(savedInstanceState);
}
/** Returns true iff there aren't anymore tutorial types to display to the user. */
public boolean isTutorialComplete() {
- return mTutorialSteps.isEmpty();
+ return mCurrentStep >= mNumSteps;
}
public int getCurrentStep() {
@@ -121,7 +119,7 @@
mFragment.closeTutorial();
return;
}
- mCurrentTutorialStep = mTutorialSteps.pop();
+ mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
@@ -131,10 +129,9 @@
}
private String[] getTutorialStepNames() {
- String[] tutorialStepNames = new String[mTutorialSteps.size() + 1];
+ String[] tutorialStepNames = new String[mTutorialSteps.length];
- int i = 1;
- tutorialStepNames[0] = mCurrentTutorialStep.name();
+ int i = 0;
for (TutorialType tutorialStep : mTutorialSteps) {
tutorialStepNames[i++] = tutorialStep.name();
}
@@ -142,25 +139,28 @@
return tutorialStepNames;
}
- private Deque<TutorialType> getTutorialSteps(Bundle extras) {
- Deque<TutorialType> defaultSteps = new ArrayDeque<>();
- defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
+ private TutorialType[] getTutorialSteps(Bundle extras) {
+ TutorialType[] defaultSteps = new TutorialType[] {TutorialType.LEFT_EDGE_BACK_NAVIGATION};
if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
return defaultSteps;
}
String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
+ int currentStep = extras.getInt(KEY_CURRENT_STEP, -1);
if (tutorialStepNames == null) {
return defaultSteps;
}
- Deque<TutorialType> tutorialSteps = new ArrayDeque<>();
- for (String tutorialStepName : tutorialStepNames) {
- tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName));
+ TutorialType[] tutorialSteps = new TutorialType[tutorialStepNames.length];
+ for (int i = 0; i < tutorialStepNames.length; i++) {
+ tutorialSteps[i] = TutorialType.valueOf(tutorialStepNames[i]);
}
+ mCurrentStep = Math.max(currentStep, 1);
+ mNumSteps = tutorialSteps.length;
+
return tutorialSteps;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index c9da609..dfe0c72 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -58,6 +58,7 @@
private static final int FEEDBACK_ANIMATION_MS = 250;
private static final int RIPPLE_VISIBLE_MS = 300;
+ private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
final TutorialFragment mTutorialFragment;
TutorialType mTutorialType;
@@ -65,6 +66,7 @@
final TextView mCloseButton;
final ViewGroup mFeedbackView;
+ final TextView mFeedbackTitleView;
final ImageView mFeedbackVideoView;
final ImageView mGestureVideoView;
final RelativeLayout mFakeLauncherView;
@@ -80,6 +82,12 @@
protected boolean mGestureCompleted = false;
+ // These runnables should be used when posting callbacks to their views and cleared from their
+ // views before posting new callbacks.
+ private final Runnable mTitleViewCallback;
+ @Nullable private Runnable mFeedbackViewCallback;
+ @Nullable private Runnable mFeedbackVideoViewCallback;
+
TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
mTutorialFragment = tutorialFragment;
mTutorialType = tutorialType;
@@ -89,6 +97,8 @@
mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
mCloseButton.setOnClickListener(button -> showSkipTutorialDialog());
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
+ mFeedbackTitleView = mFeedbackView.findViewById(
+ R.id.gesture_tutorial_fragment_feedback_title);
mFeedbackVideoView = rootView.findViewById(R.id.gesture_tutorial_feedback_video);
mGestureVideoView = rootView.findViewById(R.id.gesture_tutorial_gesture_video);
mFakeLauncherView = rootView.findViewById(R.id.gesture_tutorial_fake_launcher_view);
@@ -103,6 +113,9 @@
mTutorialStepView =
rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
mSkipTutorialDialog = createSkipTutorialDialog();
+
+ mTitleViewCallback = () -> mFeedbackTitleView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
private void showSkipTutorialDialog() {
@@ -169,7 +182,7 @@
playFeedbackVideo(tutorialAnimation, gestureAnimation, () -> {
mFeedbackView.setTranslationY(0);
title.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- });
+ }, true);
}
}
@@ -192,15 +205,17 @@
isGestureSuccessful
? R.string.gesture_tutorial_nice : R.string.gesture_tutorial_try_again,
subtitleResId,
- isGestureSuccessful);
+ isGestureSuccessful,
+ false);
}
void showFeedback(
int titleResId,
int subtitleResId,
- boolean isGestureSuccessful) {
- TextView title = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_title);
- title.setText(titleResId);
+ boolean isGestureSuccessful,
+ boolean useGestureAnimationDelay) {
+ mFeedbackTitleView.setText(titleResId);
+ mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
TextView subtitle =
mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_subtitle);
subtitle.setText(subtitleResId);
@@ -221,11 +236,8 @@
.setDuration(FEEDBACK_ANIMATION_MS)
.translationY(0)
.start();
- title.postDelayed(
- () -> title.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_FOCUSED),
- FEEDBACK_ANIMATION_MS);
- });
+ mFeedbackTitleView.postDelayed(mTitleViewCallback, FEEDBACK_ANIMATION_MS);
+ }, useGestureAnimationDelay);
return;
} else {
mTutorialFragment.releaseFeedbackVideoView();
@@ -237,10 +249,7 @@
.setDuration(FEEDBACK_ANIMATION_MS)
.translationY(0)
.start();
- title.postDelayed(
- () -> title.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_FOCUSED),
- FEEDBACK_ANIMATION_MS);
+ mFeedbackTitleView.postDelayed(mTitleViewCallback, FEEDBACK_ANIMATION_MS);
}
void hideFeedback(boolean releaseFeedbackVideo) {
@@ -254,7 +263,8 @@
private void playFeedbackVideo(
@NonNull AnimatedVectorDrawable tutorialAnimation,
@NonNull AnimatedVectorDrawable gestureAnimation,
- @NonNull Runnable onStartRunnable) {
+ @NonNull Runnable onStartRunnable,
+ boolean useGestureAnimationDelay) {
if (tutorialAnimation.isRunning()) {
tutorialAnimation.reset();
@@ -270,7 +280,9 @@
gestureAnimation.stop();
}
- onStartRunnable.run();
+ if (!useGestureAnimationDelay) {
+ onStartRunnable.run();
+ }
}
@Override
@@ -284,8 +296,28 @@
}
});
- tutorialAnimation.start();
- mFeedbackVideoView.setVisibility(View.VISIBLE);
+ if (mFeedbackViewCallback != null) {
+ mFeedbackVideoView.removeCallbacks(mFeedbackViewCallback);
+ mFeedbackViewCallback = null;
+ }
+ if (mFeedbackVideoViewCallback != null) {
+ mFeedbackVideoView.removeCallbacks(mFeedbackVideoViewCallback);
+ mFeedbackVideoViewCallback = null;
+ }
+ if (useGestureAnimationDelay) {
+ mFeedbackViewCallback = onStartRunnable;
+ mFeedbackVideoViewCallback = () -> {
+ mFeedbackVideoView.setVisibility(View.VISIBLE);
+ tutorialAnimation.start();
+ };
+
+ mFeedbackVideoView.setVisibility(View.GONE);
+ mFeedbackView.post(mFeedbackViewCallback);
+ mFeedbackVideoView.postDelayed(mFeedbackVideoViewCallback, GESTURE_ANIMATION_DELAY_MS);
+ } else {
+ mFeedbackVideoView.setVisibility(View.VISIBLE);
+ tutorialAnimation.start();
+ }
}
void setRippleHotspot(float x, float y) {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index ec26022..da0fc66 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -170,7 +170,8 @@
Integer introTileStringResId = mTutorialController.getIntroductionTitle();
Integer introSubtitleResId = mTutorialController.getIntroductionSubtitle();
if (introTileStringResId != null && introSubtitleResId != null) {
- mTutorialController.showFeedback(introTileStringResId, introSubtitleResId, false);
+ mTutorialController.showFeedback(
+ introTileStringResId, introSubtitleResId, false, true);
mIntroductionShown = true;
}
}
@@ -252,7 +253,7 @@
@Override
public void onResume() {
super.onResume();
- if (mFragmentStopped) {
+ if (mFragmentStopped && mTutorialController != null) {
mTutorialController.showFeedback();
mFragmentStopped = false;
} else {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
index eda6158..d880d74 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
@@ -23,6 +23,7 @@
import android.widget.LinearLayout;
import androidx.appcompat.content.res.AppCompatResources;
+import androidx.core.graphics.ColorUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -86,6 +87,8 @@
for (int i = mTotalSteps; i < getChildCount(); i++) {
removeViewAt(i);
}
+ int stepIndicatorColor = GraphicsUtils.getAttrColor(
+ getContext(), android.R.attr.textColorPrimary);
for (int i = 0; i < mTotalSteps; i++) {
Drawable pageIndicatorPillDrawable = AppCompatResources.getDrawable(
getContext(), R.drawable.tutorial_step_indicator_pill);
@@ -104,13 +107,10 @@
}
if (pageIndicatorPillDrawable != null) {
if (i < mCurrentStep) {
- pageIndicatorPillDrawable.setTint(
- GraphicsUtils.getAttrColor(getContext(),
- android.R.attr.textColorPrimary));
+ pageIndicatorPillDrawable.setTint(stepIndicatorColor);
} else {
pageIndicatorPillDrawable.setTint(
- GraphicsUtils.getAttrColor(getContext(),
- android.R.attr.textColorPrimaryInverse));
+ ColorUtils.setAlphaComponent(stepIndicatorColor, 0x22));
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 2285d74..de7dbd6 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -26,6 +26,7 @@
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.prediction.AppTarget;
+import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ComponentName;
@@ -68,6 +69,7 @@
private static final long FILE_LIFE = 1000L /*ms*/ * 60L /*s*/ * 60L /*m*/ * 24L /*h*/;
private static final String SUB_FOLDER = "Overview";
private static final String BASE_NAME = "overview_image_";
+ private static final String TAG = "ImageActionUtils";
/**
* Saves screenshot to location determine by SystemUiProxy
@@ -154,11 +156,15 @@
Intent intent, BiFunction<Uri, Intent, Intent[]> uriToIntentMap, String tag) {
Intent[] intents = uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent);
- // Work around b/159412574
- if (intents.length == 1) {
- context.startActivity(intents[0]);
- } else {
- context.startActivities(intents);
+ try {
+ // Work around b/159412574
+ if (intents.length == 1) {
+ context.startActivity(intents[0]);
+ } else {
+ context.startActivities(intents);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found to receive image intent");
}
}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 8151d41..1ed2da3 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -121,7 +121,7 @@
mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
? HARDER_TRIGGER_TIMEOUT
: FORCE_PAUSE_TIMEOUT);
- float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
+ float newVelocity = mVelocityProvider.addMotionEvent(ev, ev.getPointerId(pointerIndex));
if (mPreviousVelocity != null) {
checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
}
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 3631130..1062652 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -121,6 +121,15 @@
mDestinationBoundsAnimation.set(mDestinationBounds);
mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(cornerRadius);
+ if (sourceRectHint != null && (sourceRectHint.width() < destinationBounds.width()
+ || sourceRectHint.height() < destinationBounds.height())) {
+ // This is a situation in which the source hint rect on at least one axis is smaller
+ // than the destination bounds, which presents a problem because we would have to scale
+ // up that axis to fit the bounds. So instead, just fallback to the non-source hint
+ // animation in this case.
+ sourceRectHint = null;
+ }
+
if (sourceRectHint == null) {
mSourceHintRectInsets = null;
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
index 9ea2369..65dba33 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -27,7 +27,6 @@
import android.view.ViewOutlineProvider;
import android.widget.RemoteViews.RemoteViewOutlineProvider;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.RoundedCornerEnforcement;
@@ -62,7 +61,8 @@
setClipToOutline(true);
}
- void init(LauncherAppWidgetHostView hostView, View backgroundView, float finalRadius) {
+ void init(LauncherAppWidgetHostView hostView, View backgroundView, float finalRadius,
+ int fallbackBackgroundColor) {
mFinalRadius = finalRadius;
mSourceView = backgroundView;
mInitialOutlineRadius = getOutlineRadius(hostView, backgroundView);
@@ -81,7 +81,7 @@
setBackground(mBackgroundProperties.mDrawable);
mSourceView.setBackground(null);
} else if (mOriginalForeground == null) {
- mFallbackDrawable.setColor(Themes.getColorBackground(backgroundView.getContext()));
+ mFallbackDrawable.setColor(fallbackBackgroundColor);
setBackground(mFallbackDrawable);
mIsUsingFallback = true;
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 0012dd8..22ce942 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -34,10 +34,12 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.FloatingView;
import com.android.launcher3.views.ListenerView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.RoundedCornerEnforcement;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/** A view that mimics an App Widget through a launch animation. */
@TargetApi(Build.VERSION_CODES.S)
@@ -148,7 +150,7 @@
private void init(DragLayer dragLayer, LauncherAppWidgetHostView originalView,
RectF widgetBackgroundPosition, Size windowSize, float windowCornerRadius,
- boolean appTargetIsTranslucent) {
+ boolean appTargetIsTranslucent, int fallbackBackgroundColor) {
mAppWidgetView = originalView;
mAppWidgetView.beginDeferringUpdates();
mBackgroundPosition = widgetBackgroundPosition;
@@ -163,7 +165,8 @@
getRelativePosition(mAppWidgetBackgroundView, dragLayer, mBackgroundPosition);
getRelativePosition(mAppWidgetBackgroundView, mAppWidgetView, mBackgroundOffset);
if (!mAppTargetIsTranslucent) {
- mBackgroundView.init(mAppWidgetView, mAppWidgetBackgroundView, windowCornerRadius);
+ mBackgroundView.init(mAppWidgetView, mAppWidgetBackgroundView, windowCornerRadius,
+ fallbackBackgroundColor);
// Layout call before GhostView creation so that the overlaid view isn't clipped
layout(0, 0, windowSize.getWidth(), windowSize.getHeight());
mForegroundOverlayView = GhostView.addGhost(mAppWidgetView, this);
@@ -274,7 +277,8 @@
*/
public static FloatingWidgetView getFloatingWidgetView(Launcher launcher,
LauncherAppWidgetHostView originalView, RectF widgetBackgroundPosition,
- Size windowSize, float windowCornerRadius, boolean appTargetsAreTranslucent) {
+ Size windowSize, float windowCornerRadius, boolean appTargetsAreTranslucent,
+ int fallbackBackgroundColor) {
final DragLayer dragLayer = launcher.getDragLayer();
ViewGroup parent = (ViewGroup) dragLayer.getParent();
FloatingWidgetView floatingView =
@@ -282,11 +286,22 @@
floatingView.recycle();
floatingView.init(dragLayer, originalView, widgetBackgroundPosition, windowSize,
- windowCornerRadius, appTargetsAreTranslucent);
+ windowCornerRadius, appTargetsAreTranslucent, fallbackBackgroundColor);
parent.addView(floatingView);
return floatingView;
}
+ /**
+ * Extract a background color from a target's task description, or fall back to the given
+ * context's theme background color.
+ */
+ public static int getDefaultBackgroundColor(
+ Context context, RemoteAnimationTargetCompat target) {
+ return (target != null && target.taskInfo.taskDescription != null)
+ ? target.taskInfo.taskDescription.getBackgroundColor()
+ : Themes.getColorBackground(context);
+ }
+
private static void getRelativePosition(View descendant, View ancestor, RectF position) {
float[] points = new float[]{0, 0, descendant.getWidth(), descendant.getHeight()};
Utilities.getDescendantCoordRelativeToAncestor(descendant, ancestor, points,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a46bc6b..df7f8b5 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2198,7 +2198,7 @@
.setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
.setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
FloatProperty<TaskView> dismissingTaskViewTranslate =
- taskView.getSecondaryDissmissTranslationProperty();;
+ taskView.getSecondaryDissmissTranslationProperty();
// TODO(b/186800707) translate entire grid size distance
int translateDistance = mOrientationHandler.getSecondaryDimension(taskView);
int positiveNegativeFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
@@ -2231,7 +2231,7 @@
anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
positiveNegativeFactor * translateDistance * 2).setDuration(duration), LINEAR, sp);
- if (LIVE_TILE.get() && taskView.isRunningTask()) {
+ if (LIVE_TILE.get() && mEnableDrawingLiveTile && taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
mOrientationHandler.getSecondaryValue(
@@ -2311,6 +2311,15 @@
anim.setFloat(child, translationProperty, scrollDiff, clampToProgress(LINEAR,
Utilities.boundToRange(INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ additionalDismissDuration, 0f, 1f), 1));
+ if (LIVE_TILE.get() && mEnableDrawingLiveTile && child instanceof TaskView
+ && ((TaskView) child).isRunningTask()) {
+ anim.addOnFrameCallback(() -> {
+ mLiveTileTaskViewSimulator.taskPrimaryTranslation.value =
+ mOrientationHandler.getPrimaryValue(child.getTranslationX(),
+ child.getTranslationY());
+ redrawLiveTile();
+ });
+ }
needsCurveUpdates = true;
}
} else if (child instanceof TaskView) {
@@ -2348,7 +2357,8 @@
public void accept(Boolean success) {
if (LIVE_TILE.get() && mEnableDrawingLiveTile && taskView.isRunningTask()
&& success) {
- finishRecentsAnimation(true /* toHome */, () -> onEnd(success));
+ finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ () -> onEnd(success));
} else {
onEnd(success);
}
@@ -2359,7 +2369,8 @@
if (success) {
if (shouldRemoveTask) {
if (taskView.getTask() != null) {
- switchToScreenshotAndFinishAnimationToRecents(() -> {
+ finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ () -> {
UI_HELPER_EXECUTOR.getHandler().postDelayed(() ->
ActivityManagerWrapper.getInstance().removeTask(
taskView.getTask().key.id),
@@ -2391,6 +2402,7 @@
startHome();
} else {
snapToPageImmediately(pageToSnapTo);
+ dispatchScrollChanged();
// Grid got messed up, reapply.
updateGridProperties(true);
if (showAsGrid() && getFocusedTaskView() == null
@@ -2468,7 +2480,7 @@
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
// Remove all the task views now
- switchToScreenshotAndFinishAnimationToRecents(() -> {
+ finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
UI_HELPER_EXECUTOR.getHandler().postDelayed(
ActivityManagerWrapper.getInstance()::removeAllRecentTasks,
REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
@@ -2629,7 +2641,9 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (LIVE_TILE.get() && mEnableDrawingLiveTile && newConfig.orientation != mOrientation) {
- switchToScreenshotAndFinishAnimationToRecents(this::updateRecentsRotation);
+ switchToScreenshot(
+ () -> finishRecentsAnimation(true /* toRecents */, false /* showPip */,
+ this::updateRecentsRotation));
mEnableDrawingLiveTile = false;
} else {
updateRecentsRotation();
@@ -3622,10 +3636,6 @@
}
}
- public void switchToScreenshotAndFinishAnimationToRecents(Runnable onFinishRunnable) {
- switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */, onFinishRunnable));
- }
-
/**
* Switch the current running task view to static snapshot mode,
* capturing the snapshot at the same time.
@@ -3843,4 +3853,10 @@
// The Black blending is temporary until we have the proper color token.
return ColorUtils.blendARGB(Color.BLACK, baseColor, 0.25f);
}
+
+ /** Get the RecentsAnimationController */
+ @Nullable
+ public RecentsAnimationController getRecentsAnimationController() {
+ return mRecentsAnimationController;
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index 4ca1f59..ea7b7f5 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -79,10 +79,7 @@
* directly (ex: new LinearLayout)
* Using ExtendedMockito: Mocking static methods from platform classes (loaded in zygote) makes
* the main thread extremely slow and untestable
- *
- * Suppressed until b/141579810 is resolved
*/
-@Suppress
@LargeTest
@RunWith(AndroidJUnit4.class)
public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest {
@@ -137,6 +134,7 @@
@Test
@NavigationModeSwitch(mode = ZERO_BUTTON)
+ @Suppress // until b/190729479 is fixed
public void testSwipeUpFromApp_widget_update() {
String stubText = "Some random stub text";
@@ -149,6 +147,7 @@
@Test
@NavigationModeSwitch(mode = ZERO_BUTTON)
+ @Suppress // until b/190729479 is fixed
public void testSwipeUp_with_list_widgets() {
SimpleViewsFactory viewFactory = new SimpleViewsFactory();
viewFactory.viewCount = 1;
@@ -188,6 +187,11 @@
LauncherSettings.Settings.call(mResolver,
LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
+ // Make sure the widget is big enough to show a list of items
+ info.minSpanX = 2;
+ info.minSpanY = 2;
+ info.spanX = 2;
+ info.spanY = 2;
LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
addItemToScreen(item);
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-night-v31/all_apps_tab_background_selected.xml
similarity index 76%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-night-v31/all_apps_tab_background_selected.xml
index e59e8d2..b7c9ff6 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-night-v31/all_apps_tab_background_selected.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_accent2_100"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-night-v31/all_apps_tab_text.xml
similarity index 75%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-night-v31/all_apps_tab_text.xml
index e59e8d2..83237b4 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-night-v31/all_apps_tab_text.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_50" android:state_selected="true"/>
+ <item android:color="@android:color/system_neutral2_700"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-night-v31/all_apps_tabs_background.xml
similarity index 76%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-night-v31/all_apps_tabs_background.xml
index e59e8d2..b396f33 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-night-v31/all_apps_tabs_background.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_100"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-night/all_apps_tab_background_selected.xml
similarity index 76%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-night/all_apps_tab_background_selected.xml
index e59e8d2..b22bc8b 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-night/all_apps_tab_background_selected.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#BFEBE3"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-night/all_apps_tab_text.xml
similarity index 76%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-night/all_apps_tab_text.xml
index e59e8d2..183af01 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-night/all_apps_tab_text.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#F0F0F0" android:state_selected="true"/>
+ <item android:color="#464646"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-night/all_apps_tabs_background.xml
similarity index 76%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-night/all_apps_tabs_background.xml
index e59e8d2..c0c1621 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-night/all_apps_tabs_background.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#E2E2E2"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-v31/all_apps_tab_background_selected.xml
similarity index 76%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-v31/all_apps_tab_background_selected.xml
index e59e8d2..dac8fa2 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-v31/all_apps_tab_background_selected.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_accent1_100"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-v31/all_apps_tab_text.xml
similarity index 75%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-v31/all_apps_tab_text.xml
index e59e8d2..c3520a7 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-v31/all_apps_tab_text.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_900" android:state_selected="true"/>
+ <item android:color="@android:color/system_neutral2_700"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color-v31/all_apps_tabs_background.xml
similarity index 76%
copy from res/color/all_apps_tab_bg.xml
copy to res/color-v31/all_apps_tabs_background.xml
index e59e8d2..30757b0 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color-v31/all_apps_tabs_background.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="97" />
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color/all_apps_tab_background_selected.xml
similarity index 83%
rename from res/color/all_apps_tab_bg.xml
rename to res/color/all_apps_tab_background_selected.xml
index e59e8d2..5cb9bd8 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color/all_apps_tab_background_selected.xml
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#8DF5E3"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_text.xml b/res/color/all_apps_tab_text.xml
index 2db0fb5..dace380 100644
--- a/res/color/all_apps_tab_text.xml
+++ b/res/color/all_apps_tab_text.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/black" android:state_selected="true"/>
- <item android:color="?android:attr/textColorTertiary"/>
+ <item android:color="#1B1B1B" android:state_selected="true"/>
+ <item android:color="#464646"/>
</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_bg.xml b/res/color/all_apps_tabs_background.xml
similarity index 77%
copy from res/color/all_apps_tab_bg.xml
copy to res/color/all_apps_tabs_background.xml
index e59e8d2..a4b7d1f 100644
--- a/res/color/all_apps_tab_bg.xml
+++ b/res/color/all_apps_tabs_background.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentPrimary"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#F6F6F6"/>
</selector>
\ No newline at end of file
diff --git a/res/drawable/all_apps_tabs_background.xml b/res/drawable/all_apps_tabs_background.xml
index a345dd3..4e95315 100644
--- a/res/drawable/all_apps_tabs_background.xml
+++ b/res/drawable/all_apps_tabs_background.xml
@@ -13,14 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="@dimen/all_apps_tabs_vertical_padding"
- android:bottom="@dimen/all_apps_tabs_vertical_padding
-">
+ android:bottom="@dimen/all_apps_tabs_vertical_padding">
<shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface" />
+ <solid android:color="@color/all_apps_tabs_background" />
<corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
</shape>
</item>
diff --git a/res/drawable/work_apps_toggle_background.xml b/res/drawable/work_apps_toggle_background.xml
new file mode 100644
index 0000000..a04d269
--- /dev/null
+++ b/res/drawable/work_apps_toggle_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/work_fab_radius" />
+ <solid android:color="?android:attr/colorControlHighlight" />
+ <padding android:left="@dimen/work_fab_radius" android:right="@dimen/work_fab_radius" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/work_fab_radius" />
+ <solid android:color="?android:attr/colorAccent" />
+ <padding android:left="@dimen/work_fab_radius" android:right="@dimen/work_fab_radius" />
+ </shape>
+ </item>
+</selector>
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 53db5ed..249e42c 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -83,7 +83,8 @@
android:layout_marginEnd="@dimen/widget_reconfigure_button_margin"
android:src="@drawable/widget_reconfigure_button_frame"
android:background="?android:attr/selectableItemBackground"
- android:visibility="gone" />
+ android:visibility="gone"
+ android:contentDescription="@string/widget_reconfigure_button_content_description" />
</FrameLayout>
</com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml
index aee00a9..9a6f8c3 100644
--- a/res/layout/arrow_toast.xml
+++ b/res/layout/arrow_toast.xml
@@ -28,13 +28,14 @@
android:padding="16dp"
android:background="@drawable/arrow_toast_rounded_background"
android:elevation="2dp"
+ android:outlineProvider="none"
android:textColor="@color/arrow_tip_view_content"
android:textSize="14sp"/>
<View
android:id="@+id/arrow"
android:elevation="2dp"
+ android:outlineProvider="none"
android:layout_width="@dimen/arrow_toast_arrow_width"
- android:layout_height="8dp"
- android:layout_marginTop="-2dp"/>
+ android:layout_height="10dp"/>
</merge>
diff --git a/res/layout/longpress_options_menu.xml b/res/layout/longpress_options_menu.xml
index d2f7a66..fbe28d8 100644
--- a/res/layout/longpress_options_menu.xml
+++ b/res/layout/longpress_options_menu.xml
@@ -15,7 +15,7 @@
-->
<com.android.launcher3.views.OptionsPopupView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/deep_shortcuts_container"
+ android:id="@+id/popup_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipToPadding="false"
diff --git a/res/layout/popup_container.xml b/res/layout/popup_container.xml
index 7c78e44..18014bb 100644
--- a/res/layout/popup_container.xml
+++ b/res/layout/popup_container.xml
@@ -16,12 +16,21 @@
<com.android.launcher3.popup.PopupContainerWithArrow
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/deep_shortcuts_container"
+ android:id="@+id/popup_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/deep_shortcuts_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:tag="@string/popup_container_iterate_children"
+ android:elevation="@dimen/deep_shortcuts_elevation"
+ android:orientation="vertical"/>
+
<LinearLayout
android:id="@+id/notification_container"
android:layout_width="match_parent"
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
new file mode 100644
index 0000000..21f269f
--- /dev/null
+++ b/res/layout/work_mode_fab.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.allapps.WorkModeSwitch
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/work_mode_toggle"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_height="@dimen/work_fab_height"
+ android:layout_width="wrap_content"
+ android:gravity="center"
+ android:includeFontPadding="false"
+ android:drawableTint="@android:color/white"
+ android:textColor="@android:color/white"
+ android:background="@drawable/work_apps_toggle_background"
+ android:drawablePadding="16dp"
+ android:drawableStart="@drawable/ic_corp_off"
+ android:elevation="10dp"
+ android:layout_marginBottom="@dimen/work_fab_margin"
+ android:layout_marginEnd="@dimen/work_fab_margin"
+ android:text="@string/work_apps_pause_btn_text" />
\ No newline at end of file
diff --git a/res/layout/work_mode_switch.xml b/res/layout/work_mode_switch.xml
index 31953c7..538a180 100644
--- a/res/layout/work_mode_switch.xml
+++ b/res/layout/work_mode_switch.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,8 +12,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.allapps.WorkModeSwitch
- xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.allapps.WorkModeSwitch xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/PrimaryHeadline"
@@ -35,5 +33,4 @@
android:paddingBottom="@dimen/work_profile_footer_padding"
android:paddingLeft="@dimen/work_profile_footer_padding"
android:paddingRight="@dimen/work_profile_footer_padding"
- android:paddingTop="@dimen/work_profile_footer_padding"
-/>
+ android:paddingTop="@dimen/work_profile_footer_padding" />
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
deleted file mode 100644
index c3c7010..0000000
--- a/res/layout/work_profile_edu.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 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.
--->
-<com.android.launcher3.views.WorkEduView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:gravity="bottom"
- android:orientation="vertical">
-
- <View
- android:layout_width="match_parent"
- android:layout_height="32dp"
- android:background="@drawable/bottom_sheet_top_border"
- android:backgroundTint="?attr/eduHalfSheetBGColor" />
-
- <LinearLayout
- android:id="@+id/view_wrapper"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?attr/eduHalfSheetBGColor"
- android:orientation="vertical"
- android:paddingLeft="@dimen/bottom_sheet_edu_padding"
- android:paddingRight="@dimen/bottom_sheet_edu_padding">
-
- <TextView
- android:id="@+id/content_text"
- style="@style/TextHeadline"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="48dp"
- android:layout_marginBottom="48dp"
- android:gravity="center"
- android:text="@string/work_profile_edu_personal_apps"
- android:textAlignment="center"
- android:textColor="@android:color/white"
- android:textSize="20sp" />
-
- <Button
- android:id="@+id/proceed"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_gravity="end"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center"
- android:text="@string/work_profile_edu_next"
- android:textAlignment="center"
- android:textColor="@android:color/white" />
- </LinearLayout>
-</com.android.launcher3.views.WorkEduView>
\ No newline at end of file
diff --git a/res/values-v28/dimens.xml b/res/values-v28/dimens.xml
index ffa8cc4..3f118cd 100644
--- a/res/values-v28/dimens.xml
+++ b/res/values-v28/dimens.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- 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.
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 7b37001..1434430 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -17,11 +17,11 @@
*/
-->
<resources>
- <color name="popup_color_primary_light">@android:color/system_neutral1_0</color>
+ <color name="popup_color_primary_light">@android:color/system_accent2_50</color>
<color name="popup_color_secondary_light">@android:color/system_neutral2_100</color>
<color name="popup_color_tertiary_light">@android:color/system_neutral2_300</color>
<color name="popup_color_neutral_dark">@android:color/system_neutral1_1000</color>
- <color name="popup_color_primary_dark">@android:color/system_neutral1_800</color>
+ <color name="popup_color_primary_dark">@android:color/system_neutral2_800</color>
<color name="popup_color_secondary_dark">@android:color/system_neutral1_900</color>
<color name="popup_color_tertiary_dark">@android:color/system_neutral2_700</color>
@@ -37,4 +37,6 @@
<color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
+ <color name="folder_dot_color">@android:color/system_accent2_50</color>
+
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index c2b07e0..7aab4fa 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -59,6 +59,8 @@
<color name="folder_hint_text_color_light">#FFF</color>
<color name="folder_hint_text_color_dark">#FF000000</color>
+ <color name="folder_dot_color">?attr/colorPrimary</color>
+
<color name="text_color_primary_dark">#FFFFFFFF</color>
<color name="text_color_secondary_dark">#FFFFFFFF</color>
<color name="text_color_tertiary_dark">#CCFFFFFF</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 77c7e98..299c80e 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -31,6 +31,9 @@
<!-- View tag key used to store SpringAnimation data. -->
<item type="id" name="spring_animation_tag" />
+ <!-- View tag key used to determine if we should fade in the child views.. -->
+ <string name="popup_container_iterate_children" translatable="false">popup_container_iterate_children</string>
+
<!-- Workspace -->
<!-- The duration (in ms) of the fade animation on the object outlines, used when
we are dragging objects around on the home screen. -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d065611..06bdd49 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -61,6 +61,7 @@
<dimen name="widget_reconfigure_button_padding">6dp</dimen>
<dimen name="widget_reconfigure_button_margin">32dp</dimen>
<dimen name="widget_reconfigure_button_size">36dp</dimen>
+ <dimen name="widget_reconfigure_tip_top_margin">16dp</dimen>
<!-- Fast scroll -->
<dimen name="fastscroll_track_min_width">6dp</dimen>
@@ -115,6 +116,10 @@
<dimen name="all_apps_divider_margin_vertical">8dp</dimen>
+<!-- Floating action button inside work tab to toggle work profile -->
+ <dimen name="work_fab_height">48dp</dimen>
+ <dimen name="work_fab_radius">24dp</dimen>
+ <dimen name="work_fab_margin">18dp</dimen>
<dimen name="work_profile_footer_padding">20dp</dimen>
<dimen name="work_profile_footer_text_size">16sp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c851cf8..9cfe69a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -105,9 +105,18 @@
<!-- Dialog text. This dialog lets a user know how they can use widgets on their phone.
[CHAR_LIMIT=NONE] -->
<string name="widget_education_content">To get info without opening apps, you can add widgets to your Home screen</string>
+
+ <!-- Text on an educational tip on widget informing users that they can change widget settings.
+ [CHAR_LIMIT=NONE] -->
+ <string name="reconfigurable_widget_education_tip">Tap to change widget settings</string>
+
<!-- Text on the button that closes the education dialog about widgets. [CHAR_LIMIT=50] -->
<string name="widget_education_close_button">Got it</string>
+ <!-- Spoken text for screen readers. This text is for an icon that lets the user change a
+ widget's settings. [CHAR_LIMIT=50] -->
+ <string name="widget_reconfigure_button_content_description">Change widget settings</string>
+
<!-- All Apps -->
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
<string name="all_apps_search_bar_hint">Search apps</string>
@@ -379,37 +388,30 @@
<!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
<string name="work_profile_toggle_label">Work profile</string>
- <!--- User onboarding title for personal apps -->
- <string name="work_profile_edu_personal_apps">Personal data is separate & hidden from work apps</string>
<!--- User onboarding title for work profile apps -->
- <string name="work_profile_edu_work_apps">Work apps & data are visible to your IT admin</string>
- <!-- Action label to proceed to the next work profile edu section-->
- <string name="work_profile_edu_next">Next</string>
+ <string name="work_profile_edu_work_apps">Work apps are badged andare visible to your IT admin</string>
<!-- Action label to finish work profile edu-->
<string name="work_profile_edu_accept">Got it</string>
<!--- heading shown when user opens work apps tab while work apps are paused -->
- <string name="work_apps_paused_title">Work profile is paused</string>
+ <string name="work_apps_paused_title">Work apps are off</string>
<!--- body shown when user opens work apps tab while work apps are paused -->
- <string name="work_apps_paused_body">Work apps can’t send you notifications, use your battery, or access your location</string>
+ <string name="work_apps_paused_body">Your work apps can’t send you notifications, use your battery, or access your location</string>
<!-- content description for paused work apps list -->
- <string name="work_apps_paused_content_description">Work profile is paused. Work apps can’t send you notifications, use your battery, or access your location</string>
+ <string name="work_apps_paused_content_description">Work apps are off. Your work apps can’t send you notifications, use your battery, or access your location</string>
<!-- string shown in educational banner about work profile -->
<string name="work_apps_paused_edu_banner">Work apps are badged and visible to your IT admin</string>
<!-- button string shown to dismiss work tab education -->
<string name="work_apps_paused_edu_accept">Got it</string>
<!-- button string shown pause work profile -->
- <string name="work_apps_pause_btn_text">Pause work apps</string>
+ <string name="work_apps_pause_btn_text">Turn off work apps</string>
<!-- button string shown enable work profile -->
- <string name="work_apps_enable_btn_text">Turn on</string>
+ <string name="work_apps_enable_btn_text">Turn on work apps</string>
<!-- A hint shown in launcher settings develop options filter box -->
<string name="developer_options_filter_hint">Filter</string>
- <!-- A tip shown pointing at work toggle -->
- <string name="work_switch_tip">Pause work apps and notifications</string>
-
<!-- Failed action error message: e.g. Failed: Pause -->
<string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 86dcf4c..571377c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -47,7 +47,7 @@
<item name="workspaceKeyShadowColor">#89000000</item>
<item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
- <item name="folderDotColor">?android:attr/colorPrimary</item>
+ <item name="folderDotColor">@color/folder_dot_color</item>
<item name="folderFillColor">?android:attr/colorBackgroundFloating</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
<item name="folderTextColor">?android:attr/textColorPrimary</item>
@@ -86,7 +86,7 @@
<item name="workspaceKeyShadowColor">@android:color/transparent</item>
<item name="isWorkspaceDarkText">true</item>
<item name="workspaceStatusBarScrim">@null</item>
- <item name="folderDotColor">#FF464646</item>
+ <item name="folderDotColor">@color/folder_dot_color</item>
<item name="folderFillColor">?android:attr/colorBackgroundFloating</item>
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
@@ -108,7 +108,7 @@
<item name="popupColorSecondary">@color/popup_color_secondary_dark</item>
<item name="popupColorTertiary">@color/popup_color_tertiary_dark</item>
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
- <item name="folderDotColor">?android:attr/colorPrimary</item>
+ <item name="folderDotColor">@color/folder_dot_color</item>
<item name="folderFillColor">?android:attr/colorBackgroundFloating</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
<item name="folderTextColor">?android:attr/textColorPrimary</item>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 5d9797f..9e21e1a 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -4,7 +4,6 @@
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
-import static com.android.launcher3.Utilities.ATLEAST_S;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_COMPLETED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_STARTED;
import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
@@ -13,42 +12,42 @@
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;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
-import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.SizeF;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
+import androidx.annotation.Nullable;
+import androidx.annotation.Px;
+
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.util.WidgetSizes;
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;
private static final float DIMMED_HANDLE_ALPHA = 0f;
private static final float RESIZE_THRESHOLD = 0.66f;
+ private static final String KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
+ "launcher.reconfigurable_widget_education_tip_seen";
private static final Rect sTmpRect = new Rect();
private static final int HANDLE_COUNT = 4;
@@ -245,6 +244,15 @@
mWidgetView.getAppWidgetId(),
Launcher.REQUEST_RECONFIGURE_APPWIDGET);
});
+ if (!hasSeenReconfigurableWidgetEducationTip()) {
+ post(() -> {
+ if (showReconfigurableWidgetEducationTip() != null) {
+ mLauncher.getSharedPrefs().edit()
+ .putBoolean(KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN,
+ true).apply();
+ }
+ });
+ }
}
// When we create the resize frame, we first mark all cells as unoccupied. The appropriate
@@ -415,90 +423,12 @@
mRunningHInc += hSpanDelta;
if (!onDismiss) {
- updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
+ WidgetSizes.updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
}
}
mWidgetView.requestLayout();
}
- public static void updateWidgetSizeRanges(
- AppWidgetHostView widgetView, Context context, int spanX, int spanY) {
- List<SizeF> sizes = getWidgetSizes(context, spanX, spanY);
- if (ATLEAST_S) {
- widgetView.updateAppWidgetSize(new Bundle(), sizes);
- } else {
- Rect bounds = getMinMaxSizes(sizes);
- widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right,
- bounds.bottom);
- }
- }
-
- /** Returns the list of sizes for a widget of given span, in dp. */
- public static ArrayList<SizeF> getWidgetSizes(Context context, int spanX, int spanY) {
- ArrayList<SizeF> sizes = new ArrayList<>(2);
- 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<SizeF> 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<SizeF> 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.
- * @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.
- */
- private static Rect getMinMaxSizes(List<SizeF> sizes) {
- if (sizes.isEmpty()) {
- return new Rect();
- } else {
- SizeF first = sizes.get(0);
- Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(),
- (int) first.getWidth(), (int) first.getHeight());
- for (int i = 1; i < sizes.size(); i++) {
- result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
- }
- return result;
- }
- }
-
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -764,4 +694,25 @@
|| keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END
|| keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN);
}
+
+ @Nullable private ArrowTipView showReconfigurableWidgetEducationTip() {
+ Rect rect = new Rect();
+ if (!mReconfigureButton.getGlobalVisibleRect(rect)) {
+ return null;
+ }
+ @Px int tipMargin = mLauncher.getResources()
+ .getDimensionPixelSize(R.dimen.widget_reconfigure_tip_top_margin);
+ return new ArrowTipView(mLauncher, /* isPointingUp= */ true)
+ .showAroundRect(
+ getContext().getString(R.string.reconfigurable_widget_education_tip),
+ /* arrowXCoord= */ rect.left + mReconfigureButton.getWidth() / 2,
+ /* rect= */ rect,
+ /* margin= */ tipMargin);
+ }
+
+ private boolean hasSeenReconfigurableWidgetEducationTip() {
+ return mLauncher.getSharedPrefs()
+ .getBoolean(KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN, false)
+ || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+ }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9aa7168..578379b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1076,9 +1076,10 @@
getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
}
- if (mPrevLauncherState != state && !ALL_APPS.equals(state)
+ if (ALL_APPS.equals(mPrevLauncherState) && !ALL_APPS.equals(state)
// Making sure mAllAppsSessionLogId is not null to avoid double logging.
&& mAllAppsSessionLogId != null) {
+ getAppsView().getSearchUiManager().resetSearch();
getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_EXIT);
mAllAppsSessionLogId = null;
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 2c24c3a..4579705 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,6 +18,9 @@
import static android.view.MotionEvent.ACTION_DOWN;
+import static com.android.launcher3.CellLayout.FOLDER;
+import static com.android.launcher3.CellLayout.WORKSPACE;
+
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Rect;
@@ -124,21 +127,27 @@
public void measureChild(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
- final DeviceProfile profile = mActivity.getDeviceProfile();
+ final DeviceProfile dp = mActivity.getDeviceProfile();
if (child instanceof LauncherAppWidgetHostView) {
- ((LauncherAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
+ ((LauncherAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
- profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpacing, mTempRect);
+ dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpacing, mTempRect);
} else {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
mBorderSpacing, null);
// Center the icon/folder
int cHeight = getCellContentHeight();
int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
- int cellPaddingX = mContainerType == CellLayout.WORKSPACE
- ? profile.workspaceCellPaddingXPx
- : (int) (profile.edgeMarginPx / 2f);
+
+ // No need to add padding when cell layout border spacing is present.
+ boolean noPadding = (dp.cellLayoutBorderSpacingPx > 0 && mContainerType == WORKSPACE)
+ || (dp.folderCellLayoutBorderSpacingPx > 0 && mContainerType == FOLDER);
+ int cellPaddingX = noPadding
+ ? 0
+ : mContainerType == WORKSPACE
+ ? dp.workspaceCellPaddingXPx
+ : (int) (dp.edgeMarginPx / 2f);
child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
}
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cb9e1f3..a2d86bc 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -397,6 +397,13 @@
return mapRange(interpolator.getInterpolation(progress), toMin, toMax);
}
+ /** Bounds t between a lower and upper bound and maps the result to a range. */
+ public static float mapBoundToRange(float t, float lowerBound, float upperBound,
+ float toMin, float toMax, Interpolator interpolator) {
+ return mapToRange(boundToRange(t, lowerBound, upperBound), lowerBound, upperBound,
+ toMin, toMax, interpolator);
+ }
+
public static float getProgress(float current, float min, float max) {
return Math.abs(current - min) / Math.abs(max - min);
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index c7323d0..ff3584a 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -27,10 +27,10 @@
import android.os.CancellationSignal;
import android.os.Process;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
+import android.util.Size;
import androidx.annotation.Nullable;
@@ -50,6 +50,7 @@
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Collections;
@@ -79,9 +80,6 @@
private final UserCache mUserCache;
private final CacheDb mDb;
- private final UserHandle mMyUser = Process.myUserHandle();
- private final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
-
public WidgetPreviewLoader(Context context, IconCache iconCache) {
mContext = context;
mIconCache = iconCache;
@@ -366,9 +364,9 @@
previewHeight = drawable.getIntrinsicHeight();
} else {
DeviceProfile dp = launcher.getDeviceProfile();
- int tileSize = Math.min(dp.cellWidthPx, dp.cellHeightPx);
- previewWidth = tileSize * spanX;
- previewHeight = tileSize * spanY;
+ Size widgetSize = WidgetSizes.getWidgetSizePx(dp, spanX, spanY);
+ previewWidth = widgetSize.getWidth();
+ previewHeight = widgetSize.getHeight();
}
// Scale to fit width only - let the widget preview be clipped in the
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d136cda..2e1cc58 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -114,6 +114,7 @@
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetManagerHelper;
import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
+import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
import java.util.ArrayList;
@@ -1854,7 +1855,7 @@
item.spanX = resultSpan[0];
item.spanY = resultSpan[1];
AppWidgetHostView awhv = (AppWidgetHostView) cell;
- AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
+ WidgetSizes.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
resultSpan[1]);
}
@@ -1921,6 +1922,11 @@
CellLayout layout = (CellLayout) cell.getParent().getParent();
layout.markCellsAsOccupiedForView(cell);
}
+ } else {
+ // When drag is cancelled, reattach content view back to its original parent.
+ if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
+ d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
+ }
}
final CellLayout parent = (CellLayout) cell.getParent().getParent();
@@ -2528,8 +2534,7 @@
((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
if (finalView != null && updateWidgetSize) {
- AppWidgetResizeFrame.updateWidgetSizeRanges(finalView, mLauncher, item.spanX,
- item.spanY);
+ WidgetSizes.updateWidgetSizeRanges(finalView, mLauncher, item.spanX, item.spanY);
}
int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2b36f19..9faac5b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -21,7 +21,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
@@ -51,6 +50,7 @@
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.views.OptionsPopupView.OptionItem;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Collections;
@@ -367,7 +367,7 @@
}
layout.markCellsAsOccupiedForView(host);
- AppWidgetResizeFrame.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
+ WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
info.spanX, info.spanY);
host.requestLayout();
mLauncher.getModelWriter().updateItemInDatabase(info);
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index cb20fec..7249aaf 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -228,7 +228,7 @@
}
private void resetWorkProfile() {
- mWorkModeSwitch.update(!mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED));
+ mWorkModeSwitch.updateCurrentState(!mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED));
mAH[AdapterHolder.WORK].setupOverlay();
mAH[AdapterHolder.WORK].applyPadding();
}
@@ -482,7 +482,7 @@
private void setupWorkToggle() {
if (Utilities.ATLEAST_P) {
mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
- R.layout.work_mode_switch, this, false);
+ R.layout.work_mode_fab, this, false);
this.addView(mWorkModeSwitch);
mWorkModeSwitch.setInsets(mInsets);
mWorkModeSwitch.post(this::resetWorkProfile);
@@ -669,30 +669,10 @@
}
public void onPull(float deltaDistance, float displacement) {
- absorbPullDeltaDistance(PULL_MULTIPLIER * deltaDistance,
- PULL_MULTIPLIER * displacement);
- // ideally, this should be done using EdgeEffect.onPush to create squish effect.
- // However, until such method is available, launcher to simulate the onPush method.
- mHeader.setTranslationY(-.5f * mHeaderTop * deltaDistance);
- getRecyclerViewContainer().setTranslationY(-mHeaderTop * deltaDistance);
- }
-
- public void onRelease() {
- ValueAnimator anim1 = ValueAnimator.ofFloat(1f, 0f);
- final float floatingHeaderHeight = getFloatingHeaderView().getTranslationY();
- final float recyclerViewHeight = getRecyclerViewContainer().getTranslationY();
- anim1.setDuration(200);
- anim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- getFloatingHeaderView().setTranslationY(
- ((float) valueAnimator.getAnimatedValue()) * floatingHeaderHeight);
- getRecyclerViewContainer().setTranslationY(
- ((float) valueAnimator.getAnimatedValue()) * recyclerViewHeight);
- }
- });
- anim1.start();
- super.onRelease();
+ absorbPullDeltaDistance(PULL_MULTIPLIER * deltaDistance, PULL_MULTIPLIER * displacement);
+ // Current motion spec is to actually push and not pull
+ // on this surface. However, until EdgeEffect.onPush (b/190612804) is
+ // implemented at view level, we will simply pull
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index d536c3a..5bbd02b 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -190,8 +190,6 @@
case SCROLL_STATE_DRAGGING:
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
- hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
- getApplicationWindowToken());
break;
case SCROLL_STATE_IDLE:
mgr.logger().sendToInteractionJankMonitor(
@@ -207,6 +205,8 @@
&& mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.setHotspot(e.getX(), e.getY());
}
+ hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
+ getApplicationWindowToken());
return result;
}
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 16ae250..1eb726c 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -25,7 +25,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.views.WorkEduView;
/**
* AllAppsContainerView with launcher specific callbacks
@@ -88,13 +87,6 @@
@Override
public void onActivePageChanged(int currentActivePage) {
super.onActivePageChanged(currentActivePage);
- if (mUsingTabs) {
- if (currentActivePage == AdapterHolder.WORK) {
- WorkEduView.showWorkEduIfNeeded(mLauncher);
- } else {
- mWorkTabListener = WorkEduView.showEduFlowIfNeeded(mLauncher, mWorkTabListener);
- }
- }
}
@Override
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 4567ee6..c22cd63 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -15,108 +15,61 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.Context;
-import android.content.SharedPreferences;
import android.graphics.Rect;
-import android.os.AsyncTask;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-import android.widget.Switch;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.annotation.RequiresApi;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.views.ArrowTipView;
-
-import java.lang.ref.WeakReference;
/**
* Work profile toggle switch shown at the bottom of AllApps work tab
*/
-public class WorkModeSwitch extends Switch implements Insettable {
-
- private static final int WORK_TIP_THRESHOLD = 2;
- public static final String KEY_WORK_TIP_COUNTER = "worked_tip_counter";
+public class WorkModeSwitch extends Button implements Insettable, View.OnClickListener {
private Rect mInsets = new Rect();
-
- private final float[] mTouch = new float[2];
- private int mTouchSlop;
+ private boolean mWorkEnabled;
public WorkModeSwitch(Context context) {
- super(context);
- init();
+ this(context, null, 0);
}
public WorkModeSwitch(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
+ this(context, attrs, 0);
}
public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init();
- }
-
- private void init() {
- ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
- mTouchSlop = viewConfiguration.getScaledTouchSlop();
}
@Override
- public void setChecked(boolean checked) { }
-
- @Override
- public void toggle() {
- // don't show tip if user uses toggle
- Utilities.getPrefs(getContext()).edit().putInt(KEY_WORK_TIP_COUNTER, -1).apply();
- trySetQuietModeEnabledToAllProfilesAsync(isChecked());
- }
-
- /**
- * Sets the enabled or disabled state of the button
- * @param isChecked
- */
- public void update(boolean isChecked) {
- super.setChecked(isChecked);
- setCompoundDrawablesRelativeWithIntrinsicBounds(
- isChecked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
- setEnabled(true);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mTouch[0] = ev.getX();
- mTouch[1] = ev.getY();
- } else if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
- if (Math.abs(mTouch[0] - ev.getX()) > mTouchSlop
- || Math.abs(mTouch[1] - ev.getY()) > mTouchSlop) {
- int action = ev.getAction();
- ev.setAction(MotionEvent.ACTION_CANCEL);
- super.onTouchEvent(ev);
- ev.setAction(action);
- return false;
- }
- }
- return super.onTouchEvent(ev);
- }
-
- private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
- new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setOnClickListener(this);
}
@Override
public void setInsets(Rect insets) {
int bottomInset = insets.bottom - mInsets.bottom;
mInsets.set(insets);
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
- getPaddingBottom() + bottomInset);
+ ViewGroup.MarginLayoutParams marginLayoutParams =
+ (ViewGroup.MarginLayoutParams) getLayoutParams();
+ if (marginLayoutParams != null) {
+ marginLayoutParams.bottomMargin = bottomInset + marginLayoutParams.bottomMargin;
+ }
}
/**
@@ -125,78 +78,44 @@
public void setWorkTabVisible(boolean workTabVisible) {
clearAnimation();
if (workTabVisible) {
+ setEnabled(true);
setVisibility(VISIBLE);
setAlpha(0);
animate().alpha(1).start();
- showTipIfNeeded();
} else {
animate().alpha(0).withEndAction(() -> this.setVisibility(GONE)).start();
}
}
- private static final class SetQuietModeEnabledAsyncTask
- extends AsyncTask<Void, Void, Boolean> {
-
- private final boolean enabled;
- private final WeakReference<WorkModeSwitch> switchWeakReference;
-
- SetQuietModeEnabledAsyncTask(boolean enabled,
- WeakReference<WorkModeSwitch> switchWeakReference) {
- this.enabled = enabled;
- this.switchWeakReference = switchWeakReference;
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- WorkModeSwitch workModeSwitch = switchWeakReference.get();
- if (workModeSwitch != null) {
- workModeSwitch.setEnabled(false);
- }
- }
-
- @Override
- protected Boolean doInBackground(Void... voids) {
- WorkModeSwitch workModeSwitch = switchWeakReference.get();
- if (workModeSwitch == null || !Utilities.ATLEAST_P) {
- return false;
- }
-
- Context context = workModeSwitch.getContext();
- UserManager userManager = context.getSystemService(UserManager.class);
- boolean showConfirm = false;
- for (UserHandle userProfile : UserCache.INSTANCE.get(context).getUserProfiles()) {
- if (Process.myUserHandle().equals(userProfile)) {
- continue;
- }
- showConfirm |= !userManager.requestQuietModeEnabled(enabled, userProfile);
- }
- return showConfirm;
- }
-
- @Override
- protected void onPostExecute(Boolean showConfirm) {
- if (showConfirm) {
- WorkModeSwitch workModeSwitch = switchWeakReference.get();
- if (workModeSwitch != null) {
- workModeSwitch.setEnabled(true);
- }
- }
+ @Override
+ public void onClick(View view) {
+ if (Utilities.ATLEAST_P) {
+ setEnabled(false);
+ UI_HELPER_EXECUTOR.post(() -> setToState(!mWorkEnabled));
}
}
/**
- * Shows a work tip on the Nth work tab open
+ * Sets the enabled or disabled state of the button
*/
- public void showTipIfNeeded() {
- Context context = getContext();
- SharedPreferences prefs = Utilities.getPrefs(context);
- int tipCounter = prefs.getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
- if (tipCounter < 0) return;
- if (tipCounter == 0) {
- new ArrowTipView(context)
- .show(context.getString(R.string.work_switch_tip), getTop());
+ public void updateCurrentState(boolean active) {
+ mWorkEnabled = active;
+ setEnabled(true);
+ setCompoundDrawablesRelativeWithIntrinsicBounds(
+ active ? R.drawable.ic_corp_off : R.drawable.ic_corp, 0, 0, 0);
+ setText(active ? R.string.work_apps_pause_btn_text : R.string.work_apps_enable_btn_text);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.P)
+ protected Boolean setToState(boolean toState) {
+ UserManager userManager = getContext().getSystemService(UserManager.class);
+ boolean showConfirm = false;
+ for (UserHandle userProfile : UserCache.INSTANCE.get(getContext()).getUserProfiles()) {
+ if (Process.myUserHandle().equals(userProfile)) {
+ continue;
+ }
+ showConfirm |= !userManager.requestQuietModeEnabled(!toState, userProfile);
}
- prefs.edit().putInt(KEY_WORK_TIP_COUNTER, tipCounter - 1).apply();
+ return showConfirm;
}
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index ab72a07..0e710b7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -93,7 +93,7 @@
// 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");
+ "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", false, "Show chip hints on the overview screen");
public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 68bed44..3457804 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -6,9 +6,9 @@
private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2;
private static final float MIN_SCALE = 0.44f;
- private static final float MAX_SCALE = 0.54f;
- private static final float MAX_RADIUS_DILATION = 0.10f;
- private static final float ITEM_RADIUS_SCALE_FACTOR = 1.2f;
+ private static final float MAX_SCALE = 0.51f;
+ private static final float MAX_RADIUS_DILATION = 0.1f;
+ private static final float ITEM_RADIUS_SCALE_FACTOR = 1.15f;
public static final int EXIT_INDEX = -2;
public static final int ENTER_INDEX = -3;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 17c1329..dc188f2 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -743,7 +743,7 @@
}
mContent.completePendingPageChanges();
- mContent.snapToPageImmediately(pageNo);
+ mContent.setCurrentPage(pageNo);
// This is set to true in close(), but isn't reset to false until onDropCompleted(). This
// leads to an inconsistent state if you drag out of the folder and drag back in without
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index bd0dbfd..e66e2f6 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -16,6 +16,8 @@
package com.android.launcher3.folder;
+import static android.view.View.ALPHA;
+
import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
@@ -57,6 +59,7 @@
public class FolderAnimationManager {
private static final int FOLDER_NAME_ALPHA_DURATION = 32;
+ private static final int LARGE_FOLDER_FOOTER_DURATION = 128;
private Folder mFolder;
private FolderPagedView mContent;
@@ -214,7 +217,22 @@
play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale));
play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale));
- play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
+
+ final int footerAlphaDuration;
+ final int footerStartDelay;
+ if (isLargeFolder()) {
+ if (mIsOpening) {
+ footerAlphaDuration = LARGE_FOLDER_FOOTER_DURATION;
+ footerStartDelay = mDuration - footerAlphaDuration;
+ } else {
+ footerAlphaDuration = 0;
+ footerStartDelay = 0;
+ }
+ } else {
+ footerStartDelay = 0;
+ footerAlphaDuration = mDuration;
+ }
+ play(a, getAnimator(mFolder.mFooter, ALPHA, 0, 1f), footerStartDelay, footerAlphaDuration);
// Create reveal animator for the folder background
play(a, getShape().createRevealAnimator(
@@ -225,9 +243,13 @@
+ mDeviceProfile.folderCellWidthPx * 2;
int height = mContent.getPaddingTop() + mDeviceProfile.folderCellLayoutBorderSpacingPx
+ mDeviceProfile.folderCellHeightPx * 2;
- Rect startRect2 = new Rect(0, 0, width, height);
+ int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage();
+ int left = mContent.getPaddingLeft() + page * mContent.getWidth();
+ Rect contentStart = new Rect(left, 0, left + width, height);
+ Rect contentEnd = new Rect(endRect.left + left, endRect.top, endRect.right + left,
+ endRect.bottom);
play(a, getShape().createRevealAnimator(
- mFolder.getContent(), startRect2, endRect, finalRadius, !mIsOpening));
+ mFolder.getContent(), contentStart, contentEnd, finalRadius, !mIsOpening));
// Fade in the folder name, as the text can overlap the icons when grid size is small.
@@ -420,8 +442,12 @@
as.play(a);
}
+ private boolean isLargeFolder() {
+ return mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW;
+ }
+
private TimeInterpolator getPreviewItemInterpolator() {
- if (mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW) {
+ if (isLargeFolder()) {
// With larger folders, we want the preview items to reach their final positions faster
// (when opening) and later (when closing) so that they appear aligned with the rest of
// the folder items when they are both visible.
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6b12d86..ed2d0a9 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -627,6 +627,7 @@
if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
Rect iconBounds = mDotParams.iconBounds;
BubbleTextView.getIconBounds(this, iconBounds, mActivity.getDeviceProfile().iconSizePx);
+ iconBounds.offset(0, mBackground.paddingY);
float iconScale = (float) mBackground.previewSize / iconBounds.width();
Utilities.scaleRectAboutCenter(iconBounds, iconScale);
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
index 22f7333..edfd2ba 100644
--- a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
@@ -68,6 +68,7 @@
int duration, final Runnable onCompleteRunnable) {
mItemManager = itemManager;
mParams = params;
+ mParams.index = index1;
mItemManager.computePreviewItemDrawingParams(index1, items1, sTmpParams);
finalState = new float[] {sTmpParams.scale, sTmpParams.transX, sTmpParams.transY};
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
index a14a0d8..5746be8 100644
--- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -23,6 +23,7 @@
* Manages the parameters used to draw a Folder preview item.
*/
class PreviewItemDrawingParams {
+ float index;
float transX;
float transY;
float scale;
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 6adef01..baafbc9 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -28,6 +28,8 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
@@ -81,6 +83,10 @@
// These hold the current page preview items. It is empty if the current page is the first page.
private ArrayList<PreviewItemDrawingParams> mCurrentPageParams = new ArrayList<>();
+ // We clip the preview items during the middle of the animation, so that it does not go outside
+ // of the visual shape. We stop clipping at this threshold, since the preview items ultimately
+ // do not get cropped in their resting state.
+ private final float mClipThreshold;
private float mCurrentPageItemsTransX = 0;
private boolean mShouldSlideInFirstPage;
@@ -96,6 +102,7 @@
mIcon = icon;
mIconSize = ActivityContext.lookupContext(
mContext).getDeviceProfile().folderChildIconSizePx;
+ mClipThreshold = Utilities.dpToPx(1f);
}
/**
@@ -163,41 +170,60 @@
}
public void drawParams(Canvas canvas, ArrayList<PreviewItemDrawingParams> params,
- float transX) {
- canvas.translate(transX, 0);
+ PointF offset, boolean shouldClipPath, Path clipPath) {
// The first item should be drawn last (ie. on top of later items)
for (int i = params.size() - 1; i >= 0; i--) {
PreviewItemDrawingParams p = params.get(i);
if (!p.hidden) {
- drawPreviewItem(canvas, p);
+ // Exiting param should always be clipped.
+ boolean isExiting = p.index == EXIT_INDEX;
+ drawPreviewItem(canvas, p, offset, isExiting | shouldClipPath, clipPath);
}
}
- canvas.translate(-transX, 0);
}
+ /**
+ * Draws the preview items on {@param canvas}.
+ */
public void draw(Canvas canvas) {
+ int saveCount = canvas.getSaveCount();
// The items are drawn in coordinates relative to the preview offset
PreviewBackground bg = mIcon.getFolderBackground();
- canvas.translate(bg.basePreviewOffsetX, bg.basePreviewOffsetY);
-
+ Path clipPath = bg.getClipPath();
float firstPageItemsTransX = 0;
if (mShouldSlideInFirstPage) {
- drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX);
-
+ PointF firstPageOffset = new PointF(bg.basePreviewOffsetX + mCurrentPageItemsTransX,
+ bg.basePreviewOffsetY);
+ boolean shouldClip = mCurrentPageItemsTransX > mClipThreshold;
+ drawParams(canvas, mCurrentPageParams, firstPageOffset, shouldClip, clipPath);
firstPageItemsTransX = -ITEM_SLIDE_IN_OUT_DISTANCE_PX + mCurrentPageItemsTransX;
}
- drawParams(canvas, mFirstPageParams, firstPageItemsTransX);
- canvas.translate(-bg.basePreviewOffsetX, -bg.basePreviewOffsetY);
+ PointF firstPageOffset = new PointF(bg.basePreviewOffsetX + firstPageItemsTransX,
+ bg.basePreviewOffsetY);
+ boolean shouldClipFirstPage = firstPageItemsTransX < -mClipThreshold;
+ drawParams(canvas, mFirstPageParams, firstPageOffset, shouldClipFirstPage, clipPath);
+ canvas.restoreToCount(saveCount);
}
public void onParamsChanged() {
mIcon.invalidate();
}
- private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
+ /**
+ * Draws each preview item.
+ *
+ * @param offset The offset needed to draw the preview items.
+ * @param shouldClipPath Iff true, clip path using {@param clipPath}.
+ * @param clipPath The clip path of the folder icon.
+ */
+ private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params, PointF offset,
+ boolean shouldClipPath, Path clipPath) {
canvas.save();
- canvas.translate(params.transX, params.transY);
+ if (shouldClipPath) {
+ canvas.clipPath(clipPath);
+ }
+ canvas.translate(offset.x + params.transX, offset.y + params.transY);
canvas.scale(params.scale, params.scale);
Drawable d = params.drawable;
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index 003b3bd..658c6e1 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -26,13 +26,13 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.util.WidgetSizes;
/**
* Represents a widget (either instantiated or about to be) in the Launcher.
@@ -196,7 +196,7 @@
*/
public void onBindAppWidget(Launcher launcher, AppWidgetHostView hostView) {
if (!mHasNotifiedInitialWidgetSizeChanged) {
- AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
+ WidgetSizes.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
mHasNotifiedInitialWidgetSizeChanged = true;
}
}
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index d44d158..932e721 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
+import android.animation.AnimatorSet;
import android.app.Notification;
import android.content.Context;
import android.graphics.Color;
@@ -92,6 +93,15 @@
});
}
+ /**
+ * Animates the background color to a new color.
+ * @param color The color to change to.
+ * @param animatorSetOut The AnimatorSet where we add the color animator to.
+ */
+ public void updateBackgroundColor(int color, AnimatorSet animatorSetOut) {
+ mMainView.updateBackgroundColor(color, animatorSetOut);
+ }
+
public void addGutter() {
if (mGutter == null) {
mGutter = mPopupContainer.inflateAndAdd(R.layout.notification_gutter, mRootView);
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index c995666..e9b5f32 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -20,10 +20,13 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DISMISSED;
import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
@@ -78,6 +81,9 @@
private SingleAxisSwipeDetector mSwipeDetector;
+ private final ColorDrawable mColorDrawable;
+ private final RippleDrawable mRippleDrawable;
+
public NotificationMainView(Context context) {
this(context, null, 0);
}
@@ -90,6 +96,10 @@
super(context, attrs, defStyle);
mContentTranslateAnimator = ObjectAnimator.ofFloat(this, CONTENT_TRANSLATION, 0);
+ mColorDrawable = new ColorDrawable(Color.TRANSPARENT);
+ mRippleDrawable = new RippleDrawable(ColorStateList.valueOf(
+ Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
+ mColorDrawable, null);
}
@Override
@@ -105,18 +115,31 @@
updateBackgroundColor(colorBackground.getColor());
}
- public void updateBackgroundColor(int color) {
+ private void updateBackgroundColor(int color) {
mBackgroundColor = color;
- RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
- Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
- new ColorDrawable(color), null);
- mTextAndBackground.setBackground(rippleBackground);
+ mColorDrawable.setColor(color);
+ mTextAndBackground.setBackground(mRippleDrawable);
if (mNotificationInfo != null) {
mIconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
mBackgroundColor));
}
}
+ /**
+ * Animates the background color to a new color.
+ * @param color The color to change to.
+ * @param animatorSetOut The AnimatorSet where we add the color animator to.
+ */
+ public void updateBackgroundColor(int color, AnimatorSet animatorSetOut) {
+ int oldColor = mBackgroundColor;
+ ValueAnimator colors = ValueAnimator.ofArgb(oldColor, color);
+ colors.addUpdateListener(valueAnimator -> {
+ int newColor = (int) valueAnimator.getAnimatedValue();
+ updateBackgroundColor(newColor);
+ });
+ animatorSetOut.play(colors);
+ }
+
public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
mSwipeDetector = swipeDetector;
}
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 8dea14a..2095a0d 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -25,35 +25,48 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Pair;
+import android.util.SparseIntArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.LocalColorExtractor;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
/**
* A container for shortcuts to deep links and notifications associated with an app.
@@ -76,6 +89,10 @@
private static final int CLOSE_CHILD_FADE_START_DELAY = 0;
private static final int CLOSE_CHILD_FADE_DURATION = 140;
+ // Index used to get background color when using local wallpaper color extraction,
+ private static final int DARK_COLOR_EXTRACTION_INDEX = android.R.color.system_neutral2_800;
+ private static final int LIGHT_COLOR_EXTRACTION_INDEX = android.R.color.system_accent2_50;
+
private final Rect mTempRect = new Rect();
protected final LayoutInflater mInflater;
@@ -104,9 +121,18 @@
private Runnable mOnCloseCallback = () -> { };
+ // The rect string of the view that the arrow is attached to, in screen reference frame.
+ protected String mArrowColorRectString;
+ private int mArrowColor;
+ protected final HashMap<String, View> mViewForRect = new HashMap<>();
+
+ @Nullable protected LocalColorExtractor mColorExtractor;
+
private final float mElevation;
private final int mBackgroundColor;
+ private final String mIterateChildrenTag;
+
public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mInflater = LayoutInflater.from(context);
@@ -115,6 +141,7 @@
mIsRtl = Utilities.isRtl(getResources());
mBackgroundColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
+ mArrowColor = mBackgroundColor;
mElevation = getResources().getDimension(R.dimen.deep_shortcuts_elevation);
// Initialize arrow view
@@ -139,6 +166,14 @@
mRoundedBottom.setColor(mBackgroundColor);
mRoundedBottom.setCornerRadii(new float[] { smallerRadius, smallerRadius, smallerRadius,
smallerRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius});
+
+ mIterateChildrenTag = getContext().getString(R.string.popup_container_iterate_children);
+
+ boolean isAboveAnotherSurface = getTopOpenViewWithType(mLauncher, TYPE_FOLDER) != null
+ || mLauncher.getStateManager().getState() == LauncherState.ALL_APPS;
+ if (!isAboveAnotherSurface && Utilities.ATLEAST_S) {
+ setupColorExtraction();
+ }
}
public ArrowPopup(Context context, AttributeSet attrs) {
@@ -184,11 +219,11 @@
/**
* Set the margins and radius of backgrounds after views are properly ordered.
*/
- protected void assignMarginsAndBackgrounds() {
- int count = getChildCount();
+ public void assignMarginsAndBackgrounds(ViewGroup viewGroup) {
+ int count = viewGroup.getChildCount();
int totalVisibleShortcuts = 0;
for (int i = 0; i < count; i++) {
- View view = getChildAt(i);
+ View view = viewGroup.getChildAt(i);
if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
totalVisibleShortcuts++;
}
@@ -197,8 +232,7 @@
int numVisibleShortcut = 0;
View lastView = null;
for (int i = 0; i < count; i++) {
- View view = getChildAt(i);
- boolean isShortcut = view instanceof DeepShortcutView;
+ View view = viewGroup.getChildAt(i);
if (view.getVisibility() == VISIBLE) {
if (lastView != null) {
MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
@@ -208,7 +242,12 @@
MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
mlp.bottomMargin = 0;
- if (isShortcut) {
+ if (view instanceof ViewGroup && mIterateChildrenTag.equals(view.getTag())) {
+ assignMarginsAndBackgrounds((ViewGroup) view);
+ continue;
+ }
+
+ if (view instanceof DeepShortcutView) {
if (totalVisibleShortcuts == 1) {
view.setBackgroundResource(R.drawable.single_item_primary);
} else if (totalVisibleShortcuts > 1) {
@@ -227,6 +266,118 @@
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
}
+
+ @TargetApi(Build.VERSION_CODES.S)
+ private int getExtractedColor(SparseIntArray colors) {
+ int index = Utilities.isDarkTheme(getContext())
+ ? DARK_COLOR_EXTRACTION_INDEX
+ : LIGHT_COLOR_EXTRACTION_INDEX;
+ return colors.get(index, mBackgroundColor);
+ }
+
+ @TargetApi(Build.VERSION_CODES.S)
+ private void setupColorExtraction() {
+ Workspace workspace = mLauncher.findViewById(R.id.workspace);
+ if (workspace == null) {
+ return;
+ }
+
+ mColorExtractor = LocalColorExtractor.newInstance(mLauncher);
+ mColorExtractor.setListener((rect, extractedColors) -> {
+ String rectString = rect.toShortString();
+ View v = mViewForRect.get(rectString);
+ AnimatorSet colors = new AnimatorSet();
+ if (v != null) {
+ int newColor = getExtractedColor(extractedColors);
+ setChildColor(v, newColor, colors);
+ int numChildren = v instanceof ViewGroup ? ((ViewGroup) v).getChildCount() : 0;
+ for (int i = 0; i < numChildren; ++i) {
+ View childView = ((ViewGroup) v).getChildAt(i);
+ setChildColor(childView, newColor, colors);
+
+ }
+ if (rectString.equals(mArrowColorRectString)) {
+ mArrowColor = newColor;
+ updateArrowColor();
+ }
+ }
+ colors.setDuration(150);
+ v.post(colors::start);
+ });
+ }
+
+ protected void addPreDrawForColorExtraction(Launcher launcher) {
+ getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ initColorExtractionLocations(launcher);
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Returns list of child views that will receive local color extraction treatment.
+ * Note: Order should match the view hierarchy.
+ */
+ protected List<View> getChildrenForColorExtraction() {
+ return Collections.emptyList();
+ }
+
+ private void initColorExtractionLocations(Launcher launcher) {
+ if (mColorExtractor == null) {
+ return;
+ }
+ ArrayList<RectF> locations = new ArrayList<>();
+
+ boolean firstVisibleChild = true;
+ // Order matters here, since we need the arrow to match the color of its adjacent view.
+ for (View view : getChildrenForColorExtraction()) {
+ if (view != null && view.getVisibility() == VISIBLE) {
+ RectF rf = new RectF();
+ mColorExtractor.getExtractedRectForView(launcher,
+ launcher.getWorkspace().getCurrentPage(), view, rf);
+ if (!rf.isEmpty()) {
+ locations.add(rf);
+ String rectString = rf.toShortString();
+ mViewForRect.put(rectString, view);
+ if (mIsAboveIcon) {
+ mArrowColorRectString = rectString;
+ } else {
+ if (firstVisibleChild) {
+ mArrowColorRectString = rectString;
+ }
+ }
+
+ if (firstVisibleChild) {
+ firstVisibleChild = false;
+ }
+
+ }
+ }
+ }
+ if (!locations.isEmpty()) {
+ mColorExtractor.addLocation(locations);
+ }
+ }
+
+ /**
+ * Sets the background color of the child.
+ */
+ protected void setChildColor(View view, int color, AnimatorSet animatorSetOut) {
+ Drawable bg = view.getBackground();
+ if (bg instanceof GradientDrawable) {
+ GradientDrawable gd = (GradientDrawable) bg.mutate();
+ int oldColor = ((GradientDrawable) bg).getColor().getDefaultColor();
+ animatorSetOut.play(ObjectAnimator.ofArgb(gd, "color", oldColor, color));
+ } else if (bg instanceof ColorDrawable) {
+ ColorDrawable cd = (ColorDrawable) bg.mutate();
+ int oldColor = ((ColorDrawable) bg).getColor();
+ animatorSetOut.play(ObjectAnimator.ofArgb(cd, "color", oldColor, color));
+ }
+ }
+
/**
* Shows the popup at the desired location, optionally reversing the children.
* @param viewsToFlip number of views from the top to to flip in case of reverse order
@@ -238,7 +389,7 @@
reverseOrder(viewsToFlip);
}
onInflationComplete(reverseOrder);
- assignMarginsAndBackgrounds();
+ assignMarginsAndBackgrounds(this);
if (shouldAddArrow()) {
addArrow();
}
@@ -251,7 +402,7 @@
protected void show() {
setupForDisplay();
onInflationComplete(false);
- assignMarginsAndBackgrounds();
+ assignMarginsAndBackgrounds(this);
if (shouldAddArrow()) {
addArrow();
}
@@ -297,18 +448,24 @@
// so we centered it instead. In that case we don't want to showDefaultOptions the arrow.
mArrow.setVisibility(INVISIBLE);
} else {
+ updateArrowColor();
+ }
+
+ mArrow.setPivotX(mArrowWidth / 2.0f);
+ mArrow.setPivotY(mIsAboveIcon ? mArrowHeight : 0);
+ }
+
+ private void updateArrowColor() {
+ if (!Gravity.isVertical(mGravity)) {
mArrow.setBackground(new RoundedArrowDrawable(
mArrowWidth, mArrowHeight, mArrowPointRadius,
mOutlineRadius, getMeasuredWidth(), getMeasuredHeight(),
mArrowOffsetHorizontal, -mArrowOffsetVertical,
!mIsAboveIcon, mIsLeftAligned,
- mBackgroundColor));
+ mArrowColor));
// TODO: Remove elevation when arrow is above as it casts a shadow on the container
mArrow.setElevation(mIsAboveIcon ? mElevation : 0);
}
-
- mArrow.setPivotX(mArrowWidth / 2.0f);
- mArrow.setPivotY(mIsAboveIcon ? mArrowHeight : 0);
}
/**
@@ -506,7 +663,7 @@
private AnimatorSet getOpenCloseAnimator(boolean isOpening, int totalDuration,
int fadeStartDelay, int fadeDuration, int childFadeStartDelay,
int childFadeDuration, Interpolator interpolator) {
- final AnimatorSet openAnim = new AnimatorSet();
+ final AnimatorSet animatorSet = new AnimatorSet();
float[] alphaValues = isOpening ? new float[] {0, 1} : new float[] {1, 0};
float[] scaleValues = isOpening ? new float[] {0.5f, 1} : new float[] {1, 0.5f};
@@ -519,32 +676,41 @@
mArrow.setAlpha(alpha);
setAlpha(alpha);
});
- openAnim.play(fade);
+ animatorSet.play(fade);
setPivotX(mIsLeftAligned ? 0 : getMeasuredWidth());
setPivotY(mIsAboveIcon ? getMeasuredHeight() : 0);
Animator scale = ObjectAnimator.ofFloat(this, View.SCALE_Y, scaleValues);
scale.setDuration(totalDuration);
scale.setInterpolator(interpolator);
- openAnim.play(scale);
+ animatorSet.play(scale);
- for (int i = getChildCount() - 1; i >= 0; --i) {
- View view = getChildAt(i);
+ fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet);
+
+ return animatorSet;
+ }
+
+ private void fadeInChildViews(ViewGroup group, float[] alphaValues, long startDelay,
+ long duration, AnimatorSet out) {
+ for (int i = group.getChildCount() - 1; i >= 0; --i) {
+ View view = group.getChildAt(i);
if (view.getVisibility() == VISIBLE && view instanceof ViewGroup) {
+ if (mIterateChildrenTag.equals(view.getTag())) {
+ fadeInChildViews((ViewGroup) view, alphaValues, startDelay, duration, out);
+ continue;
+ }
for (int j = ((ViewGroup) view).getChildCount() - 1; j >= 0; --j) {
View childView = ((ViewGroup) view).getChildAt(j);
-
childView.setAlpha(alphaValues[0]);
ValueAnimator childFade = ObjectAnimator.ofFloat(childView, ALPHA, alphaValues);
- childFade.setStartDelay(childFadeStartDelay);
- childFade.setDuration(childFadeDuration);
+ childFade.setStartDelay(startDelay);
+ childFade.setDuration(duration);
childFade.setInterpolator(LINEAR);
- openAnim.play(childFade);
+ out.play(childFade);
}
}
}
- return openAnim;
}
@@ -593,6 +759,12 @@
getPopupContainer().removeView(this);
getPopupContainer().removeView(mArrow);
mOnCloseCallback.run();
+ mArrowColorRectString = null;
+ mViewForRect.clear();
+ if (mColorExtractor != null) {
+ mColorExtractor.removeLocations();
+ mColorExtractor.setListener(null);
+ }
}
/**
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b115678..332390d 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -71,6 +71,7 @@
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -95,6 +96,8 @@
private int mNumNotifications;
private ViewGroup mNotificationContainer;
+ private ViewGroup mDeepShortcutContainer;
+
private ViewGroup mSystemShortcutContainer;
protected PopupItemDragHandler mPopupItemDragHandler;
@@ -172,6 +175,14 @@
return false;
}
+ @Override
+ protected void setChildColor(View view, int color, AnimatorSet animatorSetOut) {
+ super.setChildColor(view, color, animatorSetOut);
+ if (view.getId() == R.id.notification_container && mNotificationItemView != null) {
+ mNotificationItemView.updateBackgroundColor(color, animatorSetOut);
+ }
+ }
+
/**
* Returns true if we can show the container.
*/
@@ -218,6 +229,13 @@
mPopupItemDragHandler = new LauncherPopupItemDragHandler(launcher, this);
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(launcher);
launcher.getDragController().addDragListener(this);
+ addPreDrawForColorExtraction(launcher);
+ }
+
+ @Override
+ protected List<View> getChildrenForColorExtraction() {
+ return Arrays.asList(mSystemShortcutContainer, mDeepShortcutContainer,
+ mNotificationContainer);
}
@Override
@@ -262,13 +280,18 @@
}
int viewsToFlip = getChildCount();
mSystemShortcutContainer = this;
+ if (mDeepShortcutContainer == null) {
+ mDeepShortcutContainer = findViewById(R.id.deep_shortcuts_container);
+ }
if (hasDeepShortcuts) {
+ mDeepShortcutContainer.setVisibility(View.VISIBLE);
+
if (mNotificationItemView != null) {
mNotificationItemView.addGutter();
}
for (int i = shortcutCount; i > 0; i--) {
- DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut, this);
+ DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut, mDeepShortcutContainer);
v.getLayoutParams().width = containerWidth;
mShortcuts.add(v);
}
@@ -281,13 +304,16 @@
R.layout.system_shortcut_icon_only, mSystemShortcutContainer, shortcut);
}
}
- } else if (!systemShortcuts.isEmpty()) {
- if (mNotificationItemView != null) {
- mNotificationItemView.addGutter();
- }
+ } else {
+ mDeepShortcutContainer.setVisibility(View.GONE);
+ if (!systemShortcuts.isEmpty()) {
+ if (mNotificationItemView != null) {
+ mNotificationItemView.addGutter();
+ }
- for (SystemShortcut shortcut : systemShortcuts) {
- initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
+ for (SystemShortcut shortcut : systemShortcuts) {
+ initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
+ }
}
}
@@ -563,7 +589,7 @@
mNotificationItemView = null;
mNotificationContainer.setVisibility(GONE);
updateHiddenShortcuts();
- assignMarginsAndBackgrounds();
+ assignMarginsAndBackgrounds(PopupContainerWithArrow.this);
} else {
mNotificationItemView.trimNotifications(
NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 22c3f58..23ee251 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -20,8 +20,6 @@
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER;
-import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions;
-
import android.app.Activity;
import android.app.Fragment;
import android.app.SearchManager;
@@ -49,6 +47,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.FragmentWithPreview;
+import com.android.launcher3.widget.util.WidgetSizes;
/**
* A frame layout which contains a QSB. This internally uses fragment to bind the view, which
@@ -292,7 +291,8 @@
protected Bundle createBindOptions() {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
- return getWidgetSizeOptions(getContext(), mWidgetInfo.provider, idp.numColumns, 1);
+ return WidgetSizes.getWidgetSizeOptions(getContext(), mWidgetInfo.provider,
+ idp.numColumns, 1);
}
protected View getDefaultView(ViewGroup container, boolean showSetupIcon) {
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index 07d3776..e449a4b 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -17,8 +17,10 @@
package com.android.launcher3.views;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.Handler;
import android.util.Log;
@@ -53,9 +55,10 @@
protected final BaseDraggingActivity mActivity;
private final Handler mHandler = new Handler();
- private final boolean mIsPointingUp;
private final int mArrowWidth;
+ private boolean mIsPointingUp;
private Runnable mOnClosed;
+ private View mArrowView;
public ArrowTipView(Context context) {
this(context, false);
@@ -73,6 +76,9 @@
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
close(true);
+ if (mActivity.getDragLayer().isEventOverView(this, ev)) {
+ return true;
+ }
}
return false;
}
@@ -106,24 +112,8 @@
inflate(context, R.layout.arrow_toast, this);
setOrientation(LinearLayout.VERTICAL);
- View arrowView = findViewById(R.id.arrow);
- ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
- ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
- arrowLp.width, arrowLp.height, mIsPointingUp));
- Paint arrowPaint = arrowDrawable.getPaint();
- arrowPaint.setColor(ContextCompat.getColor(getContext(), R.color.arrow_tip_view_bg));
- // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
- arrowPaint.setPathEffect(new CornerPathEffect(
- context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
- arrowView.setBackground(arrowDrawable);
- if (mIsPointingUp) {
- removeView(arrowView);
- addView(arrowView, 0);
- }
-
- mIsOpen = true;
-
- mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
+ mArrowView = findViewById(R.id.arrow);
+ updateArrowTipInView();
}
/**
@@ -136,10 +126,10 @@
/**
* Show the ArrowTipView (tooltip) center, start, or end aligned.
*
- * @param text The text to be shown in the tooltip.
- * @param gravity The gravity aligns the tooltip center, start, or end.
+ * @param text The text to be shown in the tooltip.
+ * @param gravity The gravity aligns the tooltip center, start, or end.
* @param arrowMarginStart The margin from start to place arrow (ignored if center)
- * @param top The Y coordinate of the bottom of tooltip.
+ * @param top The Y coordinate of the bottom of tooltip.
* @return The tooltip.
*/
public ArrowTipView show(String text, int gravity, int arrowMarginStart, int top) {
@@ -149,8 +139,7 @@
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
params.gravity = gravity;
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) findViewById(
- R.id.arrow).getLayoutParams();
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mArrowView.getLayoutParams();
lp.gravity = gravity;
if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
@@ -166,6 +155,9 @@
params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
post(() -> setY(top - (mIsPointingUp ? 0 : getHeight())));
+
+ mIsOpen = true;
+ mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
setAlpha(0);
animate()
.alpha(1f)
@@ -178,18 +170,61 @@
}
/**
- * Show the ArrowTipView (tooltip) custom aligned.
+ * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it
+ * cannot fit on screen in the requested orientation.
*
- * @param text The text to be shown in the tooltip.
- * @param arrowXCoord The X coordinate for the arrow on the tip. The arrow is usually in the
- * center of ArrowTipView unless the ArrowTipView goes beyond screen margin.
- * @param yCoord The Y coordinate of the bottom of the tooltip.
- * @return The tool tip view.
+ * @param text The text to be shown in the tooltip.
+ * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the
+ * center of tooltip unless the tooltip goes beyond screen margin.
+ * @param yCoord The Y coordinate of the pointed tip end of the tooltip.
+ * @return The tool tip view. {@code null} if the tip can not be shown.
*/
- @Nullable
- public ArrowTipView showAtLocation(String text, int arrowXCoord, int yCoord) {
+ @Nullable public ArrowTipView showAtLocation(String text, @Px int arrowXCoord, @Px int yCoord) {
+ return showAtLocation(
+ text,
+ arrowXCoord,
+ /* yCoordDownPointingTip= */ yCoord,
+ /* yCoordUpPointingTip= */ yCoord);
+ }
+
+ /**
+ * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it
+ * cannot fit on screen in the requested orientation.
+ *
+ * @param text The text to be shown in the tooltip.
+ * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the
+ * center of tooltip unless the tooltip goes beyond screen margin.
+ * @param rect The coordinates of the view which requests the tooltip to be shown.
+ * @param margin The margin between {@param rect} and the tooltip.
+ * @return The tool tip view. {@code null} if the tip can not be shown.
+ */
+ @Nullable public ArrowTipView showAroundRect(
+ String text, @Px int arrowXCoord, Rect rect, @Px int margin) {
+ return showAtLocation(
+ text,
+ arrowXCoord,
+ /* yCoordDownPointingTip= */ rect.top - margin,
+ /* yCoordUpPointingTip= */ rect.bottom + margin);
+ }
+
+ /**
+ * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it
+ * cannot fit on screen in the requested orientation.
+ *
+ * @param text The text to be shown in the tooltip.
+ * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the
+ * center of tooltip unless the tooltip goes beyond screen margin.
+ * @param yCoordDownPointingTip The Y coordinate of the pointed tip end of the tooltip when the
+ * tooltip is placed pointing downwards.
+ * @param yCoordUpPointingTip The Y coordinate of the pointed tip end of the tooltip when the
+ * tooltip is placed pointing upwards.
+ * @return The tool tip view. {@code null} if the tip can not be shown.
+ */
+ @Nullable private ArrowTipView showAtLocation(String text, @Px int arrowXCoord,
+ @Px int yCoordDownPointingTip, @Px int yCoordUpPointingTip) {
ViewGroup parent = mActivity.getDragLayer();
@Px int parentViewWidth = parent.getWidth();
+ @Px int parentViewHeight = parent.getHeight();
@Px int maxTextViewWidth = getContext().getResources()
.getDimensionPixelSize(R.dimen.widget_picker_education_tip_max_width);
@Px int minViewMargin = getContext().getResources()
@@ -206,22 +241,45 @@
requestLayout();
post(() -> {
+ // Adjust the tooltip horizontally.
float halfWidth = getWidth() / 2f;
float xCoord;
if (arrowXCoord - halfWidth < minViewMargin) {
+ // If the tooltip is estimated to go beyond the left margin, place its start just at
+ // the left margin.
xCoord = minViewMargin;
} else if (arrowXCoord + halfWidth > parentViewWidth - minViewMargin) {
+ // If the tooltip is estimated to go beyond the right margin, place it such that its
+ // end is just at the right margin.
xCoord = parentViewWidth - minViewMargin - getWidth();
} else {
+ // Place the tooltip such that its center is at arrowXCoord.
xCoord = arrowXCoord - halfWidth;
}
setX(xCoord);
- setY(yCoord - getHeight());
- View arrowView = findViewById(R.id.arrow);
- arrowView.setX(arrowXCoord - xCoord - arrowView.getWidth() / 2f);
+
+ // Adjust the tooltip vertically.
+ @Px int viewHeight = getHeight();
+ if (mIsPointingUp
+ ? (yCoordUpPointingTip + viewHeight > parentViewHeight)
+ : (yCoordDownPointingTip - viewHeight < 0)) {
+ // Flip the view if it exceeds the vertical bounds of screen.
+ mIsPointingUp = !mIsPointingUp;
+ updateArrowTipInView();
+ }
+ // Place the tooltip such that its top is at yCoordUpPointingTip if arrow is displayed
+ // pointing upwards, otherwise place it such that its bottom is at
+ // yCoordDownPointingTip.
+ setY(mIsPointingUp ? yCoordUpPointingTip : yCoordDownPointingTip - viewHeight);
+
+ // Adjust the arrow's relative position on tooltip to make sure the actual position of
+ // arrow's pointed tip is always at arrowXCoord.
+ mArrowView.setX(arrowXCoord - xCoord - mArrowView.getWidth() / 2f);
requestLayout();
});
+ mIsOpen = true;
+ mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
setAlpha(0);
animate()
.alpha(1f)
@@ -233,6 +291,27 @@
return this;
}
+ private void updateArrowTipInView() {
+ ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams();
+ ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+ arrowLp.width, arrowLp.height, mIsPointingUp));
+ Paint arrowPaint = arrowDrawable.getPaint();
+ @Px int arrowTipRadius = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.arrow_toast_corner_radius);
+ arrowPaint.setColor(ContextCompat.getColor(getContext(), R.color.arrow_tip_view_bg));
+ arrowPaint.setPathEffect(new CornerPathEffect(arrowTipRadius));
+ mArrowView.setBackground(arrowDrawable);
+ // Add negative margin so that the rounded corners on base of arrow are not visible.
+ removeView(mArrowView);
+ if (mIsPointingUp) {
+ addView(mArrowView, 0);
+ ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, 0, 0, -1 * arrowTipRadius);
+ } else {
+ addView(mArrowView, 1);
+ ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, -1 * arrowTipRadius, 0, 0);
+ }
+ }
+
/**
* Register a callback fired when toast is hidden
*/
@@ -240,4 +319,10 @@
mOnClosed = runnable;
return this;
}
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ close(/* animate= */ false);
+ }
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 98cc876..06ccbbd 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -146,13 +146,25 @@
view.setOnLongClickListener(popup);
popup.mItemMap.put(view, item);
}
+
+ popup.addPreDrawForColorExtraction(launcher);
popup.show();
return popup;
}
+ @Override
+ protected List<View> getChildrenForColorExtraction() {
+ int childCount = getChildCount();
+ ArrayList<View> children = new ArrayList<>(childCount);
+ for (int i = 0; i < childCount; ++i) {
+ children.add(getChildAt(i));
+ }
+ return children;
+ }
+
@VisibleForTesting
public static ArrowPopup getOptionsPopup(Launcher launcher) {
- return launcher.findViewById(R.id.deep_shortcuts_container);
+ return launcher.findViewById(R.id.popup_container);
}
public static void showDefaultOptions(Launcher launcher, float x, float y) {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 9d0913a..f49d1b5 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -23,18 +23,22 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.WindowInsets;
import android.widget.TextView;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BaseRecyclerView;
@@ -51,6 +55,7 @@
*/
public class RecyclerViewFastScroller extends View {
+ private static final int FASTSCROLL_THRESHOLD_MILLIS = 200;
private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
private static final Rect sTempRect = new Rect();
@@ -101,6 +106,7 @@
private final boolean mCanThumbDetach;
private boolean mIgnoreDragGesture;
private boolean mIsRecyclerViewFirstChildInParent = true;
+ private long mDownTimeStampMillis;
// This is the offset from the top of the scrollbar when the user first starts touching. To
// prevent jumping, this offset is applied as the user scrolls.
@@ -112,6 +118,7 @@
private TextView mPopupView;
private boolean mPopupVisible;
private String mPopupSectionName;
+ private Insets mSystemGestureInsets;
protected BaseRecyclerView mRv;
private RecyclerView.OnScrollListener mOnScrollListener;
@@ -237,6 +244,7 @@
// Keep track of the down positions
mDownX = x;
mDownY = mLastY = y;
+ mDownTimeStampMillis = ev.getDownTime();
if ((Math.abs(mDy) < mDeltaThreshold &&
mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
@@ -246,22 +254,27 @@
}
if (isNearThumb(x, y)) {
mTouchOffsetY = mDownY - mThumbOffsetY;
- } else if (mRv.supportsFastScrolling()
- && isNearScrollBar(mDownX)) {
- calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
- updateFastScrollSectionNameAndThumbOffset(y);
}
break;
case MotionEvent.ACTION_MOVE:
mLastY = y;
+ int absDeltaY = Math.abs(y - mDownY);
+ int absDeltaX = Math.abs(x - mDownX);
// Check if we should start scrolling, but ignore this fastscroll gesture if we have
// exceeded some fixed movement
- mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop();
- if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
- isNearThumb(mDownX, mLastY) &&
- Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) {
- calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+ mIgnoreDragGesture |= absDeltaY > mConfig.getScaledPagingTouchSlop();
+
+ if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling()) {
+ // condition #1: triggering thumb is distance, angle based
+ if ((isNearThumb(mDownX, mLastY)
+ && absDeltaY > mConfig.getScaledPagingTouchSlop()
+ && absDeltaY > absDeltaX)
+ // condition#2: Fastscroll function is now time based
+ || (isNearScrollBar(mDownX) && ev.getEventTime() - mDownTimeStampMillis
+ > FASTSCROLL_THRESHOLD_MILLIS)) {
+ calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+ }
}
if (mIsDragging) {
updateFastScrollSectionNameAndThumbOffset(y);
@@ -328,12 +341,27 @@
canvas.drawRoundRect(mThumbBounds, r, r, mThumbPaint);
if (Utilities.ATLEAST_Q) {
mThumbBounds.roundOut(SYSTEM_GESTURE_EXCLUSION_RECT.get(0));
+ // swiping very close to the thumb area (not just within it's bound)
+ // will also prevent back gesture
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).offset(mThumbDrawOffset.x, mThumbDrawOffset.y);
+ if (Utilities.ATLEAST_Q) {
+ SYSTEM_GESTURE_EXCLUSION_RECT.get(0).left =
+ SYSTEM_GESTURE_EXCLUSION_RECT.get(0).right - mSystemGestureInsets.right;
+ }
setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
}
canvas.restoreToCount(saveCount);
}
+ @Override
+ @RequiresApi(Build.VERSION_CODES.Q)
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ if (Utilities.ATLEAST_Q) {
+ mSystemGestureInsets = insets.getSystemGestureInsets();
+ }
+ return super.onApplyWindowInsets(insets);
+ }
+
private float getScrollThumbRadius() {
return mWidth + mThumbPadding + mThumbPadding;
}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 8342d3e..8e3ac20 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -93,7 +93,7 @@
invalidate();
}
- protected void onRelease() {
+ public void onRelease() {
mEdgeGlowBottom.onRelease();
}
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
deleted file mode 100644
index 6be0c23..0000000
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2020 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.views;
-
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsPagedView;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.statemanager.StateManager.StateListener;
-
-/**
- * On boarding flow for users right after setting up work profile
- */
-public class WorkEduView extends AbstractSlideInView<Launcher>
- implements Insettable, StateListener<LauncherState> {
-
- private static final int DEFAULT_CLOSE_DURATION = 200;
- public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
- public static final String KEY_LEGACY_WORK_EDU_SEEN = "showed_bottom_user_education";
-
- private static final int WORK_EDU_NOT_STARTED = 0;
- private static final int WORK_EDU_PERSONAL_APPS = 1;
- private static final int WORK_EDU_WORK_APPS = 2;
-
- protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
-
-
- private Rect mInsets = new Rect();
- private View mViewWrapper;
- private Button mProceedButton;
- private TextView mContentText;
-
- private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
-
-
- public WorkEduView(Context context, AttributeSet attr) {
- this(context, attr, 0);
- }
-
- public WorkEduView(Context context, AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mContent = this;
- }
-
- @Override
- protected void handleClose(boolean animate) {
- mActivityContext.getSharedPrefs().edit()
- .putInt(KEY_WORK_EDU_STEP, mNextWorkEduStep).apply();
- handleClose(true, DEFAULT_CLOSE_DURATION);
- }
-
- @Override
- protected void onCloseComplete() {
- super.onCloseComplete();
- mActivityContext.getStateManager().removeStateListener(this);
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_ON_BOARD_POPUP) != 0;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mViewWrapper = findViewById(R.id.view_wrapper);
- mProceedButton = findViewById(R.id.proceed);
- mContentText = findViewById(R.id.content_text);
-
- // make sure layout does not shrink when we change the text
- mContentText.post(() -> mContentText.setMinLines(mContentText.getLineCount()));
-
- mProceedButton.setOnClickListener(view -> {
- if (getAllAppsPagedView() != null) {
- getAllAppsPagedView().snapToPage(AllAppsContainerView.AdapterHolder.WORK);
- }
- goToWorkTab(true);
- });
- }
-
- private void goToWorkTab(boolean animate) {
- mProceedButton.setText(R.string.work_profile_edu_accept);
- if (animate) {
- ObjectAnimator animator = ObjectAnimator.ofFloat(mContentText, ALPHA, 0);
- animator.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mContentText.setText(
- mActivityContext.getString(R.string.work_profile_edu_work_apps));
- ObjectAnimator.ofFloat(mContentText, ALPHA, 1).start();
- }
- });
- animator.start();
- } else {
- mContentText.setText(mActivityContext.getString(R.string.work_profile_edu_work_apps));
- }
- mNextWorkEduStep = WORK_EDU_WORK_APPS;
- mProceedButton.setOnClickListener(v -> handleClose(true));
- }
-
- @Override
- public void setInsets(Rect insets) {
- int leftInset = insets.left - mInsets.left;
- int rightInset = insets.right - mInsets.right;
- int bottomInset = insets.bottom - mInsets.bottom;
- mInsets.set(insets);
- setPadding(leftInset, getPaddingTop(), rightInset, 0);
- mViewWrapper.setPaddingRelative(mViewWrapper.getPaddingStart(),
- mViewWrapper.getPaddingTop(), mViewWrapper.getPaddingEnd(), bottomInset);
- }
-
- private void show() {
- attachToContainer();
- animateOpen();
- mActivityContext.getStateManager().addStateListener(this);
- }
-
- @Override
- protected int getScrimColor(Context context) {
- return FINAL_SCRIM_BG_COLOR;
- }
-
- private void goToFirstPage() {
- if (getAllAppsPagedView() != null) {
- getAllAppsPagedView().snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
- }
- }
-
- private void animateOpen() {
- if (mIsOpen || mOpenCloseAnimator.isRunning()) {
- return;
- }
- mIsOpen = true;
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mOpenCloseAnimator.start();
- }
-
- private AllAppsPagedView getAllAppsPagedView() {
- View v = mActivityContext.getAppsView().getContentView();
- return (v instanceof AllAppsPagedView) ? (AllAppsPagedView) v : null;
- }
-
- /**
- * Checks if user has not seen onboarding UI yet and shows it when user navigates to all apps
- */
- public static StateListener<LauncherState> showEduFlowIfNeeded(Launcher launcher,
- @Nullable StateListener<LauncherState> oldListener) {
- if (oldListener != null) {
- launcher.getStateManager().removeStateListener(oldListener);
- }
- if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
- WORK_EDU_NOT_STARTED) != WORK_EDU_NOT_STARTED) {
- return null;
- }
-
- StateListener<LauncherState> listener = new StateListener<LauncherState>() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- if (finalState != LauncherState.ALL_APPS) return;
- LayoutInflater layoutInflater = LayoutInflater.from(launcher);
- WorkEduView v = (WorkEduView) layoutInflater.inflate(
- R.layout.work_profile_edu, launcher.getDragLayer(),
- false);
- v.show();
- v.goToFirstPage();
- launcher.getStateManager().removeStateListener(this);
- }
- };
- launcher.getStateManager().addStateListener(listener);
- return listener;
- }
-
- /**
- * Shows work apps edu if user had dismissed full edu flow
- */
- public static void showWorkEduIfNeeded(Launcher launcher) {
- if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
- WORK_EDU_NOT_STARTED) != WORK_EDU_PERSONAL_APPS) {
- return;
- }
- LayoutInflater layoutInflater = LayoutInflater.from(launcher);
- WorkEduView v = (WorkEduView) layoutInflater.inflate(
- R.layout.work_profile_edu, launcher.getDragLayer(), false);
- v.show();
- v.goToWorkTab(false);
- }
-
- private static boolean hasSeenLegacyEdu(Launcher launcher) {
- return launcher.getSharedPrefs().getBoolean(KEY_LEGACY_WORK_EDU_SEEN, false);
- }
-
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- close(false);
- }
-}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index edd42b4..3bf993e 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -207,16 +207,18 @@
if (view == null || !ViewCompat.isLaidOut(view)) {
return null;
}
-
- mActivityContext.getSharedPrefs().edit()
- .putBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, true).apply();
int[] coords = new int[2];
view.getLocationOnScreen(coords);
- ArrowTipView arrowTipView = new ArrowTipView(mActivityContext);
- return arrowTipView.showAtLocation(
- getContext().getString(R.string.long_press_widget_to_add),
- /* arrowXCoord= */coords[0] + view.getWidth() / 2,
- /* yCoord= */coords[1]);
+ ArrowTipView arrowTipView =
+ new ArrowTipView(mActivityContext, /* isPointingUp= */ false).showAtLocation(
+ getContext().getString(R.string.long_press_widget_to_add),
+ /* arrowXCoord= */coords[0] + view.getWidth() / 2,
+ /* yCoord= */coords[1]);
+ if (arrowTipView != null) {
+ mActivityContext.getSharedPrefs().edit()
+ .putBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, true).apply();
+ }
+ return arrowTipView;
}
/** Returns {@code true} if tip has previously been shown on any of {@link BaseWidgetSheet}. */
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index d745754..6dc6971 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -25,15 +25,20 @@
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.os.Handler;
import android.util.SparseArray;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.widget.custom.CustomWidgetManager;
@@ -292,8 +297,13 @@
activity.startActivityForResult(intent, requestCode);
}
-
- public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) {
+ /**
+ * Launches an app widget's configuration activity.
+ * @param activity The activity from which to launch the configuration activity
+ * @param widgetId The id of the bound app widget to be configured
+ * @param requestCode An optional request code to be returned with the result
+ */
+ public void startConfigActivity(BaseDraggingActivity activity, int widgetId, int requestCode) {
if (WidgetsModel.GO_DISABLE_WIDGETS) {
sendActionCancelled(activity, requestCode);
return;
@@ -301,13 +311,27 @@
try {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startConfigActivity");
- startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
+ startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode,
+ getConfigurationActivityOptions(activity, widgetId));
} catch (ActivityNotFoundException | SecurityException e) {
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
sendActionCancelled(activity, requestCode);
}
}
+ /**
+ * Returns an {@link android.app.ActivityOptions} bundle from the {code activity} for launching
+ * the configuration of the {@code widgetId} app widget, or null of options cannot be produced.
+ */
+ @Nullable
+ private Bundle getConfigurationActivityOptions(BaseDraggingActivity activity, int widgetId) {
+ LauncherAppWidgetHostView view = mViews.get(widgetId);
+ if (view == null) return null;
+ Object tag = view.getTag();
+ if (!(tag instanceof ItemInfo)) return null;
+ return activity.getActivityLaunchOptions(view, (ItemInfo) tag).toBundle();
+ }
+
private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
new Handler().post(() -> activity.onActivityResult(requestCode, RESULT_CANCELED, null));
}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index 3377abb..c04c8dc 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -15,7 +15,6 @@
*/
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;
@@ -24,6 +23,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.widget.util.WidgetSizes;
/**
* Meta data used for late binding of {@link LauncherAppWidgetProviderInfo}.
@@ -61,6 +61,6 @@
}
public Bundle getDefaultSizeOptions(Context context) {
- return getWidgetSizeOptions(context, componentName, spanX, spanY);
+ return WidgetSizes.getWidgetSizeOptions(context, componentName, spanX, spanY);
}
}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index e1999c9..b1ccfd9 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -21,12 +21,12 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.CancellationSignal;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Size;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@@ -48,6 +48,7 @@
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.RoundDrawableWrapper;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.widget.util.WidgetSizes;
/**
* Represents the individual cell of the widget inside the widget tray. The preview is drawn
@@ -96,7 +97,7 @@
protected final BaseActivity mActivity;
private final CheckLongPressHelper mLongPressHelper;
private final float mEnforcedCornerRadius;
- private final int mPreviewPadding;
+ private final int mShortcutPreviewPadding;
private RemoteViews mRemoteViewsPreview;
private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
@@ -114,14 +115,14 @@
mActivity = BaseActivity.fromContext(context);
mLongPressHelper = new CheckLongPressHelper(this);
-
mLongPressHelper.setLongPressTimeoutFactor(1);
+
setContainerWidth();
setWillNotDraw(false);
setClipToPadding(false);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
- mPreviewPadding =
+ mShortcutPreviewPadding =
2 * getResources().getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
}
@@ -200,6 +201,8 @@
mWidgetPreviewLoader = loader;
if (item.activityInfo != null) {
setTag(new PendingAddShortcutInfo(item.activityInfo));
+ mPreviewWidth += mShortcutPreviewPadding;
+ mPreviewHeight += mShortcutPreviewPadding;
} else {
setTag(new PendingAddWidgetInfo(item.widgetInfo));
}
@@ -251,8 +254,6 @@
}
appWidgetHostViewPreview.setPadding(padding.left, padding.top, padding.right,
padding.bottom);
- mPreviewWidth += padding.left + padding.right;
- mPreviewHeight += padding.top + padding.bottom;
appWidgetHostViewPreview.updateAppWidget(remoteViews);
}
@@ -299,7 +300,7 @@
float scale = 1f;
if (getWidth() > 0 && getHeight() > 0) {
// Scale down the preview size if it's wider than the cell.
- float maxWidth = getWidth() - mPreviewPadding;
+ float maxWidth = getWidth();
float previewWidth = drawable.getIntrinsicWidth() * mPreviewScale;
scale = Math.min(maxWidth / previewWidth, 1);
}
@@ -355,11 +356,9 @@
/** Sets the widget preview image size, in number of cells, and preview scale. */
public void setPreviewSize(int spanX, int spanY, float previewScale) {
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
- Point cellSize = deviceProfile.getCellSize();
- mPreviewWidth = cellSize.x * spanX + mPreviewPadding
- + deviceProfile.cellLayoutBorderSpacingPx * (spanX - 1);
- mPreviewHeight = cellSize.y * spanY + mPreviewPadding
- + deviceProfile.cellLayoutBorderSpacingPx * (spanY - 1);
+ Size widgetSize = WidgetSizes.getWidgetSizePx(deviceProfile, spanX, spanY);
+ mPreviewWidth = widgetSize.getWidth();
+ mPreviewHeight = widgetSize.getHeight();
mPreviewScale = previewScale;
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 150bd99..e89aea7 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -298,6 +298,12 @@
scrollToPositionAndMaintainOffset(positionForPackageUserKey, topForPackageUserKey);
}
+ /** Returns the position of the currently expanded header, or empty if it's not present. */
+ public OptionalInt getSelectedHeaderPosition() {
+ if (mWidgetsContentVisiblePackageUserKey == null) return OptionalInt.empty();
+ return getPositionForPackageUserKey(mWidgetsContentVisiblePackageUserKey);
+ }
+
/**
* Returns the position of {@code key} in {@link #mVisibleEntries}, or empty if it's not
* present.
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java b/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java
new file mode 100644
index 0000000..2b7f544
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java
@@ -0,0 +1,51 @@
+/*
+ * 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.widget.picker;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.widget.picker.SearchAndRecommendationsScrollController.OnContentChangeListener;
+
+/**
+ * A layout manager for the {@link WidgetsRecyclerView}.
+ *
+ * {@link #setOnContentChangeListener(OnContentChangeListener)} can be used to register a callback
+ * for when the content of the layout manager has changed, following measurement and animation.
+ */
+public final class WidgetsListLayoutManager extends LinearLayoutManager {
+ @Nullable
+ private OnContentChangeListener mOnContentChangeListener;
+
+ public WidgetsListLayoutManager(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onLayoutCompleted(RecyclerView.State state) {
+ super.onLayoutCompleted(state);
+ if (mOnContentChangeListener != null) {
+ mOnContentChangeListener.onContentChanged();
+ }
+ }
+
+ public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
+ mOnContentChangeListener = listener;
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 62ef4ff..fe42ddf 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Size;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,6 +34,7 @@
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.List;
@@ -43,8 +45,8 @@
private static final float DOWN_SCALE_RATIO = 0.9f;
private static final float MAX_DOWN_SCALE_RATIO = 0.5f;
private final float mWidgetsRecommendationTableVerticalPadding;
+ private final float mWidgetCellVerticalPadding;
private final float mWidgetCellTextViewsHeight;
- private final float mWidgetPreviewPadding;
private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
@Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
@@ -59,10 +61,10 @@
super(context, attrs);
// There are 1 row for title, 1 row for dimension and 2 rows for description.
mWidgetsRecommendationTableVerticalPadding = 2 * getResources()
+ .getDimensionPixelSize(R.dimen.recommended_widgets_table_vertical_padding);
+ mWidgetCellVerticalPadding = 2 * getResources()
.getDimensionPixelSize(R.dimen.widget_cell_vertical_padding);
mWidgetCellTextViewsHeight = 4 * getResources().getDimension(R.dimen.widget_cell_font_size);
- mWidgetPreviewPadding = 2 * getResources()
- .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
}
/** Sets a {@link android.view.View.OnLongClickListener} for all widget cells in this table. */
@@ -149,9 +151,12 @@
List<WidgetItem> widgetItems = recommendedWidgetsInTable.get(i);
float rowHeight = 0;
for (int j = 0; j < widgetItems.size(); j++) {
- float previewHeight = widgetItems.get(j).spanY * deviceProfile.cellHeightPx
- * previewScale + mWidgetPreviewPadding;
- rowHeight = Math.max(rowHeight, previewHeight + mWidgetCellTextViewsHeight);
+ WidgetItem widgetItem = widgetItems.get(j);
+ Size widgetSize = WidgetSizes.getWidgetSizePx(
+ deviceProfile, widgetItem.spanX, widgetItem.spanY);
+ float previewHeight = widgetSize.getHeight() * previewScale;
+ rowHeight = Math.max(rowHeight,
+ previewHeight + mWidgetCellTextViewsHeight + mWidgetCellVerticalPadding);
}
totalHeight += rowHeight;
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 090362b..3ee8b33 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -23,7 +23,6 @@
import android.view.View;
import android.widget.TableLayout;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -53,6 +52,7 @@
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
private int mLastVisibleWidgetContentTableHeight = 0;
private int mWidgetHeaderHeight = 0;
+ private final int mCollapsedHeaderBottomMarginSize;
@Nullable private OnContentChangeListener mOnContentChangeListener;
public WidgetsRecyclerView(Context context) {
@@ -71,6 +71,10 @@
ActivityContext activity = ActivityContext.lookupContext(getContext());
DeviceProfile grid = activity.getDeviceProfile();
+
+ // The bottom margin used when the header is not expanded.
+ mCollapsedHeaderBottomMarginSize =
+ getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
}
@Override
@@ -78,7 +82,9 @@
super.onFinishInflate();
// create a layout manager with Launcher's context so that scroll position
// can be preserved during screen rotation.
- setLayoutManager(new LinearLayoutManager(getContext()));
+ WidgetsListLayoutManager layoutManager = new WidgetsListLayoutManager(getContext());
+ layoutManager.setOnContentChangeListener(mOnContentChangeListener);
+ setLayoutManager(layoutManager);
}
@Override
@@ -87,22 +93,6 @@
mAdapter = (WidgetsListAdapter) adapter;
}
- @Override
- public void onChildAttachedToWindow(@NonNull View child) {
- super.onChildAttachedToWindow(child);
- if (mOnContentChangeListener != null) {
- mOnContentChangeListener.onContentChanged();
- }
- }
-
- @Override
- public void onChildDetachedFromWindow(@NonNull View child) {
- super.onChildDetachedFromWindow(child);
- if (mOnContentChangeListener != null) {
- mOnContentChangeListener.onContentChanged();
- }
- }
-
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
@@ -182,10 +172,7 @@
&& mLastVisibleWidgetContentTableHeight == 0
&& view.getMeasuredHeight() > 0) {
// This assumes all header views are of the same height.
- RecyclerView.LayoutParams layoutParams =
- (RecyclerView.LayoutParams) view.getLayoutParams();
- mWidgetHeaderHeight = view.getMeasuredHeight() + layoutParams.topMargin
- + layoutParams.bottomMargin;
+ mWidgetHeaderHeight = view.getMeasuredHeight();
}
}
@@ -266,6 +253,10 @@
public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
mOnContentChangeListener = listener;
+ WidgetsListLayoutManager layoutManager = (WidgetsListLayoutManager) getLayoutManager();
+ if (layoutManager != null) {
+ layoutManager.setOnContentChangeListener(listener);
+ }
}
/**
@@ -279,12 +270,17 @@
if (untilIndex > mAdapter.getItems().size()) {
untilIndex = mAdapter.getItems().size();
}
+ int expandedHeaderPosition = mAdapter.getSelectedHeaderPosition().orElse(-1);
int totalItemsHeight = 0;
for (int i = 0; i < untilIndex; i++) {
WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
if (entry instanceof WidgetsListHeaderEntry
|| entry instanceof WidgetsListSearchHeaderEntry) {
totalItemsHeight += mWidgetHeaderHeight;
+ if (expandedHeaderPosition != i) {
+ // If the header is collapsed, include the bottom margin it will use.
+ totalItemsHeight += mCollapsedHeaderBottomMarginSize;
+ }
} else if (entry instanceof WidgetsListContentEntry) {
totalItemsHeight += mLastVisibleWidgetContentTableHeight;
} else {
diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java
new file mode 100644
index 0000000..5c8ea72
--- /dev/null
+++ b/src/com/android/launcher3/widget/util/WidgetSizes.java
@@ -0,0 +1,145 @@
+/*
+ * 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.widget.util;
+
+import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
+
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
+import android.annotation.SuppressLint;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Size;
+import android.util.SizeF;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** A utility class for widget sizes related calculations. */
+public final class WidgetSizes {
+
+ /**
+ * Returns the list of all possible sizes, in dp, for a widget of given spans on this device.
+ */
+ public static ArrayList<SizeF> getWidgetSizes(Context context, int spanX, int spanY) {
+ ArrayList<SizeF> sizes = new ArrayList<>(2);
+ final float density = context.getResources().getDisplayMetrics().density;
+ final Point cellSize = new Point();
+
+ for (DeviceProfile profile : LauncherAppState.getIDP(context).supportedProfiles) {
+ Size widgetSizePx = getWidgetSizePx(profile, spanX, spanY, cellSize);
+ sizes.add(new SizeF(widgetSizePx.getWidth() / density,
+ widgetSizePx.getHeight() / density));
+ }
+ return sizes;
+ }
+
+ /** Returns the size, in pixels, a widget of given spans & {@code profile}. */
+ public static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY) {
+ return getWidgetSizePx(profile, spanX, spanY, /* recycledCellSize= */ null);
+ }
+
+ private static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY,
+ @Nullable Point recycledCellSize) {
+ final int hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacingPx;
+ final int vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacingPx;
+ if (recycledCellSize == null) {
+ recycledCellSize = new Point();
+ }
+ profile.getCellSize(recycledCellSize);
+ return new Size(((spanX * recycledCellSize.x) + hBorderSpacing),
+ ((spanY * recycledCellSize.y) + vBorderSpacing));
+ }
+
+ /**
+ * Updates a given {@code widgetView} with size, {@code spanX}, {@code spanY}.
+ *
+ * <p>On Android S+, it also updates the given {@code widgetView} with a list of sizes derived
+ * from {@code spanX}, {@code spanY} in all supported device profiles.
+ */
+ @SuppressLint("NewApi") // Already added API check.
+ public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Context context,
+ int spanX, int spanY) {
+ List<SizeF> sizes = getWidgetSizes(context, spanX, spanY);
+ if (ATLEAST_S) {
+ widgetView.updateAppWidgetSize(new Bundle(), sizes);
+ } else {
+ Rect bounds = getMinMaxSizes(sizes);
+ widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right,
+ bounds.bottom);
+ }
+ }
+
+ /**
+ * 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<SizeF> 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<SizeF> 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 = 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.
+ * @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.
+ */
+ private static Rect getMinMaxSizes(List<SizeF> sizes) {
+ if (sizes.isEmpty()) {
+ return new Rect();
+ } else {
+ SizeF first = sizes.get(0);
+ Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(),
+ (int) first.getWidth(), (int) first.getHeight());
+ for (int i = 1; i < sizes.size(); i++) {
+ result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
index 8f9cdc8..b35c75b 100644
--- a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -59,7 +59,8 @@
R.dimen.all_apps_header_pill_corner_radius);
mSelectedIndicatorPaint = new Paint();
- mSelectedIndicatorPaint.setColor(context.getColor(R.color.all_apps_tab_bg));
+ mSelectedIndicatorPaint.setColor(
+ context.getColor(R.color.all_apps_tab_background_selected));
mIsRtl = Utilities.isRtl(getResources());
}
@@ -126,6 +127,7 @@
@Override
public void setActiveMarker(int activePage) {
updateTabTextColor(activePage);
+ updateIndicatorPosition(activePage);
if (mOnActivePageChangedListener != null && mLastActivePage != activePage) {
mOnActivePageChangedListener.onActivePageChanged(activePage);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 5de5b4a..56723b1 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -47,7 +47,7 @@
public AppIconMenu openMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
- mObject, "deep_shortcuts_container", LONG_CLICK_EVENT));
+ mObject, "popup_container", LONG_CLICK_EVENT));
}
}
@@ -58,7 +58,7 @@
@Override
protected String getLongPressIndicator() {
- return "deep_shortcuts_container";
+ return "popup_container";
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c99a81f..bf7984e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -155,7 +155,7 @@
private static final String APPS_RES_ID = "apps_view";
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
- private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
+ private static final String CONTEXT_MENU_RES_ID = "popup_container";
public static final int WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
diff --git a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
index 282fca9..787dc70 100644
--- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
@@ -26,7 +26,7 @@
OptionsPopupMenu(LauncherInstrumentation launcher) {
mLauncher = launcher;
- mDeepShortcutsContainer = launcher.waitForLauncherObject("deep_shortcuts_container");
+ mDeepShortcutsContainer = launcher.waitForLauncherObject("popup_container");
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index d43e235..3624624 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -179,7 +179,7 @@
getHotseatAppIcon("Chrome"),
new Point(mLauncher.getDevice().getDisplayWidth(),
mLauncher.getVisibleBounds(workspace).centerY()),
- "deep_shortcuts_container",
+ "popup_container",
false,
false,
() -> mLauncher.expectEvent(