Skip to content

Commit f6748a1

Browse files
committed
Add Zip file intent for AnyKernel3 Zip Flashing
1 parent e95eec2 commit f6748a1

File tree

4 files changed

+165
-7
lines changed

4 files changed

+165
-7
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,29 @@
1818
<activity
1919
android:name=".MainActivity"
2020
android:exported="true"
21+
android:launchMode="singleTop"
2122
android:theme="@style/Theme.MainSplashScreen">
2223
<intent-filter>
2324
<action android:name="android.intent.action.MAIN" />
24-
2525
<category android:name="android.intent.category.LAUNCHER" />
2626
</intent-filter>
27+
<intent-filter>
28+
<action android:name="android.intent.action.VIEW" />
29+
<category android:name="android.intent.category.DEFAULT" />
30+
<category android:name="android.intent.category.BROWSABLE" />
31+
<data android:scheme="content" android:mimeType="application/zip" />
32+
<data android:scheme="file" android:mimeType="application/zip" />
33+
</intent-filter>
34+
<intent-filter>
35+
<action android:name="android.intent.action.VIEW" />
36+
<category android:name="android.intent.category.DEFAULT" />
37+
<category android:name="android.intent.category.BROWSABLE" />
38+
<data android:mimeType="application/zip" />
39+
<data android:scheme="file" />
40+
<data android:scheme="content" />
41+
<data android:host="*" />
42+
<data android:pathPattern=".*\\.zip" />
43+
</intent-filter>
2744
</activity>
2845
<service android:name=".FilesystemService" android:exported="false" tools:ignore="Instantiatable" />
2946
</application>

app/src/main/java/com/github/capntrips/kernelflasher/MainActivity.kt

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package com.github.capntrips.kernelflasher
22

33
import android.animation.ObjectAnimator
44
import android.animation.PropertyValuesHolder
5+
import android.annotation.SuppressLint
56
import android.app.Activity
67
import android.content.ComponentName
78
import android.content.Intent
89
import android.content.ServiceConnection
10+
import android.net.Uri
911
import android.os.Build
1012
import android.os.Bundle
1113
import android.os.IBinder
14+
import android.provider.DocumentsContract
1215
import android.util.Log
1316
import android.view.View
1417
import android.view.ViewTreeObserver
@@ -141,7 +144,11 @@ class MainActivity : ComponentActivity() {
141144
super.onCreate(savedInstanceState)
142145
WindowCompat.setDecorFitsSystemWindows(window, false)
143146

147+
val isZipIntent = intent?.action == Intent.ACTION_VIEW &&
148+
(intent.type == "application/zip" || intent.data?.toString()?.endsWith(".zip") == true)
149+
144150
splashScreen.setOnExitAnimationListener { splashScreenView ->
151+
val duration = if (isZipIntent) 100L else 250L
145152
val scale = ObjectAnimator.ofPropertyValuesHolder(
146153
splashScreenView.view,
147154
PropertyValuesHolder.ofFloat(
@@ -156,7 +163,7 @@ class MainActivity : ComponentActivity() {
156163
)
157164
)
158165
scale.interpolator = AccelerateInterpolator()
159-
scale.duration = 250L
166+
scale.duration = duration
160167
scale.doOnEnd { splashScreenView.remove() }
161168
scale.start()
162169
}
@@ -175,6 +182,8 @@ class MainActivity : ComponentActivity() {
175182
}
176183
)
177184

185+
186+
178187
Shell.getShell()
179188
if (Shell.isAppGrantedRoot()!!) {
180189
val intent = Intent(this, FilesystemService::class.java)
@@ -188,6 +197,49 @@ class MainActivity : ComponentActivity() {
188197
}
189198
}
190199

200+
@SuppressLint("WrongConstant")
201+
private fun handleZipIntent(intent: Intent?) {
202+
val action = intent?.action ?: return
203+
val uri = when (action) {
204+
Intent.ACTION_VIEW -> intent.data
205+
Intent.ACTION_SEND -> intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
206+
else -> null
207+
} ?: return
208+
Log.d(MainViewModel.Companion.TAG, intent?.data.toString())
209+
210+
if (intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_SEND) {
211+
if(uri.scheme == "content" && DocumentsContract.isDocumentUri(this, uri)) {
212+
val takeFlags =
213+
intent.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
214+
try {
215+
contentResolver.takePersistableUriPermission(uri, takeFlags)
216+
} catch (se: SecurityException) {
217+
Log.e(MainViewModel.Companion.TAG, se.message, se)
218+
}
219+
}
220+
221+
viewModel?.pendingFlashUri = uri
222+
if(viewModel?.isAb == true)
223+
viewModel?.showSlotIntentDialog?.value = true
224+
else {
225+
viewModel?.slotSuffixForFlash?.value = null
226+
viewModel?.slotSuffixForFlash?.value = viewModel?.slotSuffix
227+
}
228+
}
229+
}
230+
231+
override fun onNewIntent(intent: Intent) {
232+
super.onNewIntent(intent)
233+
setIntent(intent)
234+
if (Shell.isAppGrantedRoot() == true) {
235+
handleZipIntent(intent)
236+
if (intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_SEND) {
237+
intent.replaceExtras(Bundle()) // Clear any existing data
238+
setIntent(Intent()) // Replace with empty intent
239+
}
240+
}
241+
}
242+
191243
fun onAidlConnected(fileSystemManager: FileSystemManager) {
192244
try {
193245
Shell.cmd("cd $filesDir").exec()
@@ -217,6 +269,14 @@ class MainActivity : ComponentActivity() {
217269
val mainViewModel = viewModel!!
218270
SharedViewModels.mainViewModel = mainViewModel
219271

272+
val slotSuffix by viewModel!!.slotSuffixForFlash
273+
274+
handleZipIntent(intent)
275+
if (intent.action == Intent.ACTION_VIEW || intent.action == Intent.ACTION_SEND) {
276+
intent.replaceExtras(Bundle()) // Clear any existing data
277+
setIntent(Intent()) // Replace with empty intent
278+
}
279+
220280
val context = LocalContext.current
221281
val dialogData = viewModel!!.updateDialogData
222282
LaunchedEffect(Unit) {
@@ -228,6 +288,31 @@ class MainActivity : ComponentActivity() {
228288
viewModel!!.showUpdateDialog(title, lines, confirm)
229289
}
230290
}
291+
292+
val uri = viewModel?.pendingFlashUri
293+
294+
if (uri != null) {
295+
if (viewModel?.isAb == true && slotSuffix == null) {
296+
viewModel?.pendingFlashUri = uri
297+
viewModel?.showSlotIntentDialog?.value = true
298+
} else {
299+
// Already have slot or not AB - flash directly
300+
navController.navigate("slot${slotSuffix}/flash/ak3") {
301+
popUpTo("slot${slotSuffix}")
302+
}
303+
if (viewModel?.isAb == true && slotSuffix == "_b")
304+
{
305+
viewModel?.slotB?.flashAk3(context, uri)
306+
}
307+
else
308+
{
309+
viewModel?.slotA?.flashAk3(context, uri)
310+
}
311+
312+
viewModel?.pendingFlashUri = null
313+
viewModel?.slotSuffixForFlash?.value = null
314+
}
315+
}
231316
}
232317

233318
var showExitDialog by remember { mutableStateOf(false) }
@@ -530,6 +615,53 @@ class MainActivity : ComponentActivity() {
530615
}
531616
)
532617
}
618+
619+
if (viewModel?.showSlotIntentDialog?.value == true) {
620+
AlertDialog(
621+
onDismissRequest = { viewModel?.showSlotIntentDialog?.value = false },
622+
title = { Text("Select Slot to Flash") },
623+
text = { Text("Choose the slot where the zip should be flashed.") },
624+
confirmButton = {
625+
TextButton(onClick = {
626+
viewModel?.slotSuffixForFlash?.value = null
627+
viewModel?.slotSuffixForFlash?.value = if(viewModel?.slotSuffix == "_a") "_b" else "_a"
628+
viewModel?.showSlotIntentDialog?.value = false
629+
}) {
630+
Text("Inactive Slot")
631+
}
632+
},
633+
dismissButton = {
634+
TextButton(onClick = {
635+
viewModel?.slotSuffixForFlash?.value = null
636+
viewModel?.slotSuffixForFlash?.value = viewModel?.slotSuffix
637+
viewModel?.showSlotIntentDialog?.value = false
638+
}) {
639+
Text("Active Slot")
640+
}
641+
}
642+
)
643+
}
644+
645+
LaunchedEffect(slotSuffix) {
646+
val uri = viewModel!!.pendingFlashUri
647+
648+
if (uri != null && slotSuffix != null) {
649+
navController.navigate("slot${slotSuffix}/flash/ak3") {
650+
popUpTo("slot${slotSuffix}")
651+
}
652+
if (viewModel?.isAb == true && slotSuffix == "_b")
653+
{
654+
viewModel?.slotB?.flashAk3(context, uri)
655+
}
656+
else
657+
{
658+
viewModel?.slotA?.flashAk3(context, uri)
659+
}
660+
661+
viewModel!!.pendingFlashUri = null
662+
viewModel!!.slotSuffixForFlash.value = null
663+
}
664+
}
533665
}
534666
}
535667
}

