Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4671a2d5 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Simplifying disabled widget view genration

Removing creating and holding icon bitmaps in systemServer and
directly loading resources in the Host process

Bug: 178616010
Test: Verified View on device
Change-Id: I4abf616985d68ac48f55856bb90fa598a4cebe1c
parent 4415e098
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.internal.widget;

import android.annotation.Nullable;
import android.content.Context;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RemoteViews;

/**
 * ImageView which applies a saturation image filter, used by AppWidgets to represent disabled icon
 */
@RemoteViews.RemoteView
public class DisableImageView extends ImageView {

    public DisableImageView(Context context) {
        this(context, null, 0, 0);
    }

    public DisableImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0, 0);
    }

    public DisableImageView(Context context, @Nullable AttributeSet attrs,
            int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public DisableImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        // Apply the disabled filter
        ColorMatrix brightnessMatrix = new ColorMatrix();
        float brightnessF = 0.5f;
        int brightnessI = (int) (255 * brightnessF);
        // Brightness: C-new = C-old*(1-amount) + amount
        float scale = 1f - brightnessF;
        float[] mat = brightnessMatrix.getArray();
        mat[0] = scale;
        mat[6] = scale;
        mat[12] = scale;
        mat[4] = brightnessI;
        mat[9] = brightnessI;
        mat[14] = brightnessI;

        ColorMatrix filterMatrix = new ColorMatrix();
        filterMatrix.setSaturation(0);
        filterMatrix.preConcat(brightnessMatrix);
        setColorFilter(new ColorMatrixColorFilter(filterMatrix));
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -22,7 +22,8 @@ Copyright (C) 2015 The Android Open Source Project
    android:importantForAccessibility="noHideDescendants"
    android:clickable="true">

    <ImageView android:id="@+id/work_widget_app_icon"
    <com.android.internal.widget.DisableImageView
        android:id="@+id/work_widget_app_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
+36 −76
Original line number Diff line number Diff line
@@ -55,7 +55,6 @@ import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -66,9 +65,8 @@ import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -119,7 +117,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
import com.android.server.policy.IconUtilities;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -253,8 +250,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    private boolean mSafeMode;
    private int mMaxWidgetBitmapMemory;

    private IconUtilities mIconUtilities;

    AppWidgetServiceImpl(Context context) {
        mContext = context;
    }
@@ -271,7 +266,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
        mBackupRestoreController = new BackupRestoreController();
        mSecurityPolicy = new SecurityPolicy();
        mIconUtilities = new IconUtilities(mContext);

        computeMaximumWidgetBitmapMemory();
        registerBroadcastReceiver();
@@ -578,44 +572,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        }
    }

    private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) {
        final long identity = Binder.clearCallingIdentity();
        try {
            // Load the unbadged application icon and pass it to the widget to appear on
            // the masked view.
            Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
                    UserHandle.of(providerUserId));
            PackageManager pm = userContext.getPackageManager();
            Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate();
            // Create a bitmap of the icon which is what the widget's remoteview requires.
            icon.setColorFilter(mIconUtilities.getDisabledColorFilter());
            return mIconUtilities.createIconBitmap(icon);
        } catch (NameNotFoundException e) {
            Slog.e(TAG, "Fail to get application icon", e);
            // Provider package removed, no need to mask its views as its state will be
            // purged very soon.
            return null;
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge,
            PendingIntent onClickIntent) {
        RemoteViews views = new RemoteViews(mContext.getPackageName(),
                R.layout.work_widget_mask_view);
        if (icon != null) {
            views.setImageViewBitmap(R.id.work_widget_app_icon, icon);
        }
        if (!showBadge) {
            views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
        }
        if (onClickIntent != null) {
            views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent);
        }
        return views;
    }

    /**
     * Mask the target widget belonging to the specified provider, or all active widgets
     * of the provider if target widget == null.
@@ -625,59 +581,63 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        if (widgetCount == 0) {
            return;
        }
        final String providerPackage = provider.id.componentName.getPackageName();
        final int providerUserId = provider.getUserId();
        Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId);
        if (iconBitmap == null) {
            return;
        }
        final boolean showBadge;
        final Intent onClickIntent;
        RemoteViews views = new RemoteViews(mContext.getPackageName(),
                R.layout.work_widget_mask_view);
        ApplicationInfo appInfo = provider.info.providerInfo.applicationInfo;
        final int appUserId = provider.getUserId();
        boolean showBadge;

        final long identity = Binder.clearCallingIdentity();
        try {
            final Intent onClickIntent;

            if (provider.maskedBySuspendedPackage) {
                showBadge = mUserManager.hasBadge(providerUserId);
                showBadge = mUserManager.hasBadge(appUserId);
                final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
                        providerPackage, providerUserId);
                        appInfo.packageName, appUserId);
                if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
                    onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
                            providerUserId, true);
                            appUserId, true);
                } else {
                    final SuspendDialogInfo dialogInfo =
                            mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
                                    suspendingPackage, providerUserId);
                            mPackageManagerInternal.getSuspendedDialogInfo(
                                    appInfo.packageName, suspendingPackage, appUserId);
                    // onUnsuspend is null because we don't want to start any activity on
                    // unsuspending from a suspended widget.
                    onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
                            providerPackage, suspendingPackage, dialogInfo, null, null,
                            providerUserId);
                            appInfo.packageName, suspendingPackage, dialogInfo, null, null,
                            appUserId);
                }
            } else if (provider.maskedByQuietProfile) {
                showBadge = true;
                onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
                        providerUserId);
                onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
            } else /* provider.maskedByLockedProfile */ {
                showBadge = true;
                onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
                        providerUserId);
                onClickIntent = mKeyguardManager
                        .createConfirmDeviceCredentialIntent(null, null, appUserId);
                if (onClickIntent != null) {
                    onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
                            | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                    onClickIntent.setFlags(
                            FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                }
            }

            if (onClickIntent != null) {
                views.setOnClickPendingIntent(R.id.work_widget_mask_frame,
                        PendingIntent.getActivity(mContext, 0, onClickIntent,
                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
            }

            Icon icon = appInfo.icon != 0
                    ? Icon.createWithResource(appInfo.packageName, appInfo.icon)
                    : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
            views.setImageViewIcon(R.id.work_widget_app_icon, icon);
            if (!showBadge) {
                views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
            }

            for (int j = 0; j < widgetCount; j++) {
                Widget widget = provider.widgets.get(j);
                if (targetWidget != null && targetWidget != widget) continue;
                PendingIntent intent = null;
                if (onClickIntent != null) {
                    // Rare informational activity click is okay being
                    // immutable; the tradeoff is more security in exchange for
                    // losing bounds-based window animations
                    intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
                            onClickIntent,
                            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
                }
                RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
                if (widget.replaceWithMaskedViewsLocked(views)) {
                    scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
                }
+0 −151
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.policy;

import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.TableMaskFilter;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.content.res.Resources;
import android.content.Context;

/**
 * Various utilities shared amongst the Launcher's classes.
 */
public final class IconUtilities {

    private int mIconWidth = -1;
    private int mIconHeight = -1;
    private int mIconTextureWidth = -1;
    private int mIconTextureHeight = -1;

    private final Rect mOldBounds = new Rect();
    private final Canvas mCanvas = new Canvas();
    private final DisplayMetrics mDisplayMetrics;

    private ColorFilter mDisabledColorFilter;

    public IconUtilities(Context context) {
        final Resources resources = context.getResources();
        DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
        final float density = metrics.density;
        final float blurPx = 5 * density;

        mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
        mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
                Paint.FILTER_BITMAP_FLAG));
    }

    /**
     * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
     * of two sized ARGB_8888 bitmap that can be used as a gl texture.
     */
    public Bitmap createIconBitmap(Drawable icon) {
        int width = mIconWidth;
        int height = mIconHeight;

        if (icon instanceof PaintDrawable) {
            PaintDrawable painter = (PaintDrawable) icon;
            painter.setIntrinsicWidth(width);
            painter.setIntrinsicHeight(height);
        } else if (icon instanceof BitmapDrawable) {
            // Ensure the bitmap has a density.
            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
                bitmapDrawable.setTargetDensity(mDisplayMetrics);
            }
        }
        int sourceWidth = icon.getIntrinsicWidth();
        int sourceHeight = icon.getIntrinsicHeight();

        if (sourceWidth > 0 && sourceHeight > 0) {
            // There are intrinsic sizes.
            if (width < sourceWidth || height < sourceHeight) {
                // It's too big, scale it down.
                final float ratio = (float) sourceWidth / sourceHeight;
                if (sourceWidth > sourceHeight) {
                    height = (int) (width / ratio);
                } else if (sourceHeight > sourceWidth) {
                    width = (int) (height * ratio);
                }
            } else if (sourceWidth < width && sourceHeight < height) {
                // It's small, use the size they gave us.
                width = sourceWidth;
                height = sourceHeight;
            }
        }

        // no intrinsic size --> use default size
        int textureWidth = mIconTextureWidth;
        int textureHeight = mIconTextureHeight;

        final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
                Bitmap.Config.ARGB_8888);
        final Canvas canvas = mCanvas;
        canvas.setBitmap(bitmap);

        final int left = (textureWidth-width) / 2;
        final int top = (textureHeight-height) / 2;

        mOldBounds.set(icon.getBounds());
        icon.setBounds(left, top, left+width, top+height);
        icon.draw(canvas);
        icon.setBounds(mOldBounds);

        return bitmap;
    }

    public ColorFilter getDisabledColorFilter() {
        if (mDisabledColorFilter != null) {
            return mDisabledColorFilter;
        }
        ColorMatrix brightnessMatrix = new ColorMatrix();
        float brightnessF = 0.5f;
        int brightnessI = (int) (255 * brightnessF);
        // Brightness: C-new = C-old*(1-amount) + amount
        float scale = 1f - brightnessF;
        float[] mat = brightnessMatrix.getArray();
        mat[0] = scale;
        mat[6] = scale;
        mat[12] = scale;
        mat[4] = brightnessI;
        mat[9] = brightnessI;
        mat[14] = brightnessI;

        ColorMatrix filterMatrix = new ColorMatrix();
        filterMatrix.setSaturation(0);
        filterMatrix.preConcat(brightnessMatrix);

        mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix);
        return mDisabledColorFilter;
    }
}
+4 −5
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ import android.net.Uri;
import android.os.Bundle;

import com.android.internal.R;
import com.android.server.policy.IconUtilities;
import com.android.internal.util.ImageUtils;

/** Displays an ongoing notification for a process displaying an alert window */
class AlertWindowNotification {
@@ -54,7 +54,6 @@ class AlertWindowNotification {
    private final NotificationManager mNotificationManager;
    private final String mPackageName;
    private boolean mPosted;
    private IconUtilities mIconUtilities;

    AlertWindowNotification(WindowManagerService service, String packageName) {
        mService = service;
@@ -63,7 +62,6 @@ class AlertWindowNotification {
                (NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
        mNotificationTag = CHANNEL_PREFIX + mPackageName;
        mRequestCode = sNextRequestCode++;
        mIconUtilities = new IconUtilities(mService.mContext);
    }

    void post() {
@@ -126,8 +124,9 @@ class AlertWindowNotification {

        if (aInfo != null) {
            final Drawable drawable = pm.getApplicationIcon(aInfo);
            if (drawable != null) {
                final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
            int size = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
            final Bitmap bitmap = ImageUtils.buildScaledBitmap(drawable, size, size);
            if (bitmap != null) {
                builder.setLargeIcon(bitmap);
            }
        }