WIP: Muted until
This commit is contained in:
parent
0c6f1cd540
commit
71b5d56f6a
17 changed files with 479 additions and 42 deletions
|
@ -58,6 +58,7 @@ dependencies {
|
|||
|
||||
// WorkManager
|
||||
implementation "androidx.work:work-runtime-ktx:2.6.0"
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
|
||||
// Room (SQLite)
|
||||
def roomVersion = "2.3.0"
|
||||
|
|
118
app/schemas/io.heckel.ntfy.data.Database/3.json
Normal file
118
app/schemas/io.heckel.ntfy.data.Database/3.json
Normal file
|
@ -0,0 +1,118 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "7b0ef556331f6d2dd3515425837c3d3a",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "Subscription",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `topic` TEXT NOT NULL, `instant` INTEGER NOT NULL, `mutedUntil` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "baseUrl",
|
||||
"columnName": "baseUrl",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "topic",
|
||||
"columnName": "topic",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "instant",
|
||||
"columnName": "instant",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mutedUntil",
|
||||
"columnName": "mutedUntil",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_Subscription_baseUrl_topic",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"baseUrl",
|
||||
"topic"
|
||||
],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Subscription_baseUrl_topic` ON `${TABLE_NAME}` (`baseUrl`, `topic`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "Notification",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `message` TEXT NOT NULL, `notificationId` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "subscriptionId",
|
||||
"columnName": "subscriptionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "message",
|
||||
"columnName": "message",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "notificationId",
|
||||
"columnName": "notificationId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "deleted",
|
||||
"columnName": "deleted",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7b0ef556331f6d2dd3515425837c3d3a')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -7,15 +7,11 @@
|
|||
- WAKE_LOCK & RECEIVE_BOOT_COMPLETED are required to restart the foreground service
|
||||
if it is stopped; see https://robertohuertas.com/2019/06/29/android_foreground_services/
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
||||
<!--
|
||||
Application
|
||||
- usesCleartextTraffic is required to support "use another server" feature
|
||||
-->
|
||||
<application
|
||||
android:name=".app.Application"
|
||||
android:allowBackup="true"
|
||||
|
@ -25,7 +21,8 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<activity android:name=".ui.SubscriptionSettingsActivity">
|
||||
</activity>
|
||||
<!-- Main activity -->
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
|
@ -35,28 +32,22 @@
|
|||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Detail activity -->
|
||||
</activity> <!-- Detail activity -->
|
||||
<activity
|
||||
android:name=".ui.DetailActivity"
|
||||
android:parentActivityName=".ui.MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<!-- Subscriber foreground service for hosts other than ntfy.sh -->
|
||||
<service android:name=".msg.SubscriberService" />
|
||||
|
||||
<!-- Subscriber service restart on reboot -->
|
||||
<receiver android:enabled="true" android:name=".msg.SubscriberService$StartReceiver">
|
||||
android:value=".ui.MainActivity"/>
|
||||
</activity> <!-- Subscriber foreground service for hosts other than ntfy.sh -->
|
||||
<service android:name=".msg.SubscriberService"/> <!-- Subscriber service restart on reboot -->
|
||||
<receiver
|
||||
android:name=".msg.SubscriberService$StartReceiver"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Firebase messaging -->
|
||||
</receiver> <!-- Firebase messaging -->
|
||||
<service
|
||||
android:name=".msg.FirebaseService"
|
||||
android:exported="false">
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.room.*
|
|||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.util.*
|
||||
|
||||
@Entity(indices = [Index(value = ["baseUrl", "topic"], unique = true)])
|
||||
data class Subscription(
|
||||
|
@ -12,12 +13,16 @@ data class Subscription(
|
|||
@ColumnInfo(name = "baseUrl") val baseUrl: String,
|
||||
@ColumnInfo(name = "topic") val topic: String,
|
||||
@ColumnInfo(name = "instant") val instant: Boolean,
|
||||
@ColumnInfo(name = "mutedUntil") val mutedUntil: Long,
|
||||
//val notificationSchedule: String,
|
||||
//val notificationSound: String,
|
||||
@Ignore val totalCount: Int = 0, // Total notifications
|
||||
@Ignore val newCount: Int = 0, // New notifications
|
||||
@Ignore val lastActive: Long = 0, // Unix timestamp
|
||||
@Ignore val state: ConnectionState = ConnectionState.NOT_APPLICABLE
|
||||
) {
|
||||
constructor(id: Long, baseUrl: String, topic: String, instant: Boolean) : this(id, baseUrl, topic, instant, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
|
||||
constructor(id: Long, baseUrl: String, topic: String, instant: Boolean, mutedUntil: Long) :
|
||||
this(id, baseUrl, topic, instant, mutedUntil, 0, 0, 0, ConnectionState.NOT_APPLICABLE)
|
||||
}
|
||||
|
||||
enum class ConnectionState {
|
||||
|
@ -29,6 +34,7 @@ data class SubscriptionWithMetadata(
|
|||
val baseUrl: String,
|
||||
val topic: String,
|
||||
val instant: Boolean,
|
||||
val mutedUntil: Long,
|
||||
val totalCount: Int,
|
||||
val newCount: Int,
|
||||
val lastActive: Long
|
||||
|
@ -44,7 +50,7 @@ data class Notification(
|
|||
@ColumnInfo(name = "deleted") val deleted: Boolean,
|
||||
)
|
||||
|
||||
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 2)
|
||||
@androidx.room.Database(entities = [Subscription::class, Notification::class], version = 3)
|
||||
abstract class Database : RoomDatabase() {
|
||||
abstract fun subscriptionDao(): SubscriptionDao
|
||||
abstract fun notificationDao(): NotificationDao
|
||||
|
@ -79,6 +85,12 @@ abstract class Database : RoomDatabase() {
|
|||
db.execSQL("ALTER TABLE Notification ADD COLUMN deleted INTEGER NOT NULL DEFAULT('0')")
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_2_3 = object : Migration(2, 3) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE Subscription ADD COLUMN mutedUntil INTEGER NOT NULL DEFAULT('0')")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +98,7 @@ abstract class Database : RoomDatabase() {
|
|||
interface SubscriptionDao {
|
||||
@Query("""
|
||||
SELECT
|
||||
s.id, s.baseUrl, s.topic, s.instant,
|
||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
|
||||
COUNT(n.id) totalCount,
|
||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||
|
@ -99,7 +111,7 @@ interface SubscriptionDao {
|
|||
|
||||
@Query("""
|
||||
SELECT
|
||||
s.id, s.baseUrl, s.topic, s.instant,
|
||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
|
||||
COUNT(n.id) totalCount,
|
||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||
|
@ -112,7 +124,7 @@ interface SubscriptionDao {
|
|||
|
||||
@Query("""
|
||||
SELECT
|
||||
s.id, s.baseUrl, s.topic, s.instant,
|
||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
|
||||
COUNT(n.id) totalCount,
|
||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||
|
@ -125,7 +137,7 @@ interface SubscriptionDao {
|
|||
|
||||
@Query("""
|
||||
SELECT
|
||||
s.id, s.baseUrl, s.topic, s.instant,
|
||||
s.id, s.baseUrl, s.topic, s.instant, s.mutedUntil,
|
||||
COUNT(n.id) totalCount,
|
||||
COUNT(CASE n.notificationId WHEN 0 THEN NULL ELSE n.id END) newCount,
|
||||
IFNULL(MAX(n.timestamp),0) AS lastActive
|
||||
|
|
|
@ -113,6 +113,7 @@ class Repository(private val subscriptionDao: SubscriptionDao, private val notif
|
|||
baseUrl = s.baseUrl,
|
||||
topic = s.topic,
|
||||
instant = s.instant,
|
||||
mutedUntil = s.mutedUntil,
|
||||
totalCount = s.totalCount,
|
||||
newCount = s.newCount,
|
||||
lastActive = s.lastActive,
|
||||
|
@ -130,6 +131,7 @@ class Repository(private val subscriptionDao: SubscriptionDao, private val notif
|
|||
baseUrl = s.baseUrl,
|
||||
topic = s.topic,
|
||||
instant = s.instant,
|
||||
mutedUntil = s.mutedUntil,
|
||||
totalCount = s.totalCount,
|
||||
newCount = s.newCount,
|
||||
lastActive = s.lastActive,
|
||||
|
|
|
@ -33,7 +33,7 @@ import kotlinx.coroutines.launch
|
|||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
class DetailActivity : AppCompatActivity(), ActionMode.Callback {
|
||||
class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFragment.NotificationSettingsListener {
|
||||
private val viewModel by viewModels<DetailViewModel> {
|
||||
DetailViewModelFactory((application as Application).repository)
|
||||
}
|
||||
|
@ -187,6 +187,10 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback {
|
|||
onTestClick()
|
||||
true
|
||||
}
|
||||
R.id.detail_menu_notification -> {
|
||||
onNotificationSettingsClick()
|
||||
true
|
||||
}
|
||||
R.id.detail_menu_enable_instant -> {
|
||||
onInstantEnableClick(enable = true)
|
||||
true
|
||||
|
@ -228,6 +232,38 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback {
|
|||
}
|
||||
}
|
||||
|
||||
private fun onNotificationSettingsClick() {
|
||||
Log.d(TAG, "Showing notification settings dialog for ${topicShortUrl(subscriptionBaseUrl, subscriptionTopic)}")
|
||||
val intent = Intent(this, SubscriptionSettingsActivity::class.java)
|
||||
startActivityForResult(intent, /*XXXXXX*/MainActivity.REQUEST_CODE_DELETE_SUBSCRIPTION)
|
||||
/*
|
||||
val notificationFragment = NotificationFragment()
|
||||
notificationFragment.show(supportFragmentManager, NotificationFragment.TAG)*/
|
||||
}
|
||||
|
||||
override fun onNotificationSettingsChanged(mutedUntil: Long) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val subscription = repository.getSubscription(subscriptionId)
|
||||
val newSubscription = subscription?.copy(mutedUntil = mutedUntil)
|
||||
newSubscription?.let { repository.updateSubscription(newSubscription) }
|
||||
runOnUiThread {
|
||||
when (mutedUntil) {
|
||||
0L -> Toast.makeText(this@DetailActivity, getString(R.string.notification_dialog_enabled_toast_message), Toast.LENGTH_SHORT).show()
|
||||
1L -> Toast.makeText(this@DetailActivity, getString(R.string.notification_dialog_muted_forever_toast_message), Toast.LENGTH_SHORT).show()
|
||||
else -> {
|
||||
val mutedUntilDate = Date(mutedUntil).toString()
|
||||
Toast.makeText(
|
||||
this@DetailActivity,
|
||||
getString(R.string.notification_dialog_muted_until_toast_message, mutedUntilDate),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onCopyUrlClick() {
|
||||
val url = topicUrl(subscriptionBaseUrl, subscriptionTopic)
|
||||
Log.d(TAG, "Copying topic URL $url to clipboard ")
|
||||
|
@ -478,6 +514,5 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback {
|
|||
|
||||
companion object {
|
||||
const val TAG = "NtfyDetailActivity"
|
||||
const val CANCEL_NOTIFICATION_DELAY_MILLIS = 20_000L
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,6 +165,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
|
|||
baseUrl = baseUrl,
|
||||
topic = topic,
|
||||
instant = instant,
|
||||
mutedUntil = 0,
|
||||
totalCount = 0,
|
||||
newCount = 0,
|
||||
lastActive = Date().time/1000
|
||||
|
|
73
app/src/main/java/io/heckel/ntfy/ui/NotificationFragment.kt
Normal file
73
app/src/main/java/io/heckel/ntfy/ui/NotificationFragment.kt
Normal file
|
@ -0,0 +1,73 @@
|
|||
package io.heckel.ntfy.ui
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.CheckBox
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.data.Database
|
||||
import io.heckel.ntfy.data.Repository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NotificationFragment : DialogFragment() {
|
||||
private lateinit var repository: Repository
|
||||
private lateinit var settingsListener: NotificationSettingsListener
|
||||
|
||||
interface NotificationSettingsListener {
|
||||
fun onNotificationSettingsChanged(mutedUntil: Long)
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
settingsListener = activity as NotificationSettingsListener
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
if (activity == null) {
|
||||
throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
val database = Database.getInstance(activity!!.applicationContext)
|
||||
repository = Repository.getInstance(database.subscriptionDao(), database.notificationDao())
|
||||
|
||||
// Build root view
|
||||
val view = requireActivity().layoutInflater.inflate(R.layout.notification_dialog_fragment, null)
|
||||
// topicNameText = view.findViewById(R.id.add_dialog_topic_text) as TextInputEditText
|
||||
|
||||
// Build dialog
|
||||
val alert = AlertDialog.Builder(activity)
|
||||
.setView(view)
|
||||
.setPositiveButton(R.string.notification_dialog_save) { _, _ ->
|
||||
///
|
||||
settingsListener.onNotificationSettingsChanged(0L)
|
||||
}
|
||||
.setNegativeButton(R.string.notification_dialog_cancel) { _, _ ->
|
||||
dialog?.cancel()
|
||||
}
|
||||
.create()
|
||||
|
||||
// Add logic to disable "Subscribe" button on invalid input
|
||||
alert.setOnShowListener {
|
||||
val dialog = it as AlertDialog
|
||||
///
|
||||
}
|
||||
|
||||
return alert
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val TAG = "NtfyNotificationFragment"
|
||||
}
|
||||
}
|
5
app/src/main/java/io/heckel/ntfy/ui/SettingsFragment.kt
Normal file
5
app/src/main/java/io/heckel/ntfy/ui/SettingsFragment.kt
Normal file
|
@ -0,0 +1,5 @@
|
|||
package io.heckel.ntfy.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import io.heckel.ntfy.R
|
|
@ -0,0 +1,25 @@
|
|||
package io.heckel.ntfy.ui
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import io.heckel.ntfy.R
|
||||
|
||||
class SubscriptionSettingsActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_subscription_settings)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.subscription_settings_content, SubscriptionSettingsFragment())
|
||||
.commit()
|
||||
}
|
||||
|
||||
class SubscriptionSettingsFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
18
app/src/main/res/layout/activity_subscription_settings.xml
Normal file
18
app/src/main/res/layout/activity_subscription_settings.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.SubscriptionSettingsActivity">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/subscription_settings_content" app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
</FrameLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
66
app/src/main/res/layout/notification_dialog_fragment.xml
Normal file
66
app/src/main/res/layout/notification_dialog_fragment.xml
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/add_dialog_title_text2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="@string/notification_dialog_title"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Large" android:paddingStart="4dp"
|
||||
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_marginStart="16dp" android:layout_marginEnd="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
<TextView
|
||||
android:text="Pause notifications"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" android:id="@+id/textView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/add_dialog_title_text2"
|
||||
android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="@+id/add_dialog_title_text2"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
|
||||
<RadioGroup
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="@+id/textView" app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
android:layout_marginTop="10dp">
|
||||
<RadioButton
|
||||
android:text="Pause forever"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/radioButton"
|
||||
/>
|
||||
<RadioButton
|
||||
android:text="Pause for 30 minutes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/radioButton4"
|
||||
/>
|
||||
<RadioButton
|
||||
android:text="Pause for 1 hour"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/radioButton5"
|
||||
/>
|
||||
<RadioButton
|
||||
android:text="Pause for 2 hours"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/radioButton6"
|
||||
/>
|
||||
<RadioButton
|
||||
android:text="Pause until tomorrow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" android:id="@+id/radioButton3"
|
||||
/>
|
||||
</RadioGroup>
|
||||
<Switch
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" android:id="@+id/switch1"
|
||||
app:layout_constraintStart_toEndOf="@+id/textView" app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginTop="19dp" app:layout_constraintTop_toBottomOf="@+id/add_dialog_title_text2"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,4 +1,6 @@
|
|||
<menu xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item android:id="@+id/detail_menu_notification" android:title="@string/detail_menu_enable_instant"
|
||||
app:showAsAction="ifRoom" android:icon="@drawable/ic_notifications_white_24dp"/>
|
||||
<item android:id="@+id/detail_menu_enable_instant" android:title="@string/detail_menu_enable_instant"
|
||||
app:showAsAction="ifRoom" android:icon="@drawable/ic_bolt_outline_white_24dp"/>
|
||||
<item android:id="@+id/detail_menu_disable_instant" android:title="@string/detail_menu_disable_instant"
|
||||
|
|
16
app/src/main/res/values/arrays.xml
Normal file
16
app/src/main/res/values/arrays.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<resources>
|
||||
<!-- Reply Preference -->
|
||||
<string-array name="reply_entries">
|
||||
<item>Forever</item>
|
||||
<item>30 minutes</item>
|
||||
<item>1 hour</item>
|
||||
<item>2 hours</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="reply_values">
|
||||
<item>forever</item>
|
||||
<item>30min</item>
|
||||
<item>1hr</item>
|
||||
<item>2h</item>
|
||||
</string-array>
|
||||
</resources>
|
|
@ -10,7 +10,8 @@
|
|||
<string name="channel_subscriber_notification_text">You are subscribed to instant delivery topics</string>
|
||||
<string name="channel_subscriber_notification_text_one">You are subscribed to one instant delivery topic</string>
|
||||
<string name="channel_subscriber_notification_text_two">You are subscribed to two instant delivery topics</string>
|
||||
<string name="channel_subscriber_notification_text_three">You are subscribed to three instant delivery topics</string>
|
||||
<string name="channel_subscriber_notification_text_three">You are subscribed to three instant delivery topics
|
||||
</string>
|
||||
<string name="channel_subscriber_notification_text_four">You are subscribed to four instant delivery topics</string>
|
||||
<string name="channel_subscriber_notification_text_more">You are subscribed to %1$d instant delivery topics</string>
|
||||
|
||||
|
@ -27,7 +28,9 @@
|
|||
|
||||
<!-- Main activity: Action mode -->
|
||||
<string name="main_action_mode_menu_unsubscribe">Unsubscribe</string>
|
||||
<string name="main_action_mode_delete_dialog_message">Do you really want to unsubscribe from selected topic(s) and permanently delete all the messages you received?</string>
|
||||
<string name="main_action_mode_delete_dialog_message">Do you really want to unsubscribe from selected topic(s) and
|
||||
permanently delete all the messages you received?
|
||||
</string>
|
||||
<string name="main_action_mode_delete_dialog_permanently_delete">Permanently delete</string>
|
||||
<string name="main_action_mode_delete_dialog_cancel">Cancel</string>
|
||||
|
||||
|
@ -38,12 +41,17 @@
|
|||
<string name="main_item_date_yesterday">Yesterday</string>
|
||||
<string name="main_add_button_description">Add subscription</string>
|
||||
<string name="main_no_subscriptions_text">It looks like you don\'t have any subscriptions yet.</string>
|
||||
<string name="main_how_to_intro">Click the button below to create or subscribe to a topic. After that, you can send messages via PUT or POST and you\'ll receive notifications on your phone.</string>
|
||||
<string name="main_how_to_link">For more detailed instructions, check out the ntfy.sh website and documentation.</string>
|
||||
<string name="main_how_to_intro">Click the button below to create or subscribe to a topic. After that, you can send
|
||||
messages via PUT or POST and you\'ll receive notifications on your phone.
|
||||
</string>
|
||||
<string name="main_how_to_link">For more detailed instructions, check out the ntfy.sh website and documentation.
|
||||
</string>
|
||||
|
||||
<!-- Add dialog -->
|
||||
<string name="add_dialog_title">Subscribe to topic</string>
|
||||
<string name="add_dialog_description_below">Topics are not password-protected, so choose a name that\'s not easy to guess. Once subscribed, you can PUT/POST to receive notifications on your phone.</string>
|
||||
<string name="add_dialog_description_below">Topics are not password-protected, so choose a name that\'s not easy to
|
||||
guess. Once subscribed, you can PUT/POST to receive notifications on your phone.
|
||||
</string>
|
||||
<string name="add_dialog_topic_name_hint">Topic name, e.g. phils_alerts</string>
|
||||
<string name="add_dialog_use_another_server">Use another server</string>
|
||||
<string name="add_dialog_use_another_server_description">
|
||||
|
@ -60,13 +68,18 @@
|
|||
|
||||
<!-- Detail activity -->
|
||||
<string name="detail_no_notifications_text">You haven\'t received any notifications for this topic yet.</string>
|
||||
<string name="detail_how_to_intro">To send notifications to this topic, simply PUT or POST to the topic URL.</string>
|
||||
<string name="detail_how_to_intro">To send notifications to this topic, simply PUT or POST to the topic URL.
|
||||
</string>
|
||||
<string name="detail_how_to_example"><![CDATA[ Example (using curl):<br/><tt>$ curl -d \"Hi\" %1$s</tt> ]]></string>
|
||||
<string name="detail_how_to_link">For more detailed instructions, check out the ntfy.sh website and documentation.</string>
|
||||
<string name="detail_delete_dialog_message">Do you really want to unsubscribe from this topic and delete all of the messages you received?</string>
|
||||
<string name="detail_how_to_link">For more detailed instructions, check out the ntfy.sh website and documentation.
|
||||
</string>
|
||||
<string name="detail_delete_dialog_message">Do you really want to unsubscribe from this topic and delete all of the
|
||||
messages you received?
|
||||
</string>
|
||||
<string name="detail_delete_dialog_permanently_delete">Permanently delete</string>
|
||||
<string name="detail_delete_dialog_cancel">Cancel</string>
|
||||
<string name="detail_test_message">This is a test notification from the Ntfy Android app. It was sent at %1$s.</string>
|
||||
<string name="detail_test_message">This is a test notification from the Ntfy Android app. It was sent at %1$s.
|
||||
</string>
|
||||
<string name="detail_test_message_error">Could not send test message: %1$s</string>
|
||||
<string name="detail_copied_to_clipboard_message">Copied to clipboard</string>
|
||||
<string name="detail_instant_delivery_enabled">Instant delivery enabled</string>
|
||||
|
@ -74,17 +87,44 @@
|
|||
<string name="detail_instant_info">Instant delivery cannot be disabled for subscriptions from other servers</string>
|
||||
|
||||
<!-- Detail activity: Action bar -->
|
||||
<string name="detail_menu_test">Send test notification</string>
|
||||
<string name="detail_menu_copy_url">Copy topic address</string>
|
||||
<string name="detail_menu_notification">Notification</string>
|
||||
<string name="detail_menu_enable_instant">Enable instant delivery</string>
|
||||
<string name="detail_menu_disable_instant">Disable instant delivery</string>
|
||||
<string name="detail_menu_test">Send test notification</string>
|
||||
<string name="detail_menu_copy_url">Copy topic address</string>
|
||||
<string name="detail_menu_instant_info">Instant delivery enabled</string>
|
||||
<string name="detail_menu_unsubscribe">Unsubscribe</string>
|
||||
|
||||
<!-- Detail activity: Action mode -->
|
||||
<string name="detail_action_mode_menu_copy">Copy</string>
|
||||
<string name="detail_action_mode_menu_delete">Delete</string>
|
||||
<string name="detail_action_mode_delete_dialog_message">Do you really want to permanently delete the selected message(s)?</string>
|
||||
<string name="detail_action_mode_delete_dialog_message">Do you really want to permanently delete the selected
|
||||
message(s)?
|
||||
</string>
|
||||
<string name="detail_action_mode_delete_dialog_permanently_delete">Permanently delete</string>
|
||||
<string name="detail_action_mode_delete_dialog_cancel">Cancel</string>
|
||||
|
||||
<!-- Notification dialog -->
|
||||
<string name="notification_dialog_title">Notification settings</string>
|
||||
<string name="notification_dialog_cancel">Cancel</string>
|
||||
<string name="notification_dialog_save">Save</string>
|
||||
<string name="notification_dialog_enabled_toast_message">Notifications re-enabled</string>
|
||||
<string name="notification_dialog_muted_forever_toast_message">Notifications are now paused</string>
|
||||
<string name="notification_dialog_muted_until_toast_message">Notifications are now paused until %s</string>
|
||||
|
||||
|
||||
<!-- Preference Titles -->
|
||||
<string name="subscription_settings_notifications_header">Notifications</string>
|
||||
<string name="subscription_settings_pause_title">Pause notifications</string>
|
||||
<string name="subscription_settings_pause_for_title">Until …</string>
|
||||
<string name="sync_header">Sync</string>
|
||||
|
||||
<!-- Messages Preferences -->
|
||||
<string name="signature_title">Your signature</string>
|
||||
|
||||
<!-- Sync Preferences -->
|
||||
|
||||
<string name="attachment_title">Download incoming attachments</string>
|
||||
<string name="attachment_summary_on">Automatically download attachments for incoming emails</string>
|
||||
<string name="attachment_summary_off">Only download attachments when manually requested</string>
|
||||
</resources>
|
||||
|
|
23
app/src/main/res/xml/root_preferences.xml
Normal file
23
app/src/main/res/xml/root_preferences.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/subscription_settings_notifications_header">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:key="pause"
|
||||
app:title="@string/subscription_settings_pause_title"/>
|
||||
|
||||
<ListPreference
|
||||
app:key="reply"
|
||||
app:title="@string/subscription_settings_pause_for_title"
|
||||
app:entries="@array/reply_entries"
|
||||
app:entryValues="@array/reply_values"
|
||||
app:defaultValue="false"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:dependency="pause"
|
||||
/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
Loading…
Reference in a new issue