POC: load icon app via ksu://icon/[packageName] (#674)

* manager: load app icons from package name using AppIconUti

Trying basic icon rendering from package via WebView ksu:// scheme.
Includes cache and bitmap scaling.
Still subject to refinement.

* Update WebUIActivity.kt

This proof-of-concept intercepts custom URLs of the form:
  ksu://icon/com.example.app

It fetches the app icon using PackageManager via AppIconUtil,
converts it to PNG, and returns it as a WebResourceResponse.

Used inside shouldInterceptRequest() for early experimentation
with dynamic WebView asset routing.

Fallbacks to WebViewAssetLoader for all other requests.

Notes:
- Icon size currently fixed at 512px
- No error icon or fallback image yet
- No caching headers or mime sniffing implemented

* POC: Handle ksu://icon/[packageName] to serve app icon via WebView

This proof-of-concept intercepts custom URLs of the form:
  ksu://icon/com.example.app

It fetches the app icon using PackageManager via AppIconUtil,
converts it to PNG, and returns it as a WebResourceResponse.

Used inside shouldInterceptRequest() for early experimentation
with dynamic WebView asset routing.

Fallbacks to WebViewAssetLoader for all other requests.

Notes:
- Icon size currently fixed at 512px
- No error icon or fallback image yet
- No caching headers or mime sniffing implemented
This commit is contained in:
Fahrez256Bit
2025-07-25 22:23:29 +07:00
committed by GitHub
parent d4f4c0a0cc
commit bc9927b9b6
2 changed files with 64 additions and 2 deletions

View File

@@ -0,0 +1,46 @@
package com.rifsxd.ksunext.ui.webui;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import java.util.HashMap;
import java.util.Map;
public class AppIconUtil {
private static final Map<String, Bitmap> iconCache = new HashMap<>();
public static Bitmap loadAppIconSync(Context context, String packageName, int sizePx) {
Bitmap cached = iconCache.get(packageName);
if (cached != null) return cached;
try {
PackageManager pm = context.getPackageManager();
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
Drawable drawable = pm.getApplicationIcon(appInfo);
Bitmap raw = drawableToBitmap(drawable, sizePx);
Bitmap icon = Bitmap.createScaledBitmap(raw, sizePx, sizePx, true);
iconCache.put(packageName, icon);
return icon;
} catch (Exception e) {
return null;
}
}
private static Bitmap drawableToBitmap(Drawable drawable, int size) {
if (drawable instanceof BitmapDrawable) return ((BitmapDrawable) drawable).getBitmap();
int width = drawable.getIntrinsicWidth() > 0 ? drawable.getIntrinsicWidth() : size;
int height = drawable.getIntrinsicHeight() > 0 ? drawable.getIntrinsicHeight() : size;
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bmp;
}
}

View File

@@ -96,7 +96,23 @@ class WebUIActivity : ComponentActivity() {
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
return webViewAssetLoader.shouldInterceptRequest(request.url)
val url = request.url
//POC: Handle ksu://icon/[packageName] to serve app icon via WebView
if (url.scheme.equals("ksu", ignoreCase = true) && url.host.equals("icon", ignoreCase = true)) {
val packageName = url.path?.substring(1)
if (!packageName.isNullOrEmpty()) {
val icon = AppIconUtil.loadAppIconSync(this@WebUIActivity, packageName, 512)
if (icon != null) {
val stream = java.io.ByteArrayOutputStream()
icon.compress(android.graphics.Bitmap.CompressFormat.PNG, 100, stream)
val inputStream = java.io.ByteArrayInputStream(stream.toByteArray())
return WebResourceResponse("image/png", null, inputStream)
}
}
}
return webViewAssetLoader.shouldInterceptRequest(url)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
@@ -119,4 +135,4 @@ class WebUIActivity : ComponentActivity() {
super.onDestroy()
runCatching { rootShell?.close() }
}
}
}