Merge "Animate the priority ring in the onboarding" into rvc-dev am: 675d4a78cb

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11795077

Change-Id: Id942d251c84e28b8cb2950842facda444023e6bc
This commit is contained in:
TreeHugger Robot
2020-06-10 15:03:06 +00:00
committed by Automerger Merge Worker
6 changed files with 155 additions and 14 deletions

View File

@@ -3956,6 +3956,13 @@
<java-symbol type="id" name="conversation_unread_count" /> <java-symbol type="id" name="conversation_unread_count" />
<java-symbol type="string" name="unread_convo_overflow" /> <java-symbol type="string" name="unread_convo_overflow" />
<java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Conversation.AppName" /> <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Conversation.AppName" />
<java-symbol type="drawable" name="conversation_badge_background" />
<java-symbol type="drawable" name="conversation_badge_ring" />
<java-symbol type="color" name="conversation_important_highlight" />
<java-symbol type="dimen" name="importance_ring_stroke_width" />
<java-symbol type="dimen" name="importance_ring_anim_max_stroke_width" />
<java-symbol type="dimen" name="importance_ring_size" />
<java-symbol type="dimen" name="conversation_icon_size_badged" />
<!-- Intent resolver and share sheet --> <!-- Intent resolver and share sheet -->
<java-symbol type="string" name="resolver_personal_tab" /> <java-symbol type="string" name="resolver_personal_tab" />

View File

