Hide username in Live Notifications push

This change will allow you to hide your name in the permanent
notification.
This commit is contained in:
Adolfo Santiago 2022-05-08 09:40:21 +02:00
parent 6670cd079f
commit cd69679e9a
No known key found for this signature in database
GPG key ID: 244D6F9A317B4A65
6 changed files with 155 additions and 46 deletions

View file

@ -44,6 +44,8 @@
<string name="pref_title_other">Other</string> <string name="pref_title_other">Other</string>
<string name="pref_title_privacy">Privacy</string> <string name="pref_title_privacy">Privacy</string>
<string name="key_hide_live_notification_desc">hideLiveNotifDesc</string>
<string name="pref_title_composing">Composing</string> <string name="pref_title_composing">Composing</string>
<string name="pref_title_composing_title">Composing using zero-width space characters in emojis</string> <string name="pref_title_composing_title">Composing using zero-width space characters in emojis</string>
@ -52,6 +54,8 @@
<string name="key_enable_acra">acra.enable</string> <string name="key_enable_acra">acra.enable</string>
<string name="pref_title_anonymize_upload_filenames">Anonymize uploaded file names</string> <string name="pref_title_anonymize_upload_filenames">Anonymize uploaded file names</string>
<string name="pref_title_hide_live_notification_description">Hide Live Notification username</string>
<string name="pref_title_hide_live_notification_description_toast">This will be changed once you go back to the timeline.</string>
<string name="pref_title_live_notifications">Live notifications</string> <string name="pref_title_live_notifications">Live notifications</string>
<string name="pref_summary_live_notifications">May slightly increase power consumption</string> <string name="pref_summary_live_notifications">May slightly increase power consumption</string>
<string name="pref_title_default_formatting">Default formatting syntax(if supported by instance)</string> <string name="pref_title_default_formatting">Default formatting syntax(if supported by instance)</string>

View file

