diff --git a/packages/SystemUI/res/layout/bubble_stack_user_education.xml b/packages/SystemUI/res/layout/bubble_stack_user_education.xml
index 616403219bc61..fe1ed4b6f726a 100644
--- a/packages/SystemUI/res/layout/bubble_stack_user_education.xml
+++ b/packages/SystemUI/res/layout/bubble_stack_user_education.xml
@@ -15,8 +15,8 @@
~ limitations under the License.
-->
-
+ android:background="@drawable/bubble_stack_user_education_bg"
+ >
-
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 213bb923db651..b51dc93dc373e 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,77 +14,77 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:gravity="start"
+ android:textAlignment="viewStart"
+ android:text="@string/bubbles_user_education_manage_title"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/>
-
+
+
+
+
+ android:focusable="true"
+ android:clickable="false"
+ android:text="@string/manage_bubbles_text"
+ android:textColor="?attr/wallpaperTextColor"
+ />
-
-
-
-
-
-
-
-
+ android:focusable="true"
+ android:text="@string/bubbles_user_education_got_it"
+ android:textColor="?attr/wallpaperTextColor"
+ />
-
+
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index ec9644af70131..64df2b99ee227 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,11 +19,7 @@ package com.android.systemui.bubbles;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION;
-import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -38,7 +34,6 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Outline;
@@ -54,7 +49,6 @@ import android.provider.Settings;
import android.util.Log;
import android.view.Choreographer;
import android.view.DisplayCutout;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -70,10 +64,8 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
-import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -82,7 +74,6 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Interpolators;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -115,10 +106,6 @@ public class BubbleStackView extends FrameLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
- /** Animation durations for bubble stack user education views. **/
- static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
- private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
-
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f;
@@ -556,7 +543,7 @@ public class BubbleStackView extends FrameLayout
// Otherwise, we either tapped the stack (which means we're collapsed
// and should expand) or the currently selected bubble (we're expanded
// and should collapse).
- if (!maybeShowStackUserEducation()) {
+ if (!maybeShowStackEdu()) {
mBubbleData.setExpanded(!mBubbleData.isExpanded());
}
}
@@ -582,7 +569,9 @@ public class BubbleStackView extends FrameLayout
}
if (mBubbleData.isExpanded()) {
- maybeShowManageEducation(false /* show */);
+ if (mManageEduView != null) {
+ mManageEduView.hide(false /* show */);
+ }
// If we're expanded, tell the animation controller to prepare to drag this bubble,
// dispatching to the individual bubble magnet listener.
@@ -637,7 +626,9 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.dragBubbleOut(
v, viewInitialX + dx, viewInitialY + dy);
} else {
- hideStackUserEducation(false /* fromExpansion */);
+ if (mStackEduView != null) {
+ mStackEduView.hide(false /* fromExpansion */);
+ }
mStackAnimationController.moveStackFromTouch(
viewInitialX + dx, viewInitialY + dy);
}
@@ -684,7 +675,7 @@ public class BubbleStackView extends FrameLayout
private OnClickListener mFlyoutClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
- if (maybeShowStackUserEducation()) {
+ if (maybeShowStackEdu()) {
// If we're showing user education, don't open the bubble show the education first
mBubbleToExpandAfterFlyoutCollapse = null;
} else {
@@ -728,7 +719,7 @@ public class BubbleStackView extends FrameLayout
mFlyout.removeCallbacks(mHideFlyout);
animateFlyoutCollapsed(shouldDismiss, velX);
- maybeShowStackUserEducation();
+ maybeShowStackEdu();
}
};
@@ -737,14 +728,8 @@ public class BubbleStackView extends FrameLayout
@Nullable
private BubbleOverflow mBubbleOverflow;
-
- private boolean mShouldShowUserEducation;
- private boolean mAnimatingEducationAway;
- private View mUserEducationView;
-
- private boolean mShouldShowManageEducation;
- private ManageEducationView mManageEducationView;
- private boolean mAnimatingManageEducationAway;
+ private StackEducationView mStackEduView;
+ private ManageEducationView mManageEduView;
private ViewGroup mManageMenu;
private ImageView mManageSettingsIcon;
@@ -805,8 +790,6 @@ public class BubbleStackView extends FrameLayout
onBubbleAnimatedOut);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
- setUpUserEducation();
-
// Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
// is centered. It greatly simplifies translation positioning/animations. Views that will
// actually lay out differently in RTL, such as the flyout and expanded view, will set their
@@ -819,6 +802,8 @@ public class BubbleStackView extends FrameLayout
mBubbleContainer.setClipChildren(false);
addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ updateUserEdu();
+
mExpandedViewContainer = new FrameLayout(context);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setClipChildren(false);
@@ -1092,48 +1077,66 @@ public class BubbleStackView extends FrameLayout
addView(mManageMenu);
}
- private void setUpUserEducation() {
- if (mUserEducationView != null) {
- removeView(mUserEducationView);
+ /**
+ * Whether the educational view should show for the expanded view "manage" menu.
+ */
+ private boolean shouldShowManageEdu() {
+ final boolean seen = Prefs.getBoolean(mContext,
+ Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */);
+ final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
+ && mExpandedBubble != null;
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Show manage edu: " + shouldShow);
}
- mShouldShowUserEducation = shouldShowBubblesEducation();
- if (DEBUG_USER_EDUCATION) {
- Log.d(TAG, "shouldShowUserEducation: " + mShouldShowUserEducation);
- }
- if (mShouldShowUserEducation) {
- mUserEducationView = mInflater.inflate(R.layout.bubble_stack_user_education, this,
- false /* attachToRoot */);
- mUserEducationView.setVisibility(GONE);
+ return shouldShow;
+ }
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {android.R.attr.colorAccent,
- android.R.attr.textColorPrimaryInverse});
- final int bgColor = ta.getColor(0, Color.BLACK);
- int textColor = ta.getColor(1, Color.WHITE);
- ta.recycle();
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
-
- TextView title = mUserEducationView.findViewById(R.id.user_education_title);
- TextView description = mUserEducationView.findViewById(R.id.user_education_description);
- title.setTextColor(textColor);
- description.setTextColor(textColor);
-
- updateUserEducationForLayoutDirection();
- addView(mUserEducationView);
+ private void maybeShowManageEdu() {
+ if (!shouldShowManageEdu()) {
+ return;
}
+ if (mManageEduView == null) {
+ mManageEduView = new ManageEducationView(mContext);
+ addView(mManageEduView);
+ }
+ mManageEduView.show(mExpandedBubble.getExpandedView(), mTempRect);
+ }
- if (mManageEducationView != null) {
- removeView(mManageEducationView);
+ /**
+ * Whether education view should show for the collapsed stack.
+ */
+ private boolean shouldShowStackEdu() {
+ final boolean seen = Prefs.getBoolean(getContext(),
+ Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */);
+ final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
+ if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "Show stack edu: " + shouldShow);
}
- mShouldShowManageEducation = shouldShowManageEducation();
- if (DEBUG_USER_EDUCATION) {
- Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
+ return shouldShow;
+ }
+
+ /**
+ * @return true if education view for collapsed stack should show and was not showing before.
+ */
+ private boolean maybeShowStackEdu() {
+ if (!shouldShowStackEdu()) {
+ return false;
}
- if (mShouldShowManageEducation) {
- mManageEducationView = (ManageEducationView)
- mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
- false /* attachToRoot */);
- addView(mManageEducationView);
+ if (mStackEduView == null) {
+ mStackEduView = new StackEducationView(mContext);
+ addView(mStackEduView);
+ }
+ return mStackEduView.show(mStackAnimationController.getStartPosition());
+ }
+
+ private void updateUserEdu() {
+ maybeShowStackEdu();
+ if (mManageEduView != null) {
+ mManageEduView.invalidate();
+ }
+ maybeShowManageEdu();
+ if (mStackEduView != null) {
+ mStackEduView.invalidate();
}
}
@@ -1164,9 +1167,9 @@ public class BubbleStackView extends FrameLayout
*/
public void onThemeChanged() {
setUpFlyout();
- setUpUserEducation();
setUpManageMenu();
updateOverflow();
+ updateUserEdu();
updateExpandedViewTheme();
}
@@ -1197,12 +1200,11 @@ public class BubbleStackView extends FrameLayout
public void onLayoutDirectionChanged(int direction) {
mManageMenu.setLayoutDirection(direction);
mFlyout.setLayoutDirection(direction);
- if (mUserEducationView != null) {
- mUserEducationView.setLayoutDirection(direction);
- updateUserEducationForLayoutDirection();
+ if (mStackEduView != null) {
+ mStackEduView.setLayoutDirection(direction);
}
- if (mManageEducationView != null) {
- mManageEducationView.setLayoutDirection(direction);
+ if (mManageEduView != null) {
+ mManageEduView.setLayoutDirection(direction);
}
updateExpandedViewDirection(direction);
}
@@ -1446,7 +1448,7 @@ public class BubbleStackView extends FrameLayout
Log.d(TAG, "addBubble: " + bubble);
}
- if (getBubbleCount() == 0 && mShouldShowUserEducation) {
+ if (getBubbleCount() == 0 && shouldShowStackEdu()) {
// Override the default stack position if we're showing user education.
mStackAnimationController.setStackPosition(
mStackAnimationController.getStartPosition());
@@ -1649,115 +1651,6 @@ public class BubbleStackView extends FrameLayout
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
- /**
- * If necessary, shows the user education view for the bubble stack. This appears the first
- * time a user taps on a bubble.
- *
- * @return true if user education was shown, false otherwise.
- */
- private boolean maybeShowStackUserEducation() {
- if (mShouldShowUserEducation && mUserEducationView.getVisibility() != VISIBLE) {
- mUserEducationView.setAlpha(0);
- mUserEducationView.setVisibility(VISIBLE);
- updateUserEducationForLayoutDirection();
-
- // Post so we have height of mUserEducationView
- mUserEducationView.post(() -> {
- final int viewHeight = mUserEducationView.getHeight();
- PointF stackPosition = mStackAnimationController.getStartPosition();
- final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2);
- mUserEducationView.setTranslationY(translationY);
- mUserEducationView.animate()
- .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
- .setInterpolator(FAST_OUT_SLOW_IN)
- .alpha(1);
- });
- Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, true);
- return true;
- }
- return false;
- }
-
- private void updateUserEducationForLayoutDirection() {
- if (mUserEducationView == null) {
- return;
- }
- LinearLayout textLayout = mUserEducationView.findViewById(R.id.user_education_view);
- TextView title = mUserEducationView.findViewById(R.id.user_education_title);
- TextView description = mUserEducationView.findViewById(R.id.user_education_description);
- boolean isLtr =
- getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR;
- if (isLtr) {
- mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_LTR);
- textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
- title.setGravity(Gravity.LEFT);
- description.setGravity(Gravity.LEFT);
- } else {
- mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_RTL);
- textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
- title.setGravity(Gravity.RIGHT);
- description.setGravity(Gravity.RIGHT);
- }
- }
-
- /**
- * If necessary, hides the user education view for the bubble stack.
- *
- * @param fromExpansion if true this indicates the hide is happening due to the bubble being
- * expanded, false if due to a touch outside of the bubble stack.
- */
- void hideStackUserEducation(boolean fromExpansion) {
- if (mShouldShowUserEducation
- && mUserEducationView.getVisibility() == VISIBLE
- && !mAnimatingEducationAway) {
- mAnimatingEducationAway = true;
- mUserEducationView.animate()
- .alpha(0)
- .setDuration(fromExpansion
- ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT
- : ANIMATE_STACK_USER_EDUCATION_DURATION)
- .withEndAction(() -> {
- mAnimatingEducationAway = false;
- mShouldShowUserEducation = shouldShowBubblesEducation();
- mUserEducationView.setVisibility(GONE);
- });
- }
- }
-
- /**
- * If necessary, toggles the user education view for the manage button. This is shown when the
- * bubble stack is expanded for the first time.
- *
- * @param show whether the user education view should show or not.
- */
- void maybeShowManageEducation(boolean show) {
- if (mManageEducationView == null) {
- return;
- }
- if (show
- && mShouldShowManageEducation
- && mManageEducationView.getVisibility() != VISIBLE
- && mIsExpanded
- && mExpandedBubble.getExpandedView() != null) {
- mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
- () -> maybeShowManageEducation(false) /* run on click */);
- Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
- } else if (!show
- && mManageEducationView.getVisibility() == VISIBLE
- && !mAnimatingManageEducationAway) {
- mManageEducationView.animate()
- .alpha(0)
- .setDuration(mIsExpansionAnimating
- ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT
- : ANIMATE_STACK_USER_EDUCATION_DURATION)
- .withEndAction(() -> {
- mAnimatingManageEducationAway = false;
- mShouldShowManageEducation = shouldShowManageEducation();
- mManageEducationView.setVisibility(GONE);
- });
- }
- }
-
void showExpandedViewContents(int displayId) {
if (mExpandedBubble != null
&& mExpandedBubble.getExpandedView() != null
@@ -1791,7 +1684,9 @@ public class BubbleStackView extends FrameLayout
cancelDelayedExpandCollapseSwitchAnimations();
mIsExpanded = true;
- hideStackUserEducation(true /* fromExpansion */);
+ if (mStackEduView != null) {
+ mStackEduView.hide(true /* fromExpansion */);
+ }
beforeExpandedViewAnimation();
mBubbleContainer.setActiveController(mExpandedAnimationController);
@@ -1799,7 +1694,9 @@ public class BubbleStackView extends FrameLayout
updatePointerPosition();
mExpandedAnimationController.expandFromStack(() -> {
afterExpandedViewAnimation();
- maybeShowManageEducation(true);
+ if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
+ maybeShowManageEdu();
+ }
} /* after */);
mExpandedViewContainer.setTranslationX(0);
@@ -1936,7 +1833,9 @@ public class BubbleStackView extends FrameLayout
.withEndActions(() -> {
final BubbleViewProvider previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
- maybeShowManageEducation(false);
+ if (mManageEduView != null) {
+ mManageEduView.hide(false /* fromExpansion */);
+ }
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "animateCollapse");
@@ -2104,8 +2003,8 @@ public class BubbleStackView extends FrameLayout
// from any location.
if (!mIsExpanded
|| mShowingManage
- || (mManageEducationView != null
- && mManageEducationView.getVisibility() == VISIBLE)) {
+ || (mManageEduView != null
+ && mManageEduView.getVisibility() == VISIBLE)) {
touchableRegion.setEmpty();
}
}
@@ -2289,7 +2188,7 @@ public class BubbleStackView extends FrameLayout
if (flyoutMessage == null
|| flyoutMessage.message == null
|| !bubble.showFlyout()
- || (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE)
+ || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE)
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
@@ -2398,7 +2297,7 @@ public class BubbleStackView extends FrameLayout
* them.
*/
public void getTouchableRegion(Rect outRect) {
- if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) {
+ if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
// When user education shows then capture all touches
outRect.set(0, 0, getWidth(), getHeight());
return;
@@ -2770,18 +2669,6 @@ public class BubbleStackView extends FrameLayout
return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
}
- /** Whether the educational view should appear for bubbles. **/
- private boolean shouldShowBubblesEducation() {
- return BubbleDebugConfig.forceShowUserEducation(getContext())
- || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, false);
- }
-
- /** Whether the educational view should appear for the expanded view "manage" button. **/
- private boolean shouldShowManageEducation() {
- return BubbleDebugConfig.forceShowUserEducation(getContext())
- || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false);
- }
-
/** For debugging only */
List getBubblesOnScreen() {
List bubbles = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
index c58ab31c45613..26a9773f9bb8d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -18,52 +18,56 @@ package com.android.systemui.bubbles
import android.content.Context
import android.graphics.Color
import android.graphics.Rect
-import android.util.AttributeSet
-import android.view.Gravity
+import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.Interpolators
+import com.android.systemui.Prefs
+import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION
import com.android.systemui.R
/**
- * Educational view to highlight the manage button that allows a user to configure the settings
+ * User education view to highlight the manage button that allows a user to configure the settings
* for the bubble. Shown only the first time a user expands a bubble.
*/
-class ManageEducationView @JvmOverloads constructor(
- context: Context?,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = 0,
- defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+class ManageEducationView constructor(context: Context) : LinearLayout(context) {
+
+ private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView"
+ else BubbleDebugConfig.TAG_BUBBLES
+
+ private val ANIMATE_DURATION : Long = 200
+ private val ANIMATE_DURATION_SHORT : Long = 40
private val manageView by lazy { findViewById(R.id.manage_education_view) }
private val manageButton by lazy { findViewById