@@ -86,7 +86,7 @@ public class ConversationIconFactory extends BaseIconFactory {
/** /**
* Returns the conversation info drawable * Returns the conversation info drawable
*/ */
private Drawable getBaseIconDrawable(ShortcutInfo shortcutInfo) { public Drawable getBaseIconDrawable(ShortcutInfo shortcutInfo) {
return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi); return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
} }
@@ -94,7 +94,7 @@ public class ConversationIconFactory extends BaseIconFactory {
* Get the {@link Drawable} that represents the app icon, badged with the work profile icon * Get the {@link Drawable} that represents the app icon, badged with the work profile icon
* if appropriate. * if appropriate.
*/ */
private Drawable getAppBadge(String packageName, int userId) { public Drawable getAppBadge(String packageName, int userId) {
Drawable badge = null; Drawable badge = null;
try { try {
final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(

View File

@@ -38,11 +38,61 @@
android:background="@drawable/rounded_bg_full" android:background="@drawable/rounded_bg_full"
> >
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:padding="12dp"
android:layout_gravity="center_horizontal"
>
<!-- Big icon: 52x52, 12dp padding left + top, 16dp padding right -->
<ImageView <ImageView
android:id="@+id/conversation_icon" android:id="@+id/conversation_icon"
android:layout_width="@dimen/notification_guts_conversation_icon_size" android:layout_width="@*android:dimen/conversation_avatar_size"
android:layout_height="@dimen/notification_guts_conversation_icon_size" android:layout_height="@*android:dimen/conversation_avatar_size"
android:layout_gravity="center_horizontal" /> android:scaleType="centerCrop"
android:importantForAccessibility="no"
/>
<FrameLayout
android:id="@+id/conversation_icon_badge"
android:layout_width="@*android:dimen/conversation_icon_size_badged"
android:layout_height="@*android:dimen/conversation_icon_size_badged"
android:layout_marginLeft="@*android:dimen/conversation_badge_side_margin"
android:layout_marginTop="@*android:dimen/conversation_badge_side_margin"
android:clipChildren="false"
android:clipToPadding="false"
>
<ImageView
android:id="@+id/conversation_icon_badge_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:src="@*android:drawable/conversation_badge_background"
android:forceHasOverlappingRendering="false"
/>
<ImageView
android:id="@+id/icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="4dp"
android:layout_gravity="center"
android:forceHasOverlappingRendering="false"
/>
<ImageView
android:id="@+id/conversation_icon_badge_ring"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@*android:drawable/conversation_badge_ring"
android:forceHasOverlappingRendering="false"
android:clipToPadding="false"
android:scaleType="center"
/>
</FrameLayout>
</FrameLayout>
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"

View File

@@ -126,7 +126,7 @@ public final class Prefs {
String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet"; String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount"; String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
/** Tracks whether the user has seen the onboarding screen for priority conversations */ /** Tracks whether the user has seen the onboarding screen for priority conversations */
String HAS_SEEN_PRIORITY_ONBOARDING = "HaveShownPriorityOnboarding"; String HAS_SEEN_PRIORITY_ONBOARDING = "HasUserSeenPriorityOnboarding";
} }
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {

View File

@@ -44,6 +44,7 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager; import android.content.pm.ShortcutManager;
import android.os.Handler; import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.service.notification.StatusBarNotification; import android.service.notification.StatusBarNotification;
import android.text.TextUtils; import android.text.TextUtils;
@@ -540,7 +541,9 @@ public class NotificationConversationInfo extends LinearLayout implements
.setView(onboardingView) .setView(onboardingView)
.setIgnoresDnd(ignoreDnd) .setIgnoresDnd(ignoreDnd)
.setShowsAsBubble(showAsBubble) .setShowsAsBubble(showAsBubble)
.setIcon(((ImageView) findViewById(R.id.conversation_icon)).getDrawable()) .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo))
.setBadge(mIconFactory.getAppBadge(
mPackageName, UserHandle.getUserId(mSbn.getUid())))
.setOnSettingsClick(mOnConversationSettingsClickListener) .setOnSettingsClick(mOnConversationSettingsClickListener)
.build(); .build();

View File

@@ -16,41 +16,57 @@
package com.android.systemui.statusbar.notification.row package com.android.systemui.statusbar.notification.row
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.BulletSpan import android.text.style.BulletSpan
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.Window import android.view.Window
import android.view.WindowInsets.Type.statusBars import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager import android.view.WindowManager
import android.view.animation.Interpolator
import android.view.animation.PathInterpolator
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN
import com.android.systemui.Prefs import com.android.systemui.Prefs
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.statusbar.notification.row.NotificationConversationInfo.OnConversationSettingsClickListener import com.android.systemui.statusbar.notification.row.NotificationConversationInfo.OnConversationSettingsClickListener
import javax.inject.Inject import javax.inject.Inject
/** /**
* Controller to handle presenting the priority conversations onboarding dialog * Controller to handle presenting the priority conversations onboarding dialog
*/ */
class PriorityOnboardingDialogController @Inject constructor( class PriorityOnboardingDialogController @Inject constructor(
val view: View, val view: View,
val context: Context, val context: Context,
val ignoresDnd: Boolean, private val ignoresDnd: Boolean,
val showsAsBubble: Boolean, private val showsAsBubble: Boolean,
val icon : Drawable, val icon : Drawable,
val onConversationSettingsClickListener : OnConversationSettingsClickListener private val onConversationSettingsClickListener : OnConversationSettingsClickListener,
val badge : Drawable
) { ) {
private lateinit var dialog: Dialog private lateinit var dialog: Dialog
private val OVERSHOOT: Interpolator = PathInterpolator(0.4f, 0f, 0.2f, 1.4f)
private val IMPORTANCE_ANIM_DELAY = 150L
private val IMPORTANCE_ANIM_GROW_DURATION = 250L
private val IMPORTANCE_ANIM_SHRINK_DURATION = 200L
private val IMPORTANCE_ANIM_SHRINK_DELAY = 25L
fun init() { fun init() {
initDialog() initDialog()
@@ -81,6 +97,7 @@ class PriorityOnboardingDialogController @Inject constructor(
private lateinit var icon: Drawable private lateinit var icon: Drawable
private lateinit var onConversationSettingsClickListener private lateinit var onConversationSettingsClickListener
: OnConversationSettingsClickListener : OnConversationSettingsClickListener
private lateinit var badge : Drawable
fun setView(v: View): Builder { fun setView(v: View): Builder {
view = v view = v
@@ -106,6 +123,10 @@ class PriorityOnboardingDialogController @Inject constructor(
icon = draw icon = draw
return this return this
} }
fun setBadge(badge : Drawable) : Builder {
this.badge = badge
return this
}
fun setOnSettingsClick(onClick : OnConversationSettingsClickListener) : Builder { fun setOnSettingsClick(onClick : OnConversationSettingsClickListener) : Builder {
onConversationSettingsClickListener = onClick onConversationSettingsClickListener = onClick
@@ -115,7 +136,7 @@ class PriorityOnboardingDialogController @Inject constructor(
fun build(): PriorityOnboardingDialogController { fun build(): PriorityOnboardingDialogController {
val controller = PriorityOnboardingDialogController( val controller = PriorityOnboardingDialogController(
view, context, ignoresDnd, showAsBubble, icon, view, context, ignoresDnd, showAsBubble, icon,
onConversationSettingsClickListener) onConversationSettingsClickListener, badge)
return controller return controller
} }
} }
@@ -143,6 +164,65 @@ class PriorityOnboardingDialogController @Inject constructor(
} }
findViewById<ImageView>(R.id.conversation_icon)?.setImageDrawable(icon) findViewById<ImageView>(R.id.conversation_icon)?.setImageDrawable(icon)
findViewById<ImageView>(R.id.icon)?.setImageDrawable(badge)
val mImportanceRingView = findViewById<ImageView>(R.id.conversation_icon_badge_ring)
val conversationIconBadgeBg = findViewById<ImageView>(R.id.conversation_icon_badge_bg)
val ring: GradientDrawable = mImportanceRingView.drawable as GradientDrawable
ring.mutate()
val bg = conversationIconBadgeBg.drawable as GradientDrawable
bg.mutate()
val ringColor = context.getResources()
.getColor(com.android.internal.R.color.conversation_important_highlight)
val standardThickness = context.resources.getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width)
val largeThickness = context.resources.getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_anim_max_stroke_width)
val standardSize = context.resources.getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_size)
val baseSize = standardSize - standardThickness * 2
val largeSize = baseSize + largeThickness * 2
val bgSize = context.resources.getDimensionPixelSize(
com.android.internal.R.dimen.conversation_icon_size_badged)
val animatorUpdateListener: ValueAnimator.AnimatorUpdateListener
= ValueAnimator.AnimatorUpdateListener { animation ->
val strokeWidth = animation.animatedValue as Int
ring.setStroke(strokeWidth, ringColor)
val newSize = baseSize + strokeWidth * 2
ring.setSize(newSize, newSize)
mImportanceRingView.invalidate()
}
val growAnimation: ValueAnimator = ValueAnimator.ofInt(0, largeThickness)
growAnimation.interpolator = LINEAR_OUT_SLOW_IN
growAnimation.duration = IMPORTANCE_ANIM_GROW_DURATION
growAnimation.addUpdateListener(animatorUpdateListener)
val shrinkAnimation: ValueAnimator
= ValueAnimator.ofInt(largeThickness, standardThickness)
shrinkAnimation.duration = IMPORTANCE_ANIM_SHRINK_DURATION
shrinkAnimation.startDelay = IMPORTANCE_ANIM_SHRINK_DELAY
shrinkAnimation.interpolator = OVERSHOOT
shrinkAnimation.addUpdateListener(animatorUpdateListener)
shrinkAnimation.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
// Shrink the badge bg so that it doesn't peek behind the animation
bg.setSize(baseSize, baseSize);
conversationIconBadgeBg.invalidate();
}
override fun onAnimationEnd(animation: Animator?) {
// Reset bg back to normal size
bg.setSize(bgSize, bgSize);
conversationIconBadgeBg.invalidate();
}
})
val anims = AnimatorSet()
anims.startDelay = IMPORTANCE_ANIM_DELAY
anims.playSequentially(growAnimation, shrinkAnimation)
val gapWidth = dialog.context.getResources().getDimensionPixelSize( val gapWidth = dialog.context.getResources().getDimensionPixelSize(
R.dimen.conversation_onboarding_bullet_gap_width) R.dimen.conversation_onboarding_bullet_gap_width)
@@ -180,6 +260,7 @@ class PriorityOnboardingDialogController @Inject constructor(
height = WRAP_CONTENT height = WRAP_CONTENT
} }
} }
anims.start()
} }
} }