srctree

Robin Linden parent b41a21fc df18e163
Replace deprecated ActivityResult API usage

inlinesplit
atox/src/main/kotlin/ui/addcontact/AddContactFragment.kt added: 101, removed: 176, total 0
@@ -8,6 +8,7 @@ import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@@ -22,8 +23,6 @@ import ltd.evilcorp.core.vo.Contact
import ltd.evilcorp.domain.tox.ToxID
import ltd.evilcorp.domain.tox.ToxIdValidator
 
private const val REQUEST_CODE_SCAN_QR = 6100
 
class AddContactFragment : BaseFragment<FragmentAddContactBinding>(FragmentAddContactBinding::inflate) {
private val viewModel: AddContactViewModel by viewModels { vmFactory }
 
@@ -39,6 +38,12 @@ class AddContactFragment : BaseFragment<FragmentAddContactBinding>(FragmentAddCo
if (!viewModel.isToxRunning() && !viewModel.tryLoadTox()) findNavController().navigateUp()
}
 
val scanQrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode != RESULT_OK) return@registerForActivityResult
val toxId = it.data?.getStringExtra("SCAN_RESULT") ?: return@registerForActivityResult
binding.toxId.setText(toxId.removePrefix("tox:"))
}
 
override fun onViewCreated(view: View, savedInstanceState: Bundle?) = binding.run {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, compat ->
val insets = compat.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime())
@@ -101,12 +106,13 @@ class AddContactFragment : BaseFragment<FragmentAddContactBinding>(FragmentAddCo
if (requireContext().packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
readQr.setOnClickListener {
try {
val intent = Intent("com.google.zxing.client.android.SCAN").apply {
putExtra("SCAN_FORMATS", "QR_CODE")
putExtra("SCAN_ORIENTATION_LOCKED", false)
putExtra("BEEP_ENABLED", false)
}
startActivityForResult(intent, REQUEST_CODE_SCAN_QR)
scanQrLauncher.launch(
Intent("com.google.zxing.client.android.SCAN").apply {
putExtra("SCAN_FORMATS", "QR_CODE")
putExtra("SCAN_ORIENTATION_LOCKED", false)
putExtra("BEEP_ENABLED", false)
}
)
} catch (e: ActivityNotFoundException) {
val uri = Uri.parse("https://f-droid.org/en/packages/com.google.zxing.client.android/")
startActivity(Intent(Intent.ACTION_VIEW, uri))
@@ -120,10 +126,4 @@ class AddContactFragment : BaseFragment<FragmentAddContactBinding>(FragmentAddCo
 
toxId.setText(arguments?.getString("toxId"), TextView.BufferType.EDITABLE)
}
 
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode != REQUEST_CODE_SCAN_QR || resultCode != RESULT_OK) return
val toxId = data?.getStringExtra("SCAN_RESULT") ?: return
binding.toxId.setText(toxId.removePrefix("tox:"))
}
}
 
atox/src/main/kotlin/ui/call/CallFragment.kt added: 101, removed: 176, total 0
@@ -4,6 +4,7 @@ import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
@@ -20,12 +21,21 @@ import ltd.evilcorp.atox.vmFactory
import ltd.evilcorp.domain.feature.CallState
import ltd.evilcorp.domain.tox.PublicKey
 
private val PERMISSIONS = arrayOf(Manifest.permission.RECORD_AUDIO)
private const val REQUEST_RECORD_AUDIO_PERMISSION = 8888
private const val PERMISSION = Manifest.permission.RECORD_AUDIO
 
class CallFragment : BaseFragment<FragmentCallBinding>(FragmentCallBinding::inflate) {
private val vm: CallViewModel by viewModels { vmFactory }
 
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) {
startCall()
} else {
findNavController().popBackStack()
}
}
 
