Loading core/java/com/android/internal/widget/LocalImageResolver.java +36 −44 Original line number Original line Diff line number Diff line Loading @@ -16,69 +16,61 @@ package com.android.internal.widget; package com.android.internal.widget; import android.annotation.Nullable; import android.content.Context; import android.content.Context; import android.graphics.Bitmap; import android.graphics.ImageDecoder; import android.graphics.BitmapFactory; import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.Uri; import android.util.Log; import android.util.Size; import java.io.IOException; import java.io.IOException; import java.io.InputStream; /** /** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */ * A class to extract Bitmaps from a MessagingStyle message. */ public class LocalImageResolver { public class LocalImageResolver { private static final String TAG = LocalImageResolver.class.getSimpleName(); private static final String TAG = LocalImageResolver.class.getSimpleName(); private static final int MAX_SAFE_ICON_SIZE_PX = 480; private static final int MAX_SAFE_ICON_SIZE_PX = 480; @Nullable public static Drawable resolveImage(Uri uri, Context context) throws IOException { public static Drawable resolveImage(Uri uri, Context context) throws IOException { BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context); final ImageDecoder.Source source = if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { ImageDecoder.createSource(context.getContentResolver(), uri); return null; final Drawable drawable = } ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded); return drawable; int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth; double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) ? (originalSize / MAX_SAFE_ICON_SIZE_PX) : 1.0; BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); InputStream input = context.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); input.close(); return new BitmapDrawable(context.getResources(), bitmap); } } private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context) public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight) throws IOException { throws IOException { BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); final ImageDecoder.Source source = try (InputStream input = context.getContentResolver().openInputStream(uri)) { ImageDecoder.createSource(context.getContentResolver(), uri); if (input == null) { return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> { throw new IllegalArgumentException(); final Size size = info.getSize(); if (size.getWidth() > size.getHeight()) { if (size.getWidth() > maxWidth) { final int targetHeight = size.getHeight() * maxWidth / size.getWidth(); decoder.setTargetSize(maxWidth, targetHeight); } } else { if (size.getHeight() > maxHeight) { final int targetWidth = size.getWidth() * maxHeight / size.getHeight(); decoder.setTargetSize(targetWidth, maxHeight); } } onlyBoundsOptions.inJustDecodeBounds = true; BitmapFactory.decodeStream(input, null, onlyBoundsOptions); } catch (IllegalArgumentException iae) { onlyBoundsOptions.outWidth = -1; onlyBoundsOptions.outHeight = -1; Log.e(TAG, "error loading image", iae); } } return onlyBoundsOptions; }); } } private static int getPowerOfTwoForSampleRatio(double ratio) { private static int getPowerOfTwoForSampleRatio(double ratio) { int k = Integer.highestOneBit((int) Math.floor(ratio)); final int k = Integer.highestOneBit((int) Math.floor(ratio)); return Math.max(1, k); return Math.max(1, k); } } private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, ImageDecoder.Source source) { final Size size = info.getSize(); final int originalSize = Math.max(size.getHeight(), size.getWidth()); final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX : 1.0; decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio)); } } } packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +72 −0 Original line number Original line Diff line number Diff line Loading @@ -19,18 +19,25 @@ package com.android.systemui.statusbar.notification import android.app.Notification import android.app.Notification import android.content.Context import android.content.Context import android.content.pm.LauncherApps import android.content.pm.LauncherApps import android.graphics.drawable.AnimatedImageDrawable import android.os.Handler import android.os.Handler import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.NotificationListenerService.RankingMap import com.android.internal.statusbar.NotificationVisibility import com.android.internal.statusbar.NotificationVisibility import com.android.internal.widget.ConversationLayout import com.android.internal.widget.ConversationLayout import com.android.internal.widget.MessagingImageMessage import com.android.internal.widget.MessagingLayout import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.children import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Inject Loading @@ -57,6 +64,71 @@ class ConversationNotificationProcessor @Inject constructor( } } } } /** * Tracks state related to animated images inside of notifications. Ex: starting and stopping * animations to conserve CPU and memory. */ @SysUISingleton class AnimatedImageNotificationManager @Inject constructor( private val notificationEntryManager: NotificationEntryManager, private val headsUpManager: HeadsUpManager, private val statusBarStateController: StatusBarStateController ) { private var isStatusBarExpanded = false /** Begins listening to state changes and updating animations accordingly. */ fun bind() { headsUpManager.addListener(object : OnHeadsUpChangedListener { override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { entry.row?.let { row -> updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded) } } }) statusBarStateController.addCallback(object : StatusBarStateController.StateListener { override fun onExpandedChanged(isExpanded: Boolean) { isStatusBarExpanded = isExpanded notificationEntryManager.activeNotificationsForCurrentUser.forEach { entry -> entry.row?.let { row -> updateAnimatedImageDrawables(row, animating = isExpanded || row.isHeadsUp) } } } }) notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener { override fun onEntryInflated(entry: NotificationEntry) { entry.row?.let { row -> updateAnimatedImageDrawables( row, animating = isStatusBarExpanded || row.isHeadsUp) } } override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry) }) } private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) = (row.layouts?.asSequence() ?: emptySequence()) .flatMap { layout -> layout.allViews.asSequence() } .flatMap { view -> (view as? ConversationLayout)?.messagingGroups?.asSequence() ?: (view as? MessagingLayout)?.messagingGroups?.asSequence() ?: emptySequence() } .flatMap { messagingGroup -> messagingGroup.messageContainer.children } .mapNotNull { view -> (view as? MessagingImageMessage) ?.let { imageMessage -> imageMessage.drawable as? AnimatedImageDrawable } } .forEach { animatedImageDrawable -> if (animating) animatedImageDrawable.start() else animatedImageDrawable.stop() } } /** /** * Tracks state related to conversation notifications, and updates the UI of existing notifications * Tracks state related to conversation notifications, and updates the UI of existing notifications * when necessary. * when necessary. Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +4 −1 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.NotificationEntryManager Loading Loading @@ -71,7 +72,8 @@ class NotificationsControllerImpl @Inject constructor( private val headsUpManager: HeadsUpManager, private val headsUpManager: HeadsUpManager, private val headsUpController: HeadsUpController, private val headsUpController: HeadsUpController, private val headsUpViewBinder: HeadsUpViewBinder, private val headsUpViewBinder: HeadsUpViewBinder, private val clickerBuilder: NotificationClicker.Builder private val clickerBuilder: NotificationClicker.Builder, private val animatedImageNotificationManager: AnimatedImageNotificationManager ) : NotificationsController { ) : NotificationsController { override fun initialize( override fun initialize( Loading Loading @@ -100,6 +102,7 @@ class NotificationsControllerImpl @Inject constructor( bindRowCallback) bindRowCallback) headsUpViewBinder.setPresenter(presenter) headsUpViewBinder.setPresenter(presenter) notifBindPipelineInitializer.initialize() notifBindPipelineInitializer.initialize() animatedImageNotificationManager.bind() if (featureFlags.isNewNotifPipelineEnabled) { if (featureFlags.isNewNotifPipelineEnabled) { newNotifPipeline.get().initialize( newNotifPipeline.get().initialize( Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +2 −16 Original line number Original line Diff line number Diff line Loading @@ -19,10 +19,7 @@ package com.android.systemui.statusbar.notification.row; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.Notification; import android.app.Notification; import android.content.Context; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.net.Uri; import android.os.Bundle; import android.os.Bundle; import android.os.Parcelable; import android.os.Parcelable; Loading Loading @@ -81,7 +78,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @return True if has its internal cache, false otherwise. * @return True if has its internal cache, false otherwise. */ */ public boolean hasCache() { public boolean hasCache() { return mImageCache != null && !ActivityManager.isLowRamDeviceStatic(); return mImageCache != null && !isLowRam(); } } private boolean isLowRam() { private boolean isLowRam() { Loading Loading @@ -110,11 +107,6 @@ public class NotificationInlineImageResolver implements ImageResolver { : R.dimen.notification_custom_view_max_image_height); : R.dimen.notification_custom_view_max_image_height); } } @VisibleForTesting protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException { return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext); } /** /** * To resolve image from specified uri directly. If the resulting image is larger than the * To resolve image from specified uri directly. If the resulting image is larger than the * maximum allowed size, scale it down. * maximum allowed size, scale it down. Loading @@ -123,13 +115,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @throws IOException Throws if failed at resolving the image. * @throws IOException Throws if failed at resolving the image. */ */ Drawable resolveImage(Uri uri) throws IOException { Drawable resolveImage(Uri uri) throws IOException { BitmapDrawable image = resolveImageInternal(uri); return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); if (image == null || image.getBitmap() == null) { throw new IOException("resolveImageInternal returned null for uri: " + uri); } Bitmap bitmap = image.getBitmap(); image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight)); return image; } } @Override @Override Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java +0 −28 Original line number Original line Diff line number Diff line Loading @@ -69,32 +69,4 @@ public class NotificationInlineImageResolverTest extends SysuiTestCase { assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20); assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20); assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15); assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15); } } @Test public void resolveImage_sizeTooBig() throws IOException { doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); mResolver.mMaxImageHeight = 5; mResolver.mMaxImageWidth = 5; // original bitmap size is 10x10 BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); Bitmap resolvedBitmap = resolved.getBitmap(); assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth()); assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight()); assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap); } @Test public void resolveImage_sizeOK() throws IOException { doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); mResolver.mMaxImageWidth = 15; mResolver.mMaxImageHeight = 15; // original bitmap size is 10x10 BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); Bitmap resolvedBitmap = resolved.getBitmap(); assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth()); assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight()); assertSame("Bitmap not replaced", resolvedBitmap, mBitmap); } } } Loading
core/java/com/android/internal/widget/LocalImageResolver.java +36 −44 Original line number Original line Diff line number Diff line Loading @@ -16,69 +16,61 @@ package com.android.internal.widget; package com.android.internal.widget; import android.annotation.Nullable; import android.content.Context; import android.content.Context; import android.graphics.Bitmap; import android.graphics.ImageDecoder; import android.graphics.BitmapFactory; import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.Uri; import android.util.Log; import android.util.Size; import java.io.IOException; import java.io.IOException; import java.io.InputStream; /** /** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */ * A class to extract Bitmaps from a MessagingStyle message. */ public class LocalImageResolver { public class LocalImageResolver { private static final String TAG = LocalImageResolver.class.getSimpleName(); private static final String TAG = LocalImageResolver.class.getSimpleName(); private static final int MAX_SAFE_ICON_SIZE_PX = 480; private static final int MAX_SAFE_ICON_SIZE_PX = 480; @Nullable public static Drawable resolveImage(Uri uri, Context context) throws IOException { public static Drawable resolveImage(Uri uri, Context context) throws IOException { BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context); final ImageDecoder.Source source = if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { ImageDecoder.createSource(context.getContentResolver(), uri); return null; final Drawable drawable = } ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded); return drawable; int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth; double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) ? (originalSize / MAX_SAFE_ICON_SIZE_PX) : 1.0; BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); InputStream input = context.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); input.close(); return new BitmapDrawable(context.getResources(), bitmap); } } private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context) public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight) throws IOException { throws IOException { BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); final ImageDecoder.Source source = try (InputStream input = context.getContentResolver().openInputStream(uri)) { ImageDecoder.createSource(context.getContentResolver(), uri); if (input == null) { return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> { throw new IllegalArgumentException(); final Size size = info.getSize(); if (size.getWidth() > size.getHeight()) { if (size.getWidth() > maxWidth) { final int targetHeight = size.getHeight() * maxWidth / size.getWidth(); decoder.setTargetSize(maxWidth, targetHeight); } } else { if (size.getHeight() > maxHeight) { final int targetWidth = size.getWidth() * maxHeight / size.getHeight(); decoder.setTargetSize(targetWidth, maxHeight); } } onlyBoundsOptions.inJustDecodeBounds = true; BitmapFactory.decodeStream(input, null, onlyBoundsOptions); } catch (IllegalArgumentException iae) { onlyBoundsOptions.outWidth = -1; onlyBoundsOptions.outHeight = -1; Log.e(TAG, "error loading image", iae); } } return onlyBoundsOptions; }); } } private static int getPowerOfTwoForSampleRatio(double ratio) { private static int getPowerOfTwoForSampleRatio(double ratio) { int k = Integer.highestOneBit((int) Math.floor(ratio)); final int k = Integer.highestOneBit((int) Math.floor(ratio)); return Math.max(1, k); return Math.max(1, k); } } private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, ImageDecoder.Source source) { final Size size = info.getSize(); final int originalSize = Math.max(size.getHeight(), size.getWidth()); final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX : 1.0; decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio)); } } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +72 −0 Original line number Original line Diff line number Diff line Loading @@ -19,18 +19,25 @@ package com.android.systemui.statusbar.notification import android.app.Notification import android.app.Notification import android.content.Context import android.content.Context import android.content.pm.LauncherApps import android.content.pm.LauncherApps import android.graphics.drawable.AnimatedImageDrawable import android.os.Handler import android.os.Handler import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.NotificationListenerService.RankingMap import com.android.internal.statusbar.NotificationVisibility import com.android.internal.statusbar.NotificationVisibility import com.android.internal.widget.ConversationLayout import com.android.internal.widget.ConversationLayout import com.android.internal.widget.MessagingImageMessage import com.android.internal.widget.MessagingLayout import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.children import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Inject Loading @@ -57,6 +64,71 @@ class ConversationNotificationProcessor @Inject constructor( } } } } /** * Tracks state related to animated images inside of notifications. Ex: starting and stopping * animations to conserve CPU and memory. */ @SysUISingleton class AnimatedImageNotificationManager @Inject constructor( private val notificationEntryManager: NotificationEntryManager, private val headsUpManager: HeadsUpManager, private val statusBarStateController: StatusBarStateController ) { private var isStatusBarExpanded = false /** Begins listening to state changes and updating animations accordingly. */ fun bind() { headsUpManager.addListener(object : OnHeadsUpChangedListener { override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { entry.row?.let { row -> updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded) } } }) statusBarStateController.addCallback(object : StatusBarStateController.StateListener { override fun onExpandedChanged(isExpanded: Boolean) { isStatusBarExpanded = isExpanded notificationEntryManager.activeNotificationsForCurrentUser.forEach { entry -> entry.row?.let { row -> updateAnimatedImageDrawables(row, animating = isExpanded || row.isHeadsUp) } } } }) notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener { override fun onEntryInflated(entry: NotificationEntry) { entry.row?.let { row -> updateAnimatedImageDrawables( row, animating = isStatusBarExpanded || row.isHeadsUp) } } override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry) }) } private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) = (row.layouts?.asSequence() ?: emptySequence()) .flatMap { layout -> layout.allViews.asSequence() } .flatMap { view -> (view as? ConversationLayout)?.messagingGroups?.asSequence() ?: (view as? MessagingLayout)?.messagingGroups?.asSequence() ?: emptySequence() } .flatMap { messagingGroup -> messagingGroup.messageContainer.children } .mapNotNull { view -> (view as? MessagingImageMessage) ?.let { imageMessage -> imageMessage.drawable as? AnimatedImageDrawable } } .forEach { animatedImageDrawable -> if (animating) animatedImageDrawable.start() else animatedImageDrawable.stop() } } /** /** * Tracks state related to conversation notifications, and updates the UI of existing notifications * Tracks state related to conversation notifications, and updates the UI of existing notifications * when necessary. * when necessary. Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +4 −1 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.NotificationEntryManager Loading Loading @@ -71,7 +72,8 @@ class NotificationsControllerImpl @Inject constructor( private val headsUpManager: HeadsUpManager, private val headsUpManager: HeadsUpManager, private val headsUpController: HeadsUpController, private val headsUpController: HeadsUpController, private val headsUpViewBinder: HeadsUpViewBinder, private val headsUpViewBinder: HeadsUpViewBinder, private val clickerBuilder: NotificationClicker.Builder private val clickerBuilder: NotificationClicker.Builder, private val animatedImageNotificationManager: AnimatedImageNotificationManager ) : NotificationsController { ) : NotificationsController { override fun initialize( override fun initialize( Loading Loading @@ -100,6 +102,7 @@ class NotificationsControllerImpl @Inject constructor( bindRowCallback) bindRowCallback) headsUpViewBinder.setPresenter(presenter) headsUpViewBinder.setPresenter(presenter) notifBindPipelineInitializer.initialize() notifBindPipelineInitializer.initialize() animatedImageNotificationManager.bind() if (featureFlags.isNewNotifPipelineEnabled) { if (featureFlags.isNewNotifPipelineEnabled) { newNotifPipeline.get().initialize( newNotifPipeline.get().initialize( Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +2 −16 Original line number Original line Diff line number Diff line Loading @@ -19,10 +19,7 @@ package com.android.systemui.statusbar.notification.row; import android.app.ActivityManager; import android.app.ActivityManager; import android.app.Notification; import android.app.Notification; import android.content.Context; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.net.Uri; import android.os.Bundle; import android.os.Bundle; import android.os.Parcelable; import android.os.Parcelable; Loading Loading @@ -81,7 +78,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @return True if has its internal cache, false otherwise. * @return True if has its internal cache, false otherwise. */ */ public boolean hasCache() { public boolean hasCache() { return mImageCache != null && !ActivityManager.isLowRamDeviceStatic(); return mImageCache != null && !isLowRam(); } } private boolean isLowRam() { private boolean isLowRam() { Loading Loading @@ -110,11 +107,6 @@ public class NotificationInlineImageResolver implements ImageResolver { : R.dimen.notification_custom_view_max_image_height); : R.dimen.notification_custom_view_max_image_height); } } @VisibleForTesting protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException { return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext); } /** /** * To resolve image from specified uri directly. If the resulting image is larger than the * To resolve image from specified uri directly. If the resulting image is larger than the * maximum allowed size, scale it down. * maximum allowed size, scale it down. Loading @@ -123,13 +115,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @throws IOException Throws if failed at resolving the image. * @throws IOException Throws if failed at resolving the image. */ */ Drawable resolveImage(Uri uri) throws IOException { Drawable resolveImage(Uri uri) throws IOException { BitmapDrawable image = resolveImageInternal(uri); return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); if (image == null || image.getBitmap() == null) { throw new IOException("resolveImageInternal returned null for uri: " + uri); } Bitmap bitmap = image.getBitmap(); image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight)); return image; } } @Override @Override Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java +0 −28 Original line number Original line Diff line number Diff line Loading @@ -69,32 +69,4 @@ public class NotificationInlineImageResolverTest extends SysuiTestCase { assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20); assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20); assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15); assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15); } } @Test public void resolveImage_sizeTooBig() throws IOException { doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); mResolver.mMaxImageHeight = 5; mResolver.mMaxImageWidth = 5; // original bitmap size is 10x10 BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); Bitmap resolvedBitmap = resolved.getBitmap(); assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth()); assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight()); assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap); } @Test public void resolveImage_sizeOK() throws IOException { doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); mResolver.mMaxImageWidth = 15; mResolver.mMaxImageHeight = 15; // original bitmap size is 10x10 BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); Bitmap resolvedBitmap = resolved.getBitmap(); assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth()); assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight()); assertSame("Bitmap not replaced", resolvedBitmap, mBitmap); } } }