From ed330a2b634408075c42dfd62476193dec9ae1d3 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Tue, 24 Feb 2026 19:27:24 +0800 Subject: [PATCH] feat: support provisioning (#187) --- app/src/main/AndroidManifest.xml | 14 +++ .../owndroid/activity/ProvisioningActivity.kt | 32 +++++++ .../ProvisioningPolicyComplianceActivity.kt | 15 ++++ .../feature/provisioning/ProvisioningModel.kt | 9 ++ .../provisioning/ProvisioningScreen.kt | 87 +++++++++++++++++++ .../provisioning/ProvisioningViewModel.kt | 31 +++++++ 6 files changed, 188 insertions(+) create mode 100644 app/src/main/java/com/bintianqi/owndroid/activity/ProvisioningActivity.kt create mode 100644 app/src/main/java/com/bintianqi/owndroid/activity/ProvisioningPolicyComplianceActivity.kt create mode 100644 app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningModel.kt create mode 100644 app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningScreen.kt create mode 100644 app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningViewModel.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e0e9e7d..38c707b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -94,6 +94,20 @@ + + + + + + + + + + () + vm.params = vm.getParamsFromIntent(intent) + setContent { + val theme by myApp.container.themeState.collectAsState() + OwnDroidTheme(theme) { + ProvisioningScreen(vm.params) { + setResult(RESULT_OK, vm.buildResultIntent(it)) + finish() + } + } + } + } +} diff --git a/app/src/main/java/com/bintianqi/owndroid/activity/ProvisioningPolicyComplianceActivity.kt b/app/src/main/java/com/bintianqi/owndroid/activity/ProvisioningPolicyComplianceActivity.kt new file mode 100644 index 0000000..e317298 --- /dev/null +++ b/app/src/main/java/com/bintianqi/owndroid/activity/ProvisioningPolicyComplianceActivity.kt @@ -0,0 +1,15 @@ +package com.bintianqi.owndroid.activity + +import android.os.Bundle +import androidx.activity.ComponentActivity +import com.bintianqi.owndroid.R +import com.bintianqi.owndroid.utils.popToast + +class ProvisioningPolicyComplianceActivity: ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_OK) + popToast(R.string.app_name) + finish() + } +} diff --git a/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningModel.kt b/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningModel.kt new file mode 100644 index 0000000..0d1b13e --- /dev/null +++ b/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningModel.kt @@ -0,0 +1,9 @@ +package com.bintianqi.owndroid.feature.provisioning + +class ProvisioningParams( + val imei: String?, val serial: String?, val modes: List +) + +class ProvisioningOptions( + val skipEncryption: Boolean +) diff --git a/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningScreen.kt b/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningScreen.kt new file mode 100644 index 0000000..1c6d1c9 --- /dev/null +++ b/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningScreen.kt @@ -0,0 +1,87 @@ +package com.bintianqi.owndroid.feature.provisioning + +import android.app.admin.DevicePolicyManager +import androidx.annotation.RequiresApi +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.bintianqi.owndroid.R +import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem +import com.bintianqi.owndroid.utils.BottomPadding +import com.bintianqi.owndroid.utils.HorizontalPadding +import com.bintianqi.owndroid.utils.adaptiveInsets + +@RequiresApi(29) +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ProvisioningScreen(params: ProvisioningParams, callback: (ProvisioningOptions) -> Unit) { + Scaffold( + topBar = { + TopAppBar( + { Text(stringResource(R.string.app_name)) } + ) + }, + contentWindowInsets = adaptiveInsets() + ) { paddingValues -> + Column( + Modifier + .padding(paddingValues) + .verticalScroll(rememberScrollState()) + ) { + Column(Modifier.padding(horizontal = HorizontalPadding)) { + if (!params.imei.isNullOrEmpty()) { + Text("IMEI", style = MaterialTheme.typography.titleMedium) + Text(params.imei, Modifier.padding(bottom = 8.dp)) + } + if (!params.serial.isNullOrEmpty()) { + Text( + stringResource(R.string.serial_number), + style = MaterialTheme.typography.titleMedium + ) + Text(params.serial, Modifier.padding(bottom = 8.dp)) + } + } + if (DevicePolicyManager.PROVISIONING_MODE_FULLY_MANAGED_DEVICE in params.modes) { + Spacer(Modifier.height(10.dp)) + var skipEncryption by remember { mutableStateOf(false) } + FullWidthCheckBoxItem(R.string.skip_encryption, skipEncryption) { + skipEncryption = it + } + Button( + { + callback(ProvisioningOptions(skipEncryption)) + }, + Modifier + .fillMaxWidth() + .padding(HorizontalPadding, 4.dp) + ) { + Text(stringResource(R.string.continue_str)) + } + } else { + Text( + stringResource(R.string.unsupported), + style = MaterialTheme.typography.titleLarge + ) + } + Spacer(Modifier.height(BottomPadding)) + } + } +} diff --git a/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningViewModel.kt new file mode 100644 index 0000000..9de3114 --- /dev/null +++ b/app/src/main/java/com/bintianqi/owndroid/feature/provisioning/ProvisioningViewModel.kt @@ -0,0 +1,31 @@ +package com.bintianqi.owndroid.feature.provisioning + +import android.app.admin.DevicePolicyManager +import android.content.Intent +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.lifecycle.ViewModel + +@RequiresApi(29) +class ProvisioningViewModel : ViewModel() { + lateinit var params: ProvisioningParams + + fun getParamsFromIntent(intent: Intent): ProvisioningParams { + val modes = if (Build.VERSION.SDK_INT >= 31) intent.getIntegerArrayListExtra( + DevicePolicyManager.EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES + ) else null + return ProvisioningParams( + intent.getStringExtra(DevicePolicyManager.EXTRA_PROVISIONING_IMEI), + intent.getStringExtra(DevicePolicyManager.EXTRA_PROVISIONING_SERIAL_NUMBER), + modes ?: listOf(DevicePolicyManager.PROVISIONING_MODE_FULLY_MANAGED_DEVICE) + ) + } + + fun buildResultIntent(options: ProvisioningOptions): Intent { + val intent = Intent() + intent.putExtra( + DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION, options.skipEncryption + ) + return intent + } +}