diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml index 08635c6e60..1859bd8596 100644 --- a/res/layout/widgets_bottom_sheet.xml +++ b/res/layout/widgets_bottom_sheet.xml @@ -21,7 +21,6 @@ android:layout_height="wrap_content" android:paddingTop="16dp" android:background="@drawable/widgets_bottom_sheet_background" - android:elevation="@dimen/deep_shortcuts_elevation" android:layout_gravity="bottom" android:theme="?attr/widgetsTheme"> diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java index a6f2b42a29..4ee365ede0 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -196,7 +196,6 @@ public class ArrowTipView extends AbstractFloatingView { parent.addView(this); requestLayout(); - post(() -> setY(yCoord - getHeight())); post(() -> { float halfWidth = getWidth() / 2f; float xCoord; @@ -208,7 +207,9 @@ public class ArrowTipView extends AbstractFloatingView { xCoord = arrowXCoord - halfWidth; } setX(xCoord); - findViewById(R.id.arrow).setX(arrowXCoord - xCoord); + setY(yCoord - getHeight()); + View arrowView = findViewById(R.id.arrow); + arrowView.setX(arrowXCoord - xCoord - arrowView.getWidth() / 2f); requestLayout(); }); diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index f7e295e7e8..e6791c3e9c 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -26,6 +26,9 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; + import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.Launcher; @@ -39,6 +42,7 @@ import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; import com.android.launcher3.views.AbstractSlideInView; +import com.android.launcher3.views.ArrowTipView; /** * Base class for various widgets popup @@ -47,6 +51,9 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView implements OnClickListener, OnLongClickListener, DragSource, PopupDataProvider.PopupDataChangeListener { + protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN = + "launcher.widgets_education_tip_seen"; + /* Touch handling related member variables. */ private Toast mWidgetInstructionToast; @@ -196,4 +203,28 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView toast.show(); return toast; } + + /** Shows education tip on top center of {@code view} if view is laid out. */ + @Nullable + protected ArrowTipView showEducationTipOnViewIfPossible(@Nullable View view) { + 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]); + } + + /** Returns {@code true} if tip has previously been shown on any of {@link BaseWidgetSheet}. */ + protected boolean hasSeenEducationTip() { + return mActivityContext.getSharedPrefs().getBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, false) + || Utilities.IS_RUNNING_IN_TEST_HARNESS; + } } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 787a2d1262..81df100094 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -68,12 +68,43 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { }; private static final int DEFAULT_CLOSE_DURATION = 200; + private static final long EDUCATION_TIP_DELAY_MS = 300; + private ItemInfo mOriginalItemInfo; private Rect mInsets; private final int mMaxTableHeight; private int mMaxHorizontalSpan = 4; private Configuration mCurrentConfiguration; + private final OnLayoutChangeListener mLayoutChangeListenerToShowTips = + new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + if (hasSeenEducationTip()) { + removeOnLayoutChangeListener(this); + return; + } + // Widgets are loaded asynchronously, We are adding a delay because we only want + // to show the tip when the widget preview has finished loading and rendering in + // this view. + removeCallbacks(mShowEducationTipTask); + postDelayed(mShowEducationTipTask, EDUCATION_TIP_DELAY_MS); + } + }; + + private final Runnable mShowEducationTipTask = () -> { + if (hasSeenEducationTip()) { + removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips); + return; + } + View viewForTip = ((ViewGroup) ((TableLayout) findViewById(R.id.widgets_table)) + .getChildAt(0)).getChildAt(0); + if (showEducationTipOnViewIfPossible(viewForTip) != null) { + removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips); + } + }; + public WidgetsBottomSheet(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -88,6 +119,9 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { // take over the entire view vertically. mMaxTableHeight = deviceProfile.inv.numRows * 2 / 3 * deviceProfile.cellHeightPx; mCurrentConfiguration = new Configuration(getResources().getConfiguration()); + if (!hasSeenEducationTip()) { + addOnLayoutChangeListener(mLayoutChangeListenerToShowTips); + } } @Override diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 42ff249a4a..145d5cbf4f 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -41,7 +41,6 @@ import android.widget.TextView; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.DeviceProfile; @@ -49,11 +48,9 @@ import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.views.ArrowTipView; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.TopRoundedCornerView; import com.android.launcher3.widget.BaseWidgetSheet; @@ -86,7 +83,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet // resolution or landscape on phone. This ratio defines the max percentage of content area that // the table can display. private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f; - private static final String WIDGETS_EDUCATION_TIP_SEEN = "launcher.widgets_education_tip_seen"; private final Rect mInsets = new Rect(); private final boolean mHasWorkProfile; @@ -120,9 +116,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet return; } View viewForTip = getViewToShowEducationTip(); - if (viewForTip != null && ViewCompat.isLaidOut(viewForTip)) { + if (showEducationTipOnViewIfPossible(viewForTip) != null) { removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips); - showEducationTipOnView(viewForTip); } }; private final int mTabsHeight; @@ -618,18 +613,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet getWindowInsetsController().hide(WindowInsets.Type.ime()); } - private void showEducationTipOnView(View view) { - mActivityContext.getSharedPrefs().edit() - .putBoolean(WIDGETS_EDUCATION_TIP_SEEN, true).apply(); - int[] coords = new int[2]; - view.getLocationOnScreen(coords); - ArrowTipView arrowTipView = new ArrowTipView(mActivityContext); - arrowTipView.showAtLocation( - getContext().getString(R.string.long_press_widget_to_add), - /* arrowXCoord= */coords[0] + view.getWidth() / 2, - /* yCoord= */coords[1]); - } - @Nullable private View getViewToShowEducationTip() { if (mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getVisibility() == VISIBLE && mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getChildCount() > 0 @@ -658,11 +641,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet return null; } - private boolean hasSeenEducationTip() { - return mActivityContext.getSharedPrefs().getBoolean(WIDGETS_EDUCATION_TIP_SEEN, false) - || Utilities.IS_RUNNING_IN_TEST_HARNESS; - } - /** A holder class for holding adapters & their corresponding recycler view. */ private final class AdapterHolder { static final int PRIMARY = 0;