From bc9927b9b66ec6ffaba5948d3cadff34f619533a Mon Sep 17 00:00:00 2001 From: Fahrez256Bit <167403685+fahrez256@users.noreply.github.com> Date: Fri, 25 Jul 2025 22:23:29 +0700 Subject: [PATCH] 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 --- .../rifsxd/ksunext/ui/webui/AppIconUtil.java | 46 +++++++++++++++++++ .../rifsxd/ksunext/ui/webui/WebUIActivity.kt | 20 +++++++- 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/AppIconUtil.java diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/AppIconUtil.java b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/AppIconUtil.java new file mode 100644 index 00000000..1640539f --- /dev/null +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/AppIconUtil.java @@ -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 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; + } +} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebUIActivity.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebUIActivity.kt index f0228202..1dbd971c 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebUIActivity.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/webui/WebUIActivity.kt @@ -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() } } -} \ No newline at end of file +}