From 0828fec3d28880ba94bf47db3d0333658fa8850e Mon Sep 17 00:00:00 2001 From: "sfufa@google.com" Date: Sun, 19 Sep 2021 19:27:07 -0700 Subject: [PATCH 1/2] [Hotseat] Show edu tip within screen bounds Screenshot: https://screenshot.googleplex.com/8grkbkYQu5jQhU8 Bug: 163162675 Test: enable hotseat predictions while in landscape mode, dismiss hotseat edu dialog and verify toolTip is shown properly. Change-Id: Ia6daa7412d728be702e30972136f4c849315793d --- .../hybridhotseat/HotseatEduController.java | 52 ++++++++++++++++--- .../HotseatPredictionController.java | 34 ++---------- .../android/launcher3/views/ArrowTipView.java | 25 ++++++--- 3 files changed, 68 insertions(+), 43 deletions(-) diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index ffbe53211c..63e7390d34 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -18,8 +18,12 @@ package com.android.launcher3.hybridhotseat; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_ONLY_TIP; import android.content.Intent; +import android.graphics.Rect; +import android.util.Log; +import android.view.Gravity; import android.view.View; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; @@ -46,6 +50,8 @@ import java.util.stream.IntStream; */ public class HotseatEduController { + private static final String TAG = "HotseatEduController"; + public static final String SETTINGS_ACTION = "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS"; @@ -232,8 +238,7 @@ public class HotseatEduController { R.string.hotseat_prediction_settings, null, () -> mLauncher.startActivity(getSettingsIntent())); } else { - new ArrowTipView(mLauncher).show( - mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop()); + showHotseatArrowTip(true, mLauncher.getString(R.string.hotseat_tip_no_empty_slots)); } } @@ -254,15 +259,50 @@ public class HotseatEduController { if (requiresMigration && canMigrateToFirstPage) { showDialog(); } else { - new ArrowTipView(mLauncher).show(mLauncher.getString( + if (showHotseatArrowTip(requiresMigration, mLauncher.getString( requiresMigration ? R.string.hotseat_tip_no_empty_slots - : R.string.hotseat_auto_enrolled), - mHotseat.getTop()); - mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP); + : R.string.hotseat_auto_enrolled))) { + mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP); + } finishOnboarding(); } } + /** + * Finds a child suitable child in hotseat and shows arrow tip pointing at it. + * + * @param usePinned used to determine target view. If true, will use the first matching pinned + * item. Otherwise, will use the first predicted child + * @param message String to be shown inside the arrowView + * @return whether suitable child was found and tip was shown + */ + private boolean showHotseatArrowTip(boolean usePinned, String message) { + int childCount = mHotseat.getShortcutsAndWidgets().getChildCount(); + boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout(); + + BubbleTextView tipTargetView = null; + for (int i = childCount - 1; i > -1; i--) { + int x = isPortrait ? i : 0; + int y = isPortrait ? 0 : i; + View v = mHotseat.getShortcutsAndWidgets().getChildAt(x, y); + if (v instanceof BubbleTextView && v.getTag() instanceof WorkspaceItemInfo) { + ItemInfo info = (ItemInfo) v.getTag(); + boolean isPinned = info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT; + if (isPinned == usePinned) { + tipTargetView = (BubbleTextView) v; + break; + } + } + } + if (tipTargetView == null) { + Log.e(TAG, "Unable to find suitable view for ArrowTip"); + return false; + } + Rect bounds = mLauncher.getViewBounds(tipTargetView); + new ArrowTipView(mLauncher).show(message, Gravity.END, bounds.centerX(), bounds.top); + return true; + } + void showDialog() { if (mPredictedApps == null || mPredictedApps.isEmpty()) { return; diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index a408e6c720..85d9f01735 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -57,7 +57,6 @@ import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.OnboardingPrefs; -import com.android.launcher3.views.ArrowTipView; import com.android.launcher3.views.Snackbar; import java.util.ArrayList; @@ -153,37 +152,14 @@ public class HotseatPredictionController implements DragController.DragListener, */ public void showEdu() { mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> { - if (mPredictedItems.isEmpty()) { - // launcher has empty predictions set - Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled, - R.string.hotseat_prediction_settings, null, - () -> mLauncher.startActivity(getSettingsIntent())); - } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) { - showDiscoveryTip(); - } else { - HotseatEduController eduController = new HotseatEduController(mLauncher); - eduController.setPredictedApps(mPredictedItems.stream() - .map(i -> (WorkspaceItemInfo) i) - .collect(Collectors.toList())); - eduController.showEdu(); - } + HotseatEduController eduController = new HotseatEduController(mLauncher); + eduController.setPredictedApps(mPredictedItems.stream() + .map(i -> (WorkspaceItemInfo) i) + .collect(Collectors.toList())); + eduController.showEdu(); })); } - /** - * Shows educational tip for hotseat if user does not go through Tips app. - */ - private void showDiscoveryTip() { - if (getPredictedIcons().isEmpty()) { - new ArrowTipView(mLauncher).show( - mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop()); - } else { - Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled, - R.string.hotseat_prediction_settings, null, - () -> mLauncher.startActivity(getSettingsIntent())); - } - } - /** * Returns if hotseat client has predictions */ diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java index e449a4bfbf..ce26a66da5 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -37,6 +37,7 @@ import androidx.core.content.ContextCompat; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dragndrop.DragLayer; @@ -56,6 +57,7 @@ public class ArrowTipView extends AbstractFloatingView { protected final BaseDraggingActivity mActivity; private final Handler mHandler = new Handler(); private final int mArrowWidth; + private final int mArrowMinOffset; private boolean mIsPointingUp; private Runnable mOnClosed; private View mArrowView; @@ -69,6 +71,8 @@ public class ArrowTipView extends AbstractFloatingView { mActivity = BaseDraggingActivity.fromContext(context); mIsPointingUp = isPointingUp; mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width); + mArrowMinOffset = context.getResources().getDimensionPixelSize( + R.dimen.dynamic_grid_cell_border_spacing); init(context); } @@ -126,10 +130,10 @@ public class ArrowTipView extends AbstractFloatingView { /** * 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) { @@ -137,23 +141,28 @@ public class ArrowTipView extends AbstractFloatingView { ViewGroup parent = mActivity.getDragLayer(); parent.addView(this); + DeviceProfile grid = mActivity.getDeviceProfile(); + DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams(); params.gravity = gravity; + params.leftMargin = mArrowMinOffset + grid.getInsets().left; + params.rightMargin = mArrowMinOffset + grid.getInsets().right; LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mArrowView.getLayoutParams(); + lp.gravity = gravity; if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) { arrowMarginStart = parent.getMeasuredWidth() - arrowMarginStart; } if (gravity == Gravity.END) { - lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart - mArrowWidth); + lp.setMarginEnd(Math.max(mArrowMinOffset, + parent.getMeasuredWidth() - params.rightMargin - arrowMarginStart + - mArrowWidth / 2)); } else if (gravity == Gravity.START) { - lp.setMarginStart(arrowMarginStart - mArrowWidth / 2); + lp.setMarginStart(Math.max(mArrowMinOffset, + arrowMarginStart - params.leftMargin - mArrowWidth / 2)); } requestLayout(); - - params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left; - params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right; post(() -> setY(top - (mIsPointingUp ? 0 : getHeight()))); mIsOpen = true; From 5b38ff6d164e1de50d084e0cf5ff030bfb6e124d Mon Sep 17 00:00:00 2001 From: "sfufa@google.com" Date: Sun, 19 Sep 2021 21:55:55 -0700 Subject: [PATCH 2/2] [Hotseat] Fix hotseat edu flicker When edu view animation is not started in closed state, it results in a single frame showing right before the animation begins. Bug: 184610272 Test: visual Change-Id: I675dbe9927771f4b64d1d81c637f19621037b415 --- .../android/launcher3/hybridhotseat/HotseatEduDialog.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index c7c25670ce..c41f2ceb14 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -77,6 +77,11 @@ public class HotseatEduDialog extends AbstractSlideInView implements I mContent = this; } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setTranslationShift(TRANSLATION_SHIFT_CLOSED); + } @Override protected void onFinishInflate() { @@ -200,9 +205,9 @@ public class HotseatEduDialog extends AbstractSlideInView implements I } AbstractFloatingView.closeAllOpenViews(mActivityContext); attachToContainer(); - mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN); animateOpen(); populatePreview(predictions); + mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN); } /**