diff --git a/Android.mk b/Android.mk index 422c6f02b1..d9e4641164 100644 --- a/Android.mk +++ b/Android.mk @@ -76,7 +76,7 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ androidx.recyclerview_recyclerview \ androidx.dynamicanimation_dynamicanimation \ androidx.preference_preference \ - iconloader + iconloader_base LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib diff --git a/iconloaderlib/Android.bp b/iconloaderlib/Android.bp index 8a71f94ed8..f12d16e42a 100644 --- a/iconloaderlib/Android.bp +++ b/iconloaderlib/Android.bp @@ -13,7 +13,7 @@ // limitations under the License. android_library { - name: "iconloader", + name: "iconloader_base", sdk_version: "28", min_sdk_version: "21", static_libs: [ @@ -26,3 +26,19 @@ android_library { "src/**/*.java", ], } + +android_library { + name: "iconloader", + sdk_version: "system_current", + min_sdk_version: "21", + static_libs: [ + "androidx.core_core", + ], + resource_dirs: [ + "res", + ], + srcs: [ + "src/**/*.java", + "src_full_lib/**/*.java", + ], +} diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle index a6433ad1a6..f6a820ae98 100644 --- a/iconloaderlib/build.gradle +++ b/iconloaderlib/build.gradle @@ -24,7 +24,7 @@ android { sourceSets { main { - java.srcDirs = ['src'] + java.srcDirs = ['src', 'src_full_lib'] manifest.srcFile 'AndroidManifest.xml' res.srcDirs = ['res'] } diff --git a/iconloaderlib/res/values/config.xml b/iconloaderlib/res/values/config.xml new file mode 100644 index 0000000000..68c2d2e3aa --- /dev/null +++ b/iconloaderlib/res/values/config.xml @@ -0,0 +1,27 @@ + + + + + + + 56dp + false + app_icons.db + + \ No newline at end of file diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java index 065a14d5f0..a3185741cc 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java @@ -1,5 +1,10 @@ package com.android.launcher3.icons; +import static android.graphics.Paint.DITHER_FLAG; +import static android.graphics.Paint.FILTER_BITMAP_FLAG; + +import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR; + import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -18,29 +23,26 @@ import android.os.Build; import android.os.Process; import android.os.UserHandle; -import static android.graphics.Paint.DITHER_FLAG; -import static android.graphics.Paint.FILTER_BITMAP_FLAG; -import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR; - /** * This class will be moved to androidx library. There shouldn't be any dependency outside * this package. */ public class BaseIconFactory implements AutoCloseable { + private static final String TAG = "BaseIconFactory"; private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; private final Rect mOldBounds = new Rect(); - private final Context mContext; + protected final Context mContext; private final Canvas mCanvas; private final PackageManager mPm; private final ColorExtractor mColorExtractor; private boolean mDisableColorExtractor; - private int mFillResIconDpi; - private int mIconBitmapSize; + protected final int mFillResIconDpi; + protected final int mIconBitmapSize; private IconNormalizer mNormalizer; private ShadowGenerator mShadowGenerator; @@ -302,7 +304,21 @@ public class BaseIconFactory implements AutoCloseable { } @Override - public void close() { } + public void close() { + clear(); + } + + public BitmapInfo makeDefaultIcon(UserHandle user) { + return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi), + user, Build.VERSION.SDK_INT); + } + + public static Drawable getFullResDefaultActivityIcon(int iconDpi) { + return Resources.getSystem().getDrawableForDensity( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + ? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon, + iconDpi); + } /** * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size. diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java index 2fa4b68838..63820f6d0d 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java +++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.icons.cache; +import static com.android.launcher3.icons.BaseIconFactory.getFullResDefaultActivityIcon; import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; @@ -34,7 +35,6 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.os.Build; -import android.os.Build.VERSION; import android.os.Handler; import android.os.Looper; import android.os.Process; @@ -50,9 +50,13 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.Provider; import com.android.launcher3.util.SQLiteCacheHelper; +import java.util.AbstractMap; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; +import java.util.Map; +import java.util.Set; import androidx.annotation.NonNull; @@ -76,8 +80,7 @@ public abstract class BaseIconCache { protected final Context mContext; protected final PackageManager mPackageManager; - private final HashMap mCache = - new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); + private final Map mCache; protected final Handler mWorkerHandler; protected int mIconDpi; @@ -89,13 +92,30 @@ public abstract class BaseIconCache { private final Looper mBgLooper; public BaseIconCache(Context context, String dbFileName, Looper bgLooper, - int iconDpi, int iconPixelSize) { + int iconDpi, int iconPixelSize, boolean inMemoryCache) { mContext = context; mDbFileName = dbFileName; mPackageManager = context.getPackageManager(); mBgLooper = bgLooper; mWorkerHandler = new Handler(mBgLooper); + if (inMemoryCache) { + mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); + } else { + // Use a dummy cache + mCache = new AbstractMap() { + @Override + public Set> entrySet() { + return Collections.emptySet(); + } + + @Override + public CacheEntry put(ComponentKey key, CacheEntry value) { + return value; + } + }; + } + if (BitmapRenderer.USE_HARDWARE_BITMAP && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mDecodeOptions = new BitmapFactory.Options(); mDecodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE; @@ -137,27 +157,20 @@ public abstract class BaseIconCache { mCache.clear(); } - private Drawable getFullResDefaultActivityIcon() { - return Resources.getSystem().getDrawableForDensity( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - ? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon, - mIconDpi); - } - private Drawable getFullResIcon(Resources resources, int iconId) { if (resources != null && iconId != 0) { try { return resources.getDrawableForDensity(iconId, mIconDpi); } catch (Resources.NotFoundException e) { } } - return getFullResDefaultActivityIcon(); + return getFullResDefaultActivityIcon(mIconDpi); } public Drawable getFullResIcon(String packageName, int iconId) { try { return getFullResIcon(mPackageManager.getResourcesForApplication(packageName), iconId); } catch (PackageManager.NameNotFoundException e) { } - return getFullResDefaultActivityIcon(); + return getFullResDefaultActivityIcon(mIconDpi); } public Drawable getFullResIcon(ActivityInfo info) { @@ -165,13 +178,12 @@ public abstract class BaseIconCache { return getFullResIcon(mPackageManager.getResourcesForApplication(info.applicationInfo), info.getIconResource()); } catch (PackageManager.NameNotFoundException e) { } - return getFullResDefaultActivityIcon(); + return getFullResDefaultActivityIcon(mIconDpi); } - protected BitmapInfo makeDefaultIcon(UserHandle user) { + private BitmapInfo makeDefaultIcon(UserHandle user) { try (BaseIconFactory li = getIconFactory()) { - return li.createBadgedIconBitmap( - getFullResDefaultActivityIcon(), user, VERSION.SDK_INT); + return li.makeDefaultIcon(user); } } diff --git a/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java b/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java new file mode 100644 index 0000000000..48f11fde3b --- /dev/null +++ b/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 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.launcher3.icons; + +import android.content.Context; + +/** + * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class + * that are threadsafe. + */ +public class IconFactory extends BaseIconFactory { + + private static final Object sPoolSync = new Object(); + private static IconFactory sPool; + private static int sPoolId = 0; + + /** + * Return a new Message instance from the global pool. Allows us to + * avoid allocating new objects in many cases. + */ + public static IconFactory obtain(Context context) { + int poolId; + synchronized (sPoolSync) { + if (sPool != null) { + IconFactory m = sPool; + sPool = m.next; + m.next = null; + return m; + } + poolId = sPoolId; + } + + return new IconFactory(context, + context.getResources().getConfiguration().densityDpi, + context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size), + poolId); + } + + public static void clearPool() { + synchronized (sPoolSync) { + sPool = null; + sPoolId++; + } + } + + private final int mPoolId; + + private IconFactory next; + + private IconFactory(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { + super(context, fillResIconDpi, iconBitmapSize); + mPoolId = poolId; + } + + /** + * Recycles a LauncherIcons that may be in-use. + */ + public void recycle() { + synchronized (sPoolSync) { + if (sPoolId != mPoolId) { + return; + } + // Clear any temporary state variables + clear(); + + next = sPool; + sPool = this; + } + } + + @Override + public void close() { + recycle(); + } +} diff --git a/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java b/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java new file mode 100644 index 0000000000..1337975f19 --- /dev/null +++ b/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 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.launcher3.icons; + +import static android.content.Intent.ACTION_MANAGED_PROFILE_ADDED; +import static android.content.Intent.ACTION_MANAGED_PROFILE_REMOVED; + +import android.annotation.TargetApi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.SparseLongArray; + +import com.android.launcher3.icons.cache.BaseIconCache; + +/** + * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class + * that are threadsafe. + */ +@TargetApi(Build.VERSION_CODES.P) +public class SimpleIconCache extends BaseIconCache { + + private static SimpleIconCache sIconCache = null; + private static final Object CACHE_LOCK = new Object(); + + private final SparseLongArray mUserSerialMap = new SparseLongArray(2); + private final UserManager mUserManager; + + public SimpleIconCache(Context context, String dbFileName, Looper bgLooper, int iconDpi, + int iconPixelSize, boolean inMemoryCache) { + super(context, dbFileName, bgLooper, iconDpi, iconPixelSize, inMemoryCache); + mUserManager = context.getSystemService(UserManager.class); + + // Listen for user cache changes. + IntentFilter filter = new IntentFilter(ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(ACTION_MANAGED_PROFILE_REMOVED); + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + resetUserCache(); + } + }, filter, null, new Handler(bgLooper), 0); + } + + @Override + protected long getSerialNumberForUser(UserHandle user) { + synchronized (mUserSerialMap) { + int index = mUserSerialMap.indexOfKey(user.getIdentifier()); + if (index >= 0) { + return mUserSerialMap.valueAt(index); + } + long serial = mUserManager.getSerialNumberForUser(user); + mUserSerialMap.put(user.getIdentifier(), serial); + return serial; + } + } + + private void resetUserCache() { + synchronized (mUserSerialMap) { + mUserSerialMap.clear(); + } + } + + @Override + protected boolean isInstantApp(ApplicationInfo info) { + return info.isInstantApp(); + } + + @Override + protected BaseIconFactory getIconFactory() { + return IconFactory.obtain(mContext); + } + + public static SimpleIconCache getIconCache(Context context) { + synchronized (CACHE_LOCK) { + if (sIconCache != null) { + return sIconCache; + } + boolean inMemoryCache = + context.getResources().getBoolean(R.bool.simple_cache_enable_im_memory); + String dbFileName = context.getString(R.string.cache_db_name); + + HandlerThread bgThread = new HandlerThread("simple-icon-cache"); + bgThread.start(); + + sIconCache = new SimpleIconCache(context.getApplicationContext(), dbFileName, + bgThread.getLooper(), context.getResources().getConfiguration().densityDpi, + context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size), + inMemoryCache); + return sIconCache; + } + } +} diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 6bb9dab6b5..4b54bc3ea1 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -72,7 +72,7 @@ public class IconCache extends BaseIconCache { public IconCache(Context context, InvariantDeviceProfile inv) { super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(), - inv.fillResIconDpi, inv.iconBitmapSize); + inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */); mComponentWithLabelCachingLogic = new ComponentCachingLogic(context); mLauncherActivityInfoCachingLogic = new LauncherActivtiyCachingLogic(this); mLauncherApps = LauncherAppsCompat.getInstance(mContext); diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java index 153c5650a9..4b869cff69 100644 --- a/src/com/android/launcher3/icons/LauncherIcons.java +++ b/src/com/android/launcher3/icons/LauncherIcons.java @@ -73,18 +73,12 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { } } - private final Context mContext; - private final int mFillResIconDpi; - private final int mIconBitmapSize; private final int mPoolId; private LauncherIcons next; private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { super(context, fillResIconDpi, iconBitmapSize); - mContext = context.getApplicationContext(); - mFillResIconDpi = fillResIconDpi; - mIconBitmapSize = iconBitmapSize; mPoolId = poolId; } diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java index c39d712abb..afe9dc655e 100644 --- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java +++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java @@ -227,10 +227,5 @@ public class BaseModelUpdateTaskTestCase { public Bitmap newIcon() { return Bitmap.createBitmap(1, 1, Config.ARGB_8888); } - - @Override - protected BitmapInfo makeDefaultIcon(UserHandle user) { - return BitmapInfo.fromBitmap(newIcon()); - } } }