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

Commit cdb51ebb authored by Sunny Goyal's avatar Sunny Goyal Committed by android-build-merger
Browse files

Merge "Creating a common class for loading drawables and handling various badging" into oc-dev

am: 15b48ff4

Change-Id: Ifce032deebbe1dd66d4e80dd211030765daab4a0
parents ded76ed8 15b48ff4
Loading
Loading
Loading
Loading
+6 −24
Original line number Diff line number Diff line
@@ -79,6 +79,8 @@ import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.IconDrawableFactory;
import android.util.LauncherIcons;
import android.util.Log;
import android.view.Display;

@@ -1245,16 +1247,9 @@ public class ApplicationPackageManager extends PackageManager {
        if (!isManagedProfile(user.getIdentifier())) {
            return icon;
        }
        Drawable badgeShadow = getDrawable("system",
                com.android.internal.R.drawable.ic_corp_icon_badge_shadow, null);
        Drawable badgeColor = getDrawable("system",
                com.android.internal.R.drawable.ic_corp_icon_badge_color, null);
        badgeColor.setTint(getUserBadgeColor(user));
        Drawable badgeForeground = getDrawable("system",
                com.android.internal.R.drawable.ic_corp_icon_badge_case, null);

        Drawable badge = new LayerDrawable(
                new Drawable[] {badgeShadow, badgeColor, badgeForeground });
        Drawable badge = new LauncherIcons(mContext).getBadgeDrawable(
                com.android.internal.R.drawable.ic_corp_icon_badge_case,
                getUserBadgeColor(user));
        return getBadgedDrawable(icon, badge, null, true);
    }

@@ -1268,14 +1263,6 @@ public class ApplicationPackageManager extends PackageManager {
        return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true);
    }

    // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
    @VisibleForTesting
    public static final int[] CORP_BADGE_COLORS = new int[] {
        com.android.internal.R.color.profile_badge_1,
        com.android.internal.R.color.profile_badge_2,
        com.android.internal.R.color.profile_badge_3
    };

    @VisibleForTesting
    public static final int[] CORP_BADGE_LABEL_RES_ID = new int[] {
        com.android.internal.R.string.managed_profile_label_badge,
@@ -1284,12 +1271,7 @@ public class ApplicationPackageManager extends PackageManager {
    };

    private int getUserBadgeColor(UserHandle user) {
        int badge = getUserManager().getManagedProfileBadge(user.getIdentifier());
        if (badge < 0) {
            badge = 0;
        }
        int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
        return Resources.getSystem().getColor(resourceId, null);
        return IconDrawableFactory.getUserBadgeColor(getUserManager(), user.getIdentifier());
    }

    @Override
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 android.util;

import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Utility class to load app drawables with appropriate badging.
 *
 * @hide
 */
public class IconDrawableFactory {

    protected final Context mContext;
    protected final PackageManager mPm;
    protected final UserManager mUm;
    protected final LauncherIcons mLauncherIcons;
    protected final boolean mEmbedShadow;

    private IconDrawableFactory(Context context, boolean embedShadow) {
        mContext = context;
        mPm = context.getPackageManager();
        mUm = context.getSystemService(UserManager.class);
        mLauncherIcons = new LauncherIcons(context);
        mEmbedShadow = embedShadow;
    }

    protected boolean needsBadging(ApplicationInfo appInfo, @UserIdInt int userId) {
        return appInfo.isInstantApp() || mUm.isManagedProfile(userId);
    }

    public Drawable getBadgedIcon(ApplicationInfo appInfo) {
        return getBadgedIcon(appInfo, UserHandle.getUserId(appInfo.uid));
    }

    public Drawable getBadgedIcon(ApplicationInfo appInfo, @UserIdInt int userId) {
        return getBadgedIcon(appInfo, appInfo, userId);
    }

    public Drawable getBadgedIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo,
            @UserIdInt int userId) {
        Drawable icon = mPm.loadUnbadgedItemIcon(itemInfo, appInfo);
        if (!mEmbedShadow && !needsBadging(appInfo, userId)) {
            return icon;
        }

        // Before badging, add shadow to adaptive icon if needed.
        icon = mLauncherIcons.wrapIconDrawableWithShadow(icon);
        if (appInfo.isInstantApp()) {
            int badgeColor = Resources.getSystem().getColor(
                    com.android.internal.R.color.instant_app_badge, null);
            icon = mLauncherIcons.getBadgedDrawable(icon,
                    com.android.internal.R.drawable.ic_instant_icon_badge_bolt,
                    badgeColor);
        }
        if (mUm.isManagedProfile(userId)) {
            icon = mLauncherIcons.getBadgedDrawable(icon,
                    com.android.internal.R.drawable.ic_corp_icon_badge_case,
                    getUserBadgeColor(mUm, userId));
        }
        return icon;
    }

    // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
    @VisibleForTesting
    public static final int[] CORP_BADGE_COLORS = new int[] {
            com.android.internal.R.color.profile_badge_1,
            com.android.internal.R.color.profile_badge_2,
            com.android.internal.R.color.profile_badge_3
    };

    public static int getUserBadgeColor(UserManager um, @UserIdInt int userId) {
        int badge = um.getManagedProfileBadge(userId);
        if (badge < 0) {
            badge = 0;
        }
        int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
        return Resources.getSystem().getColor(resourceId, null);
    }

    public static IconDrawableFactory newInstance(Context context) {
        return new IconDrawableFactory(context, true);
    }

    public static IconDrawableFactory newInstance(Context context, boolean embedShadow) {
        return new IconDrawableFactory(context, embedShadow);
    }
}
+129 −54
Original line number Diff line number Diff line
@@ -21,10 +21,11 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableWrapper;
import android.graphics.drawable.LayerDrawable;

