主页固定显示
This commit is contained in:
1
iconloader/.gitignore
vendored
Normal file
1
iconloader/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
37
iconloader/build.gradle
Normal file
37
iconloader/build.gradle
Normal file
@@ -0,0 +1,37 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 36
|
||||
// buildToolsVersion "36.0.0"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 36
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles 'consumer-rules.pro'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
|
||||
//磁盘缓存
|
||||
implementation 'com.jakewharton:disklrucache:2.0.2'
|
||||
}
|
||||
0
iconloader/consumer-rules.pro
Normal file
0
iconloader/consumer-rules.pro
Normal file
21
iconloader/proguard-rules.pro
vendored
Normal file
21
iconloader/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.ttstd.iconloader.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
1
iconloader/src/main/AndroidManifest.xml
Normal file
1
iconloader/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest package="com.uiui.iconloader" />
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
|
||||
public class CacheKeyGenerator {
|
||||
// 生成ComponentName的标准字符串格式(包名/类名)
|
||||
public static String generateKey(ComponentName componentName) {
|
||||
return componentName.flattenToShortString(); // 如:com.example.app/.MainActivity
|
||||
}
|
||||
|
||||
// 从Intent中提取ComponentName作为键
|
||||
public static String generateKey(Intent intent) {
|
||||
return generateKey(intent.getComponent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.collection.LruCache;
|
||||
|
||||
import com.jakewharton.disklrucache.DiskLruCache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class IconCacheManager {
|
||||
private static final String TAG = "IconCacheManager";
|
||||
private LruCache<String, Drawable> mMemoryCache;
|
||||
private DiskLruCache mDiskCache;
|
||||
|
||||
public IconCacheManager(Context context) {
|
||||
// 内存缓存(最大8MB)
|
||||
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
|
||||
int cacheSize = maxMemory / 8;
|
||||
mMemoryCache = new LruCache<String, Drawable>(cacheSize) {
|
||||
@Override
|
||||
protected int sizeOf(String key, Drawable icon) {
|
||||
if (icon instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) icon).getBitmap().getByteCount() / 1024;
|
||||
}
|
||||
return 1; // 非Bitmap对象按1KB计算
|
||||
}
|
||||
};
|
||||
|
||||
// 磁盘缓存(50MB)
|
||||
File cacheDir = new File(context.getCacheDir(), "icon_cache");
|
||||
try {
|
||||
mDiskCache = DiskLruCache.open(cacheDir, 1, 1, 50 * 1024 * 1024);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "IconCacheManager: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable getFromMemory(String key) {
|
||||
return mMemoryCache.get(key);
|
||||
}
|
||||
|
||||
public Drawable getFromDisk(String key) {
|
||||
try {
|
||||
DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
|
||||
if (snapshot != null) {
|
||||
InputStream in = snapshot.getInputStream(0);
|
||||
return Drawable.createFromStream(in, null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("IconCache", "Disk read error", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void putToMemory(String key, Drawable icon) {
|
||||
mMemoryCache.put(key, icon);
|
||||
}
|
||||
|
||||
|
||||
public void putToDisk(String key, Drawable icon) {
|
||||
try {
|
||||
DiskLruCache.Editor editor = mDiskCache.edit(key);
|
||||
if (icon instanceof BitmapDrawable) {
|
||||
Bitmap bitmap = ((BitmapDrawable) icon).getBitmap();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, editor.newOutputStream(0));
|
||||
editor.commit();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("IconCache", "Disk write error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
97
iconloader/src/main/java/com/uiui/iconloader/IconLoader.java
Normal file
97
iconloader/src/main/java/com/uiui/iconloader/IconLoader.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.loader.content.AsyncTaskLoader;
|
||||
|
||||
public class IconLoader extends AsyncTaskLoader<Drawable> {
|
||||
private final ComponentName mComponentName; // 关键:使用ComponentName标识组件
|
||||
private final IconCacheManager mCache;
|
||||
|
||||
public IconLoader(Context context, ComponentName componentName, IconCacheManager cache) {
|
||||
super(context);
|
||||
mComponentName = componentName;
|
||||
mCache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
String key = CacheKeyGenerator.generateKey(mComponentName);
|
||||
// 1. 检查内存缓存
|
||||
Drawable cachedIcon = mCache.getFromMemory(key);
|
||||
if (cachedIcon != null) {
|
||||
deliverResult(cachedIcon); // 直接返回缓存
|
||||
return;
|
||||
}
|
||||
forceLoad(); // 触发后台加载
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable loadInBackground() {
|
||||
String key = CacheKeyGenerator.generateKey(mComponentName);
|
||||
|
||||
// 1. 检查内存缓存
|
||||
Drawable cachedIcon = mCache.getFromMemory(key);
|
||||
if (cachedIcon != null) return cachedIcon;
|
||||
|
||||
// 2. 检查磁盘缓存
|
||||
Drawable diskCached = mCache.getFromDisk(key);
|
||||
if (diskCached != null) {
|
||||
mCache.putToMemory(key, diskCached);
|
||||
return diskCached;
|
||||
}
|
||||
|
||||
// 3. 从PackageManager加载组件专属图标
|
||||
try {
|
||||
PackageManager pm = getContext().getPackageManager();
|
||||
ActivityInfo info = pm.getActivityInfo(mComponentName, 0); // 获取ActivityInfo
|
||||
Drawable rawIcon = info.loadIcon(pm); // 加载该组件的图标
|
||||
|
||||
// 4. 图标处理(压缩/主题适配)
|
||||
Drawable processedIcon = processIcon(rawIcon);
|
||||
|
||||
// 5. 更新缓存
|
||||
mCache.putToMemory(key, processedIcon);
|
||||
mCache.putToDisk(key, processedIcon);
|
||||
return processedIcon;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable processIcon(Drawable icon) {
|
||||
// 自适应图标处理(Android 8.0+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
&& icon instanceof AdaptiveIconDrawable) {
|
||||
AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) icon;
|
||||
int targetColor = isDarkMode() ? Color.WHITE : Color.BLACK;
|
||||
adaptiveIcon.setColorFilter(targetColor, PorterDuff.Mode.SRC_ATOP); // 根据主题切换单色模式
|
||||
}
|
||||
|
||||
// 图标尺寸压缩(避免内存溢出)
|
||||
if (icon instanceof BitmapDrawable) {
|
||||
Bitmap bitmap = ((BitmapDrawable) icon).getBitmap();
|
||||
int scaledWidth = bitmap.getWidth() / 2;
|
||||
int scaledHeight = bitmap.getHeight() / 2;
|
||||
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
|
||||
return new BitmapDrawable(getContext().getResources(), scaledBitmap);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
private boolean isDarkMode() {
|
||||
return (getContext().getResources().getConfiguration().uiMode
|
||||
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.uiui.iconloader;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user