Skip to content

Commit 80dcef3

Browse files
committed
refactor(android): use WebViewAssetLoader instead of file access
- Remove MANAGE_EXTERNAL_STORAGE permission and blocking UI - Implement WebViewAssetLoader to serve assets from secure virtual domain - Disable direct file access in WebView settings for better security - Update loadUrl to use https://appassets.androidplatform.net
1 parent 7d3f428 commit 80dcef3

3 files changed

Lines changed: 27 additions & 107 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
66
tools:ignore="QueryAllPackagesPermission" />
77
<uses-permission android:name="android.permission.INTERNET" />
8-
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
98
<uses-permission android:name="moe.shizuku.manager.permission.API_V23" />
109

1110
<application

app/src/main/java/com/appcontrolx/ui/MainActivity.kt

Lines changed: 27 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
package com.appcontrolx.ui
22

33
import android.annotation.SuppressLint
4-
import android.content.Intent
54
import android.graphics.Color
6-
import android.net.Uri
7-
import android.os.Build
85
import android.os.Bundle
9-
import android.os.Environment
10-
import android.provider.Settings
116
import android.view.View
127
import android.webkit.WebChromeClient
8+
import android.webkit.WebResourceRequest
9+
import android.webkit.WebResourceResponse
1310
import android.webkit.WebSettings
1411
import android.webkit.WebView
1512
import android.webkit.WebViewClient
16-
import android.widget.Button
17-
import android.widget.LinearLayout
1813
import androidx.activity.OnBackPressedCallback
19-
import androidx.activity.result.contract.ActivityResultContracts
2014
import androidx.appcompat.app.AppCompatActivity
21-
import com.appcontrolx.R
15+
import androidx.webkit.WebViewAssetLoader
16+
import androidx.webkit.WebViewAssetLoader.AssetsPathHandler
2217
import com.appcontrolx.bridge.NativeBridge
2318
import com.appcontrolx.databinding.ActivityMainBinding
2419
import dagger.hilt.android.AndroidEntryPoint
@@ -32,83 +27,36 @@ class MainActivity : AppCompatActivity() {
3227
@Inject
3328
lateinit var nativeBridge: NativeBridge
3429

35-
private val storagePermissionLauncher = registerForActivityResult(
36-
ActivityResultContracts.StartActivityForResult()
37-
) {
38-
checkAndSetup()
39-
}
40-
4130
override fun onCreate(savedInstanceState: Bundle?) {
4231
super.onCreate(savedInstanceState)
4332
binding = ActivityMainBinding.inflate(layoutInflater)
4433
setContentView(binding.root)
4534

46-
setupPermissionButton()
47-
checkAndSetup()
35+
setupWebView()
4836
setupBackHandler()
4937
}
5038

51-
private fun setupPermissionButton() {
52-
findViewById<Button>(R.id.btn_grant_permission).setOnClickListener {
53-
requestStoragePermission()
54-
}
55-
}
56-
57-
private fun checkAndSetup() {
58-
if (hasStoragePermission()) {
59-
showWebView()
60-
setupWebView()
61-
} else {
62-
showPermissionLayout()
63-
}
64-
}
65-
66-
private fun hasStoragePermission(): Boolean {
67-
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
68-
Environment.isExternalStorageManager()
69-
} else {
70-
true // For older versions, standard permissions flow applies, assuming granted for simplicity here or handled elsewhere if needed
71-
}
72-
}
73-
74-
private fun requestStoragePermission() {
75-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
76-
try {
77-
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
78-
intent.addCategory("android.intent.category.DEFAULT")
79-
intent.data = Uri.parse(String.format("package:%s", applicationContext.packageName))
80-
storagePermissionLauncher.launch(intent)
81-
} catch (e: Exception) {
82-
val intent = Intent()
83-
intent.action = Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
84-
storagePermissionLauncher.launch(intent)
85-
}
86-
}
87-
}
88-
89-
private fun showWebView() {
90-
binding.webView.visibility = View.VISIBLE
91-
binding.progressBar.visibility = View.VISIBLE // Will be hidden by onPageFinished
92-
findViewById<LinearLayout>(R.id.permission_layout).visibility = View.GONE
93-
}
94-
95-
private fun showPermissionLayout() {
96-
binding.webView.visibility = View.GONE
97-
binding.progressBar.visibility = View.GONE
98-
findViewById<LinearLayout>(R.id.permission_layout).visibility = View.VISIBLE
99-
}
100-
10139
@SuppressLint("SetJavaScriptEnabled")
10240
private fun setupWebView() {
41+
// Create an AssetLoader to load assets from a virtual domain
42+
// This avoids file:// protocol issues and CORS problems
43+
val assetLoader = WebViewAssetLoader.Builder()
44+
.setDomain("appassets.androidplatform.net")
45+
.addPathHandler("/assets/", AssetsPathHandler(this))
46+
.build()
47+
10348
binding.webView.apply {
10449
setBackgroundColor(Color.TRANSPARENT)
10550

10651
settings.apply {
10752
javaScriptEnabled = true
10853
domStorageEnabled = true
10954
databaseEnabled = true
110-
allowFileAccess = true
111-
allowContentAccess = true
55+
56+
// Security: Disable direct file access as we are using AssetLoader
57+
allowFileAccess = false
58+
allowContentAccess = false
59+
11260
cacheMode = WebSettings.LOAD_DEFAULT
11361
mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
11462
setSupportZoom(false)
@@ -120,6 +68,14 @@ class MainActivity : AppCompatActivity() {
12068
}
12169

12270
webViewClient = object : WebViewClient() {
71+
override fun shouldInterceptRequest(
72+
view: WebView,
73+
request: WebResourceRequest
74+
): WebResourceResponse? {
75+
// Intercept requests and forward them to AssetLoader
76+
return assetLoader.shouldInterceptRequest(request.url)
77+
}
78+
12379
override fun onPageFinished(view: WebView?, url: String?) {
12480
super.onPageFinished(view, url)
12581
binding.progressBar.visibility = View.GONE
@@ -137,7 +93,8 @@ class MainActivity : AppCompatActivity() {
13793
addJavascriptInterface(nativeBridge, "NativeBridge")
13894
nativeBridge.setWebView(this)
13995

140-
loadUrl("file:///android_asset/www/index.html")
96+
// Load the app from the virtual secure origin
97+
loadUrl("https://appassets.androidplatform.net/assets/www/index.html")
14198
}
14299
}
143100

app/src/main/res/layout/activity_main.xml

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,4 @@
1717
android:indeterminateTint="@color/primary"
1818
android:visibility="gone" />
1919

20-
<LinearLayout
21-
android:id="@+id/permission_layout"
22-
android:layout_width="match_parent"
23-
android:layout_height="match_parent"
24-
android:orientation="vertical"
25-
android:gravity="center"
26-
android:padding="24dp"
27-
android:background="@color/background"
28-
android:visibility="gone">
29-
30-
<TextView
31-
android:layout_width="wrap_content"
32-
android:layout_height="wrap_content"
33-
android:text="Storage Permission Required"
34-
android:textSize="24sp"
35-
android:textStyle="bold"
36-
android:textColor="@color/on_background"
37-
android:layout_marginBottom="16dp" />
38-
39-
<TextView
40-
android:layout_width="wrap_content"
41-
android:layout_height="wrap_content"
42-
android:text="This app is built on web technologies and requires full file access to load local resources and function correctly.\n\nWithout this permission, the app cannot access its core files due to Android's storage restrictions."
43-
android:textSize="16sp"
44-
android:textColor="@color/on_surface_secondary"
45-
android:textAlignment="center"
46-
android:layout_marginBottom="32dp" />
47-
48-
<Button
49-
android:id="@+id/btn_grant_permission"
50-
android:layout_width="wrap_content"
51-
android:layout_height="wrap_content"
52-
android:text="Grant Permission" />
53-
54-
</LinearLayout>
55-
5620
</FrameLayout>

0 commit comments

Comments
 (0)