| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- package im.angry.openeuicc.ui
- import android.annotation.SuppressLint
- import android.app.PendingIntent
- import android.content.BroadcastReceiver
- import android.content.Context
- import android.content.Intent
- import android.content.IntentFilter
- import android.hardware.usb.UsbDevice
- import android.hardware.usb.UsbManager
- import android.os.Build
- import android.os.Bundle
- import android.view.LayoutInflater
- import android.view.View
- import android.view.ViewGroup
- import android.widget.Button
- import android.widget.ProgressBar
- import android.widget.TextView
- import androidx.fragment.app.Fragment
- import androidx.fragment.app.commit
- import androidx.lifecycle.lifecycleScope
- import im.angry.openeuicc.common.R
- import im.angry.openeuicc.core.EuiccChannel
- import im.angry.openeuicc.core.EuiccChannelManager
- import im.angry.openeuicc.util.*
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.launch
- import kotlinx.coroutines.withContext
- /**
- * A wrapper fragment over EuiccManagementFragment where we handle
- * logic specific to USB devices. This is mainly USB permission
- * requests, and the fact that USB devices may or may not be
- * available by the time the user selects it from MainActivity.
- *
- * Having this fragment allows MainActivity to be (mostly) agnostic
- * of the underlying implementation of different types of channels.
- * When permission is granted, this fragment will simply load
- * EuiccManagementFragment using its own childFragmentManager.
- *
- * Note that for now we assume there will only be one USB card reader
- * device. This is also an implicit assumption in EuiccChannelManager.
- */
- class UsbCcidReaderFragment : Fragment(), OpenEuiccContextMarker {
- companion object {
- const val ACTION_USB_PERMISSION = "im.angry.openeuicc.USB_PERMISSION"
- }
- private val euiccChannelManager: EuiccChannelManager by lazy {
- (requireActivity() as MainActivity).euiccChannelManager
- }
- private val usbManager: UsbManager by lazy {
- requireContext().getSystemService(Context.USB_SERVICE) as UsbManager
- }
- private val usbPermissionReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- if (intent?.action == ACTION_USB_PERMISSION) {
- if (usbDevice != null && usbManager.hasPermission(usbDevice)) {
- lifecycleScope.launch(Dispatchers.Main) {
- tryLoadUsbChannel()
- }
- }
- }
- }
- }
- private lateinit var usbPendingIntent: PendingIntent
- private lateinit var text: TextView
- private lateinit var permissionButton: Button
- private lateinit var loadingProgress: ProgressBar
- private var usbDevice: UsbDevice? = null
- private var usbChannel: EuiccChannel? = null
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- val view = inflater.inflate(R.layout.fragment_usb_ccid_reader, container, false)
- text = view.requireViewById(R.id.usb_reader_text)
- permissionButton = view.requireViewById(R.id.usb_grant_permission)
- loadingProgress = view.requireViewById(R.id.loading)
- permissionButton.setOnClickListener {
- usbManager.requestPermission(usbDevice, usbPendingIntent)
- }
- return view
- }
- @SuppressLint("UnspecifiedRegisterReceiverFlag", "WrongConstant")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- usbPendingIntent = PendingIntent.getBroadcast(
- requireContext(), 0,
- Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE
- )
- val filter = IntentFilter(ACTION_USB_PERMISSION)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- requireContext().registerReceiver(
- usbPermissionReceiver,
- filter,
- Context.RECEIVER_EXPORTED
- )
- } else {
- requireContext().registerReceiver(usbPermissionReceiver, filter)
- }
- lifecycleScope.launch(Dispatchers.Main) {
- tryLoadUsbChannel()
- }
- }
- override fun onDetach() {
- super.onDetach()
- try {
- requireContext().unregisterReceiver(usbPermissionReceiver)
- } catch (_: Exception) {
- }
- }
- override fun onDestroy() {
- super.onDestroy()
- try {
- requireContext().unregisterReceiver(usbPermissionReceiver)
- } catch (_: Exception) {
- }
- }
- private suspend fun tryLoadUsbChannel() {
- text.visibility = View.GONE
- permissionButton.visibility = View.GONE
- loadingProgress.visibility = View.VISIBLE
- val (device, channel) = withContext(Dispatchers.IO) {
- euiccChannelManager.enumerateUsbEuiccChannel()
- }
- loadingProgress.visibility = View.GONE
- usbDevice = device
- usbChannel = channel
- if (device != null && channel == null && !usbManager.hasPermission(device)) {
- text.text = getString(R.string.usb_permission_needed)
- text.visibility = View.VISIBLE
- permissionButton.visibility = View.VISIBLE
- } else if (device != null && channel != null) {
- childFragmentManager.commit {
- replace(
- R.id.child_container,
- appContainer.uiComponentFactory.createEuiccManagementFragment(channel)
- )
- }
- } else {
- text.text = getString(R.string.usb_failed)
- text.visibility = View.VISIBLE
- permissionButton.visibility = View.GONE
- }
- }
- }
|