From d04a92d1ed318eaa83c2b2d02167f1a807f7b18e Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 16 Jun 2021 09:46:25 -0500 Subject: [PATCH] Refactor FolderInsetAnimationCallback to be used with any view - Update ic_corp_off icon to outline - Remove shadow from work toggle - Translate work fab when keyboard is shown Screenshot: https://screenshot.googleplex.com/593tEg7bE4kSS4y Bug: 191251404 Bug: 191250785 Test: local Change-Id: Ie7dddfd17eb90575a1e1f67e281070dd8d268f8d --- res/drawable/ic_corp_off.xml | 13 +- ...card_btn.xml => rounded_action_button.xml} | 6 +- res/layout/work_apps_edu.xml | 3 +- res/layout/work_apps_paused.xml | 2 +- res/layout/work_mode_fab.xml | 1 - res/values/dimens.xml | 7 +- .../allapps/AllAppsContainerView.java | 5 +- .../launcher3/allapps/WorkModeSwitch.java | 24 ++++ .../anim/KeyboardInsetAnimationCallback.java | 71 ++++++++++ .../launcher3/config/FeatureFlags.java | 3 + src/com/android/launcher3/folder/Folder.java | 128 ++++++------------ .../launcher3/util/OnboardingPrefs.java | 13 +- 12 files changed, 170 insertions(+), 106 deletions(-) rename res/drawable/{work_card_btn.xml => rounded_action_button.xml} (84%) create mode 100644 src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java diff --git a/res/drawable/ic_corp_off.xml b/res/drawable/ic_corp_off.xml index 62a978741d..117258e3bd 100644 --- a/res/drawable/ic_corp_off.xml +++ b/res/drawable/ic_corp_off.xml @@ -1,5 +1,4 @@ - - 48dp 24dp - 18dp + 16dp 20dp 16sp 16dp + + 32dp + 16dp + 8dp + 8dp diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 7003df10c2..90bb5c855a 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -132,7 +132,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo private ScrimView mScrimView; private int mHeaderColor; - public AllAppsContainerView(Context context) { this(context, null); } @@ -572,6 +571,10 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mViewPager == null ? getActiveRecyclerView() : mViewPager; } + public int getCurrentPage() { + return mViewPager != null ? mViewPager.getCurrentPage() : AdapterHolder.MAIN; + } + /** * Handles selection on focused view and returns success */ diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java index 866694fdca..31f0c4fc86 100644 --- a/src/com/android/launcher3/allapps/WorkModeSwitch.java +++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java @@ -18,6 +18,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.content.Context; +import android.graphics.Insets; import android.graphics.Rect; import android.os.Build; import android.os.Process; @@ -26,12 +27,15 @@ import android.os.UserManager; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.view.WindowInsets; import android.widget.Button; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.android.launcher3.Insettable; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.KeyboardInsetAnimationCallback; import com.android.launcher3.pm.UserCache; /** @@ -42,6 +46,10 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi private Rect mInsets = new Rect(); private boolean mWorkEnabled; + + @Nullable + private KeyboardInsetAnimationCallback mKeyboardInsetAnimationCallback; + public WorkModeSwitch(Context context) { this(context, null, 0); } @@ -58,6 +66,10 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi protected void onFinishInflate() { super.onFinishInflate(); setOnClickListener(this); + if (Utilities.ATLEAST_R) { + mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this); + setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback); + } } @Override @@ -117,4 +129,16 @@ public class WorkModeSwitch extends Button implements Insettable, View.OnClickLi } return showConfirm; } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + if (Utilities.ATLEAST_R) { + setTranslationY(0); + if (insets.isVisible(WindowInsets.Type.ime())) { + Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime()); + setTranslationY(mInsets.bottom - keyboardInsets.bottom); + } + } + return insets; + } } diff --git a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java new file mode 100644 index 0000000000..ef4ada3cd9 --- /dev/null +++ b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java @@ -0,0 +1,71 @@ +/* + * 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.anim; + +import android.os.Build; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; + +import androidx.annotation.RequiresApi; + +import com.android.launcher3.Utilities; + +import java.util.List; + +/** + * Callback that animates views above the IME + */ +@RequiresApi(api = Build.VERSION_CODES.R) +public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callback { + private final View mView; + + private float mInitialTranslation; + private float mTerminalTranslation; + + public KeyboardInsetAnimationCallback(View view) { + super(DISPATCH_MODE_STOP); + mView = view; + } + + @Override + public void onPrepare(WindowInsetsAnimation animation) { + mInitialTranslation = mView.getTranslationY(); + } + + + @Override + public WindowInsets onProgress(WindowInsets windowInsets, List list) { + if (list.size() == 0) { + mView.setTranslationY(mInitialTranslation); + return windowInsets; + } + float progress = list.get(0).getInterpolatedFraction(); + + mView.setTranslationY( + Utilities.mapRange(progress, mInitialTranslation, mTerminalTranslation)); + + return windowInsets; + } + + @Override + public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation, + WindowInsetsAnimation.Bounds bounds) { + mTerminalTranslation = mView.getTranslationY(); + mView.setTranslationY(mInitialTranslation); + return super.onStart(animation, bounds); + } +} diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 0e710b7898..00ce80fc72 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -99,6 +99,9 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag( "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps"); + public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag( + "IME_STICKY_SNACKBAR_EDU", true, "Show sticky IME edu in AllApps"); + public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag( "ENABLE_PEOPLE_TILE_PREVIEW", false, "Experimental: Shows conversation shortcuts on home screen as search results"); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 5dcd75a364..22bb56c472 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -17,7 +17,6 @@ package com.android.launcher3.folder; import static android.text.TextUtils.isEmpty; -import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.NORMAL; @@ -39,7 +38,6 @@ import android.graphics.Path; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; -import android.os.Build; import android.text.InputType; import android.text.Selection; import android.text.TextUtils; @@ -54,15 +52,12 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.WindowInsets; -import android.view.WindowInsetsAnimation; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; import android.view.inputmethod.EditorInfo; import android.widget.TextView; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.core.content.res.ResourcesCompat; import com.android.launcher3.AbstractFloatingView; @@ -83,6 +78,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.FolderAccessibilityHelper; +import com.android.launcher3.anim.KeyboardInsetAnimationCallback; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; @@ -164,9 +160,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private final Alarm mReorderAlarm = new Alarm(); private final Alarm mOnExitAlarm = new Alarm(); private final Alarm mOnScrollHintAlarm = new Alarm(); - @Thunk final Alarm mScrollPauseAlarm = new Alarm(); + final Alarm mScrollPauseAlarm = new Alarm(); - @Thunk final ArrayList mItemsInReadingOrder = new ArrayList(); + final ArrayList mItemsInReadingOrder = new ArrayList(); private AnimatorSet mCurrentAnimator; private boolean mIsAnimatingClosed = false; @@ -182,9 +178,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private CharSequence mFromTitle; private FromState mFromLabelState; - @Thunk FolderIcon mFolderIcon; + @Thunk + FolderIcon mFolderIcon; - @Thunk FolderPagedView mContent; + @Thunk + FolderPagedView mContent; public FolderNameEditText mFolderName; private PageIndicatorDots mPageIndicator; @@ -192,7 +190,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private int mFooterHeight; // Cell ranks used for drag and drop - @Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank; + @Thunk + int mTargetRank, mPrevTargetRank, mEmptyCellRank; private Path mClipPath; @@ -203,7 +202,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @ViewDebug.IntToString(from = STATE_ANIMATING, to = "STATE_ANIMATING"), @ViewDebug.IntToString(from = STATE_OPEN, to = "STATE_OPEN"), }) - @Thunk int mState = STATE_NONE; + @Thunk + int mState = STATE_NONE; @ViewDebug.ExportedProperty(category = "launcher") private boolean mRearrangeOnClose = false; boolean mItemsInvalidated = false; @@ -221,12 +221,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Folder scrolling private int mScrollAreaOffset; - @Thunk int mScrollHintDir = SCROLL_NONE; - @Thunk int mCurrentScrollDir = SCROLL_NONE; + @Thunk + int mScrollHintDir = SCROLL_NONE; + @Thunk + int mCurrentScrollDir = SCROLL_NONE; private StatsLogManager mStatsLogManager; - @Nullable private FolderWindowInsetsAnimationCallback mFolderWindowInsetsAnimationCallback; + @Nullable + private KeyboardInsetAnimationCallback mKeyboardInsetAnimationCallback; private GradientDrawable mBackground; @@ -234,7 +237,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo * Used to inflate the Workspace from XML. * * @param context The application's context. - * @param attrs The attributes set containing the Workspace's customization values. + * @param attrs The attributes set containing the Workspace's customization values. */ public Folder(Context context, AttributeSet attrs) { super(context, attrs); @@ -246,7 +249,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mStatsLogManager = StatsLogManager.newInstance(context); // We need this view to be focusable in touch mode so that when text editing of the folder // name is complete, we have something to focus on, thus hiding the cursor and giving - // reliable behavior when clicking the text field (since it will always gain focus on click). + // reliable behavior when clicking the text field (since it will always gain focus on + // click). setFocusableInTouchMode(true); } @@ -286,10 +290,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mFooterHeight = getResources().getDimensionPixelSize(R.dimen.folder_label_height); if (Utilities.ATLEAST_R) { - mFolderWindowInsetsAnimationCallback = - new FolderWindowInsetsAnimationCallback(DISPATCH_MODE_STOP, this); - - setWindowInsetsAnimationCallback(mFolderWindowInsetsAnimationCallback); + mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this); + setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback); } } @@ -311,14 +313,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (options.isAccessibleDrag) { mDragController.addDragListener(new AccessibleDragListenerAdapter( mContent, FolderAccessibilityHelper::new) { - @Override - protected void enableAccessibleDrag(boolean enable) { - super.enableAccessibleDrag(enable); - mFooter.setImportantForAccessibility(enable - ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS - : IMPORTANT_FOR_ACCESSIBILITY_AUTO); - } - }); + @Override + protected void enableAccessibleDrag(boolean enable) { + super.enableAccessibleDrag(enable); + mFooter.setImportantForAccessibility(enable + ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } + }); } mLauncherDelegate.beginDragShared(v, this, options); @@ -539,7 +541,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo * * @param activityContext The main ActivityContext in which to inflate this Folder. It must also * be an instance or ContextWrapper around the Launcher activity context. - * * @return A new UserFolder. */ @SuppressLint("InflateParams") @@ -677,6 +678,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mFolderIcon.setIconVisible(false); mFolderIcon.drawLeaveBehindIfExists(); } + @Override public void onAnimationEnd(Animator animation) { mState = STATE_OPEN; @@ -691,7 +693,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo int footerWidth = mContent.getDesiredWidth() - mFooter.getPaddingLeft() - mFooter.getPaddingRight(); - float textWidth = mFolderName.getPaint().measureText(mFolderName.getText().toString()); + float textWidth = mFolderName.getPaint().measureText(mFolderName.getText().toString()); float translation = (footerWidth - textWidth) / 2; mFolderName.setTranslationX(mContent.mIsRtl ? -translation : translation); mPageIndicator.prepareEntryAnimation(); @@ -705,9 +707,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Override public void onAnimationEnd(Animator animation) { mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION) - .translationX(0) - .setInterpolator(AnimationUtils.loadInterpolator( - getContext(), android.R.interpolator.fast_out_slow_in)); + .translationX(0) + .setInterpolator(AnimationUtils.loadInterpolator( + getContext(), android.R.interpolator.fast_out_slow_in)); mPageIndicator.playEntryAnimation(); if (updateAnimationFlag) { @@ -794,8 +796,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Override public void onAnimationEnd(Animator animation) { - if (Utilities.ATLEAST_R && mFolderWindowInsetsAnimationCallback != null) { - setWindowInsetsAnimationCallback(mFolderWindowInsetsAnimationCallback); + if (Utilities.ATLEAST_R && mKeyboardInsetAnimationCallback != null) { + setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback); } closeComplete(true); announceAccessibilityChanges(); @@ -1109,7 +1111,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo sTempRect.set(mActivityContext.getFolderBoundingBox()); int left = Utilities.boundToRange(centeredLeft, sTempRect.left, sTempRect.right - width); int top = Utilities.boundToRange(centeredTop, sTempRect.top, sTempRect.bottom - height); - int[] inOutPosition = new int[] {left, top}; + int[] inOutPosition = new int[]{left, top}; mActivityContext.updateOpenFolderPosition(inOutPosition, sTempRect, width, height); left = inOutPosition[0]; top = inOutPosition[1]; @@ -1193,7 +1195,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return mInfo.contents.size(); } - @Thunk void replaceFolderWithFinalItem() { + void replaceFolderWithFinalItem() { mLauncherDelegate.replaceFolderWithFinalItem(this); mDestroyed = true; } @@ -1352,6 +1354,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo v.setVisibility(INVISIBLE); } } + public void showItem(WorkspaceItemInfo info) { View v = getViewForInfo(info); if (v != null) { @@ -1646,55 +1649,4 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return windowBottomPx - folderBottomPx; } - - /** Callback that animates a folder sliding up above the ime. */ - @RequiresApi(api = Build.VERSION_CODES.R) - private static class FolderWindowInsetsAnimationCallback - extends WindowInsetsAnimation.Callback { - - private final Folder mFolder; - float mFolderTranslationStart; - float mFolderTranslationEnd; - - FolderWindowInsetsAnimationCallback(int dispatchMode, Folder folder) { - super(dispatchMode); - - mFolder = folder; - } - - @Override - public void onPrepare(@NonNull WindowInsetsAnimation animation) { - mFolderTranslationStart = mFolder.getTranslationY(); - } - - @NonNull - @Override - public WindowInsetsAnimation.Bounds onStart( - @NonNull WindowInsetsAnimation animation, - @NonNull WindowInsetsAnimation.Bounds bounds) { - mFolderTranslationEnd = mFolder.getTranslationY(); - - mFolder.setTranslationY(mFolderTranslationStart); - - return super.onStart(animation, bounds); - } - - @NonNull - @Override - public WindowInsets onProgress(@NonNull WindowInsets windowInsets, - @NonNull List list) { - if (list.size() == 0) { - mFolder.setTranslationY(0); - - return windowInsets; - } - float progress = list.get(0).getInterpolatedFraction(); - - mFolder.setTranslationY( - Utilities.mapRange(progress, mFolderTranslationStart, mFolderTranslationEnd)); - - return windowInsets; - } - - } } diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java index 2e279fa7d3..c395d6ced3 100644 --- a/src/com/android/launcher3/util/OnboardingPrefs.java +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -37,6 +37,7 @@ public class OnboardingPrefs { public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count"; public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen"; public static final String SEARCH_EDU_SEEN = "launcher.search_edu"; + public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count"; /** * Events that either have happened or have not (booleans). @@ -47,23 +48,28 @@ public class OnboardingPrefs { SEARCH_EDU_SEEN }) @Retention(RetentionPolicy.SOURCE) - public @interface EventBoolKey {} + public @interface EventBoolKey { + } /** * Events that occur multiple times, which we count up to a max defined in {@link #MAX_COUNTS}. */ @StringDef(value = { HOME_BOUNCE_COUNT, - HOTSEAT_DISCOVERY_TIP_COUNT + HOTSEAT_DISCOVERY_TIP_COUNT, + SEARCH_SNACKBAR_COUNT }) @Retention(RetentionPolicy.SOURCE) - public @interface EventCountKey {} + public @interface EventCountKey { + } private static final Map MAX_COUNTS; + static { Map maxCounts = new ArrayMap<>(4); maxCounts.put(HOME_BOUNCE_COUNT, 3); maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5); + maxCounts.put(SEARCH_SNACKBAR_COUNT, 3); MAX_COUNTS = Collections.unmodifiableMap(maxCounts); } @@ -103,6 +109,7 @@ public class OnboardingPrefs { /** * Add 1 to the given event count, if we haven't already reached the max count. + * * @return Whether we have now reached the max count. */ public boolean incrementEventCount(@EventCountKey String eventKey) {