simplify code of getting file

optimize animation
This commit is contained in:
BinTianqi
2024-05-11 15:34:10 +08:00
parent 8224211f3c
commit a8de0ed986
11 changed files with 82 additions and 59 deletions

View File

@@ -1,6 +1,5 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.app.Activity
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
@@ -10,7 +9,6 @@ import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
@@ -49,28 +47,11 @@ import kotlinx.coroutines.delay
var backToHome = false var backToHome = false
@ExperimentalMaterial3Api @ExperimentalMaterial3Api
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
private fun registerActivityResult(){
getUserIcon = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { userIconUri = it.data?.data }
getApk = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { apkUri = it.data?.data }
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
uriToStream(applicationContext,it.data?.data){stream->
caCert = stream.readBytes()
if(caCert.size>5000){ Toast.makeText(applicationContext, R.string.file_too_large, Toast.LENGTH_SHORT).show(); caCert = byteArrayOf() }
}
}
createManagedProfile = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {}
addDeviceAdmin = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val myDpm = applicationContext.getSystemService(DEVICE_POLICY_SERVICE) as DevicePolicyManager
if(myDpm.isAdminActive(ComponentName(applicationContext, Receiver::class.java))){
backToHome = true
}
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
registerActivityResult() registerActivityResult(this)
setContent { setContent {
OwnDroidTheme { OwnDroidTheme {
MyScaffold() MyScaffold()

View File

@@ -51,6 +51,7 @@ class PackageInstallerReceiver:BroadcastReceiver(){
else->R.string.unknown else->R.string.unknown
} }
Log.e("OwnDroid", intent.getIntExtra(EXTRA_STATUS,999).toString()) Log.e("OwnDroid", intent.getIntExtra(EXTRA_STATUS,999).toString())
if(toastText!=999){Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show()} val text = context.getString(R.string.app_installer_status) + context.getString(toastText)
if(toastText!=999){Toast.makeText(context, text, Toast.LENGTH_SHORT).show()}
} }
} }

View File

@@ -1,14 +1,20 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.content.ClipData import android.app.admin.DevicePolicyManager
import android.content.ClipboardManager import android.content.*
import android.content.Context
import android.net.Uri import android.net.Uri
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import com.bintianqi.owndroid.dpm.*
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
lateinit var getFile: ActivityResultLauncher<Intent>
var fileUri: Uri? = null
fun uriToStream( fun uriToStream(
context: Context, context: Context,
uri: Uri?, uri: Uri?,
@@ -54,3 +60,23 @@ fun writeClipBoard(context: Context, string: String):Boolean{
} }
return true return true
} }
fun registerActivityResult(context: ComponentActivity){
getFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {activityResult ->
activityResult.data.let {
if(it==null){
Toast.makeText(context.applicationContext, R.string.file_not_exist, Toast.LENGTH_SHORT).show()
}else{
fileUri = it.data
}
}
}
createManagedProfile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {}
addDeviceAdmin = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val myDpm = context.applicationContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
if(myDpm.isAdminActive(ComponentName(context.applicationContext, Receiver::class.java))){
backToHome = true
}
}
}

View File