app/src/main/java/com/github/capntrips/kernelflasher/ui/screens/main/MainViewModel.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.github.capntrips.kernelflasher.ui.screens.main
22

33
import android.annotation.SuppressLint
44
import android.content.Context
5+
import android.net.Uri
56
import android.util.Log
67
import android.widget.Toast
78
import androidx.compose.runtime.MutableState
@@ -53,6 +54,10 @@ class MainViewModel(
5354
private val _isRefreshRequired = mutableStateOf(true)
5455
private var _error: String? = null
5556
private var _backups: MutableMap<String, Backup> = mutableMapOf()
57+
var showSlotIntentDialog: MutableState<Boolean> = mutableStateOf(false)
58+
59+
var pendingFlashUri: Uri? = null
60+
var slotSuffixForFlash = mutableStateOf<String?>(null)
5661

5762
val isRefreshing: Boolean
5863
get() = _isRefreshing.value

app/src/main/java/com/github/capntrips/kernelflasher/ui/screens/slot/SlotViewModel.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -555,11 +555,15 @@ class SlotViewModel(
555555
@Suppress("FunctionName")
556556
private fun _copyFile(context: Context, uri: Uri) {
557557
flashUri = uri
558-
flashFilename = context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
559-
if (!cursor.moveToFirst()) return@use null
560-
val name = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
561-
return@use cursor.getString(name)
562-
} ?: "ak3.zip"
558+
flashFilename = if (uri.scheme == "file") {
559+
File(uri.path ?: "").name
560+
} else {
561+
context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
562+
if (!cursor.moveToFirst()) return@use null
563+
val name = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
564+
return@use cursor.getString(name)
565+
} ?: "ak3.zip"
566+
}
563567
val source = context.contentResolver.openInputStream(uri)
564568
val file = File(context.filesDir, flashFilename!!)
565569
source.use { inputStream ->

0 commit comments

Comments
 (0)