Skip to content

Commit efe4d39

Browse files
committed
fix: Improve APK load error messages with distinct failure reasons
1 parent cb3551d commit efe4d39

2 files changed

Lines changed: 40 additions & 20 deletions

File tree

app/src/main/java/app/morphe/manager/ui/viewmodel/HomeViewModel.kt

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,14 +1217,15 @@ class HomeViewModel(
12171217
}
12181218

12191219
viewModelScope.launch {
1220-
val selectedApp = withContext(Dispatchers.IO) {
1220+
val result = withContext(Dispatchers.IO) {
12211221
loadLocalApk(app, uri)
12221222
}
12231223

1224-
if (selectedApp != null) {
1225-
processSelectedApp(selectedApp)
1226-
} else {
1227-
app.toast(app.getString(R.string.home_invalid_apk))
1224+
when (result) {
1225+
is ApkLoadResult.Success -> processSelectedApp(result.app)
1226+
is ApkLoadResult.Unreadable -> app.toast(app.getString(R.string.home_invalid_apk_unreadable))
1227+
is ApkLoadResult.NotAnApk -> app.toast(app.getString(R.string.home_invalid_apk_not_an_apk))
1228+
is ApkLoadResult.IoError -> app.toast(app.getString(R.string.home_invalid_apk_io_error))
12281229
}
12291230
}
12301231
}
@@ -2211,22 +2212,25 @@ class HomeViewModel(
22112212
private suspend fun loadLocalApk(
22122213
context: Context,
22132214
uri: Uri
2214-
): SelectedApp.Local? = withContext(Dispatchers.IO) {
2215+
): ApkLoadResult = withContext(Dispatchers.IO) {
22152216
try {
22162217
// Copy file to uiTempDir with original extension detection
22172218
val fileName = context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
22182219
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
2219-
cursor.moveToFirst()
2220-
cursor.getString(nameIndex)
2220+
if (cursor.moveToFirst() && nameIndex != -1) cursor.getString(nameIndex) else null
22212221
} ?: "temp_${System.currentTimeMillis()}"
22222222

22232223
val extension = fileName.substringAfterLast('.', "apk").lowercase()
22242224
val tempFile = filesystem.uiTempDir.resolve("temp_apk_${System.currentTimeMillis()}.$extension")
22252225

2226-
context.contentResolver.openInputStream(uri)?.use { input ->
2227-
tempFile.outputStream().use { output ->
2228-
input.copyTo(output)
2229-
}
2226+
// openInputStream can return null when the provider is unavailable
2227+
// e.g. Samsung External Storage restricted by Battery Optimization
2228+
val bytesCopied = context.contentResolver.openInputStream(uri)?.use { input ->
2229+
tempFile.outputStream().use { output -> input.copyTo(output) }
2230+
}
2231+
if (bytesCopied == null || bytesCopied == 0L) {
2232+
tempFile.delete()
2233+
return@withContext ApkLoadResult.Unreadable
22302234
}
22312235

22322236
// Check if it's a split APK archive
@@ -2252,18 +2256,32 @@ class HomeViewModel(
22522256

22532257
if (packageInfo == null) {
22542258
tempFile.delete()
2255-
return@withContext null
2259+
return@withContext ApkLoadResult.NotAnApk
22562260
}
22572261

2258-
SelectedApp.Local(
2259-
packageName = packageInfo.packageName,
2260-
version = packageInfo.versionName ?: "unknown",
2261-
file = tempFile,
2262-
temporary = true
2262+
ApkLoadResult.Success(
2263+
SelectedApp.Local(
2264+
packageName = packageInfo.packageName,
2265+
version = packageInfo.versionName ?: "unknown",
2266+
file = tempFile,
2267+
temporary = true
2268+
)
22632269
)
22642270
} catch (e: Exception) {
22652271
Log.e(tag, "Failed to load APK", e)
2266-
null
2272+
ApkLoadResult.IoError
22672273
}
22682274
}
22692275
}
2276+
2277+
/** Result of attempting to load a local APK file. */
2278+
private sealed interface ApkLoadResult {
2279+
/** File was read and parsed successfully. */
2280+
data class Success(val app: SelectedApp.Local) : ApkLoadResult
2281+
/** File could not be read - provider returned null stream or zero bytes. */
2282+
data object Unreadable : ApkLoadResult
2283+
/** File was read but is not a valid APK/split archive. */
2284+
data object NotAnApk : ApkLoadResult
2285+
/** An unexpected IO or system exception occurred while copying or parsing. */
2286+
data object IoError : ApkLoadResult
2287+
}

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@
125125
<string name="home_selected_version">Selected version</string>
126126
<string name="home_select_apk_title">Select APK</string>
127127
<string name="home_select_any_apk_description">Tap the button below to select an APK file of any app for patching</string>
128-
<string name="home_invalid_apk">Invalid APK file</string>
128+
<string name="home_invalid_apk_unreadable">Could not read the file. Check that 'External Storage' is not battery restricted</string>
129+
<string name="home_invalid_apk_not_an_apk">The selected file is not a valid APK</string>
130+
<string name="home_invalid_apk_io_error">Failed to open the file. Please try again</string>
129131

130132
<!-- Home Screen - Dialog 4: Expert Mode Dialog -->
131133
<string name="expert_mode_title">Expert mode</string>

0 commit comments

Comments
 (0)