OpenEUICC/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt
Peter Cai 3a0d805eb2
All checks were successful
/ build-debug (push) Successful in 4m48s
treewide: Nullability fixes for AOSP 14 r29
Mainly a treewide `findViewById` -> `requireViewById`, with
miscellaneous fixes.
2024-03-11 18:55:32 -04:00

219 lines
7.9 KiB
Kotlin

package im.angry.openeuicc.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.text.Html
import android.view.ContextMenu
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.MenuItem.OnMenuItemClickListener
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.forEach
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import im.angry.openeuicc.common.R
import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.LocalProfileNotification
class NotificationsActivity: AppCompatActivity(), OpenEuiccContextMarker {
private lateinit var swipeRefresh: SwipeRefreshLayout
private lateinit var notificationList: RecyclerView
private val notificationAdapter = NotificationAdapter()
private lateinit var euiccChannel: EuiccChannel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_notifications)
setSupportActionBar(requireViewById(R.id.toolbar))
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
euiccChannel = euiccChannelManager
.findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!!
swipeRefresh = requireViewById(R.id.swipe_refresh)
notificationList = requireViewById(R.id.recycler_view)
notificationList.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
notificationList.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
notificationList.adapter = notificationAdapter
registerForContextMenu(notificationList)
swipeRefresh.setOnRefreshListener {
refresh()
}
refresh()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.activity_notifications, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
android.R.id.home -> {
finish()
true
}
R.id.help -> {
AlertDialog.Builder(this, R.style.AlertDialogTheme).apply {
setMessage(R.string.profile_notifications_help)
setPositiveButton(android.R.string.ok) { dialog, _ ->
dialog.dismiss()
}
show()
}
true
}
else -> super.onOptionsItemSelected(item)
}
private fun launchTask(task: suspend () -> Unit) {
swipeRefresh.isRefreshing = true
lifecycleScope.launch {
task()
swipeRefresh.isRefreshing = false
}
}
private fun refresh() {
launchTask {
val profiles = withContext(Dispatchers.IO) {
euiccChannel.lpa.profiles
}
notificationAdapter.notifications =
withContext(Dispatchers.IO) {
euiccChannel.lpa.notifications.map {
val profile = profiles.find { p -> p.iccid == it.iccid }
LocalProfileNotificationWrapper(it, profile?.displayName ?: "???")
}
}
}
}
data class LocalProfileNotificationWrapper(
val inner: LocalProfileNotification,
val profileName: String
)
@SuppressLint("ClickableViewAccessibility")
inner class NotificationViewHolder(private val root: View):
RecyclerView.ViewHolder(root), View.OnCreateContextMenuListener, OnMenuItemClickListener {
private val address: TextView = root.requireViewById(R.id.notification_address)
private val profileName: TextView = root.requireViewById(R.id.notification_profile_name)
private lateinit var notification: LocalProfileNotificationWrapper
private var lastTouchX = 0f
private var lastTouchY = 0f
init {
root.isClickable = true
root.setOnCreateContextMenuListener(this)
root.setOnTouchListener { _, event ->
lastTouchX = event.x
lastTouchY = event.y
false
}
root.setOnLongClickListener {
root.showContextMenu(lastTouchX, lastTouchY)
true
}
}
private fun operationToLocalizedText(operation: LocalProfileNotification.Operation) =
root.context.getText(
when (operation) {
LocalProfileNotification.Operation.Install -> R.string.profile_notification_operation_download
LocalProfileNotification.Operation.Delete -> R.string.profile_notification_operation_delete
LocalProfileNotification.Operation.Enable -> R.string.profile_notification_operation_enable
LocalProfileNotification.Operation.Disable -> R.string.profile_notification_operation_disable
})
fun updateNotification(value: LocalProfileNotificationWrapper) {
notification = value
address.text = value.inner.notificationAddress
profileName.text = Html.fromHtml(
root.context.getString(R.string.profile_notification_name_format,
operationToLocalizedText(value.inner.profileManagementOperation),
value.profileName, value.inner.iccid),
Html.FROM_HTML_MODE_COMPACT)
}
override fun onCreateContextMenu(
menu: ContextMenu?,
v: View?,
menuInfo: ContextMenu.ContextMenuInfo?
) {
menuInflater.inflate(R.menu.notification_options, menu)
menu!!.forEach {
it.setOnMenuItemClickListener(this)
}
}
override fun onMenuItemClick(item: MenuItem): Boolean =
when (item.itemId) {
R.id.notification_process -> {
launchTask {
withContext(Dispatchers.IO) {
euiccChannel.lpa.handleNotification(notification.inner.seqNumber)
}
}
refresh()
true
}
R.id.notification_delete -> {
launchTask {
withContext(Dispatchers.IO) {
euiccChannel.lpa.deleteNotification(notification.inner.seqNumber)
}
}
refresh()
true
}
else -> false
}
}
inner class NotificationAdapter: RecyclerView.Adapter<NotificationViewHolder>() {
var notifications: List<LocalProfileNotificationWrapper> = listOf()
@SuppressLint("NotifyDataSetChanged")
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationViewHolder {
val root = LayoutInflater.from(parent.context)
.inflate(R.layout.notification_item, parent, false)
return NotificationViewHolder(root)
}
override fun getItemCount(): Int = notifications.size
override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) =
holder.updateNotification(notifications[position])
}
}