import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutInfo;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
/**
+ * @hide
+ */
+ public static final String EXTRA_REDUCED_IMAGES = "android.reduced.images";
+
+ /**
* {@link #extras} key: the audio contents of this notification.
*
* This is for use when rendering the notification on an audio-focused interface;
buildUnstyled();
if (mStyle != null) {
+ mStyle.reduceImageSizes(mContext);
+ mStyle.purgeResources();
mStyle.buildStyled(mN);
}
+ mN.reduceImageSizes(mContext);
+
if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
&& (useExistingRemoteView())) {
if (mN.contentView == null) {
}
/**
+ * Reduces the image sizes to conform to a maximum allowed size. This also processes all custom
+ * remote views.
+ *
+ * @hide
+ */
+ void reduceImageSizes(Context context) {
+ if (extras.getBoolean(EXTRA_REDUCED_IMAGES)) {
+ return;
+ }
+ if (mLargeIcon != null || largeIcon != null) {
+ Resources resources = context.getResources();
+ Class<? extends Style> style = getNotificationStyle();
+ int maxWidth = resources.getDimensionPixelSize(R.dimen.notification_right_icon_size);
+ int maxHeight = maxWidth;
+ if (MediaStyle.class.equals(style)
+ || DecoratedMediaCustomViewStyle.class.equals(style)) {
+ maxHeight = resources.getDimensionPixelSize(
+ R.dimen.notification_media_image_max_height);
+ maxWidth = resources.getDimensionPixelSize(
+ R.dimen.notification_media_image_max_width);
+ }
+ if (mLargeIcon != null) {
+ mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight);
+ }
+ if (largeIcon != null) {
+ largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight);
+ }
+ }
+ reduceImageSizesForRemoteView(contentView, context);
+ reduceImageSizesForRemoteView(headsUpContentView, context);
+ reduceImageSizesForRemoteView(bigContentView, context);
+ extras.putBoolean(EXTRA_REDUCED_IMAGES, true);
+ }
+
+ private void reduceImageSizesForRemoteView(RemoteViews remoteView, Context context) {
+ if (remoteView != null) {
+ Resources resources = context.getResources();
+ int maxWidth = resources.getDimensionPixelSize(
+ R.dimen.notification_custom_view_max_image_width);
+ int maxHeight = resources.getDimensionPixelSize(
+ R.dimen.notification_custom_view_max_image_height);
+ remoteView.reduceImageSizes(maxWidth, maxHeight);
+ }
+ }
+
+ /**
* @return whether this notification is a foreground service notification
*/
private boolean isForegroundService() {
public boolean displayCustomViewInline() {
return false;
}
+
+ /**
+ * Reduces the image sizes contained in this style.
+ *
+ * @hide
+ */
+ public void reduceImageSizes(Context context) {
+ }
}
/**
/**
* @hide
*/
+ @Override
+ public void reduceImageSizes(Context context) {
+ super.reduceImageSizes(context);
+ Resources resources = context.getResources();
+ if (mPicture != null) {
+ int maxPictureWidth = resources.getDimensionPixelSize(
+ R.dimen.notification_big_picture_max_height);
+ int maxPictureHeight = resources.getDimensionPixelSize(
+ R.dimen.notification_big_picture_max_width);
+ mPicture = Icon.scaleDownIfNecessary(mPicture, maxPictureWidth, maxPictureHeight);
+ }
+ if (mBigLargeIcon != null) {
+ int rightIconSize = resources.getDimensionPixelSize(
+ R.dimen.notification_right_icon_size);
+ mBigLargeIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
+ }
+ }
+
+ /**
+ * @hide
+ */
public RemoteViews makeBigContentView() {
// Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
// This covers the following cases:
}
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+ notification.reduceImageSizes(mContext);
final Notification copy = Builder.maybeCloneStrippedForDelivery(notification);
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
}
/**
+ * Reduces all images and ensures that they are all below the given sizes.
+ *
+ * @param maxWidth the maximum width allowed
+ * @param maxHeight the maximum height allowed
+ *
+ * @hide
+ */
+ public void reduceImageSizes(int maxWidth, int maxHeight) {
+ ArrayList<Bitmap> cache = mBitmapCache.mBitmaps;
+ for (int i = 0; i < cache.size(); i++) {
+ Bitmap bitmap = cache.get(i);
+ cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
+ }
+ }
+
+ /**
* Handle with care!
*/
static class MutablePair<F, S> {
android:layout_height="wrap_content"
android:layout_gravity="top|end">
<ImageView android:id="@+id/right_icon"
- android:layout_width="40dp"
- android:layout_height="40dp"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
android:layout_gravity="top|end"
android:layout_marginTop="36dp"
android:layout_marginEnd="@dimen/notification_content_margin_end"
<dimen name="item_touch_helper_swipe_escape_velocity">120dp</dimen>
<dimen name="item_touch_helper_swipe_escape_max_velocity">800dp</dimen>
+ <!-- The maximum height of any image in a remote view. This is applied to all images in custom remoteviews. This value is determined by the maximum notification height -->
+ <dimen name="notification_custom_view_max_image_height">284dp</dimen>
+ <!-- The maximum height of any image in a remote view. This is applied to all images in custom remoteviews. This value is determined a maximum notification width -->
+ <dimen name="notification_custom_view_max_image_width">450dp</dimen>
+ <!-- The maximum height of a big picture in a notification. The images will be reduced to that height in case they are bigger. This value is determined by the maximum notification height -->
+ <dimen name="notification_big_picture_max_height">284dp</dimen>
+ <!-- The maximum width of a big picture in a notification. The images will be reduced to that width in case they are bigger. This value is determined by the standard panel size -->
+ <dimen name="notification_big_picture_max_width">416dp</dimen>
+ <!-- The maximum height of a image in a media notification. The images will be reduced to that height in case they are bigger. This value is determined by the expanded media template-->
+ <dimen name="notification_media_image_max_height">140dp</dimen>
+ <!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
+ <dimen name="notification_media_image_max_width">280dp</dimen>
+ <!-- The size of the right icon -->
+ <dimen name="notification_right_icon_size">40dp</dimen>
+
<!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
<dimen name="autofill_dataset_picker_max_size">90%</dimen>
<java-symbol type="style" name="AutofillDatasetPicker" />
<java-symbol type="dimen" name="autofill_dataset_picker_max_size"/>
+ <java-symbol type="dimen" name="notification_big_picture_max_height"/>
+ <java-symbol type="dimen" name="notification_big_picture_max_width"/>
+ <java-symbol type="dimen" name="notification_media_image_max_width"/>
+ <java-symbol type="dimen" name="notification_media_image_max_height"/>
+ <java-symbol type="dimen" name="notification_right_icon_size"/>
+ <java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
+ <java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
+
<!-- Accessibility fingerprint gestures -->
<java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
<java-symbol type="string" name="capability_desc_canCaptureFingerprintGestures" />
};
/**
+ * Scale down a bitmap to a given max width and max height. The scaling will be done in a uniform way
+ * @param bitmap the bitmap to scale down
+ * @param maxWidth the maximum width allowed
+ * @param maxHeight the maximum height allowed
+ *
+ * @return the scaled bitmap if necessary or the original bitmap if no scaling was needed
+ * @hide
+ */
+ public static Bitmap scaleDownIfNecessary(Bitmap bitmap, int maxWidth, int maxHeight) {
+ int bitmapWidth = bitmap.getWidth();
+ int bitmapHeight = bitmap.getHeight();
+ if (bitmapWidth > maxWidth || bitmapHeight > maxHeight) {
+ float scale = Math.min((float) maxWidth / bitmapWidth,
+ (float) maxHeight / bitmapHeight);
+ bitmap = Bitmap.createScaledBitmap(bitmap, (int) (scale * bitmapWidth),
+ (int) (scale * bitmapHeight), true /* filter */);
+ }
+ return bitmap;
+ }
+
+ /**
+ * Scale down this icon to a given max width and max height.
+ * The scaling will be done in a uniform way and currently only bitmaps are supported.
+ * @param maxWidth the maximum width allowed
+ * @param maxHeight the maximum height allowed
+ *
+ * @hide
+ */
+ public void scaleDownIfNecessary(int maxWidth, int maxHeight) {
+ if (mType != TYPE_BITMAP && mType != TYPE_ADAPTIVE_BITMAP) {
+ return;
+ }
+ Bitmap bitmap = getBitmap();
+ setBitmap(scaleDownIfNecessary(bitmap, maxWidth, maxHeight));
+ }
+
+ /**
* Implement this interface to receive a callback when
* {@link #loadDrawableAsync(Context, OnDrawableLoadedListener, Handler) loadDrawableAsync}
* is finished and your Drawable is ready.