Merge "Moving IconProvider extensions to a separate subclass instead of routing via APIWrapper" into main
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.launcher3.dagger
|
||||
|
||||
import com.android.launcher3.icons.LauncherIconProvider
|
||||
import com.android.launcher3.icons.LauncherIconProviderImpl
|
||||
import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepWidgetHolderFactory
|
||||
import com.android.launcher3.uioverrides.SystemApiWrapper
|
||||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl
|
||||
@@ -41,6 +43,9 @@ abstract class WindowManagerProxyModule {
|
||||
@Module
|
||||
abstract class ApiWrapperModule {
|
||||
@Binds abstract fun bindApiWrapper(systemApiWrapper: SystemApiWrapper): ApiWrapper
|
||||
|
||||
@Binds
|
||||
abstract fun bindIconProvider(iconProviderImpl: LauncherIconProviderImpl): LauncherIconProvider
|
||||
}
|
||||
|
||||
@Module
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageItemInfo
|
||||
import android.content.res.Resources.NotFoundException
|
||||
import android.graphics.drawable.AdaptiveIconDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Log
|
||||
import com.android.launcher3.LauncherModel
|
||||
import com.android.launcher3.dagger.ApplicationContext
|
||||
import com.android.launcher3.dagger.LauncherAppSingleton
|
||||
import com.android.launcher3.graphics.ShapeDelegate.Circle
|
||||
import com.android.launcher3.graphics.ThemeManager
|
||||
import com.android.launcher3.icons.cache.CachingLogic
|
||||
import com.android.launcher3.icons.cache.LauncherActivityCachingLogic
|
||||
import com.android.launcher3.util.ComponentKey
|
||||
import com.android.launcher3.util.DaggerSingletonTracker
|
||||
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
|
||||
import com.android.launcher3.util.PluginManagerWrapper
|
||||
import com.android.systemui.plugins.IconProcessorPlugin
|
||||
import com.android.systemui.plugins.PluginLifecycleManager
|
||||
import com.android.systemui.plugins.PluginListener
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
/** Extension of LauncherIconProvider with system APIs and plugin support */
|
||||
private const val TAG = "LauncherIconProviderImpl"
|
||||
|
||||
@LauncherAppSingleton
|
||||
class LauncherIconProviderImpl
|
||||
@Inject
|
||||
constructor(
|
||||
@ApplicationContext ctx: Context,
|
||||
themeManager: ThemeManager,
|
||||
private val modelProvider: Provider<LauncherModel>,
|
||||
private val iconCacheProvider: Provider<IconCache>,
|
||||
pluginManagerWrapper: PluginManagerWrapper,
|
||||
lifecycle: DaggerSingletonTracker,
|
||||
) : LauncherIconProvider(ctx, themeManager), PluginListener<IconProcessorPlugin> {
|
||||
|
||||
init {
|
||||
pluginManagerWrapper.addPluginListener(this, IconProcessorPlugin::class.java)
|
||||
lifecycle.addCloseable { pluginManagerWrapper.removePluginListener(this) }
|
||||
}
|
||||
|
||||
private var processor: IconProcessorPlugin? = null
|
||||
|
||||
override fun getApplicationInfoHash(appInfo: ApplicationInfo): String =
|
||||
(appInfo.sourceDir?.hashCode() ?: 0).toString() + " " + appInfo.longVersionCode
|
||||
|
||||
override fun loadPackageIcon(
|
||||
info: PackageItemInfo,
|
||||
appInfo: ApplicationInfo,
|
||||
density: Int,
|
||||
): Drawable? {
|
||||
fun Drawable.preprocess(resId: Int) =
|
||||
processor?.preprocessDrawable(this, resId, appInfo) ?: this
|
||||
|
||||
try {
|
||||
val resources = mContext.packageManager.getResourcesForApplication(appInfo)
|
||||
// Try to load the package item icon first
|
||||
if (info !== appInfo && info.icon != 0) {
|
||||
try {
|
||||
val icon = resources.getDrawableForDensity(info.icon, density, null)
|
||||
if (icon != null) return icon.preprocess(info.icon)
|
||||
} catch (_: NotFoundException) {}
|
||||
}
|
||||
// Load the fallback app icon
|
||||
if (appInfo.icon != 0) {
|
||||
// Tries to load the round icon res, if the app defines it as an adaptive icon
|
||||
if (mThemeManager.iconShape is Circle) {
|
||||
if (appInfo.roundIconRes != 0 && appInfo.roundIconRes != appInfo.icon) {
|
||||
try {
|
||||
val d =
|
||||
resources.getDrawableForDensity(appInfo.roundIconRes, density, null)
|
||||
if (d is AdaptiveIconDrawable) return d.preprocess(appInfo.roundIconRes)
|
||||
} catch (_: NotFoundException) {}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return resources
|
||||
.getDrawableForDensity(appInfo.icon, density, null)
|
||||
?.preprocess(appInfo.icon)
|
||||
} catch (_: NotFoundException) {}
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPluginLoaded(
|
||||
plugin: IconProcessorPlugin?,
|
||||
pluginContext: Context?,
|
||||
manager: PluginLifecycleManager<IconProcessorPlugin>?,
|
||||
) {
|
||||
plugin?.setIconChangeNotifier { pkg, userHandle ->
|
||||
modelProvider.get().onAppIconChanged(pkg, userHandle)
|
||||
}
|
||||
processor = plugin
|
||||
Log.d(TAG, "Plugin connected $plugin")
|
||||
MODEL_EXECUTOR.execute {
|
||||
iconCacheProvider.get().clearMemoryCache()
|
||||
modelProvider.get().reloadIfActive()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPluginUnloaded(
|
||||
plugin: IconProcessorPlugin?,
|
||||
manager: PluginLifecycleManager<IconProcessorPlugin>?,
|
||||
) {
|
||||
processor = null
|
||||
Log.d(TAG, "Plugin disconnected")
|
||||
}
|
||||
|
||||
override fun notifyIconLoaded(icon: BitmapInfo, key: ComponentKey, logic: CachingLogic<*>) {
|
||||
if (logic == LauncherActivityCachingLogic)
|
||||
processor?.notifyAppIconLoaded(key.componentName, key.user, icon.flags)
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import android.content.IIntentReceiver
|
||||
import android.content.IIntentSender
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.LauncherActivityInfo
|
||||
import android.content.pm.LauncherApps
|
||||
import android.content.pm.ShortcutInfo
|
||||
@@ -198,11 +197,6 @@ open class SystemApiWrapper @Inject constructor(@ApplicationContext context: Con
|
||||
}
|
||||
}
|
||||
|
||||
override fun getApplicationInfoHash(appInfo: ApplicationInfo): String =
|
||||
(appInfo.sourceDir?.hashCode() ?: 0).toString() + " " + appInfo.longVersionCode
|
||||
|
||||
override fun getRoundIconRes(appInfo: ApplicationInfo) = appInfo.roundIconRes
|
||||
|
||||
override fun isFileDrawable(shortcutInfo: ShortcutInfo) =
|
||||
shortcutInfo.hasIconFile() || shortcutInfo.hasIconUri()
|
||||
}
|
||||
|
||||
@@ -16,25 +16,17 @@
|
||||
package com.android.launcher3.icons;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dagger.ApplicationContext;
|
||||
import com.android.launcher3.dagger.LauncherAppSingleton;
|
||||
import com.android.launcher3.graphics.ShapeDelegate;
|
||||
import com.android.launcher3.graphics.ThemeManager;
|
||||
import com.android.launcher3.util.ApiWrapper;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
@@ -58,17 +50,14 @@ public class LauncherIconProvider extends IconProvider {
|
||||
|
||||
private Map<String, ThemeData> mThemedIconMap;
|
||||
|
||||
private final ApiWrapper mApiWrapper;
|
||||
private final ThemeManager mThemeManager;
|
||||
protected final ThemeManager mThemeManager;
|
||||
|
||||
@Inject
|
||||
public LauncherIconProvider(
|
||||
@ApplicationContext Context context,
|
||||
ThemeManager themeManager,
|
||||
ApiWrapper apiWrapper) {
|
||||
ThemeManager themeManager) {
|
||||
super(context);
|
||||
mThemeManager = themeManager;
|
||||
mApiWrapper = apiWrapper;
|
||||
mThemedIconMap = FeatureFlags.USE_LOCAL_ICON_OVERRIDES.get() ? null : DISABLED_MAP;
|
||||
}
|
||||
|
||||
@@ -83,29 +72,6 @@ public class LauncherIconProvider extends IconProvider {
|
||||
mSystemState += "," + mThemeManager.getIconState().toUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getApplicationInfoHash(@NonNull ApplicationInfo appInfo) {
|
||||
return mApiWrapper.getApplicationInfoHash(appInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Drawable loadAppInfoIcon(ApplicationInfo info, Resources resources, int density) {
|
||||
// Tries to load the round icon res, if the app defines it as an adaptive icon
|
||||
if (mThemeManager.getIconShape() instanceof ShapeDelegate.Circle) {
|
||||
int roundIconRes = mApiWrapper.getRoundIconRes(info);
|
||||
if (roundIconRes != 0 && roundIconRes != info.icon) {
|
||||
try {
|
||||
Drawable d = resources.getDrawableForDensity(roundIconRes, density);
|
||||
if (d instanceof AdaptiveIconDrawable) {
|
||||
return d;
|
||||
}
|
||||
} catch (Resources.NotFoundException exc) { }
|
||||
}
|
||||
}
|
||||
return super.loadAppInfoIcon(info, resources, density);
|
||||
}
|
||||
|
||||
private Map<String, ThemeData> getThemedIconMap() {
|
||||
if (mThemedIconMap != null) {
|
||||
return mThemedIconMap;
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.app.Person;
|
||||
import android.app.role.RoleManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
@@ -201,21 +200,6 @@ public class ApiWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash to uniquely identify a particular version of appInfo
|
||||
*/
|
||||
public String getApplicationInfoHash(@NonNull ApplicationInfo appInfo) {
|
||||
// The hashString in source dir changes with every install
|
||||
return appInfo.sourceDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the round icon resource Id if defined by the app
|
||||
*/
|
||||
public int getRoundIconRes(@NonNull ApplicationInfo appInfo) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the shortcut is using an icon with file or URI source
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.systemui.plugins;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Implement this interface to process Launcher icon drawables before they are displayed.
|
||||
*/
|
||||
@ProvidesInterface(action = IconProcessorPlugin.ACTION, version = IconProcessorPlugin.VERSION)
|
||||
public interface IconProcessorPlugin extends Plugin {
|
||||
String ACTION = "com.android.systemui.action.ICON_WRAPPER_PLUGIN";
|
||||
int VERSION = 1;
|
||||
|
||||
/**
|
||||
* Sets a callback to be called with packageName and userHandle, whenever an icon changes
|
||||
*/
|
||||
void setIconChangeNotifier(BiConsumer<String, UserHandle> callback);
|
||||
|
||||
/** Preprocess the provided drawable and returns the modified drawable */
|
||||
Drawable preprocessDrawable(Drawable original, int resId, ApplicationInfo appInfo);
|
||||
|
||||
/** Notifies when an app icon is loaded from cache */
|
||||
void notifyAppIconLoaded(ComponentName cn, UserHandle user, int bitmapInfoFlags);
|
||||
}
|
||||
Reference in New Issue
Block a user