override fun onViewCreated(view: View, savedInstanceState: Bundle?) = binding.run {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, compat ->
val insets = compat.getInsets(WindowInsetsCompat.Type.systemBars())
@@ -52,28 +62,12 @@ class CallFragment : BaseFragment<FragmentCallBinding>(FragmentCallBinding::infl
return
}
 
if (ContextCompat.checkSelfPermission(requireContext(), PERMISSIONS[0]) == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(requireContext(), PERMISSION) == PackageManager.PERMISSION_GRANTED) {
startCall()
return
}
 
requestPermissions(PERMISSIONS, REQUEST_RECORD_AUDIO_PERMISSION)
}
 
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
 
val granted = if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
grantResults[0] == PackageManager.PERMISSION_GRANTED
} else {
false
}
 
if (!granted) {
findNavController().popBackStack()
} else {
startCall()
}
requestPermissionLauncher.launch(PERMISSION)
}
 
private fun startCall() {
 
atox/src/main/kotlin/ui/chat/ChatFragment.kt added: 101, removed: 176, total 0
@@ -1,6 +1,5 @@
package ltd.evilcorp.atox.ui.chat
 
import android.app.Activity
import android.app.AlertDialog
import android.content.ActivityNotFoundException
import android.content.ClipData
@@ -17,6 +16,7 @@ import android.view.WindowInsets
import android.view.WindowInsetsAnimation
import android.widget.AdapterView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.FileProvider
import androidx.core.content.getSystemService
import androidx.core.content.res.ResourcesCompat
@@ -49,8 +49,6 @@ import ltd.evilcorp.core.vo.isComplete
import ltd.evilcorp.domain.tox.PublicKey
 
const val CONTACT_PUBLIC_KEY = "publicKey"
private const val REQUEST_CODE_FT_EXPORT = 1234
private const val REQUEST_CODE_ATTACH = 5678
private const val MAX_CONFIRM_DELETE_STRING_LENGTH = 20
 
class ChatFragment : BaseFragment<FragmentChatBinding>(FragmentChatBinding::inflate) {
@@ -61,6 +59,19 @@ class ChatFragment : BaseFragment<FragmentChatBinding>(FragmentChatBinding::infl
private var selectedFt: Int = Int.MIN_VALUE
private var fts: List<FileTransfer> = listOf()
 
private val exportFtLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { dest ->
if (dest == null) return@registerForActivityResult
viewModel.exportFt(selectedFt, dest)
}
 
private val attachFilesLauncher =
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { files ->
viewModel.setActiveChat(PublicKey(contactPubKey))
for (file in files) {
viewModel.createFt(file)
}
}
 
override fun onViewCreated(view: View, savedInstanceState: Bundle?): Unit = binding.run {
contactPubKey = requireStringArg(CONTACT_PUBLIC_KEY)
viewModel.setActiveChat(PublicKey(contactPubKey))
@@ -241,13 +252,7 @@ class ChatFragment : BaseFragment<FragmentChatBinding>(FragmentChatBinding::infl
send.setOnClickListener { send(MessageType.Normal) }
 
attach.setOnClickListener {
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}.also {
startActivityForResult(it, REQUEST_CODE_ATTACH)
}
attachFilesLauncher.launch(arrayOf("*/*"))
}
 
outgoingMessage.doAfterTextChanged {
@@ -334,46 +339,13 @@ class ChatFragment : BaseFragment<FragmentChatBinding>(FragmentChatBinding::infl
val info = item.menuInfo as AdapterView.AdapterContextMenuInfo
val message = messages.adapter.getItem(info.position) as Message
selectedFt = message.correlationId
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/octet-stream"
putExtra(Intent.EXTRA_TITLE, message.message)
}.let {
startActivityForResult(it, REQUEST_CODE_FT_EXPORT)
}
exportFtLauncher.launch(message.message)
true
}
else -> super.onContextItemSelected(item)
}
}
 
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// Runs before onResume, so add back some required state..
viewModel.setActiveChat(PublicKey(contactPubKey))
when (requestCode) {
REQUEST_CODE_FT_EXPORT -> {
if (resultCode == Activity.RESULT_OK && data != null) {
viewModel.exportFt(selectedFt, data.data as Uri)
}
}
REQUEST_CODE_ATTACH -> {
if (resultCode == Activity.RESULT_OK && data != null) {
if (data.data != null) {
// Single file.
viewModel.createFt(data.data as Uri)
} else if (data.clipData != null) {
// Multiple files.
val clipData = data.clipData ?: return
for (i in 0 until clipData.itemCount) {
viewModel.createFt(clipData.getItemAt(i).uri)
}
}
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
 
private fun send(type: MessageType) = binding.run {
viewModel.clearDraft()
viewModel.send(outgoingMessage.text.toString(), type)
 
atox/src/main/kotlin/ui/contactlist/ContactListFragment.kt added: 101, removed: 176, total 0
@@ -1,8 +1,5 @@
package ltd.evilcorp.atox.ui.contactlist
 
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.ContextMenu
import android.view.LayoutInflater
@@ -14,6 +11,7 @@ import android.view.inputmethod.InputMethodManager
import android.widget.AdapterView
import android.widget.Toast
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService
@@ -46,7 +44,6 @@ import ltd.evilcorp.domain.tox.PublicKey
import ltd.evilcorp.domain.tox.ToxSaveStatus
 
const val ARG_SHARE = "share"
private const val REQUEST_CODE_BACKUP_TOX = 9202
private const val MAX_CONFIRM_DELETE_STRING_LENGTH = 32
 
private fun User.online(): Boolean =
@@ -65,6 +62,11 @@ class ContactListFragment :
 
private var shareDialog: ReceiveShareDialog? = null
 
private val exportToxSaveLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { dest ->
if (dest == null) return@registerForActivityResult
viewModel.saveToxBackupTo(dest)
}
 
private fun colorFromStatus(status: UserStatus) = when (status) {
UserStatus.None -> ResourcesCompat.getColor(resources, R.color.statusAvailable, null)
UserStatus.Away -> ResourcesCompat.getColor(resources, R.color.statusAway, null)
@@ -280,15 +282,7 @@ class ContactListFragment :
}
R.id.add_contact -> findNavController().navigate(R.id.action_contactListFragment_to_addContactFragment)
R.id.settings -> findNavController().navigate(R.id.action_contactListFragment_to_settingsFragment)
R.id.export_tox_save -> {
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/octet-stream"
putExtra(Intent.EXTRA_TITLE, backupFileNameHint)
}.also {
startActivityForResult(it, REQUEST_CODE_BACKUP_TOX)
}
}
R.id.export_tox_save -> exportToxSaveLauncher.launch(backupFileNameHint)
R.id.quit_tox -> {
AlertDialog.Builder(requireContext())
.setTitle(R.string.quit_confirm)
@@ -304,17 +298,6 @@ class ContactListFragment :
return false
}
 
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CODE_BACKUP_TOX -> {
if (resultCode == Activity.RESULT_OK && data != null) {
viewModel.saveToxBackupTo(data.data as Uri)
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
 
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!viewModel.isToxRunning()) viewModel.tryLoadTox()
 
atox/src/main/kotlin/ui/create_profile/CreateProfileFragment.kt added: 101, removed: 176, total 0
@@ -1,12 +1,11 @@
package ltd.evilcorp.atox.ui.create_profile
 
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@@ -19,11 +18,28 @@ import ltd.evilcorp.atox.vmFactory
import ltd.evilcorp.core.vo.User
import ltd.evilcorp.domain.tox.ToxSaveStatus
 
private const val IMPORT = 42
 
class CreateProfileFragment : BaseFragment<FragmentProfileBinding>(FragmentProfileBinding::inflate) {
private val viewModel: CreateProfileViewModel by viewModels { vmFactory }
 
private val importLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
if (uri == null) return@registerForActivityResult
 
Log.i("ProfileFragment", "Importing file $uri")
viewModel.tryImportToxSave(uri)?.also { save ->
val startStatus = viewModel.startTox(save)
if (startStatus == ToxSaveStatus.Ok) {
viewModel.verifyUserExists(viewModel.publicKey)
findNavController().popBackStack()
} else {
Toast.makeText(
requireContext(),
resources.getString(R.string.import_tox_save_failed, startStatus.name),
Toast.LENGTH_LONG
).show()
}
}
}
 
override fun onViewCreated(view: View, savedInstanceState: Bundle?) = binding.run {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, compat ->
val insets = compat.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.ime())
@@ -51,35 +67,7 @@ class CreateProfileFragment : BaseFragment<FragmentProfileBinding>(FragmentProfi
}
 
btnImport.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
 
startActivityForResult(intent, IMPORT)
}
}
 
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode != IMPORT || resultCode != Activity.RESULT_OK) {
return
}
 
resultData?.data?.let { uri ->
Log.e("ProfileFragment", "Importing file $uri")
viewModel.tryImportToxSave(uri)?.also { save ->
val startStatus = viewModel.startTox(save)
if (startStatus == ToxSaveStatus.Ok) {
viewModel.verifyUserExists(viewModel.publicKey)
findNavController().popBackStack()
} else {
Toast.makeText(
requireContext(),
resources.getString(R.string.import_tox_save_failed, startStatus.name),
Toast.LENGTH_LONG
).show()
}
}
importLauncher.launch(arrayOf("*/*"))
}
}
}
 
atox/src/main/kotlin/ui/settings/SettingsFragment.kt added: 101, removed: 176, total 0
@@ -1,15 +1,13 @@
package ltd.evilcorp.atox.ui.settings
 
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@@ -30,8 +28,6 @@ import ltd.evilcorp.atox.ui.BaseFragment
import ltd.evilcorp.atox.vmFactory
import ltd.evilcorp.domain.tox.ProxyType
 
private const val REQUEST_CODE_NODE_JSON = 6660
 
class SettingsFragment : BaseFragment<FragmentSettingsBinding>(FragmentSettingsBinding::inflate) {
private val vm: SettingsViewModel by viewModels { vmFactory }
private val blockBackCallback = object : OnBackPressedCallback(false) {
@@ -46,6 +42,27 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding>(FragmentSettingsB
}
}
 
private val importNodesLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
GlobalScope.launch {
if (uri != null && vm.validateNodeJson(uri)) {
if (vm.importNodeJson(uri)) {
vm.setBootstrapNodeSource(BootstrapNodeSource.UserProvided)
return@launch
}
 
withContext(Dispatchers.Main) {
binding.settingBootstrapNodes.setSelection(BootstrapNodeSource.BuiltIn.ordinal)
 
Toast.makeText(
requireContext(),
getString(R.string.warn_node_json_import_failed),
Toast.LENGTH_LONG
).show()
}
}
}
}
 
override fun onAttach(context: Context) {
super.onAttach(context)
requireActivity().onBackPressedDispatcher.addCallback(this, applySettingsCallback)
@@ -209,40 +226,11 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding>(FragmentSettingsB
if (source == BootstrapNodeSource.BuiltIn) {
vm.setBootstrapNodeSource(source)
} else {
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/json"
}.also {
startActivityForResult(it, REQUEST_CODE_NODE_JSON)
}
importNodesLauncher.launch(arrayOf("application/json"))
}
}
}
 
version.text = getString(R.string.version_display, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
}
 
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CODE_NODE_JSON -> GlobalScope.launch {
if (resultCode == Activity.RESULT_OK && data != null && vm.validateNodeJson(data.data as Uri)) {
if (vm.importNodeJson(data.data as Uri)) {
vm.setBootstrapNodeSource(BootstrapNodeSource.UserProvided)
return@launch
}
}
 
withContext(Dispatchers.Main) {
binding.settingBootstrapNodes.setSelection(BootstrapNodeSource.BuiltIn.ordinal)
 
Toast.makeText(
requireContext(),
getString(R.string.warn_node_json_import_failed),
Toast.LENGTH_LONG
).show()
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
}