@@ -229,6 +229,9 @@ private fun Home(navCtrl:NavHostController, pkgName: String){
SubPageItem(R.string.set_default_dialer,"",R.drawable.call_fill0){navCtrl.navigate("DefaultDialer")} SubPageItem(R.string.set_default_dialer,"",R.drawable.call_fill0){navCtrl.navigate("DefaultDialer")}
} }
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) {
fileUri = null
}
} }
} }
@@ -810,19 +813,19 @@ private fun InstallApp(){
val installApkIntent = Intent(Intent.ACTION_GET_CONTENT) val installApkIntent = Intent(Intent.ACTION_GET_CONTENT)
installApkIntent.setType("application/vnd.android.package-archive") installApkIntent.setType("application/vnd.android.package-archive")
installApkIntent.addCategory(Intent.CATEGORY_OPENABLE) installApkIntent.addCategory(Intent.CATEGORY_OPENABLE)
getApk.launch(installApkIntent) getFile.launch(installApkIntent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.select_apk)) Text(stringResource(R.string.select_apk))
} }
var selected by remember{mutableStateOf(false)} var selected by remember{mutableStateOf(false)}
LaunchedEffect(selected){while(true){ delay(800); selected = apkUri!=null}} LaunchedEffect(selected){while(true){ delay(800); selected = fileUri!=null}}
AnimatedVisibility(selected) { AnimatedVisibility(selected) {
Spacer(Modifier.padding(vertical = 3.dp)) Spacer(Modifier.padding(vertical = 3.dp))
Column(modifier = Modifier.fillMaxWidth()){ Column(modifier = Modifier.fillMaxWidth()){
Button( Button(
onClick = { uriToStream(myContext, apkUri){stream -> installPackage(myContext,stream)} }, onClick = { uriToStream(myContext, fileUri){stream -> installPackage(myContext,stream)} },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.silent_install)) Text(stringResource(R.string.silent_install))
@@ -830,7 +833,7 @@ private fun InstallApp(){
Button( Button(
onClick = { onClick = {
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
intent.setData(apkUri) intent.setData(fileUri)
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
myContext.startActivity(intent) myContext.startActivity(intent)
}, },

View File

@@ -9,16 +9,9 @@ var selectedPackage = ""
var applySelectedPackage = false var applySelectedPackage = false
var selectedPermission = "" var selectedPermission = ""
var applySelectedPermission = false var applySelectedPermission = false
lateinit var getCaCert: ActivityResultLauncher<Intent>
lateinit var createManagedProfile: ActivityResultLauncher<Intent> lateinit var createManagedProfile: ActivityResultLauncher<Intent>
lateinit var getApk: ActivityResultLauncher<Intent>
lateinit var getUserIcon: ActivityResultLauncher<Intent>
lateinit var addDeviceAdmin: ActivityResultLauncher<Intent> lateinit var addDeviceAdmin: ActivityResultLauncher<Intent>
var userIconUri: Uri? = null
var apkUri: Uri? = null
var caCert = byteArrayOf()
fun isDeviceOwner(dpm: DevicePolicyManager): Boolean { fun isDeviceOwner(dpm: DevicePolicyManager): Boolean {
return dpm.isDeviceOwnerApp("com.bintianqi.owndroid") return dpm.isDeviceOwnerApp("com.bintianqi.owndroid")
} }

View File

@@ -321,8 +321,7 @@ fun DeviceInfo(){
val encryptionStatus = mutableMapOf( val encryptionStatus = mutableMapOf(
DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE to stringResource(R.string.es_inactive), DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE to stringResource(R.string.es_inactive),
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE to stringResource(R.string.es_active), DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE to stringResource(R.string.es_active),
DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED to stringResource(R.string.es_unsupported), DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED to stringResource(R.string.es_unsupported)
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING to stringResource(R.string.unknown)
) )
if(VERSION.SDK_INT>=23){ encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY] = stringResource(R.string.es_active_default_key) } if(VERSION.SDK_INT>=23){ encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY] = stringResource(R.string.es_active_default_key) }
if(VERSION.SDK_INT>=24){ encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER] = stringResource(R.string.es_active_per_user) } if(VERSION.SDK_INT>=24){ encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER] = stringResource(R.string.es_active_per_user) }
@@ -428,6 +427,8 @@ private fun SupportMsg(){
focusMgr.clearFocus() focusMgr.clearFocus()
myDpm.setShortSupportMessage(myComponent, shortMsg) myDpm.setShortSupportMessage(myComponent, shortMsg)
myDpm.setLongSupportMessage(myComponent, longMsg) myDpm.setLongSupportMessage(myComponent, longMsg)
shortMsg = myDpm.getShortSupportMessage(myComponent)?.toString() ?: ""
longMsg = myDpm.getLongSupportMessage(myComponent)?.toString() ?: ""
Toast.makeText(myContext, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(myContext, R.string.success, Toast.LENGTH_SHORT).show()
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
@@ -440,6 +441,8 @@ private fun SupportMsg(){
focusMgr.clearFocus() focusMgr.clearFocus()
myDpm.setShortSupportMessage(myComponent, null) myDpm.setShortSupportMessage(myComponent, null)
myDpm.setLongSupportMessage(myComponent, null) myDpm.setLongSupportMessage(myComponent, null)
shortMsg = myDpm.getShortSupportMessage(myComponent)?.toString() ?: ""
longMsg = myDpm.getLongSupportMessage(myComponent)?.toString() ?: ""
Toast.makeText(myContext, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(myContext, R.string.success, Toast.LENGTH_SHORT).show()
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()

View File

@@ -41,9 +41,8 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.*
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.toText
import com.bintianqi.owndroid.ui.* import com.bintianqi.owndroid.ui.*
import com.bintianqi.owndroid.ui.theme.bgColor import com.bintianqi.owndroid.ui.theme.bgColor
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -152,7 +151,7 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState){
} }
SubPageItem(R.string.wipe_data,"",R.drawable.warning_fill0){navCtrl.navigate("WipeData")} SubPageItem(R.string.wipe_data,"",R.drawable.warning_fill0){navCtrl.navigate("WipeData")}
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit){caCert =byteArrayOf()} LaunchedEffect(Unit){fileUri=null}
} }
} }
@@ -596,34 +595,52 @@ private fun CaCert(){
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext,Receiver::class.java) val myComponent = ComponentName(myContext,Receiver::class.java)
var exist by remember{mutableStateOf(false)} var exist by remember{mutableStateOf(false)}
var isEmpty by remember{mutableStateOf(true)} var uriPath by remember{mutableStateOf("")}
var caCertByteArray = byteArrayOf()
val refresh = { val refresh = {
isEmpty = caCert.isEmpty() if(uriPath!=fileUri?.path){
exist = if(!isEmpty){ myDpm.hasCaCertInstalled(myComponent, caCert) }else{ false } if(caCertByteArray.isEmpty()){
uriToStream(myContext, fileUri){
val array = it.readBytes()
caCertByteArray = if(array.size<10000){
array
}else{
byteArrayOf()
}
}
exist = myDpm.hasCaCertInstalled(myComponent, caCertByteArray)
}
}
} }
LaunchedEffect(exist){ while(true){ refresh();delay(600) } } LaunchedEffect(exist){ while(true){ refresh();delay(500) } }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){ Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.ca_cert), style = typography.headlineLarge) Text(text = stringResource(R.string.ca_cert), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Text(text = if(isEmpty){stringResource(R.string.please_select_ca_cert)}else{stringResource(R.string.cacert_installed, exist)}, modifier = Modifier.animateContentSize()) AnimatedVisibility(uriPath!="") {
Text(text = uriPath)
}
Text(
text = if(uriPath==""){stringResource(R.string.please_select_ca_cert)}else{stringResource(R.string.cacert_installed, exist)},
modifier = Modifier.animateContentSize()
)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
val caCertIntent = Intent(Intent.ACTION_GET_CONTENT) val caCertIntent = Intent(Intent.ACTION_GET_CONTENT)
caCertIntent.setType("*/*") caCertIntent.setType("*/*")
caCertIntent.addCategory(Intent.CATEGORY_OPENABLE) caCertIntent.addCategory(Intent.CATEGORY_OPENABLE)
getCaCert.launch(caCertIntent) getFile.launch(caCertIntent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.select_ca_cert)) Text(stringResource(R.string.select_ca_cert))
} }
AnimatedVisibility(!isEmpty) { AnimatedVisibility(uriPath!="") {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
Button( Button(
onClick = { onClick = {
val result = myDpm.installCaCert(myComponent, caCert) val result = myDpm.installCaCert(myComponent, caCertByteArray)
Toast.makeText(myContext, if(result){R.string.success}else{R.string.fail}, Toast.LENGTH_SHORT).show() Toast.makeText(myContext, if(result){R.string.success}else{R.string.fail}, Toast.LENGTH_SHORT).show()
refresh() refresh()
}, },
@@ -634,8 +651,8 @@ private fun CaCert(){
Button( Button(
onClick = { onClick = {
if(exist){ if(exist){
myDpm.uninstallCaCert(myComponent, caCert) myDpm.uninstallCaCert(myComponent, caCertByteArray)
exist = myDpm.hasCaCertInstalled(myComponent, caCert) exist = myDpm.hasCaCertInstalled(myComponent, caCertByteArray)
Toast.makeText(myContext, if(exist){R.string.fail}else{R.string.success}, Toast.LENGTH_SHORT).show() Toast.makeText(myContext, if(exist){R.string.fail}else{R.string.success}, Toast.LENGTH_SHORT).show()
}else{ Toast.makeText(myContext, R.string.not_exist, Toast.LENGTH_SHORT).show() } }else{ Toast.makeText(myContext, R.string.not_exist, Toast.LENGTH_SHORT).show() }
}, },

View File

@@ -40,12 +40,10 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.*
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.toText
import com.bintianqi.owndroid.ui.* import com.bintianqi.owndroid.ui.*
import com.bintianqi.owndroid.ui.theme.bgColor import com.bintianqi.owndroid.ui.theme.bgColor
import com.bintianqi.owndroid.uriToStream
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
var affiliationID = mutableSetOf<String>() var affiliationID = mutableSetOf<String>()
@@ -122,6 +120,9 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState){
SubPageItem(R.string.affiliation_id,"",R.drawable.id_card_fill0){navCtrl.navigate("AffiliationID")} SubPageItem(R.string.affiliation_id,"",R.drawable.id_card_fill0){navCtrl.navigate("AffiliationID")}
} }
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) {
fileUri = null
}
} }
} }
@@ -509,17 +510,17 @@ private fun UserIcon(){
val intent = Intent(if(getContent){Intent.ACTION_GET_CONTENT}else{Intent.ACTION_PICK}) val intent = Intent(if(getContent){Intent.ACTION_GET_CONTENT}else{Intent.ACTION_PICK})
if(getContent){intent.addCategory(Intent.CATEGORY_OPENABLE)} if(getContent){intent.addCategory(Intent.CATEGORY_OPENABLE)}
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*") intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
getUserIcon.launch(intent) getFile.launch(intent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.select_picture)) Text(stringResource(R.string.select_picture))
} }
LaunchedEffect(Unit){ delay(600); canApply = userIconUri!=null } LaunchedEffect(Unit){ delay(600); canApply = fileUri!=null }
AnimatedVisibility(canApply) { AnimatedVisibility(canApply) {
Button( Button(
onClick = { onClick = {
uriToStream(myContext, userIconUri){stream -> uriToStream(myContext, fileUri){stream ->
val bitmap = BitmapFactory.decodeStream(stream) val bitmap = BitmapFactory.decodeStream(stream)
myDpm.setUserIcon(myComponent,bitmap) myDpm.setUserIcon(myComponent,bitmap)
Toast.makeText(myContext, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(myContext, R.string.success, Toast.LENGTH_SHORT).show()

View File

@@ -14,7 +14,7 @@ object Animations{
private val tween: FiniteAnimationSpec<IntOffset> = tween(450, easing = bezier) private val tween: FiniteAnimationSpec<IntOffset> = tween(450, easing = bezier)
val navHostEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = { val navHostEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
fadeIn(tween(83, easing = LinearEasing)) + fadeIn(tween(150, easing = LinearEasing)) +
slideIntoContainer( slideIntoContainer(
animationSpec = tween, animationSpec = tween,
towards = AnimatedContentTransitionScope.SlideDirection.End, towards = AnimatedContentTransitionScope.SlideDirection.End,

View File

@@ -43,7 +43,6 @@
<string name="copy_command">复制代码</string> <string name="copy_command">复制代码</string>
<string name="unknown_status">未知状态</string> <string name="unknown_status">未知状态</string>
<string name="copy">复制</string> <string name="copy">复制</string>
<string name="file_too_large">文件太大了</string>
<string name="file_not_exist">文件不存在</string> <string name="file_not_exist">文件不存在</string>
<string name="io_exception">IO异常</string> <string name="io_exception">IO异常</string>

View File

@@ -46,7 +46,6 @@
<string name="not_exist">Not exist</string> <string name="not_exist">Not exist</string>
<string name="unknown_status">Unknown status</string> <string name="unknown_status">Unknown status</string>
<string name="copy">Copy</string> <string name="copy">Copy</string>
<string name="file_too_large">File too large</string>
<string name="file_not_exist">File not exist</string> <string name="file_not_exist">File not exist</string>
<string name="io_exception">IO Exception</string> <string name="io_exception">IO Exception</string>