Merge "Replace get/setPendingIntentBackgroundActivityLaunchAllowedByPermission" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 5c1e638..ea2adcf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -48,7 +48,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
-import static com.android.wm.shell.Flags.enableTaskbarOnPhones;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
@@ -248,7 +247,7 @@
? context.getColor(R.color.taskbar_nav_icon_light_color)
: context.getColor(R.color.taskbar_nav_icon_dark_color);
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions = new TaskbarTransitions(mContext, mNavButtonsView);
}
}
@@ -365,7 +364,7 @@
R.bool.floating_rotation_button_position_left);
mControllers.rotationButtonController.setRotationButton(mFloatingRotationButton,
mRotationButtonListener);
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions.init();
}
@@ -373,7 +372,7 @@
mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
// Initialize things needed to move nav buttons to separate window.
- mSeparateWindowParent = new BaseDragLayer<TaskbarActivityContext>(mContext, null, 0) {
+ mSeparateWindowParent = new BaseDragLayer<>(mContext, null, 0) {
@Override
public void recreateControllers() {
mControllers = new TouchController[0];
@@ -629,7 +628,7 @@
}
public void setWallpaperVisible(boolean isVisible) {
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions.setWallpaperVisibility(isVisible);
}
}
@@ -642,20 +641,20 @@
}
public void checkNavBarModes() {
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
boolean isBarHidden = (mSysuiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) != 0;
mTaskbarTransitions.transitionTo(mTransitionMode, !isBarHidden);
}
}
public void finishBarAnimations() {
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions.finishAnimations();
}
}
public void touchAutoDim(boolean reset) {
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions.setAutoDim(false);
mHandler.removeCallbacks(mAutoDim);
if (reset) {
@@ -665,7 +664,7 @@
}
public void transitionTo(@BarTransitions.TransitionMode int barMode, boolean animate) {
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions.transitionTo(barMode, animate);
}
}
@@ -769,7 +768,7 @@
private void onDarkIntensityChanged() {
updateNavButtonColor();
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions.onDarkIntensityChanged(mTaskbarNavButtonDarkIntensity.value);
}
}
@@ -1119,7 +1118,7 @@
+ mOnBackgroundNavButtonColorOverrideMultiplier.value);
mNavButtonsView.dumpLogs(prefix + "\t", pw);
- if (enableTaskbarOnPhones() && mContext.isPhoneButtonNavMode()) {
+ if (mContext.isPhoneMode()) {
mTaskbarTransitions.dumpLogs(prefix + "\t", pw);
}
}
diff --git a/res/layout/private_space_header.xml b/res/layout/private_space_header.xml
index 9c0f129..52180cf 100644
--- a/res/layout/private_space_header.xml
+++ b/res/layout/private_space_header.xml
@@ -43,6 +43,7 @@
android:layout_height="@dimen/ps_header_image_height"
android:background="@drawable/ps_settings_background"
android:src="@drawable/ic_ps_settings"
+ android:visibility="gone"
android:contentDescription="@string/ps_container_settings" />
<LinearLayout
android:id="@+id/ps_lock_unlock_button"
@@ -71,7 +72,9 @@
android:textColor="@color/material_color_on_primary_fixed"
android:textSize="14sp"
android:text="@string/ps_container_lock_title"
+ android:maxLines="1"
android:visibility="gone"
+ android:alpha="0"
style="@style/TextHeadline"/>
</LinearLayout>
</LinearLayout>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index a296f46..a448228 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -106,8 +106,6 @@
import java.util.List;
import java.util.Locale;
import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Various utilities shared amongst the Launcher's classes.
@@ -116,8 +114,7 @@
private static final String TAG = "Launcher.Utilities";
- private static final Pattern sTrimPattern =
- Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$");
+ private static final String TRIM_PATTERN = "(^\\h+|\\h+$)";
private static final Matrix sMatrix = new Matrix();
private static final Matrix sInverseMatrix = new Matrix();
@@ -445,10 +442,7 @@
if (s == null) {
return "";
}
-
- // Just strip any sequence of whitespace or java space characters from the beginning and end
- Matcher m = sTrimPattern.matcher(s);
- return m.replaceAll("$1");
+ return s.toString().replaceAll(TRIM_PATTERN, "").trim();
}
/**
@@ -722,14 +716,59 @@
}
/**
- * Rotates `inOutBounds` by `delta` 90-degree increments. Rotation is visually CCW. Parent
+ * Rotates `inOutBounds` by `delta` 90-degree increments. Rotation is visually CW. Parent
* sizes represent the "space" that will rotate carrying inOutBounds along with it to determine
* the final bounds.
+ *
+ * As an example if this is the input:
+ * +-------------+
+ * | +-----+ |
+ * | | | |
+ * | +-----+ |
+ * | |
+ * | |
+ * | |
+ * +-------------+
+ * This would be case delta % 4 == 0:
+ * +-------------+
+ * | +-----+ |
+ * | | | |
+ * | +-----+ |
+ * | |
+ * | |
+ * | |
+ * +-------------+
+ * This would be case delta % 4 == 1:
+ * +----------------+
+ * | +--+ |
+ * | | | |
+ * | | | |
+ * | +--+ |
+ * | |
+ * +----------------+
+ * This would be case delta % 4 == 2:
+ * +-------------+
+ * | |
+ * | |
+ * | |
+ * | +-----+ |
+ * | | | |
+ * | +-----+ |
+ * +-------------+
+ * This would be case delta % 4 == 3:
+ * +----------------+
+ * | +--+ |
+ * | | | |
+ * | | | |
+ * | +--+ |
+ * | |
+ * +----------------+
*/
public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
int delta) {
int rdelta = ((delta % 4) + 4) % 4;
int origLeft = inOutBounds.left;
+ int origTop = inOutBounds.top;
switch (rdelta) {
case 0:
return;
@@ -741,6 +780,8 @@
return;
case 2:
inOutBounds.left = parentWidth - inOutBounds.right;
+ inOutBounds.top = parentHeight - inOutBounds.bottom;
+ inOutBounds.bottom = parentHeight - origTop;
inOutBounds.right = parentWidth - origLeft;
return;
case 3:
@@ -830,6 +871,9 @@
@NonNull Rect inclusionBounds,
@NonNull Rect exclusionBounds,
@AdjustmentDirection int adjustmentDirection) {
+ if (!Rect.intersects(targetViewBounds, exclusionBounds)) {
+ return;
+ }
switch (adjustmentDirection) {
case TRANSLATE_RIGHT:
targetView.setTranslationX(Math.min(
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 0f4204f..aefb7e9 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -41,7 +41,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -92,14 +91,14 @@
public class PrivateProfileManager extends UserProfileManager {
private static final String TAG = "PrivateProfileManager";
- private static final int EXPAND_COLLAPSE_DURATION = 800;
+ private static final int EXPAND_COLLAPSE_DURATION = 400;
private static final int SETTINGS_OPACITY_DURATION = 400;
private static final int TEXT_UNLOCK_OPACITY_DURATION = 300;
private static final int TEXT_LOCK_OPACITY_DURATION = 50;
private static final int APP_OPACITY_DURATION = 400;
private static final int MASK_VIEW_DURATION = 200;
private static final int APP_OPACITY_DELAY = 400;
- private static final int SETTINGS_AND_LOCK_GROUP_TRANSITION_DELAY = 400;
+ private static final int PILL_TRANSITION_DELAY = 400;
private static final int SETTINGS_OPACITY_DELAY = 400;
private static final int LOCK_TEXT_OPACITY_DELAY = 500;
private static final int MASK_VIEW_DELAY = 400;
@@ -109,6 +108,8 @@
private final Predicate<UserHandle> mPrivateProfileMatcher;
private final int mPsHeaderHeight;
private final int mFloatingMaskViewCornerRadius;
+ private final int mLockTextMarginStart;
+ private final int mLockTextMarginEnd;
private final RecyclerView.OnScrollListener mOnIdleScrollListener =
new RecyclerView.OnScrollListener() {
@Override
@@ -133,6 +134,11 @@
private Runnable mOnPSHeaderAdded;
@Nullable
private RelativeLayout mPSHeader;
+ @Nullable
+ private TextView mLockText;
+ @Nullable
+ private PrivateSpaceSettingsButton mPrivateSpaceSettingsButton;
+ @Nullable
private ConstraintLayout mFloatingMaskView;
private final String mLockedStateContentDesc;
private final String mUnLockedStateContentDesc;
@@ -155,6 +161,10 @@
.getString(R.string.ps_container_unlock_button_content_description);
mFloatingMaskViewCornerRadius = mAllApps.getContext().getResources().getDimensionPixelSize(
R.dimen.ps_floating_mask_corner_radius);
+ mLockTextMarginStart = mAllApps.getContext().getResources().getDimensionPixelSize(
+ R.dimen.ps_lock_icon_text_margin_start_expanded);
+ mLockTextMarginEnd = mAllApps.getContext().getResources().getDimensionPixelSize(
+ R.dimen.ps_lock_icon_text_margin_end_expanded);
}
/** Adds Private Space Header to the layout. */
@@ -354,20 +364,11 @@
/** Add Private Space Header view elements based upon {@link UserProfileState} */
public void bindPrivateSpaceHeaderViewElements(RelativeLayout parent) {
mPSHeader = parent;
+ updateView();
if (mOnPSHeaderAdded != null) {
MAIN_EXECUTOR.execute(mOnPSHeaderAdded);
mOnPSHeaderAdded = null;
}
- // Set the transition duration for the settings and lock button to animate.
- ViewGroup settingAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
- if (mReadyToAnimate) {
- enableLayoutTransition(settingAndLockGroup);
- } else {
- // Ensure any unwanted animations to not happen.
- settingAndLockGroup.setLayoutTransition(null);
- Log.d(TAG, "bindPrivateSpaceHeaderViewElements: removing transitions ");
- }
- updateView();
}
/** Update the states of the views that make up the header at the state it is called in. */
@@ -378,9 +379,10 @@
mPSHeader.setAlpha(1);
ViewGroup lockPill = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
assert lockPill != null;
- TextView lockText = lockPill.findViewById(R.id.lock_text);
- PrivateSpaceSettingsButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
- assert settingsButton != null;
+ mLockText = lockPill.findViewById(R.id.lock_text);
+ assert mLockText != null;
+ mPrivateSpaceSettingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
+ assert mPrivateSpaceSettingsButton != null;
//Add image for private space transitioning view
ImageView transitionView = mPSHeader.findViewById(R.id.ps_transition_image);
assert transitionView != null;
@@ -391,12 +393,18 @@
// Remove header from accessibility target when enabled.
mPSHeader.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- lockText.setVisibility(VISIBLE);
+ if (!mReadyToAnimate) {
+ // Don't set visibilities when animating as the animation will handle it.
+ mLockText.setVisibility(VISIBLE);
+ mLockText.setAlpha(1);
+ mLockText.setHorizontallyScrolling(false);
+ mPrivateSpaceSettingsButton.setVisibility(
+ isPrivateSpaceSettingsAvailable() ? VISIBLE : GONE);
+ }
lockPill.setVisibility(VISIBLE);
lockPill.setOnClickListener(view -> lockingAction(/* lock */ true));
lockPill.setContentDescription(mUnLockedStateContentDesc);
- settingsButton.setVisibility(isPrivateSpaceSettingsAvailable() ? VISIBLE : GONE);
transitionView.setVisibility(GONE);
}
case STATE_DISABLED -> {
@@ -406,12 +414,14 @@
mPSHeader.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
mPSHeader.setContentDescription(mLockedStateContentDesc);
- lockText.setVisibility(GONE);
+ mLockText.setVisibility(GONE);
+ mLockText.setAlpha(0);
+ mLockText.setHorizontallyScrolling(false);
lockPill.setVisibility(VISIBLE);
lockPill.setOnClickListener(view -> lockingAction(/* lock */ false));
lockPill.setContentDescription(mLockedStateContentDesc);
- settingsButton.setVisibility(GONE);
+ mPrivateSpaceSettingsButton.setVisibility(GONE);
transitionView.setVisibility(GONE);
}
case STATE_TRANSITION -> {
@@ -585,6 +595,51 @@
return alphaAnim;
}
+ private ValueAnimator animatePillTransition(boolean isExpanding) {
+ if (mLockText == null) {
+ return new ValueAnimator().setDuration(0);
+ }
+ mLockText.measure(0,0);
+ int currentWidth = mLockText.getWidth();
+ int fullWidth = mLockText.getMeasuredWidth();
+ float from = isExpanding ? 0 : currentWidth;
+ float to = isExpanding ? fullWidth : 0;
+ ValueAnimator pillAnim = ObjectAnimator.ofFloat(from, to);
+ pillAnim.setStartDelay(isExpanding ? PILL_TRANSITION_DELAY : 0);
+ pillAnim.setDuration(EXPAND_COLLAPSE_DURATION);
+ pillAnim.setInterpolator(Interpolators.STANDARD);
+ pillAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float translation = (float) valueAnimator.getAnimatedValue();
+ float translationFraction = translation / fullWidth;
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) mLockText.getLayoutParams();
+ layoutParams.width = (int) translation;
+ layoutParams.setMarginStart((int) (mLockTextMarginStart * translationFraction));
+ layoutParams.setMarginEnd((int) (mLockTextMarginEnd * translationFraction));
+ mLockText.setLayoutParams(layoutParams);
+ mLockText.requestLayout();
+ }
+ });
+ pillAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (!isExpanding) {
+ mLockText.setVisibility(GONE);
+ }
+ mLockText.setHorizontallyScrolling(false);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animator) {
+ mLockText.setHorizontallyScrolling(true);
+ mLockText.setVisibility(VISIBLE);
+ }
+ });
+ return pillAnim;
+ }
+
/**
* Using PropertySetter{@link PropertySetter}, we can update the view's attributes within an
* animation. At the moment, collapsing, setting alpha changes, and animating the text is done
@@ -596,22 +651,12 @@
}
if (mPSHeader == null) {
mOnPSHeaderAdded = () -> updatePrivateStateAnimator(expand);
- setAnimationRunning(false);
+ // Set animation to true, because onBind will be called after this return where we want
+ // the views to be updated accordingly so animation can happen.
+ setAnimationRunning(true);
return;
}
attachFloatingMaskView(expand);
- ViewGroup settingsAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
- TextView lockText = mPSHeader.findViewById(R.id.lock_text);
- PrivateSpaceSettingsButton privateSpaceSettingsButton =
- mPSHeader.findViewById(R.id.ps_settings_button);
- if (settingsAndLockGroup.getLayoutTransition() == null) {
- // Set a new transition if the current ViewGroup does not already contain one as each
- // transition should only happen once when applied.
- enableLayoutTransition(settingsAndLockGroup);
- }
- settingsAndLockGroup.getLayoutTransition().setStartDelay(
- LayoutTransition.CHANGING,
- expand ? SETTINGS_AND_LOCK_GROUP_TRANSITION_DELAY : NO_DELAY);
PropertySetter headerSetter = new AnimatedPropertySetter();
headerSetter.add(updateSettingsGearAlpha(expand));
headerSetter.add(updateLockTextAlpha(expand));
@@ -626,8 +671,6 @@
? LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN
: LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN,
mAllApps.getActiveRecyclerView());
- // Animate the collapsing of the text at the same time while updating lock button.
- lockText.setVisibility(expand ? VISIBLE : GONE);
setAnimationRunning(true);
}
@@ -646,10 +689,10 @@
: LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END,
mAllApps.getActiveRecyclerView());
Log.d(TAG, "updatePrivateStateAnimator: lockText visibility: "
- + lockText.getVisibility() + " lockTextAlpha: " + lockText.getAlpha());
+ + mLockText.getVisibility() + " lockTextAlpha: " + mLockText.getAlpha());
Log.d(TAG, "updatePrivateStateAnimator: settingsCog visibility: "
- + privateSpaceSettingsButton.getVisibility()
- + " settingsCogAlpha: " + privateSpaceSettingsButton.getAlpha());
+ + mPrivateSpaceSettingsButton.getVisibility()
+ + " settingsCogAlpha: " + mPrivateSpaceSettingsButton.getAlpha());
if (!expand) {
mAllApps.mAH.get(MAIN).mRecyclerView.removeItemDecoration(
mPrivateAppsSectionDecorator);
@@ -663,15 +706,19 @@
}));
if (expand) {
animatorSet.playTogether(animateAlphaOfIcons(true),
+ animatePillTransition(true),
translateFloatingMaskView(false));
} else {
+ AnimatorSet parallelSet = new AnimatorSet();
+ parallelSet.playTogether(animateAlphaOfIcons(false),
+ animatePillTransition(false));
if (isPrivateSpaceHidden()) {
- animatorSet.playSequentially(animateAlphaOfIcons(false),
+ animatorSet.playSequentially(parallelSet,
animateAlphaOfPrivateSpaceContainer(),
animateCollapseAnimation());
} else {
animatorSet.playSequentially(translateFloatingMaskView(true),
- animateAlphaOfIcons(false),
+ parallelSet,
animateCollapseAnimation());
}
}
@@ -702,7 +749,7 @@
/** Fades out the private space container. */
private ValueAnimator translateFloatingMaskView(boolean animateIn) {
if (!Flags.privateSpaceAddFloatingMaskView() || mFloatingMaskView == null) {
- return new ValueAnimator();
+ return new ValueAnimator().setDuration(0);
}
// Translate base on the height amount. Translates out on expand and in on collapse.
float floatingMaskViewHeight = getFloatingMaskViewHeight();
@@ -720,36 +767,10 @@
return alphaAnim;
}
- /** Animates the layout changes when the text of the button becomes visible/gone. */
- private void enableLayoutTransition(ViewGroup settingsAndLockGroup) {
- LayoutTransition settingsAndLockTransition = new LayoutTransition();
- settingsAndLockTransition.enableTransitionType(LayoutTransition.CHANGING);
- settingsAndLockTransition.setDuration(EXPAND_COLLAPSE_DURATION);
- settingsAndLockTransition.setInterpolator(LayoutTransition.CHANGING,
- Interpolators.STANDARD);
- settingsAndLockTransition.addTransitionListener(new LayoutTransition.TransitionListener() {
- @Override
- public void startTransition(LayoutTransition transition, ViewGroup viewGroup,
- View view, int i) {
- Log.d(TAG, "updatePrivateStateAnimator: transition started: " + transition);
- }
- @Override
- public void endTransition(LayoutTransition transition, ViewGroup viewGroup,
- View view, int i) {
- settingsAndLockGroup.setLayoutTransition(null);
- mReadyToAnimate = false;
- Log.d(TAG, "updatePrivateStateAnimator: transition finished: " + transition);
- }
- });
- settingsAndLockGroup.setLayoutTransition(settingsAndLockTransition);
- Log.d(TAG, "updatePrivateStateAnimator: setting transition: "
- + settingsAndLockTransition);
- }
-
/** Change the settings gear alpha when expanded or collapsed. */
private ValueAnimator updateSettingsGearAlpha(boolean expand) {
- if (mPSHeader == null) {
- return new ValueAnimator();
+ if (mPrivateSpaceSettingsButton == null || !isPrivateSpaceSettingsAvailable()) {
+ return new ValueAnimator().setDuration(0);
}
float from = expand ? 0 : 1;
float to = expand ? 1 : 0;
@@ -760,16 +781,21 @@
settingsAlphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- mPSHeader.findViewById(R.id.ps_settings_button)
- .setAlpha((float) valueAnimator.getAnimatedValue());
+ mPrivateSpaceSettingsButton.setAlpha((float) valueAnimator.getAnimatedValue());
+ }
+ });
+ settingsAlphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ mPrivateSpaceSettingsButton.setVisibility(VISIBLE);
}
});
return settingsAlphaAnim;
}
private ValueAnimator updateLockTextAlpha(boolean expand) {
- if (mPSHeader == null) {
- return new ValueAnimator();
+ if (mLockText == null) {
+ return new ValueAnimator().setDuration(0);
}
float from = expand ? 0 : 1;
float to = expand ? 1 : 0;
@@ -780,8 +806,7 @@
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- mPSHeader.findViewById(R.id.lock_text).setAlpha(
- (float) valueAnimator.getAnimatedValue());
+ mLockText.setAlpha((float) valueAnimator.getAnimatedValue());
}
});
return alphaAnim;
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 778b32a..00f1c67 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -16,22 +16,15 @@
package com.android.launcher3.graphics;
-import android.app.Notification;
import android.content.Context;
import android.graphics.Color;
-import android.util.Log;
-import androidx.core.graphics.ColorUtils;
-
-import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
/**
* Contains colors based on the dominant color of an icon.
*/
public class IconPalette {
-
- private static final boolean DEBUG = false;
private static final String TAG = "IconPalette";
private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
@@ -54,95 +47,4 @@
}
return result;
}
-
- /**
- * Resolves a color such that it has enough contrast to be used as the
- * color of an icon or text on the given background color.
- *
- * @return a color of the same hue with enough contrast against the background.
- *
- * This was copied from com.android.internal.util.NotificationColorUtil.
- */
- public static int resolveContrastColor(Context context, int color, int background) {
- final int resolvedColor = resolveColor(context, color);
-
- int contrastingColor = ensureTextContrast(resolvedColor, background);
-
- if (contrastingColor != resolvedColor) {
- if (DEBUG){
- Log.w(TAG, String.format(
- "Enhanced contrast of notification for %s " +
- "%s (over background) by changing #%s to %s",
- context.getPackageName(),
- contrastChange(resolvedColor, contrastingColor, background),
- Integer.toHexString(resolvedColor), Integer.toHexString(contrastingColor)));
- }
- }
- return contrastingColor;
- }
-
- /**
- * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
- *
- * This was copied from com.android.internal.util.NotificationColorUtil.
- */
- private static int resolveColor(Context context, int color) {
- if (color == Notification.COLOR_DEFAULT) {
- return context.getColor(R.color.notification_icon_default_color);
- }
- return color;
- }
-
- /** For debugging. This was copied from com.android.internal.util.NotificationColorUtil. */
- private static String contrastChange(int colorOld, int colorNew, int bg) {
- return String.format("from %.2f:1 to %.2f:1",
- ColorUtils.calculateContrast(colorOld, bg),
- ColorUtils.calculateContrast(colorNew, bg));
- }
-
- /**
- * Finds a text color with sufficient contrast over bg that has the same hue as the original
- * color.
- *
- * This was copied from com.android.internal.util.NotificationColorUtil.
- */
- private static int ensureTextContrast(int color, int bg) {
- return findContrastColor(color, bg, 4.5);
- }
- /**
- * Finds a suitable color such that there's enough contrast.
- *
- * @param fg the color to start searching from.
- * @param bg the color to ensure contrast against.
- * @param minRatio the minimum contrast ratio required.
- * @return a color with the same hue as {@param color}, potentially darkened to meet the
- * contrast ratio.
- *
- * This was copied from com.android.internal.util.NotificationColorUtil.
- */
- private static int findContrastColor(int fg, int bg, double minRatio) {
- if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
- return fg;
- }
-
- double[] lab = new double[3];
- ColorUtils.colorToLAB(bg, lab);
- double bgL = lab[0];
- ColorUtils.colorToLAB(fg, lab);
- double fgL = lab[0];
- boolean isBgDark = bgL < 50;
-
- double low = isBgDark ? fgL : 0, high = isBgDark ? 100 : fgL;
- final double a = lab[1], b = lab[2];
- for (int i = 0; i < 15 && high - low > 0.00001; i++) {
- final double l = (low + high) / 2;
- fg = ColorUtils.LABToColor(l, a, b);
- if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
- if (isBgDark) high = l; else low = l;
- } else {
- if (isBgDark) low = l; else high = l;
- }
- }
- return ColorUtils.LABToColor(low, a, b);
- }
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
index 60a4197..5a26087 100644
--- a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
@@ -17,12 +17,19 @@
package com.android.launcher3
import android.content.Context
+import android.content.ContextWrapper
+import android.graphics.Rect
+import android.graphics.RectF
import android.view.View
import android.view.ViewGroup
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.util.ActivityContextWrapper
-import org.junit.Assert.*
+import kotlin.random.Random
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,6 +37,10 @@
@RunWith(AndroidJUnit4::class)
class UtilitiesTest {
+ companion object {
+ const val SEED = 827
+ }
+
private lateinit var mContext: Context
@Before
@@ -94,4 +105,283 @@
assertTrue(Utilities.pointInView(view, -5f, -5f, 10f)) // Inside slop
assertFalse(Utilities.pointInView(view, 115f, 115f, 10f)) // Outside slop
}
+
+ @Test
+ fun testNumberBounding() {
+ assertEquals(887.99f, Utilities.boundToRange(887.99f, 0f, 1000f))
+ assertEquals(2.777f, Utilities.boundToRange(887.99f, 0f, 2.777f))
+ assertEquals(900f, Utilities.boundToRange(887.99f, 900f, 1000f))
+
+ assertEquals(9383667L, Utilities.boundToRange(9383667L, -999L, 9999999L))
+ assertEquals(9383668L, Utilities.boundToRange(9383667L, 9383668L, 9999999L))
+ assertEquals(42L, Utilities.boundToRange(9383667L, -999L, 42L))
+
+ assertEquals(345, Utilities.boundToRange(345, 2, 500))
+ assertEquals(400, Utilities.boundToRange(345, 400, 500))
+ assertEquals(300, Utilities.boundToRange(345, 2, 300))
+
+ val random = Random(SEED)
+ for (i in 1..300) {
+ val value = random.nextFloat()
+ val lowerBound = random.nextFloat()
+ val higherBound = lowerBound + random.nextFloat()
+
+ assertEquals(
+ "Utilities.boundToRange doesn't match Kotlin coerceIn",
+ value.coerceIn(lowerBound, higherBound),
+ Utilities.boundToRange(value, lowerBound, higherBound)
+ )
+ assertEquals(
+ "Utilities.boundToRange doesn't match Kotlin coerceIn",
+ value.toInt().coerceIn(lowerBound.toInt(), higherBound.toInt()),
+ Utilities.boundToRange(value.toInt(), lowerBound.toInt(), higherBound.toInt())
+ )
+ assertEquals(
+ "Utilities.boundToRange doesn't match Kotlin coerceIn",
+ value.toLong().coerceIn(lowerBound.toLong(), higherBound.toLong()),
+ Utilities.boundToRange(value.toLong(), lowerBound.toLong(), higherBound.toLong())
+ )
+ assertEquals(
+ "If the lower bound is higher than lower bound, it should return the lower bound",
+ higherBound,
+ Utilities.boundToRange(value, higherBound, lowerBound)
+ )
+ }
+ }
+
+ @Test
+ fun testTranslateOverlappingView() {
+ testConcentricOverlap()
+ leftDownCornerOverlap()
+ noOverlap()
+ }
+
+ /*
+ Test Case: Rectangle Contained Within Another Rectangle
+
+ +-------------+ <-- exclusionBounds
+ | |
+ | +-----+ |
+ | | | | <-- targetViewBounds
+ | | | |
+ | +-----+ |
+ | |
+ +-------------+
+ */
+ private fun testConcentricOverlap() {
+ val targetView = View(ContextWrapper(getApplicationContext()))
+ val targetViewBounds = Rect(40, 40, 60, 60)
+ val inclusionBounds = Rect(0, 0, 100, 100)
+ val exclusionBounds = Rect(30, 30, 70, 70)
+
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_RIGHT
+ )
+ assertEquals(30f, targetView.translationX)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_LEFT
+ )
+ assertEquals(-30f, targetView.translationX)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_DOWN
+ )
+ assertEquals(30f, targetView.translationY)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_UP
+ )
+ assertEquals(-30f, targetView.translationY)
+ }
+
+ /*
+ Test Case: Non-Overlapping Rectangles
+
+ +-----------------+ <-- targetViewBounds
+ | |
+ | |
+ +-----------------+
+
+ +-----------+ <-- exclusionBounds
+ | |
+ | |
+ +-----------+
+ */
+ private fun noOverlap() {
+ val targetView = View(ContextWrapper(getApplicationContext()))
+ val targetViewBounds = Rect(10, 10, 20, 20)
+
+ val inclusionBounds = Rect(0, 0, 100, 100)
+ val exclusionBounds = Rect(30, 30, 40, 40)
+
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_RIGHT
+ )
+ assertEquals(0f, targetView.translationX)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_LEFT
+ )
+ assertEquals(0f, targetView.translationX)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_DOWN
+ )
+ assertEquals(0f, targetView.translationY)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_UP
+ )
+ assertEquals(0f, targetView.translationY)
+ }
+
+ /*
+ Test Case: Rectangles Overlapping at Corners
+
+ +------------+ <-- exclusionBounds
+ | |
+ +-------+ |
+ | | | | <-- targetViewBounds
+ | +------------+
+ | |
+ +-------+
+ */
+ private fun leftDownCornerOverlap() {
+ val targetView = View(ContextWrapper(getApplicationContext()))
+ val targetViewBounds = Rect(20, 20, 30, 30)
+
+ val inclusionBounds = Rect(0, 0, 100, 100)
+ val exclusionBounds = Rect(25, 25, 35, 35)
+
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_RIGHT
+ )
+ assertEquals(15f, targetView.translationX)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_LEFT
+ )
+ assertEquals(-5f, targetView.translationX)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_DOWN
+ )
+ assertEquals(15f, targetView.translationY)
+ Utilities.translateOverlappingView(
+ targetView,
+ targetViewBounds,
+ inclusionBounds,
+ exclusionBounds,
+ Utilities.TRANSLATE_UP
+ )
+ assertEquals(-5f, targetView.translationY)
+ }
+
+ @Test
+ fun trim() {
+ val expectedString = "Hello World"
+ assertEquals(expectedString, Utilities.trim("Hello World "))
+ // Basic trimming
+ assertEquals(expectedString, Utilities.trim(" Hello World "))
+ assertEquals(expectedString, Utilities.trim(" Hello World"))
+
+ // Non-breaking whitespace
+ assertEquals("Hello World", Utilities.trim("\u00A0\u00A0Hello World\u00A0\u00A0"))
+
+ // Whitespace combinations
+ assertEquals(expectedString, Utilities.trim("\t \r\n Hello World \n\r"))
+ assertEquals(expectedString, Utilities.trim("\nHello World "))
+
+ // Null input
+ assertEquals("", Utilities.trim(null))
+
+ // Empty String
+ assertEquals("", Utilities.trim(""))
+ }
+
+ @Test
+ fun getProgress() {
+ // Basic test
+ assertEquals(0.5f, Utilities.getProgress(50f, 0f, 100f), 0.001f)
+
+ // Negative values
+ assertEquals(0.5f, Utilities.getProgress(-20f, -50f, 10f), 0.001f)
+
+ // Outside of range
+ assertEquals(1.2f, Utilities.getProgress(120f, 0f, 100f), 0.001f)
+ }
+
+ @Test
+ fun scaleRectFAboutPivot() {
+ // Enlarge
+ var rectF = RectF(10f, 20f, 50f, 80f)
+ Utilities.scaleRectFAboutPivot(rectF, 30f, 50f, 1.5f)
+ assertEquals(RectF(0f, 5f, 60f, 95f), rectF)
+
+ // Shrink
+ rectF = RectF(10f, 20f, 50f, 80f)
+ Utilities.scaleRectFAboutPivot(rectF, 30f, 50f, 0.5f)
+ assertEquals(RectF(20f, 35f, 40f, 65f), rectF)
+
+ // No scale
+ rectF = RectF(10f, 20f, 50f, 80f)
+ Utilities.scaleRectFAboutPivot(rectF, 30f, 50f, 1.0f)
+ assertEquals(RectF(10f, 20f, 50f, 80f), rectF)
+ }
+
+ @Test
+ fun rotateBounds() {
+ var rect = Rect(20, 70, 60, 80)
+ Utilities.rotateBounds(rect, 100, 100, 0)
+ assertEquals(Rect(20, 70, 60, 80), rect)
+
+ rect = Rect(20, 70, 60, 80)
+ Utilities.rotateBounds(rect, 100, 100, 1)
+ assertEquals(Rect(70, 40, 80, 80), rect)
+
+ rect = Rect(20, 70, 60, 80)
+ Utilities.rotateBounds(rect, 100, 100, 2)
+ assertEquals(Rect(40, 20, 80, 30), rect)
+
+ rect = Rect(20, 70, 60, 80)
+ Utilities.rotateBounds(rect, 100, 100, 3)
+ assertEquals(Rect(20, 20, 30, 60), rect)
+ }
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutMethodsTest.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutMethodsTest.kt
new file mode 100644
index 0000000..e8459d6
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutMethodsTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 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.celllayout
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class CellLayoutMethodsTest {
+
+ @JvmField @Rule var cellLayoutBuilder = UnitTestCellLayoutBuilderRule()
+
+ @Test
+ fun pointToCellExact() {
+ val width = 1000
+ val height = 1000
+ val columns = 30
+ val rows = 30
+ val cl = cellLayoutBuilder.createCellLayout(columns, rows, false, width, height)
+
+ val res = intArrayOf(0, 0)
+ for (col in 0..<columns) {
+ for (row in 0..<rows) {
+ val x = (width / columns) * col
+ val y = (height / rows) * row
+ cl.pointToCellExact(x, y, res)
+ cl.pointToCellExact(x, y, res)
+ assertValues(col, res, row, columns, rows, width, height, x, y)
+ }
+ }
+
+ cl.pointToCellExact(-10, -10, res)
+ assertValues(0, res, 0, columns, rows, width, height, -10, -10)
+ cl.pointToCellExact(width + 10, height + 10, res)
+ assertValues(columns - 1, res, rows - 1, columns, rows, width, height, -10, -10)
+ }
+
+ private fun assertValues(
+ col: Int,
+ res: IntArray,
+ row: Int,
+ columns: Int,
+ rows: Int,
+ width: Int,
+ height: Int,
+ x: Int,
+ y: Int
+ ) {
+ assert(col == res[0] && row == res[1]) {
+ "Cell Layout with values (c= $columns, r= $rows, w= $width, h= $height) didn't mapped correctly the pixels ($x, $y) to the cells ($col, $row) with result (${res[0]}, ${res[1]})"
+ }
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index 8a9711d..30953cc 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -144,8 +144,8 @@
public ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX,
int spanY, int minSpanX, int minSpanY, boolean isMulti) {
- CellLayout cl = mCellLayoutBuilder.createCellLayout(board.getWidth(), board.getHeight(),
- isMulti);
+ CellLayout cl = mCellLayoutBuilder.createCellLayoutDefaultSize(board.getWidth(),
+ board.getHeight(), isMulti);
// The views have to be sorted or the result can vary
board.getIcons()
diff --git a/tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
index b63966d..f624be1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
@@ -64,11 +64,21 @@
dp.inv.numRows = prevNumRows
}
- fun createCellLayout(width: Int, height: Int, isMulti: Boolean): CellLayout {
+ fun createCellLayoutDefaultSize(columns: Int, rows: Int, isMulti: Boolean): CellLayout {
+ return createCellLayout(columns, rows, isMulti)
+ }
+
+ fun createCellLayout(
+ columns: Int,
+ rows: Int,
+ isMulti: Boolean,
+ width: Int = 1000,
+ height: Int = 1000
+ ): CellLayout {
val dp = getDeviceProfile()
// modify the device profile.
- dp.inv.numColumns = if (isMulti) width / 2 else width
- dp.inv.numRows = height
+ dp.inv.numColumns = if (isMulti) columns / 2 else columns
+ dp.inv.numRows = rows
dp.cellLayoutBorderSpacePx = Point(0, 0)
val cl =
if (isMulti) MultipageCellLayout(getWrappedContext(applicationContext, dp))
@@ -76,8 +86,8 @@
// I put a very large number for width and height so that all the items can fit, it doesn't
// need to be exact, just bigger than the sum of cell border
cl.measure(
- View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY)
+ View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
)
return cl
}