Skip to content

Commit cc5330b

Browse files
committed
fix installing sources on android 16
1 parent 9572472 commit cc5330b

File tree

10 files changed

+650
-144
lines changed

10 files changed

+650
-144
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ render.experimental.xml
4343
*.keystore
4444

4545
# Android Profiling
46-
*.hprof
46+
*.hprof
47+
/.kotlin

android/src/main/AndroidManifest.xml

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5-
5+
<!-- Notification permissions -->
66
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
77

8-
<!-- Storage -->
9-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
10-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
11-
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
12-
<!-- Internet -->
8+
<!-- Storage permissions -->
9+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
10+
android:maxSdkVersion="32" />
11+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
12+
android:maxSdkVersion="32" />
13+
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
14+
tools:ignore="ScopedStorage" />
15+
16+
<!-- Media permissions for Android 13+ -->
17+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
18+
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
19+
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
20+
21+
<!-- Internet permissions -->
1322
<uses-permission android:name="android.permission.INTERNET" />
1423
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
1524
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -23,18 +32,26 @@
2332
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
2433
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
2534
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
26-
35+
2736
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
2837
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
2938

3039
<!-- To view extension packages in API 30+ -->
3140
<uses-permission
3241
android:name="android.permission.QUERY_ALL_PACKAGES"
3342
tools:ignore="QueryAllPackagesPermission" />
43+
3444
<queries>
3545
<intent>
3646
<action android:name="android.intent.action.TTS_SERVICE" />
3747
</intent>
48+
49+
<!-- Add other extension package names as needed -->
50+
<!-- Also add a generic intent filter for package visibility -->
51+
<intent>
52+
<action android:name="android.intent.action.VIEW" />
53+
<data android:scheme="ireader.*" />
54+
</intent>
3855
</queries>
3956

4057
<application
@@ -51,6 +68,7 @@
5168
android:roundIcon="@mipmap/ic_launcher"
5269
android:screenOrientation="fullSensor"
5370
android:supportsRtl="true"
71+
android:extractNativeLibs="true"
5472
android:theme="@style/Theme.Splash">
5573
<activity
5674
android:name="org.ireader.app.MainActivity"

android/src/main/java/org/ireader/app/MainActivity.kt

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import androidx.compose.runtime.mutableStateOf
1818
import androidx.compose.runtime.remember
1919
import androidx.compose.runtime.rememberCoroutineScope
2020
import androidx.compose.runtime.setValue
21+
import androidx.compose.ui.platform.LocalContext
2122
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
2223
import androidx.core.util.Consumer
2324
import androidx.core.view.WindowCompat
@@ -75,17 +76,38 @@ class MainActivity : ComponentActivity(), SecureActivityDelegate by SecureActivi
7576
@OptIn(ExperimentalMaterial3Api::class, ExperimentalCoilApi::class)
7677
override fun onCreate(savedInstanceState: Bundle?) {
7778
super.onCreate(savedInstanceState)
78-
registerSecureActivity(this, uiPreferences,initializers)
79+
registerSecureActivity(this, uiPreferences, initializers)
80+
81+
// Provide activity to storage helper
7982
getSimpleStorage.provideActivity(this, null)
83+
84+
// Set up window to handle gesture navigation
8085
WindowCompat.setDecorFitsSystemWindows(window, false)
86+
87+
// Initialize automatic backup in the background
8188
lifecycleScope.launchIO {
8289
automaticBackup.initialize()
8390
}
91+
92+
// Set up logging
8493
Napier.base(DebugAntilog())
94+
95+
// Set locale
8596
localeHelper.setLocaleLang()
97+
98+
// Install splash screen
8699
installSplashScreen()
87100

101+
// Request all necessary permissions early
102+
lifecycleScope.launch {
103+
// Delay slightly to let the UI initialize
104+
delay(500)
105+
// Request important storage permissions based on Android version
106+
requestNecessaryPermissions()
107+
}
108+
88109
setContent {
110+
val context = LocalContext.current
89111
SetDefaultTheme()
90112
KoinContext {
91113
setSingletonImageLoaderFactory { context ->
@@ -94,13 +116,10 @@ class MainActivity : ComponentActivity(), SecureActivityDelegate by SecureActivi
94116
)
95117
}
96118
AppTheme(this.lifecycleScope) {
97-
98119
Surface(
99120
color = MaterialTheme.colorScheme.surface,
100121
contentColor = MaterialTheme.colorScheme.onSurface,
101-
102-
) {
103-
122+
) {
104123
Navigator(
105124
screen = MainStarterScreen,
106125
disposeBehavior = NavigatorDisposeBehavior(
@@ -116,19 +135,30 @@ class MainActivity : ComponentActivity(), SecureActivityDelegate by SecureActivi
116135
}
117136
IScaffold {
118137
DefaultNavigatorScreenTransition(navigator = navigator)
119-
GetPermissions(uiPreferences)
138+
// Pass the application context to GetPermissions
139+
GetPermissions(uiPreferences, context = this@MainActivity)
120140
}
121141

122142
HandleOnNewIntent(this, navigator)
123143
}
124-
125144
}
126145
}
127-
128146
}
129147
}
130148
}
131149

