add InstallAppActivity (#31)

This commit is contained in:
BinTianqi
2024-06-05 14:06:02 +08:00
parent c8e7b23cd8
commit 0918445314
5 changed files with 152 additions and 28 deletions

View File

@@ -51,6 +51,22 @@
android:windowSoftInputMode="adjustResize|stateHidden"
android:theme="@style/Theme.OwnDroid">
</activity>
<activity
android:name=".InstallAppActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize|stateHidden"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:theme="@style/Theme.OwnDroidAppInstaller">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.intent.action.INSTALL_PACKAGE"/>
<data android:scheme="content"/>
<data android:scheme="file"/>
<data android:mimeType="application/vnd.android.package-archive"/>
</intent-filter>
</activity>
<receiver
android:name=".Receiver"
android:description="@string/app_name"

View File

@@ -0,0 +1,60 @@
package com.bintianqi.owndroid
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.fragment.app.FragmentActivity
import com.bintianqi.owndroid.dpm.installPackage
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
class InstallAppActivity: FragmentActivity() {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
this.intent = intent
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val sharedPref = applicationContext.getSharedPreferences("data", Context.MODE_PRIVATE)
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
setContent {
OwnDroidTheme(
sharedPref.getBoolean("material_you", true),
sharedPref.getBoolean("black_theme", false)
) {
AlertDialog(
title = {
Text(stringResource(R.string.install_app))
},
onDismissRequest = {
finish()
},
dismissButton = {
TextButton(onClick = { finish() }) { Text(stringResource(R.string.cancel)) }
},
confirmButton = {
TextButton(
onClick = {
uriToStream(applicationContext, this.intent.data) { stream -> installPackage(applicationContext, stream)}
}
) {
Text(stringResource(R.string.confirm))
}
},
modifier = Modifier.fillMaxWidth()
)
}
}
}
}

View File

@@ -11,9 +11,7 @@ import android.app.admin.PackagePolicy.PACKAGE_POLICY_ALLOWLIST
import android.app.admin.PackagePolicy.PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM
import android.app.admin.PackagePolicy.PACKAGE_POLICY_BLOCKLIST
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager.NameNotFoundException
import android.net.Uri
import android.os.Build.VERSION
@@ -23,16 +21,45 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material3.*
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.*
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -51,11 +78,19 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.*
import com.bintianqi.owndroid.PackageInstallerReceiver
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ui.*
import java.io.IOException
import java.io.InputStream
import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.toText
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.uriToStream
import java.util.concurrent.Executors
private var dialogConfirmButtonAction = {}
@@ -1104,21 +1139,3 @@ private fun AppControlDialog(status: MutableIntState) {
}
}
}
@Throws(IOException::class)
private fun installPackage(context: Context, inputStream: InputStream) {
val packageInstaller = context.packageManager.packageInstaller
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = packageInstaller.createSession(params)
val session = packageInstaller.openSession(sessionId)
val out = session.openWrite("COSU", 0, -1)
val buffer = ByteArray(65536)
var c: Int
while(inputStream.read(buffer).also{c = it}!=-1) { out.write(buffer, 0, c) }
session.fsync(out)
inputStream.close()
out.close()
val intent = Intent(context, PackageInstallerReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_IMMUTABLE).intentSender
session.commit(pendingIntent)
}

View File

@@ -1,11 +1,17 @@
package com.bintianqi.owndroid.dpm
import android.app.PendingIntent
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.os.Build.VERSION
import androidx.activity.result.ActivityResultLauncher
import com.bintianqi.owndroid.PackageInstallerReceiver
import kotlinx.coroutines.flow.MutableStateFlow
import java.io.IOException
import java.io.InputStream
var selectedPermission = MutableStateFlow("")
lateinit var createManagedProfile: ActivityResultLauncher<Intent>
@@ -22,3 +28,21 @@ fun isProfileOwner(dpm: DevicePolicyManager): Boolean {
fun DevicePolicyManager.isOrgProfile(receiver: ComponentName):Boolean {
return VERSION.SDK_INT >= 30 && isProfileOwner(this) && isManagedProfile(receiver) && isOrganizationOwnedDeviceWithManagedProfile
}
@Throws(IOException::class)
fun installPackage(context: Context, inputStream: InputStream) {
val packageInstaller = context.packageManager.packageInstaller
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = packageInstaller.createSession(params)
val session = packageInstaller.openSession(sessionId)
val out = session.openWrite("COSU", 0, -1)
val buffer = ByteArray(65536)
var c: Int
while(inputStream.read(buffer).also{c = it}!=-1) { out.write(buffer, 0, c) }
session.fsync(out)
inputStream.close()
out.close()
val intent = Intent(context, PackageInstallerReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_IMMUTABLE).intentSender
session.commit(pendingIntent)
}

View File

@@ -4,4 +4,11 @@
<item name="android:navigationBarColor">#FFFFFF</item>
<item name="android:statusBarColor">#FFFFFF</item>
</style>
<style name="Theme.OwnDroidAppInstaller" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>