@ -53,7 +53,13 @@ import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.google.android.material.tabs.TabLayoutMediator.TabConfigurationStrategy import com.google.android.material.tabs.TabLayoutMediator.TabConfigurationStrategy
import com.keylesspalace.tusky.appstore.* import com.keylesspalace.tusky.appstore.AnnouncementReadEvent
import com.keylesspalace.tusky.appstore.CacheUpdater
import com.keylesspalace.tusky.appstore.Event
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.appstore.ProfileEditedEvent
import com.keylesspalace.tusky.components.announcements.AnnouncementsActivity import com.keylesspalace.tusky.components.announcements.AnnouncementsActivity
import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.canHandleMimeType import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.canHandleMimeType
@ -74,7 +80,14 @@ import com.keylesspalace.tusky.interfaces.ReselectableFragment
import com.keylesspalace.tusky.pager.MainPagerAdapter import com.keylesspalace.tusky.pager.MainPagerAdapter
import com.keylesspalace.tusky.service.StreamingService import com.keylesspalace.tusky.service.StreamingService
import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.ViewPager2Fix
import com.keylesspalace.tusky.util.deleteStaleCachedMedia
import com.keylesspalace.tusky.util.emojify
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.removeShortcut
import com.keylesspalace.tusky.util.updateShortcut
import com.keylesspalace.tusky.util.visible
import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.colorInt
@ -83,9 +96,27 @@ import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.ColorHolder import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.StringHolder import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.iconics.iconicsIcon import com.mikepenz.materialdrawer.iconics.iconicsIcon
import com.mikepenz.materialdrawer.model.* import com.mikepenz.materialdrawer.model.AbstractDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.* import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.util.* import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IProfile
import com.mikepenz.materialdrawer.model.interfaces.descriptionRes
import com.mikepenz.materialdrawer.model.interfaces.descriptionText
import com.mikepenz.materialdrawer.model.interfaces.iconRes
import com.mikepenz.materialdrawer.model.interfaces.iconUrl
import com.mikepenz.materialdrawer.model.interfaces.nameRes
import com.mikepenz.materialdrawer.model.interfaces.nameText
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.addItemAtPosition
import com.mikepenz.materialdrawer.util.addItems
import com.mikepenz.materialdrawer.util.addItemsAtPosition
import com.mikepenz.materialdrawer.util.getDrawerItem
import com.mikepenz.materialdrawer.util.removeItems
import com.mikepenz.materialdrawer.util.updateBadge
import com.mikepenz.materialdrawer.widget.AccountHeaderView import com.mikepenz.materialdrawer.widget.AccountHeaderView
import com.uber.autodispose.android.lifecycle.autoDispose import com.uber.autodispose.android.lifecycle.autoDispose
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
@ -93,7 +124,14 @@ import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import javax.inject.Inject import javax.inject.Inject
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.bottomNav
import kotlinx.android.synthetic.main.activity_main.bottomTabLayout
import kotlinx.android.synthetic.main.activity_main.composeButton
import kotlinx.android.synthetic.main.activity_main.mainDrawer
import kotlinx.android.synthetic.main.activity_main.mainDrawerLayout
import kotlinx.android.synthetic.main.activity_main.mainToolbar
import kotlinx.android.synthetic.main.activity_main.tabLayout
import kotlinx.android.synthetic.main.activity_main.viewPager
import timber.log.Timber import timber.log.Timber
class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector { class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
@ -237,6 +275,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
PrefKeys.LIVE_NOTIFICATIONS -> { PrefKeys.LIVE_NOTIFICATIONS -> {
initPullNotifications() initPullNotifications()
} }
PrefKeys.HIDE_LIVE_NOTIFICATION_DESCRIPTION -> {
initPullNotifications(rebootPush = true)
}
} }
} }
is AnnouncementReadEvent -> { is AnnouncementReadEvent -> {
@ -259,7 +300,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
) )
} }
private fun initPullNotifications() { private fun initPullNotifications(rebootPush: Boolean = false) {
if(rebootPush) {
disablePushNotifications()
}
if(NotificationHelper.areNotificationsEnabled(this, accountManager)) { if(NotificationHelper.areNotificationsEnabled(this, accountManager)) {
if(accountManager.areNotificationsStreamingEnabled()) { if(accountManager.areNotificationsStreamingEnabled()) {
StreamingService.startStreaming(this) StreamingService.startStreaming(this)
@ -269,12 +314,16 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
NotificationHelper.enablePullNotifications(this) NotificationHelper.enablePullNotifications(this)
} }
} else { } else {
StreamingService.stopStreaming(this) disablePushNotifications()
NotificationHelper.disablePullNotifications(this)
} }
draftWarning() draftWarning()
} }
private fun disablePushNotifications() {
StreamingService.stopStreaming(this)
NotificationHelper.disablePullNotifications(this)
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
NotificationHelper.clearNotificationsForActiveAccount(this, accountManager) NotificationHelper.clearNotificationsForActiveAccount(this, accountManager)

View file

@ -21,12 +21,17 @@
package com.keylesspalace.tusky.components.preference package com.keylesspalace.tusky.components.preference
import android.os.Bundle import android.os.Bundle
import android.widget.Toast
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.components.notifications.NotificationHelper
import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.Notification import com.keylesspalace.tusky.entity.Notification
import com.keylesspalace.tusky.service.StreamingService
import com.keylesspalace.tusky.settings.AppTheme import com.keylesspalace.tusky.settings.AppTheme
import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.settings.emojiPreference import com.keylesspalace.tusky.settings.emojiPreference
@ -55,6 +60,9 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
@Inject @Inject
lateinit var accountManager: AccountManager lateinit var accountManager: AccountManager
@Inject
lateinit var eventHub: EventHub
private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) }
private var httpProxyPref: Preference? = null private var httpProxyPref: Preference? = null
@ -246,6 +254,24 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
setTitle(R.string.pref_title_anonymize_upload_filenames) setTitle(R.string.pref_title_anonymize_upload_filenames)
isSingleLineTitle = false isSingleLineTitle = false
} }
switchPreference {
setDefaultValue(false)
key = PrefKeys.HIDE_LIVE_NOTIFICATION_DESCRIPTION
setTitle(R.string.pref_title_hide_live_notification_description)
isSingleLineTitle = false
setOnPreferenceChangeListener { _, _ ->
eventHub.dispatch(PreferenceChangedEvent(key))
Toast.makeText(
context,
getString(R.string.pref_title_hide_live_notification_description_toast),
Toast.LENGTH_LONG
).show()
true
}
}
} }
preferenceCategory(R.string.pref_title_browser_settings) { preferenceCategory(R.string.pref_title_browser_settings) {

View file

@ -7,10 +7,10 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import com.google.gson.Gson import com.google.gson.Gson
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.appstore.ChatMessageReceivedEvent import com.keylesspalace.tusky.appstore.ChatMessageReceivedEvent
@ -22,12 +22,19 @@ import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.Notification import com.keylesspalace.tusky.entity.Notification
import com.keylesspalace.tusky.entity.StreamEvent import com.keylesspalace.tusky.entity.StreamEvent
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.isLessThan import com.keylesspalace.tusky.util.isLessThan
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import okhttp3.*
import javax.inject.Inject import javax.inject.Inject
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import timber.log.Timber
class StreamingService : Service(), Injectable {
class StreamingService: Service(), Injectable {
@Inject @Inject
lateinit var api: MastodonApi lateinit var api: MastodonApi
@ -58,7 +65,7 @@ class StreamingService: Service(), Injectable {
private fun stopStreamingForId(id: Long) { private fun stopStreamingForId(id: Long) {
if(id in sockets) { if(id in sockets) {
sockets[id]!!.close(1000, null) sockets[id]?.close(1000, null)
sockets.remove(id) sockets.remove(id)
} }
} }
@ -69,7 +76,7 @@ class StreamingService: Service(), Injectable {
} }
sockets.clear() sockets.clear()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH)
} }
@ -87,7 +94,7 @@ class StreamingService: Service(), Injectable {
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if(intent.getBooleanExtra(KEY_STOP_STREAMING, false)) { if(intent.getBooleanExtra(KEY_STOP_STREAMING, false)) {
Log.d(TAG, "Stream goes suya..") Timber.d("Stream goes suya..")
stopStreaming() stopStreaming()
stopSelfResult(startId) stopSelfResult(startId)
return START_NOT_STICKY return START_NOT_STICKY
@ -99,20 +106,22 @@ class StreamingService: Service(), Injectable {
for(account in accounts) { for(account in accounts) {
stopStreamingForId(account.id) stopStreamingForId(account.id)
if(!account.notificationsStreamingEnabled) if(!account.notificationsStreamingEnabled) {
continue continue
}
val endpoint = "wss://${account.domain}/api/v1/streaming/?access_token=${account.accessToken}&stream=user:notification" val endpoint =
"wss://${account.domain}/api/v1/streaming/?access_token=${account.accessToken}&stream=user:notification"
val request = Request.Builder().url(endpoint).build() val request = Request.Builder().url(endpoint).build()
Log.d(TAG, "Running stream for ${account.fullName}") Timber.d("Running stream for ${account.fullName}")
sockets[account.id] = client.newWebSocket( sockets[account.id] = client.newWebSocket(
request, request,
makeStreamingListener( makeStreamingListener(
"${account.fullName}/user:notification", "${account.fullName}/user:notification",
account account
) )
) )
description += "\n" + account.fullName description += "\n" + account.fullName
@ -120,27 +129,36 @@ class StreamingService: Service(), Injectable {
} }
if(count <= 0) { if(count <= 0) {
Log.d(TAG, "No accounts. Stopping stream") Timber.d("No accounts. Stopping stream")
stopStreaming() stopStreaming()
stopSelfResult(startId) stopSelfResult(startId)
return START_NOT_STICKY return START_NOT_STICKY
} }
if (NotificationHelper.NOTIFICATION_USE_CHANNELS) { if(NotificationHelper.NOTIFICATION_USE_CHANNELS) {
val channel = NotificationChannel(CHANNEL_ID, getString(R.string.streaming_notification_name), NotificationManager.IMPORTANCE_LOW) val channel = NotificationChannel(
CHANNEL_ID,
getString(R.string.streaming_notification_name),
NotificationManager.IMPORTANCE_LOW
)
notificationManager.createNotificationChannel(channel) notificationManager.createNotificationChannel(channel)
} }
val builder = NotificationCompat.Builder(this, CHANNEL_ID) val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notify) .setSmallIcon(R.drawable.ic_notify)
.setContentTitle(getString(R.string.streaming_notification_name)) .setContentTitle(getString(R.string.streaming_notification_name))
.setContentText(description) .setOngoing(true)
.setOngoing(true) .setSilent(true)
.setNotificationSilent() .setPriority(NotificationCompat.PRIORITY_MIN)
.setPriority(NotificationCompat.PRIORITY_MIN) .setColor(ContextCompat.getColor(this, R.color.tusky_blue))
.setColor(ContextCompat.getColor(this, R.color.tusky_blue))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val showDescription = PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(PrefKeys.HIDE_LIVE_NOTIFICATION_DESCRIPTION, false)
if(!showDescription) {
builder.setContentText(description)
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH)
startForeground(1337, builder.build()) startForeground(1337, builder.build())
} else { } else {
@ -155,9 +173,8 @@ class StreamingService: Service(), Injectable {
} }
companion object { companion object {
val CHANNEL_ID = "streaming" const val CHANNEL_ID = "streaming"
val KEY_STOP_STREAMING = "stop_streaming" const val KEY_STOP_STREAMING = "stop_streaming"
val TAG = "StreamingService"
@JvmStatic @JvmStatic
var serviceRunning = false var serviceRunning = false
@ -176,7 +193,7 @@ class StreamingService: Service(), Injectable {
val intent = Intent(context, StreamingService::class.java) val intent = Intent(context, StreamingService::class.java)
intent.putExtra(KEY_STOP_STREAMING, false) intent.putExtra(KEY_STOP_STREAMING, false)
Log.d(TAG, "Starting notifications streaming service...") Timber.d("Starting notifications streaming service...")
startForegroundService(context, intent) startForegroundService(context, intent)
} }
@ -184,13 +201,14 @@ class StreamingService: Service(), Injectable {
@JvmStatic @JvmStatic
fun stopStreaming(context: Context) { fun stopStreaming(context: Context) {
synchronized(serviceRunning) { synchronized(serviceRunning) {
if(!serviceRunning) if(!serviceRunning) {
return return
}
val intent = Intent(context, StreamingService::class.java) val intent = Intent(context, StreamingService::class.java)
intent.putExtra(KEY_STOP_STREAMING, true) intent.putExtra(KEY_STOP_STREAMING, true)
Log.d(TAG, "Stopping notifications streaming service...") Timber.d("Stopping notifications streaming service...")
serviceRunning = false serviceRunning = false
@ -199,18 +217,20 @@ class StreamingService: Service(), Injectable {
} }
} }
private fun makeStreamingListener(tag: String, account: AccountEntity) : WebSocketListener { private fun makeStreamingListener(tag: String, account: AccountEntity): WebSocketListener {
return object : WebSocketListener() { return object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) { override fun onOpen(webSocket: WebSocket, response: Response) {
Log.d(TAG, "Stream connected to: $tag") Timber.d("Stream connected to: $tag")
} }
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
Log.d(TAG, "Stream closed for: $tag") Timber.d("Stream closed for: $tag")
} }
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Log.d(TAG, "Stream failed for $tag", t) Timber.e("Stream failed for $tag", t)
} }
override fun onMessage(webSocket: WebSocket, text: String) { override fun onMessage(webSocket: WebSocket, text: String) {
@ -230,10 +250,10 @@ class StreamingService: Service(), Injectable {
} }
} }
else -> { else -> {
Log.d(TAG, "Unknown event type: ${event.event}") Timber.w("Unknown event type: ${event.event}")
} }
} }
} }
} }
} }
} }

View file

@ -54,6 +54,7 @@ object PrefKeys {
const val BIG_EMOJIS = "bigEmojis" const val BIG_EMOJIS = "bigEmojis"
const val STICKERS = "stickers" const val STICKERS = "stickers"
const val ANONYMIZE_FILENAMES = "anonymizeFilenames" const val ANONYMIZE_FILENAMES = "anonymizeFilenames"
const val HIDE_LIVE_NOTIFICATION_DESCRIPTION = "hideLiveNotifDesc"
const val HIDE_MUTED_USERS = "hideMutedUsers" const val HIDE_MUTED_USERS = "hideMutedUsers"
const val ANIMATE_CUSTOM_EMOJIS = "animateCustomEmojis" const val ANIMATE_CUSTOM_EMOJIS = "animateCustomEmojis"
const val RENDER_STATUS_AS_MENTION = "renderStatusAsMention" const val RENDER_STATUS_AS_MENTION = "renderStatusAsMention"

View file

@ -1,6 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/pref_title_privacy">
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_hide_live_notification_desc"
android:title="@string/pref_title_hide_live_notification_description" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_acra_category"> <PreferenceCategory android:title="@string/pref_acra_category">
<CheckBoxPreference <CheckBoxPreference