150+
/**
151+
* Request necessary permissions based on Android version
152+
*/
153+
private fun requestNecessaryPermissions() {
154+
// Handled by GetPermissions composable, but we want to
155+
// trigger system dialogs as early as possible
156+
if (!uiPreferences.savedLocalCatalogLocation().get()) {
157+
// Let the GetPermissions composable handle the actual permission requests
158+
// as it has proper UI feedback
159+
}
160+
}
161+
132162
@Composable
133163
private fun SetDefaultTheme() {
134164
val themeMode by uiPreferences.themeMode().asStateIn(rememberCoroutineScope())
Lines changed: 127 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,152 @@
11
package org.ireader.app.initiators
22

3+
import android.content.Context
4+
import android.content.Intent
5+
import android.net.Uri
36
import android.os.Build
7+
import android.os.Environment
8+
import android.provider.Settings
9+
import androidx.activity.compose.rememberLauncherForActivityResult
10+
import androidx.activity.result.contract.ActivityResultContracts
11+
import androidx.compose.material3.AlertDialog
12+
import androidx.compose.material3.Text
13+
import androidx.compose.material3.TextButton
414
import androidx.compose.runtime.Composable
515
import androidx.compose.runtime.LaunchedEffect
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.runtime.mutableStateOf
618
import androidx.compose.runtime.remember
19+
import androidx.compose.runtime.rememberCoroutineScope
20+
import androidx.compose.runtime.setValue
721
import com.google.accompanist.permissions.ExperimentalPermissionsApi
22+
import com.google.accompanist.permissions.isGranted
23+
import com.google.accompanist.permissions.rememberMultiplePermissionsState
824
import com.google.accompanist.permissions.rememberPermissionState
925
import ireader.domain.preferences.prefs.UiPreferences
26+
import ireader.i18n.localize
27+
import ireader.i18n.resources.MR
28+
import kotlinx.coroutines.launch
1029

1130
@OptIn(ExperimentalPermissionsApi::class)
1231
@Composable
13-
fun GetPermissions(uiPreferences: UiPreferences) {
14-
32+
fun GetPermissions(uiPreferences: UiPreferences, context: Context) {
33+
var showRationale by remember { mutableStateOf(false) }
34+
val scope = rememberCoroutineScope()
35+
1536
val useLocalCache = remember {
1637
uiPreferences.savedLocalCatalogLocation().get()
1738
}
18-
val readStoragePermission = rememberPermissionState(
19-
android.Manifest.permission.READ_EXTERNAL_STORAGE,
20-
) {
21-
uiPreferences.savedLocalCatalogLocation().set(!it)
22-
}
23-
val writeStoragePermission = rememberPermissionState(
24-
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
25-
) {
26-
uiPreferences.savedLocalCatalogLocation().set(!it)
39+
40+
// Permission state based on Android version
41+
val permissionsState = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
42+
// Android 13+ uses READ_MEDIA_* permissions
43+
rememberMultiplePermissionsState(
44+
listOf(
45+
android.Manifest.permission.READ_MEDIA_IMAGES,
46+
android.Manifest.permission.READ_MEDIA_VIDEO,
47+
android.Manifest.permission.POST_NOTIFICATIONS
48+
)
49+
) { results ->
50+
val allGranted = results.all { it.value }
51+
uiPreferences.savedLocalCatalogLocation().set(!allGranted)
52+
}
53+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
54+
// Android 11-12 uses scoped storage
55+
rememberMultiplePermissionsState(
56+
listOf(
57+
android.Manifest.permission.READ_EXTERNAL_STORAGE,
58+
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
59+
)
60+
) { results ->
61+
val allGranted = results.all { it.value }
62+
uiPreferences.savedLocalCatalogLocation().set(!allGranted)
63+
}
64+
} else {
65+
// Android 10 and below use traditional storage permissions
66+
rememberMultiplePermissionsState(
67+
listOf(
68+
android.Manifest.permission.READ_EXTERNAL_STORAGE,
69+
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
70+
)
71+
) { results ->
72+
val allGranted = results.all { it.value }
73+
uiPreferences.savedLocalCatalogLocation().set(!allGranted)
74+
}
2775
}
28-
val allFileAccessPermission = rememberPermissionState(
29-
android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,
76+
77+
// Request ALL_FILES_ACCESS for Android 11+
78+
val allFilesAccessLauncher = rememberLauncherForActivityResult(
79+
ActivityResultContracts.StartActivityForResult()
3080
) {
31-
uiPreferences.savedLocalCatalogLocation().set(!it)
81+
// Check if permission was granted
82+
val hasAllFilesAccess = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
83+
Environment.isExternalStorageManager()
84+
} else {
85+
false
86+
}
87+
uiPreferences.savedLocalCatalogLocation().set(!hasAllFilesAccess)
3288
}
89+
3390
LaunchedEffect(key1 = true) {
3491
if (!useLocalCache) {
35-
readStoragePermission.launchPermissionRequest()
36-
writeStoragePermission.launchPermissionRequest()
92+
// Request permissions based on Android version
3793
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
38-
allFileAccessPermission.launchPermissionRequest()
94+
// For Android 11+ request MANAGE_EXTERNAL_STORAGE
95+
if (!Environment.isExternalStorageManager()) {
96+
try {
97+
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
98+
data = Uri.parse("package:${context.packageName}")
99+
addCategory("android.intent.category.DEFAULT")
100+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
101+
}
102+
allFilesAccessLauncher.launch(intent)
103+
} catch (e: Exception) {
104+
// Fallback if the specific intent doesn't work
105+
try {
106+
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
107+
allFilesAccessLauncher.launch(intent)
108+
} catch (e: Exception) {
109+
showRationale = true
110+
}
111+
}
112+
}
113+
}
114+
115+
// Request media permissions
116+
if (!permissionsState.allPermissionsGranted) {
117+
permissionsState.launchMultiplePermissionRequest()
39118
}
40119
}
41120
}
121+
122+
// Show dialog if permissions can't be requested automatically
123+
if (showRationale) {
124+
AlertDialog(
125+
onDismissRequest = { showRationale = false },
126+
title = { Text(localize(MR.strings.permission_required)) },
127+
text = { Text(localize(MR.strings.storage_permission_required_explanation)) },
128+
confirmButton = {
129+
TextButton(
130+
onClick = {
131+
scope.launch {
132+
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
133+
data = Uri.fromParts("package", context.packageName, null)
134+
}
135+
context.startActivity(intent)
136+
showRationale = false
137+
}
138+
}
139+
) {
140+
Text(localize(MR.strings.settings))
141+
}
142+
},
143+
dismissButton = {
144+
TextButton(
145+
onClick = { showRationale = false }
146+
) {
147+
Text(localize(MR.strings.cancel))
148+
}
149+
}
150+
)
151+
}
42152
}

android/src/main/res/xml/provider_paths.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
<external-cache-path
1010
name="ext_cache_files"
1111
path="." />
12+
<external-files-path
13+
name="external_files"
14+
path="downloads" />
15+
<external-files-path
16+
name="all_external_files"
17+
path="." />
1218
<!--suppress AndroidElementNotAllowed -->
1319
<root-path
1420
name="ext_storage"

0 commit comments

Comments
 (0)