VancedMicroG/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsConfir...

241 lines
10 KiB
Kotlin

/*
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.gms.nearby.core.ui
import android.bluetooth.BluetoothAdapter
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Build
import android.os.Bundle
import android.os.ResultReceiver
import android.provider.Settings
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.location.LocationManagerCompat
import androidx.lifecycle.lifecycleScope
import org.microg.gms.nearby.exposurenotification.*
import org.microg.gms.ui.getApplicationInfoIfExists
class ExposureNotificationsConfirmActivity : AppCompatActivity() {
private var resultCode: Int = RESULT_CANCELED
set(value) {
setResult(value)
field = value
}
private val receiver: ResultReceiver?
get() = intent.getParcelableExtra(KEY_CONFIRM_RECEIVER)
private val action: String?
get() = intent.getStringExtra(KEY_CONFIRM_ACTION)
private val targetPackageName: String?
get() = intent.getStringExtra(KEY_CONFIRM_PACKAGE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.exposure_notifications_confirm_activity)
val applicationInfo = packageManager.getApplicationInfoIfExists(targetPackageName)
val selfApplicationInfo = packageManager.getApplicationInfoIfExists(packageName)
when (action) {
CONFIRM_ACTION_START -> {
findViewById<TextView>(android.R.id.title).text = getString(R.string.exposure_confirm_start_title)
findViewById<TextView>(android.R.id.summary).text = getString(R.string.exposure_confirm_start_summary, applicationInfo?.loadLabel(packageManager)
?: targetPackageName)
findViewById<Button>(android.R.id.button1).text = getString(R.string.exposure_confirm_start_button)
findViewById<TextView>(R.id.grant_permission_summary).text = getString(R.string.exposure_confirm_permission_description, selfApplicationInfo?.loadLabel(packageManager)
?: packageName)
checkPermissions()
checkBluetooth()
checkLocation()
}
CONFIRM_ACTION_STOP -> {
findViewById<TextView>(android.R.id.title).text = getString(R.string.exposure_confirm_stop_title)
findViewById<TextView>(android.R.id.summary).text = getString(R.string.exposure_confirm_stop_summary)
findViewById<Button>(android.R.id.button1).text = getString(R.string.exposure_confirm_stop_button)
}
CONFIRM_ACTION_KEYS -> {
findViewById<TextView>(android.R.id.title).text = getString(R.string.exposure_confirm_keys_title, applicationInfo?.loadLabel(packageManager)
?: targetPackageName)
findViewById<TextView>(android.R.id.summary).text = getString(R.string.exposure_confirm_keys_summary)
findViewById<Button>(android.R.id.button1).text = getString(R.string.exposure_confirm_keys_button)
}
else -> {
resultCode = RESULT_CANCELED
finish()
}
}
findViewById<Button>(android.R.id.button1).setOnClickListener {
resultCode = RESULT_OK
finish()
}
findViewById<Button>(android.R.id.button2).setOnClickListener {
resultCode = RESULT_CANCELED
finish()
}
findViewById<Button>(R.id.grant_permission_button).setOnClickListener {
requestPermissions()
}
findViewById<Button>(R.id.grant_background_location_button).setOnClickListener {
requestBackgroundLocation()
}
findViewById<Button>(R.id.enable_bluetooth_button).setOnClickListener {
requestBluetooth()
}
findViewById<Button>(R.id.enable_location_button).setOnClickListener {
requestLocation()
}
}
override fun onResume() {
super.onResume()
if (permissionNeedsHandling) checkPermissions()
if (bluetoothNeedsHandling) checkBluetooth()
if (locationNeedsHandling) checkLocation()
}
private fun updateButton() {
findViewById<Button>(android.R.id.button1).isEnabled =
!permissionNeedsHandling && !backgroundLocationNeedsHandling && !bluetoothNeedsHandling && !locationNeedsHandling
}
// Permissions
private var permissionNeedsHandling: Boolean = false
private var backgroundLocationNeedsHandling: Boolean = false
private var permissionRequestCode = 33
private val permissions by lazy {
when {
Build.VERSION.SDK_INT >= 31 -> {
// We shouldn't be needing the LOCATION permissions on 31+ anymore, at least when
// apps making use of this target 31+ as well, but this needs more testing. See
// https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location
arrayOf(
"android.permission.BLUETOOTH_ADVERTISE",
"android.permission.BLUETOOTH_SCAN",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION"
)
}
Build.VERSION.SDK_INT == 29 -> {
// We only can directly request background location permission on 29.
// We need it on 30 (and possibly later) as well, but it has to be requested in a two
// step process, see https://fosstodon.org/@utf8equalsX/104359649537615235
arrayOf(
"android.permission.ACCESS_BACKGROUND_LOCATION",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION"
)
}
else -> {
// Below 29 or equals 30
arrayOf(
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION"
)
}
}
}
private fun checkPermissions() {
permissionNeedsHandling = Build.VERSION.SDK_INT >= 23 && permissions.any {
ContextCompat.checkSelfPermission(
this,
it
) != PackageManager.PERMISSION_GRANTED
}
backgroundLocationNeedsHandling = Build.VERSION.SDK_INT >= 30
&& ContextCompat.checkSelfPermission(
this,
"android.permission.ACCESS_FINE_LOCATION"
) == PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(
this,
"android.permission.ACCESS_BACKGROUND_LOCATION"
) != PackageManager.PERMISSION_GRANTED
findViewById<View>(R.id.grant_permission_view).visibility =
if (permissionNeedsHandling) View.VISIBLE else View.GONE
findViewById<View>(R.id.grant_background_location_view).visibility =
if (!permissionNeedsHandling && backgroundLocationNeedsHandling) View.VISIBLE else View.GONE
updateButton()
}
private fun requestPermissions() {
if (Build.VERSION.SDK_INT >= 23) {
requestPermissions(permissions, ++permissionRequestCode)
}
}
private fun requestBackgroundLocation() {
if (Build.VERSION.SDK_INT >= 23) {
requestPermissions(arrayOf("android.permission.ACCESS_BACKGROUND_LOCATION"), ++permissionRequestCode)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == this.permissionRequestCode) checkPermissions()
}
// Bluetooth
private var bluetoothNeedsHandling: Boolean = false
private var bluetoothRequestCode = 112
private fun checkBluetooth() {
val adapter = BluetoothAdapter.getDefaultAdapter()
bluetoothNeedsHandling = adapter?.isEnabled != true
findViewById<View>(R.id.enable_bluetooth_view).visibility = if (adapter?.isEnabled == false) View.VISIBLE else View.GONE
updateButton()
}
private fun requestBluetooth() {
val adapter = BluetoothAdapter.getDefaultAdapter()
findViewById<View>(R.id.enable_bluetooth_spinner).visibility = View.VISIBLE
findViewById<View>(R.id.enable_bluetooth_button).visibility = View.INVISIBLE
lifecycleScope.launchWhenStarted {
if (adapter != null && !adapter.enableAsync(this@ExposureNotificationsConfirmActivity)) {
requestBluetoothViaIntent()
} else {
checkBluetooth()
}
findViewById<View>(R.id.enable_bluetooth_spinner).visibility = View.INVISIBLE
findViewById<View>(R.id.enable_bluetooth_button).visibility = View.VISIBLE
}
}
private fun requestBluetoothViaIntent() {
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(intent, ++bluetoothRequestCode)
}
// Location
private var locationNeedsHandling: Boolean = false
private var locationRequestCode = 231
private fun checkLocation() {
locationNeedsHandling = !LocationManagerCompat.isLocationEnabled(getSystemService(Context.LOCATION_SERVICE) as LocationManager)
findViewById<View>(R.id.enable_location_view).visibility = if (locationNeedsHandling) View.VISIBLE else View.GONE
updateButton()
}
private fun requestLocation() {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivityForResult(intent, ++locationRequestCode)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == bluetoothRequestCode) checkBluetooth()
}
override fun finish() {
receiver?.send(resultCode, Bundle())
super.finish()
}
}