/**
 * Utility class to handle icon treatments (e.g., shadow generation) for the Launcher icons.
@@ -32,78 +33,152 @@ import android.graphics.drawable.Drawable;
 */
public final class LauncherIcons {

    private final Paint mPaint = new Paint();
    private final Canvas mCanvas = new Canvas();
    // Percent of actual icon size
    private static final float ICON_SIZE_BLUR_FACTOR = 0.5f/48;
    // Percent of actual icon size
    private static final float ICON_SIZE_KEY_SHADOW_DELTA_FACTOR = 1f/48;

    private static final int KEY_SHADOW_ALPHA = 61;
    private static final int AMBIENT_SHADOW_ALPHA = 30;
    private static final float BLUR_FACTOR = 0.5f / 48;
    private int mShadowInset;
    private Bitmap mShadowBitmap;
    private int mIconSize;
    private Resources mRes;

    private final SparseArray<Bitmap> mShadowCache = new SparseArray<>();
    private final int mIconSize;
    private final Resources mRes;

    public LauncherIcons(Context context) {
        mRes = context.getResources();
        DisplayMetrics metrics = mRes.getDisplayMetrics();
        mShadowInset = (int)(2 * metrics.density);
        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
            Paint.FILTER_BITMAP_FLAG));
        mIconSize = (int) mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
    }

    /**
     * Draw the drawable into a bitmap.
     */
    public Bitmap createIconBitmap(Drawable icon) {
        final Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
        mPaint.setAlpha(255);
        mCanvas.setBitmap(bitmap);
        int iconInset = 0;
        if (mShadowBitmap != null) {
            mCanvas.drawBitmap(mShadowBitmap, 0, 0, mPaint);
            iconInset = mShadowInset;
        }

        icon.setBounds(iconInset, iconInset, mIconSize - iconInset,
            mIconSize - iconInset);
        icon.draw(mCanvas);
        mCanvas.setBitmap(null);
        return bitmap;
        mIconSize = mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
    }

    public Drawable wrapIconDrawableWithShadow(Drawable drawable) {
        if (!(drawable instanceof AdaptiveIconDrawable)) {
            return drawable;
        }
        AdaptiveIconDrawable d =
            (AdaptiveIconDrawable) drawable.getConstantState().newDrawable().mutate();
        getShadowBitmap(d);
        Bitmap iconbitmap = createIconBitmap(d);
        return new BitmapDrawable(mRes, iconbitmap);
        Bitmap shadow = getShadowBitmap((AdaptiveIconDrawable) drawable);
        return new ShadowDrawable(shadow, drawable);
    }

    private Bitmap getShadowBitmap(AdaptiveIconDrawable d) {
        if (mShadowBitmap != null) {
            return mShadowBitmap;
        int shadowSize = Math.max(mIconSize, d.getIntrinsicHeight());
        synchronized (mShadowCache) {
            Bitmap shadow = mShadowCache.get(shadowSize);
            if (shadow != null) {
                return shadow;
            }
        }

        mShadowBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ALPHA_8);
        mCanvas.setBitmap(mShadowBitmap);
        d.setBounds(0, 0, shadowSize, shadowSize);

        // Draw key shadow
        mPaint.setColor(Color.TRANSPARENT);
        float blur = BLUR_FACTOR * mIconSize;
        mPaint.setShadowLayer(blur, 0, mShadowInset, KEY_SHADOW_ALPHA << 24);
        d.setBounds(mShadowInset, mShadowInset, mIconSize - mShadowInset, mIconSize - mShadowInset);
        mCanvas.drawPath(d.getIconMask(), mPaint);
        float blur = ICON_SIZE_BLUR_FACTOR * shadowSize;
        float keyShadowDistance = ICON_SIZE_KEY_SHADOW_DELTA_FACTOR * shadowSize;

        int bitmapSize = (int) (shadowSize + 2 * blur + keyShadowDistance);
        Bitmap shadow = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(shadow);
        canvas.translate(blur + keyShadowDistance / 2, blur);

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.TRANSPARENT);

        // Draw ambient shadow
        mPaint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
        d.setBounds(mShadowInset, 2 * mShadowInset, mIconSize - mShadowInset, mIconSize);
        mCanvas.drawPath(d.getIconMask(), mPaint);
        mPaint.clearShadowLayer();
        paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
        canvas.drawPath(d.getIconMask(), paint);

        // Draw key shadow
        canvas.translate(0, keyShadowDistance);
        paint.setShadowLayer(blur, 0, 0, KEY_SHADOW_ALPHA << 24);
        canvas.drawPath(d.getIconMask(), paint);

        canvas.setBitmap(null);
        synchronized (mShadowCache) {
            mShadowCache.put(shadowSize, shadow);
        }
        return shadow;
    }

    public Drawable getBadgeDrawable(int foregroundRes, int backgroundColor) {
        return getBadgedDrawable(null, foregroundRes, backgroundColor);
    }

    public Drawable getBadgedDrawable(Drawable base, int foregroundRes, int backgroundColor) {
        Resources sysRes = Resources.getSystem();

        Drawable badgeShadow = sysRes.getDrawable(
                com.android.internal.R.drawable.ic_corp_icon_badge_shadow);

        Drawable badgeColor = sysRes.getDrawable(
                com.android.internal.R.drawable.ic_corp_icon_badge_color)
                .getConstantState().newDrawable().mutate();
        badgeColor.setTint(backgroundColor);

        Drawable badgeForeground = sysRes.getDrawable(foregroundRes);

        return mShadowBitmap;
        Drawable[] drawables = base == null
                ? new Drawable[] {badgeShadow, badgeColor, badgeForeground }
                : new Drawable[] {base, badgeShadow, badgeColor, badgeForeground };
        return new LayerDrawable(drawables);
    }

    /**
     * A drawable which draws a shadow bitmap behind a drawable
     */
    private static class ShadowDrawable extends DrawableWrapper {

        final MyConstantState mState;

        public ShadowDrawable(Bitmap shadow, Drawable dr) {
            super(dr);
            mState = new MyConstantState(shadow, dr.getConstantState());
        }

        ShadowDrawable(MyConstantState state) {
            super(state.mChildState.newDrawable());
            mState = state;
        }

        @Override
        public ConstantState getConstantState() {
            return mState;
        }

        @Override
        public void draw(Canvas canvas) {
            Rect bounds = getBounds();
            canvas.drawBitmap(mState.mShadow, null, bounds, mState.mPaint);
            canvas.save();
            // Ratio of child drawable size to shadow bitmap size
            float factor = 1 / (1 + 2 * ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR);

            canvas.translate(
                    bounds.width() * factor *
                            (ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR / 2),
                    bounds.height() * factor * ICON_SIZE_BLUR_FACTOR);
            canvas.scale(factor, factor);
            super.draw(canvas);
            canvas.restore();
        }

        private static class MyConstantState extends ConstantState {

            final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
            final Bitmap mShadow;
            final ConstantState mChildState;

            MyConstantState(Bitmap shadow, ConstantState childState) {
                mShadow = shadow;
                mChildState = childState;
            }

            @Override
            public Drawable newDrawable() {
                return new ShadowDrawable(this);
            }

            @Override
            public int getChangingConfigurations() {
                return mChildState.getChangingConfigurations();
            }
        }
    }
}
+29 −0
Original line number Diff line number Diff line
<!--
Copyright (C) 2014 The Android Open Source Project
Copyright (C) 2017 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.
@@ -14,20 +14,16 @@ Copyright (C) 2014 The Android Open Source Project
    limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="20.0dp"
        android:height="20.0dp"
        android:viewportWidth="20.0"
        android:viewportHeight="20.0">
    <path
        android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0"
        android:fillColor="#FF5722"/>
    <path
        android:pathData="M15.2,6.2L4.8,6.2c-0.5,0.0 -0.9,0.4 -0.9,1.0L3.9,10.0c0.0,0.5 0.4,1.0 0.9,1.0l3.8,0.0l0.0,-1.0l2.9,0.0l0.0,1.0l3.8,0.0c0.5,0.0 1.0,-0.4 1.0,-1.0L16.3,7.1C16.2,6.6 15.8,6.2 15.2,6.2z"
        android:fillColor="#FFFFFF"/>
    <path
        android:pathData="M8.6,12.9l0.0,-1.0L4.3,11.9l0.0,2.4c0.0,0.5 0.4,0.9 0.9,0.9l9.5,0.0c0.5,0.0 0.9,-0.4 0.9,-0.9l0.0,-2.4l-4.3,0.0l0.0,1.0L8.6,12.9z"
        android:fillColor="#FFFFFF"/>
        android:width="64.0dp"
        android:height="64.0dp"
        android:viewportWidth="64.0"
        android:viewportHeight="64.0">

    <!--
     The path is similar to ic_corp_icon_badge_case, such that it positions on top of
     ic_corp_icon_badge_shadow.
    -->
    <path
        android:pathData="M7.1,5.2l0.0,1.0 1.0,0.0 0.0,-1.0 3.799999,0.0 0.0,1.0 1.0,0.0 0.0,-1.0 -1.0,-0.9 -3.799999,0.0z"
        android:fillColor="#FFFFFF"/>
        android:pathData="M43.9 50.9h4v8l6-12h-4v-8l-6 12z"
        android:fillColor="#757575"/>
</vector>
+3 −0
Original line number Diff line number Diff line
@@ -171,6 +171,9 @@
    <color name="profile_badge_2">#ff000000</color><!-- Black -->
    <color name="profile_badge_3">#ff22f033</color><!-- Green -->

    <!-- Default instant app badge color -->
    <color name="instant_app_badge">#FFFFFFFF</color><!-- White -->

    <!-- Multi-sim sim colors -->
    <color name="Teal_700">#ff00796b</color>
    <color name="Teal_800">#ff00695c</color>
Loading