Add delete menu item
This commit is contained in:
parent
5a6d45d810
commit
109a4f7e41
6 changed files with 41 additions and 6 deletions
|
@ -74,6 +74,7 @@ data class Attachment(
|
||||||
const val PROGRESS_NONE = -1
|
const val PROGRESS_NONE = -1
|
||||||
const val PROGRESS_INDETERMINATE = -2
|
const val PROGRESS_INDETERMINATE = -2
|
||||||
const val PROGRESS_FAILED = -3
|
const val PROGRESS_FAILED = -3
|
||||||
|
const val PROGRESS_DELETED = -4
|
||||||
const val PROGRESS_DONE = 100
|
const val PROGRESS_DONE = 100
|
||||||
|
|
||||||
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 6)
|
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 6)
|
||||||
|
|
|
@ -7,14 +7,19 @@ import android.media.RingtoneManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import io.heckel.ntfy.R
|
import io.heckel.ntfy.R
|
||||||
import io.heckel.ntfy.data.*
|
import io.heckel.ntfy.data.*
|
||||||
import io.heckel.ntfy.data.Notification
|
import io.heckel.ntfy.data.Notification
|
||||||
import io.heckel.ntfy.ui.DetailActivity
|
import io.heckel.ntfy.ui.DetailActivity
|
||||||
|
import io.heckel.ntfy.ui.DetailAdapter
|
||||||
import io.heckel.ntfy.ui.MainActivity
|
import io.heckel.ntfy.ui.MainActivity
|
||||||
import io.heckel.ntfy.util.*
|
import io.heckel.ntfy.util.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class NotificationService(val context: Context) {
|
class NotificationService(val context: Context) {
|
||||||
private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
|
@ -111,7 +111,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
|
||||||
val onNotificationClick = { n: Notification -> onNotificationClick(n) }
|
val onNotificationClick = { n: Notification -> onNotificationClick(n) }
|
||||||
val onNotificationLongClick = { n: Notification -> onNotificationLongClick(n) }
|
val onNotificationLongClick = { n: Notification -> onNotificationLongClick(n) }
|
||||||
|
|
||||||
adapter = DetailAdapter(this, onNotificationClick, onNotificationLongClick)
|
adapter = DetailAdapter(this, repository, onNotificationClick, onNotificationLongClick)
|
||||||
mainList = findViewById(R.id.detail_notification_list)
|
mainList = findViewById(R.id.detail_notification_list)
|
||||||
mainList.adapter = adapter
|
mainList.adapter = adapter
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,12 @@ import io.heckel.ntfy.data.*
|
||||||
import io.heckel.ntfy.msg.DownloadManager
|
import io.heckel.ntfy.msg.DownloadManager
|
||||||
import io.heckel.ntfy.msg.DownloadWorker
|
import io.heckel.ntfy.msg.DownloadWorker
|
||||||
import io.heckel.ntfy.util.*
|
import io.heckel.ntfy.util.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class DetailAdapter(private val activity: Activity, private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) :
|
class DetailAdapter(private val activity: Activity, private val repository: Repository, private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) :
|
||||||
ListAdapter<Notification, DetailAdapter.DetailViewHolder>(TopicDiffCallback) {
|
ListAdapter<Notification, DetailAdapter.DetailViewHolder>(TopicDiffCallback) {
|
||||||
val selected = mutableSetOf<String>() // Notification IDs
|
val selected = mutableSetOf<String>() // Notification IDs
|
||||||
|
|
||||||
|
@ -37,7 +40,7 @@ class DetailAdapter(private val activity: Activity, private val onClick: (Notifi
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
.inflate(R.layout.fragment_detail_item, parent, false)
|
.inflate(R.layout.fragment_detail_item, parent, false)
|
||||||
return DetailViewHolder(activity, view, selected, onClick, onLongClick)
|
return DetailViewHolder(activity, repository, view, selected, onClick, onLongClick)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gets current topic and uses it to bind view. */
|
/* Gets current topic and uses it to bind view. */
|
||||||
|
@ -54,7 +57,7 @@ class DetailAdapter(private val activity: Activity, private val onClick: (Notifi
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ViewHolder for Topic, takes in the inflated view and the onClick behavior. */
|
/* ViewHolder for Topic, takes in the inflated view and the onClick behavior. */
|
||||||
class DetailViewHolder(private val activity: Activity, itemView: View, private val selected: Set<String>, val onClick: (Notification) -> Unit, val onLongClick: (Notification) -> Unit) :
|
class DetailViewHolder(private val activity: Activity, private val repository: Repository, itemView: View, private val selected: Set<String>, val onClick: (Notification) -> Unit, val onLongClick: (Notification) -> Unit) :
|
||||||
RecyclerView.ViewHolder(itemView) {
|
RecyclerView.ViewHolder(itemView) {
|
||||||
private var notification: Notification? = null
|
private var notification: Notification? = null
|
||||||
private val priorityImageView: ImageView = itemView.findViewById(R.id.detail_item_priority_image)
|
private val priorityImageView: ImageView = itemView.findViewById(R.id.detail_item_priority_image)
|
||||||
|
@ -185,6 +188,7 @@ class DetailAdapter(private val activity: Activity, private val onClick: (Notifi
|
||||||
val cancelItem = popup.menu.findItem(R.id.detail_item_menu_cancel)
|
val cancelItem = popup.menu.findItem(R.id.detail_item_menu_cancel)
|
||||||
val openItem = popup.menu.findItem(R.id.detail_item_menu_open)
|
val openItem = popup.menu.findItem(R.id.detail_item_menu_open)
|
||||||
val browseItem = popup.menu.findItem(R.id.detail_item_menu_browse)
|
val browseItem = popup.menu.findItem(R.id.detail_item_menu_browse)
|
||||||
|
val deleteItem = popup.menu.findItem(R.id.detail_item_menu_delete)
|
||||||
val copyUrlItem = popup.menu.findItem(R.id.detail_item_menu_copy_url)
|
val copyUrlItem = popup.menu.findItem(R.id.detail_item_menu_copy_url)
|
||||||
val expired = attachment.expires != null && attachment.expires < System.currentTimeMillis()/1000
|
val expired = attachment.expires != null && attachment.expires < System.currentTimeMillis()/1000
|
||||||
val inProgress = attachment.progress in 0..99
|
val inProgress = attachment.progress in 0..99
|
||||||
|
@ -214,6 +218,27 @@ class DetailAdapter(private val activity: Activity, private val onClick: (Notifi
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
if (attachment.contentUri != null) {
|
||||||
|
deleteItem.setOnMenuItemClickListener {
|
||||||
|
try {
|
||||||
|
val contentUri = Uri.parse(attachment.contentUri)
|
||||||
|
val resolver = context.applicationContext.contentResolver
|
||||||
|
val deleted = resolver.delete(contentUri, null, null) > 0
|
||||||
|
if (!deleted) throw Exception("no rows deleted")
|
||||||
|
val newAttachment = attachment.copy(progress = PROGRESS_DELETED)
|
||||||
|
val newNotification = notification.copy(attachment = newAttachment)
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
repository.updateNotification(newNotification)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Failed to update notification: ${e.message}", e)
|
||||||
|
Toast
|
||||||
|
.makeText(context, context.getString(R.string.detail_item_delete_failed, e.message), Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
copyUrlItem.setOnMenuItemClickListener {
|
copyUrlItem.setOnMenuItemClickListener {
|
||||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = ClipData.newPlainText("attachment url", attachment.url)
|
val clip = ClipData.newPlainText("attachment url", attachment.url)
|
||||||
|
@ -239,9 +264,10 @@ class DetailAdapter(private val activity: Activity, private val onClick: (Notifi
|
||||||
openItem.isVisible = exists
|
openItem.isVisible = exists
|
||||||
browseItem.isVisible = exists
|
browseItem.isVisible = exists
|
||||||
downloadItem.isVisible = !exists && !expired && !inProgress
|
downloadItem.isVisible = !exists && !expired && !inProgress
|
||||||
|
deleteItem.isVisible = exists
|
||||||
copyUrlItem.isVisible = !expired
|
copyUrlItem.isVisible = !expired
|
||||||
cancelItem.isVisible = inProgress
|
cancelItem.isVisible = inProgress
|
||||||
val noOptions = !openItem.isVisible && !browseItem.isVisible && !downloadItem.isVisible && !copyUrlItem.isVisible && !cancelItem.isVisible
|
val noOptions = !openItem.isVisible && !browseItem.isVisible && !downloadItem.isVisible && !copyUrlItem.isVisible && !cancelItem.isVisible && !deleteItem.isVisible
|
||||||
if (noOptions) {
|
if (noOptions) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -252,7 +278,7 @@ class DetailAdapter(private val activity: Activity, private val onClick: (Notifi
|
||||||
val name = queryFilename(context, attachment.contentUri, attachment.name)
|
val name = queryFilename(context, attachment.contentUri, attachment.name)
|
||||||
val notYetDownloaded = !exists && attachment.progress == PROGRESS_NONE
|
val notYetDownloaded = !exists && attachment.progress == PROGRESS_NONE
|
||||||
val downloading = !exists && attachment.progress in 0..99
|
val downloading = !exists && attachment.progress in 0..99
|
||||||
val deleted = !exists && attachment.progress == PROGRESS_DONE
|
val deleted = !exists && (attachment.progress == PROGRESS_DONE || attachment.progress == PROGRESS_DELETED)
|
||||||
val failed = !exists && attachment.progress == PROGRESS_FAILED
|
val failed = !exists && attachment.progress == PROGRESS_FAILED
|
||||||
val expired = attachment.expires != null && attachment.expires < System.currentTimeMillis()/1000
|
val expired = attachment.expires != null && attachment.expires < System.currentTimeMillis()/1000
|
||||||
val expires = attachment.expires != null && attachment.expires > System.currentTimeMillis()/1000
|
val expires = attachment.expires != null && attachment.expires > System.currentTimeMillis()/1000
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
<item android:id="@+id/detail_item_menu_cancel" android:title="@string/detail_item_menu_cancel"/>
|
<item android:id="@+id/detail_item_menu_cancel" android:title="@string/detail_item_menu_cancel"/>
|
||||||
<item android:id="@+id/detail_item_menu_open" android:title="@string/detail_item_menu_open"/>
|
<item android:id="@+id/detail_item_menu_open" android:title="@string/detail_item_menu_open"/>
|
||||||
<item android:id="@+id/detail_item_menu_browse" android:title="@string/detail_item_menu_browse"/>
|
<item android:id="@+id/detail_item_menu_browse" android:title="@string/detail_item_menu_browse"/>
|
||||||
|
<item android:id="@+id/detail_item_menu_delete" android:title="@string/detail_item_menu_delete"/>
|
||||||
<item android:id="@+id/detail_item_menu_copy_url" android:title="@string/detail_item_menu_copy_url"/>
|
<item android:id="@+id/detail_item_menu_copy_url" android:title="@string/detail_item_menu_copy_url"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
<string name="detail_item_tags">Tags: %1$s</string>
|
<string name="detail_item_tags">Tags: %1$s</string>
|
||||||
<string name="detail_item_menu_open">Open file</string>
|
<string name="detail_item_menu_open">Open file</string>
|
||||||
<string name="detail_item_menu_browse">Browse file</string>
|
<string name="detail_item_menu_browse">Browse file</string>
|
||||||
|
<string name="detail_item_menu_delete">Delete file</string>
|
||||||
<string name="detail_item_menu_download">Download file</string>
|
<string name="detail_item_menu_download">Download file</string>
|
||||||
<string name="detail_item_menu_cancel">Cancel download</string>
|
<string name="detail_item_menu_cancel">Cancel download</string>
|
||||||
<string name="detail_item_menu_copy_url">Copy URL</string>
|
<string name="detail_item_menu_copy_url">Copy URL</string>
|
||||||
|
@ -118,6 +119,7 @@
|
||||||
<string name="detail_item_cannot_download">Cannot open or download attachment. Link expired and no local file found.</string>
|
<string name="detail_item_cannot_download">Cannot open or download attachment. Link expired and no local file found.</string>
|
||||||
<string name="detail_item_cannot_open">Cannot open attachment: %1$s</string>
|
<string name="detail_item_cannot_open">Cannot open attachment: %1$s</string>
|
||||||
<string name="detail_item_cannot_open_not_found">Cannot open attachment: File may have been deleted, or there is no app to open the file.</string>
|
<string name="detail_item_cannot_open_not_found">Cannot open attachment: File may have been deleted, or there is no app to open the file.</string>
|
||||||
|
<string name="detail_item_delete_failed">Cannot delete attachment: %1$s</string>
|
||||||
<string name="detail_item_download_failed">Attachment download failed: %1$s</string>
|
<string name="detail_item_download_failed">Attachment download failed: %1$s</string>
|
||||||
<string name="detail_item_download_info_not_downloaded">not downloaded</string>
|
<string name="detail_item_download_info_not_downloaded">not downloaded</string>
|
||||||
<string name="detail_item_download_info_not_downloaded_expired">not downloaded, link expired</string>
|
<string name="detail_item_download_info_not_downloaded_expired">not downloaded, link expired</string>
|
||||||
|
|
Loading…
Reference in a new issue