Discord RPC
This commit is contained in:
@@ -172,6 +172,7 @@ dependencies {
|
|||||||
implementation libs.ssiv
|
implementation libs.ssiv
|
||||||
implementation libs.disk.lru.cache
|
implementation libs.disk.lru.cache
|
||||||
implementation libs.markwon
|
implementation libs.markwon
|
||||||
|
implementation libs.kizzyrpc
|
||||||
|
|
||||||
implementation libs.acra.http
|
implementation libs.acra.http
|
||||||
implementation libs.acra.dialog
|
implementation libs.acra.dialog
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import android.os.Bundle
|
|||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.result.ActivityResultCallback
|
import androidx.activity.result.ActivityResultCallback
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
@@ -86,9 +85,7 @@ class PeriodicalBackupSettingsFragment : BasePreferenceFragment(R.string.periodi
|
|||||||
else -> path
|
else -> path
|
||||||
}
|
}
|
||||||
preference.icon = if (path == null) {
|
preference.icon = if (path == null) {
|
||||||
ContextCompat.getDrawable(preference.context, R.drawable.ic_alert_outline)?.also {
|
getWarningIcon()
|
||||||
it.setTint(ContextCompat.getColor(preference.context, R.color.warning))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,6 +281,10 @@ class AppRouter private constructor(
|
|||||||
startActivity(sourcesSettingsIntent(contextOrNull() ?: return))
|
startActivity(sourcesSettingsIntent(contextOrNull() ?: return))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun openDiscordSettings() {
|
||||||
|
startActivity(discordSettingsIntent(contextOrNull() ?: return))
|
||||||
|
}
|
||||||
|
|
||||||
fun openReaderTapGridSettings() = startActivity(ReaderTapGridConfigActivity::class.java)
|
fun openReaderTapGridSettings() = startActivity(ReaderTapGridConfigActivity::class.java)
|
||||||
|
|
||||||
fun openScrobblerSettings(scrobbler: ScrobblerService) {
|
fun openScrobblerSettings(scrobbler: ScrobblerService) {
|
||||||
@@ -745,6 +749,10 @@ class AppRouter private constructor(
|
|||||||
Intent(context, SettingsActivity::class.java)
|
Intent(context, SettingsActivity::class.java)
|
||||||
.setAction(ACTION_PERIODIC_BACKUP)
|
.setAction(ACTION_PERIODIC_BACKUP)
|
||||||
|
|
||||||
|
fun discordSettingsIntent(context: Context) =
|
||||||
|
Intent(context, SettingsActivity::class.java)
|
||||||
|
.setAction(ACTION_MANAGE_DISCORD)
|
||||||
|
|
||||||
fun proxySettingsIntent(context: Context) =
|
fun proxySettingsIntent(context: Context) =
|
||||||
Intent(context, SettingsActivity::class.java)
|
Intent(context, SettingsActivity::class.java)
|
||||||
.setAction(ACTION_PROXY)
|
.setAction(ACTION_PROXY)
|
||||||
@@ -827,6 +835,7 @@ class AppRouter private constructor(
|
|||||||
const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS"
|
const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS"
|
||||||
const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS"
|
const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS"
|
||||||
const val ACTION_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES"
|
const val ACTION_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES"
|
||||||
|
const val ACTION_MANAGE_DISCORD = "${BuildConfig.APPLICATION_ID}.action.MANAGE_DISCORD"
|
||||||
const val ACTION_SUGGESTIONS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SUGGESTIONS"
|
const val ACTION_SUGGESTIONS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SUGGESTIONS"
|
||||||
const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER"
|
const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER"
|
||||||
const val ACTION_PERIODIC_BACKUP = "${BuildConfig.APPLICATION_ID}.action.MANAGE_PERIODIC_BACKUP"
|
const val ACTION_PERIODIC_BACKUP = "${BuildConfig.APPLICATION_ID}.action.MANAGE_PERIODIC_BACKUP"
|
||||||
|
|||||||
@@ -517,6 +517,12 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
val is32BitColorsEnabled: Boolean
|
val is32BitColorsEnabled: Boolean
|
||||||
get() = prefs.getBoolean(KEY_32BIT_COLOR, false)
|
get() = prefs.getBoolean(KEY_32BIT_COLOR, false)
|
||||||
|
|
||||||
|
val isDiscordRpcEnabled: Boolean
|
||||||
|
get() = prefs.getBoolean(KEY_DISCORD_RPC, false)
|
||||||
|
|
||||||
|
val discordToken: String?
|
||||||
|
get() = prefs.getString(KEY_DISCORD_TOKEN, null)?.trim()?.nullIfEmpty()
|
||||||
|
|
||||||
val isPeriodicalBackupEnabled: Boolean
|
val isPeriodicalBackupEnabled: Boolean
|
||||||
get() = prefs.getBoolean(KEY_BACKUP_PERIODICAL_ENABLED, false)
|
get() = prefs.getBoolean(KEY_BACKUP_PERIODICAL_ENABLED, false)
|
||||||
|
|
||||||
@@ -782,6 +788,8 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
const val KEY_BACKUP_TG_CHAT = "backup_periodic_tg_chat_id"
|
const val KEY_BACKUP_TG_CHAT = "backup_periodic_tg_chat_id"
|
||||||
const val KEY_MANGA_LIST_BADGES = "manga_list_badges"
|
const val KEY_MANGA_LIST_BADGES = "manga_list_badges"
|
||||||
const val KEY_TAGS_WARNINGS = "tags_warnings"
|
const val KEY_TAGS_WARNINGS = "tags_warnings"
|
||||||
|
const val KEY_DISCORD_RPC = "discord_rpc"
|
||||||
|
const val KEY_DISCORD_TOKEN = "discord_token"
|
||||||
|
|
||||||
// keys for non-persistent preferences
|
// keys for non-persistent preferences
|
||||||
const val KEY_APP_VERSION = "app_version"
|
const val KEY_APP_VERSION = "app_version"
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.koitharu.kotatsu.core.ui
|
package org.koitharu.kotatsu.core.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.OnApplyWindowInsetsListener
|
import androidx.core.view.OnApplyWindowInsetsListener
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
@@ -86,6 +88,12 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
|
|||||||
(activity as? SettingsActivity)?.setSectionTitle(title)
|
(activity as? SettingsActivity)?.setSectionTitle(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun getWarningIcon(): Drawable? = context?.let { ctx ->
|
||||||
|
ContextCompat.getDrawable(ctx, R.drawable.ic_alert_outline)?.also {
|
||||||
|
it.setTint(ContextCompat.getColor(ctx, R.color.warning))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun focusPreference(key: String) {
|
private fun focusPreference(key: String) {
|
||||||
val pref = findPreference<Preference>(key)
|
val pref = findPreference<Preference>(key)
|
||||||
if (pref == null) {
|
if (pref == null) {
|
||||||
|
|||||||
@@ -190,6 +190,11 @@ class ReaderActivity :
|
|||||||
viewModel.onPause()
|
viewModel.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
viewModel.onStop()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onProvideAssistContent(outContent: AssistContent) {
|
override fun onProvideAssistContent(outContent: AssistContent) {
|
||||||
super.onProvideAssistContent(outContent)
|
super.onProvideAssistContent(outContent)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@@ -201,6 +206,7 @@ class ReaderActivity :
|
|||||||
|
|
||||||
override fun onIdle() {
|
override fun onIdle() {
|
||||||
viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState())
|
viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState())
|
||||||
|
viewModel.onIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVisibilityChanged(v: View, visibility: Int) {
|
override fun onVisibilityChanged(v: View, visibility: Int) {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import org.koitharu.kotatsu.reader.domain.DetectReaderModeUseCase
|
|||||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
||||||
|
import org.koitharu.kotatsu.scrobbling.discord.DiscordRpc
|
||||||
import org.koitharu.kotatsu.stats.domain.StatsCollector
|
import org.koitharu.kotatsu.stats.domain.StatsCollector
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -84,6 +85,7 @@ class ReaderViewModel @Inject constructor(
|
|||||||
private val historyUpdateUseCase: HistoryUpdateUseCase,
|
private val historyUpdateUseCase: HistoryUpdateUseCase,
|
||||||
private val detectReaderModeUseCase: DetectReaderModeUseCase,
|
private val detectReaderModeUseCase: DetectReaderModeUseCase,
|
||||||
private val statsCollector: StatsCollector,
|
private val statsCollector: StatsCollector,
|
||||||
|
private val discordRpc: DiscordRpc,
|
||||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
||||||
interactor: DetailsInteractor,
|
interactor: DetailsInteractor,
|
||||||
deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
|
deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
|
||||||
@@ -210,6 +212,14 @@ class ReaderViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onStop() {
|
||||||
|
discordRpc.clearRpc()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onIdle() {
|
||||||
|
discordRpc.setIdle()
|
||||||
|
}
|
||||||
|
|
||||||
fun switchMode(newMode: ReaderMode) {
|
fun switchMode(newMode: ReaderMode) {
|
||||||
launchJob {
|
launchJob {
|
||||||
val manga = checkNotNull(getMangaOrNull())
|
val manga = checkNotNull(getMangaOrNull())
|
||||||
@@ -450,6 +460,7 @@ class ReaderViewModel @Inject constructor(
|
|||||||
uiState.value = newState
|
uiState.value = newState
|
||||||
if (isIncognitoMode.value == false) {
|
if (isIncognitoMode.value == false) {
|
||||||
statsCollector.onStateChanged(m.id, state)
|
statsCollector.onStateChanged(m.id, state)
|
||||||
|
discordRpc.updateRpc(m.toManga(), newState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package org.koitharu.kotatsu.scrobbling.discord
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.my.kizzyrpc.KizzyRPC
|
||||||
|
import com.my.kizzyrpc.entities.presence.Activity
|
||||||
|
import com.my.kizzyrpc.entities.presence.Assets
|
||||||
|
import com.my.kizzyrpc.entities.presence.Metadata
|
||||||
|
import com.my.kizzyrpc.entities.presence.Timestamps
|
||||||
|
import dagger.hilt.android.ViewModelLifecycle
|
||||||
|
import dagger.hilt.android.lifecycle.RetainedLifecycle
|
||||||
|
import dagger.hilt.android.scopes.ViewModelScoped
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.plus
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.LocalizedAppContext
|
||||||
|
import org.koitharu.kotatsu.core.model.appUrl
|
||||||
|
import org.koitharu.kotatsu.core.model.getTitle
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.lifecycleScope
|
||||||
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
|
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val STATUS_ONLINE = "online"
|
||||||
|
private const val STATUS_IDLE = "idle"
|
||||||
|
|
||||||
|
@ViewModelScoped
|
||||||
|
class DiscordRpc @Inject constructor(
|
||||||
|
@LocalizedAppContext private val context: Context,
|
||||||
|
private val settings: AppSettings,
|
||||||
|
lifecycle: ViewModelLifecycle,
|
||||||
|
) : RetainedLifecycle.OnClearedListener {
|
||||||
|
|
||||||
|
private val coroutineScope = lifecycle.lifecycleScope + Dispatchers.Default
|
||||||
|
private val appId = context.getString(R.string.discord_app_id)
|
||||||
|
private val appName = context.getString(R.string.app_name)
|
||||||
|
private val rpc = if (settings.isDiscordRpcEnabled) {
|
||||||
|
settings.discordToken?.let { KizzyRPC(it) }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
private var lastActivity: Activity? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
lifecycle.addOnClearedListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
clearRpc()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearRpc() {
|
||||||
|
rpc?.closeRPC()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setIdle() {
|
||||||
|
if (rpc != null) {
|
||||||
|
lastActivity?.let { activity ->
|
||||||
|
updateRpcAsync(activity, idle = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateRpc(manga: Manga, state: ReaderUiState) {
|
||||||
|
if (rpc != null) {
|
||||||
|
updateRpcAsync(
|
||||||
|
activity = Activity(
|
||||||
|
applicationId = appId,
|
||||||
|
name = appName,
|
||||||
|
details = manga.title,
|
||||||
|
state = context.getString(R.string.chapter_d_of_d, state.chapterNumber, state.chaptersTotal),
|
||||||
|
type = 3,
|
||||||
|
timestamps = Timestamps(
|
||||||
|
start = System.currentTimeMillis(),
|
||||||
|
),
|
||||||
|
assets = Assets(
|
||||||
|
largeImage = "mp:attachments/1396092865544716390/1396123149921419465/Kotatsu.png?ex=687d9941&is=687c47c1&hm=61da2b66445adaea18ad16cc2c7f829d1c97f0622beec332f123a56f4d294820&=&format=webp&quality=lossless&width=256&height=256",
|
||||||
|
largeText = "Reading manga on Kotatsu - A manga reader app",
|
||||||
|
smallText = "Reading: ${manga.title}",
|
||||||
|
smallImage = "mp:attachments/1282576939831529473/1395712714415800392/button.png?ex=687b7242&is=687a20c2&hm=828ad97537c94128504402b43512523fe30801d534a48258f80c6fd29fda67c2&=&format=webp&quality=lossless",
|
||||||
|
),
|
||||||
|
buttons = listOf(
|
||||||
|
context.getString(R.string.link_to_manga_in_app),
|
||||||
|
context.getString(R.string.link_to_manga_on_s, manga.source.getTitle(context)),
|
||||||
|
),
|
||||||
|
metadata = Metadata(listOf(manga.appUrl.toString(), manga.publicUrl)),
|
||||||
|
),
|
||||||
|
idle = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateRpcAsync(activity: Activity, idle: Boolean) {
|
||||||
|
val rpc = rpc ?: return
|
||||||
|
lastActivity = activity
|
||||||
|
coroutineScope.launch {
|
||||||
|
rpc.updateRPC(
|
||||||
|
activity = activity,
|
||||||
|
status = if (idle) STATUS_IDLE else STATUS_ONLINE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.koitharu.kotatsu.settings
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
|
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.observe
|
||||||
|
import org.koitharu.kotatsu.settings.utils.EditTextFallbackSummaryProvider
|
||||||
|
|
||||||
|
class DiscordSettingsFragment : BasePreferenceFragment(R.string.discord) {
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
addPreferencesFromResource(R.xml.pref_discord)
|
||||||
|
findPreference<EditTextPreference>(AppSettings.KEY_DISCORD_TOKEN)?.let { pref ->
|
||||||
|
pref.summaryProvider = EditTextFallbackSummaryProvider(R.string.discord_token_summary)
|
||||||
|
pref.setDialogMessage(R.string.discord_token_summary)
|
||||||
|
pref.setOnBindEditTextListener {
|
||||||
|
it.setHint(R.string.discord_token_hint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
settings.observe(
|
||||||
|
AppSettings.KEY_DISCORD_RPC,
|
||||||
|
AppSettings.KEY_DISCORD_TOKEN,
|
||||||
|
).observe(viewLifecycleOwner) {
|
||||||
|
bindTokenWarning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindTokenWarning() {
|
||||||
|
val pref = findPreference<EditTextPreference>(AppSettings.KEY_DISCORD_TOKEN) ?: return
|
||||||
|
val shouldShowError = settings.isDiscordRpcEnabled && settings.discordToken == null
|
||||||
|
pref.icon = if (shouldShowError) {
|
||||||
|
getWarningIcon()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -149,6 +149,7 @@ class SettingsActivity :
|
|||||||
AppRouter.ACTION_TRACKER -> TrackerSettingsFragment()
|
AppRouter.ACTION_TRACKER -> TrackerSettingsFragment()
|
||||||
AppRouter.ACTION_PERIODIC_BACKUP -> PeriodicalBackupSettingsFragment()
|
AppRouter.ACTION_PERIODIC_BACKUP -> PeriodicalBackupSettingsFragment()
|
||||||
AppRouter.ACTION_SOURCES -> SourcesSettingsFragment()
|
AppRouter.ACTION_SOURCES -> SourcesSettingsFragment()
|
||||||
|
AppRouter.ACTION_MANAGE_DISCORD -> DiscordSettingsFragment()
|
||||||
AppRouter.ACTION_PROXY -> ProxySettingsFragment()
|
AppRouter.ACTION_PROXY -> ProxySettingsFragment()
|
||||||
AppRouter.ACTION_MANAGE_DOWNLOADS -> DownloadsSettingsFragment()
|
AppRouter.ACTION_MANAGE_DOWNLOADS -> DownloadsSettingsFragment()
|
||||||
AppRouter.ACTION_SOURCE -> SourceSettingsFragment.newInstance(
|
AppRouter.ACTION_SOURCE -> SourceSettingsFragment.newInstance(
|
||||||
|
|||||||
11
app/src/main/res/drawable/ic_discord.xml
Normal file
11
app/src/main/res/drawable/ic_discord.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19.27,5.33C17.94,4.71 16.5,4.26 15,4c-0.03,0 -0.05,0.01 -0.07,0.03c-0.18,0.33 -0.39,0.76 -0.53,1.09c-1.61,-0.24 -3.22,-0.24 -4.8,0C9.46,4.78 9.25,4.36 9.06,4.03C9.05,4.01 9.02,4 8.99,4c-1.5,0.26 -2.93,0.71 -4.27,1.33c-0.01,0 -0.02,0.01 -0.03,0.02c-2.72,4.07 -3.47,8.03 -3.1,11.95c0,0.02 0.01,0.04 0.03,0.05c1.8,1.32 3.53,2.12 5.24,2.65c0.03,0.01 0.06,0 0.07,-0.02c0.4,-0.55 0.76,-1.13 1.07,-1.74c0.02,-0.04 0,-0.08 -0.04,-0.09c-0.57,-0.22 -1.11,-0.48 -1.64,-0.78c-0.04,-0.02 -0.04,-0.08 -0.01,-0.11c0.11,-0.08 0.22,-0.17 0.33,-0.25c0.02,-0.02 0.05,-0.02 0.07,-0.01c3.44,1.57 7.15,1.57 10.55,0c0.02,-0.01 0.05,-0.01 0.07,0.01c0.11,0.09 0.22,0.17 0.33,0.26c0.04,0.03 0.04,0.09 -0.01,0.11c-0.52,0.31 -1.07,0.56 -1.64,0.78c-0.04,0.01 -0.05,0.06 -0.04,0.09c0.32,0.61 0.68,1.19 1.07,1.74C17.07,20 17.1,20.01 17.13,20c1.72,-0.53 3.45,-1.33 5.25,-2.65c0.02,-0.01 0.03,-0.03 0.03,-0.05c0.44,-4.53 -0.73,-8.46 -3.1,-11.95C19.3,5.34 19.29,5.33 19.27,5.33zM8.52,14.91c-1.03,0 -1.89,-0.95 -1.89,-2.12s0.84,-2.12 1.89,-2.12c1.06,0 1.9,0.96 1.89,2.12C10.41,13.96 9.57,14.91 8.52,14.91zM15.49,14.91c-1.03,0 -1.89,-0.95 -1.89,-2.12s0.84,-2.12 1.89,-2.12c1.06,0 1.9,0.96 1.89,2.12C17.38,13.96 16.55,14.91 15.49,14.91z" />
|
||||||
|
</vector>
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
<string name="sync_authority_favourites" translatable="false">org.koitharu.kotatsu.favourites</string>
|
<string name="sync_authority_favourites" translatable="false">org.koitharu.kotatsu.favourites</string>
|
||||||
<string name="tg_backup_bot_token" translatable="false">7455491254:AAHq5AJmizJJpVqFgx16pEAO4g0AX8V6NTY</string>
|
<string name="tg_backup_bot_token" translatable="false">7455491254:AAHq5AJmizJJpVqFgx16pEAO4g0AX8V6NTY</string>
|
||||||
<string name="tg_backup_bot_name" translatable="false">kotatsu_backup_bot</string>
|
<string name="tg_backup_bot_name" translatable="false">kotatsu_backup_bot</string>
|
||||||
|
<string name="discord_app_id" translatable="false">1395464028611940393</string>
|
||||||
<string-array name="values_theme" translatable="false">
|
<string-array name="values_theme" translatable="false">
|
||||||
<item>-1</item>
|
<item>-1</item>
|
||||||
<item>1</item>
|
<item>1</item>
|
||||||
|
|||||||
@@ -860,4 +860,10 @@
|
|||||||
<string name="main_screen_fab">Show floating Continue button</string>
|
<string name="main_screen_fab">Show floating Continue button</string>
|
||||||
<string name="main_screen_fab_summary">Allows to continue reading in a one click. This button will not appear in incognito mode or when the history is empty</string>
|
<string name="main_screen_fab_summary">Allows to continue reading in a one click. This button will not appear in incognito mode or when the history is empty</string>
|
||||||
<string name="error_corrupted_zip">Corrupted ZIP archive (%s)</string>
|
<string name="error_corrupted_zip">Corrupted ZIP archive (%s)</string>
|
||||||
|
<string name="discord" translatable="false">Discord</string>
|
||||||
|
<string name="discord_rpc">Discord Rich Presence</string>
|
||||||
|
<string name="discord_token">Discord Token</string>
|
||||||
|
<string name="discord_token_summary">Enter your Discord Token to enable Rich Presence</string>
|
||||||
|
<string name="discord_token_hint">Paste your Discord Token here</string>
|
||||||
|
<string name="discord_rpc_summary">Show your reading status on Discord</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
17
app/src/main/res/xml/pref_discord.xml
Normal file
17
app/src/main/res/xml/pref_discord.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.preference.PreferenceScreen
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="discord_rpc"
|
||||||
|
android:layout="@layout/preference_toggle_header"
|
||||||
|
android:title="@string/discord_rpc" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:dependency="discord_rpc"
|
||||||
|
android:key="discord_token"
|
||||||
|
android:summary="@string/discord_token_summary"
|
||||||
|
android:title="@string/discord_token" />
|
||||||
|
|
||||||
|
</androidx.preference.PreferenceScreen>
|
||||||
@@ -45,24 +45,36 @@
|
|||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="anilist"
|
android:key="anilist"
|
||||||
|
android:summary="@string/loading_"
|
||||||
android:title="@string/anilist"
|
android:title="@string/anilist"
|
||||||
app:icon="@drawable/ic_anilist" />
|
app:icon="@drawable/ic_anilist" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="kitsu"
|
android:key="kitsu"
|
||||||
|
android:summary="@string/loading_"
|
||||||
android:title="@string/kitsu"
|
android:title="@string/kitsu"
|
||||||
app:icon="@drawable/ic_kitsu" />
|
app:icon="@drawable/ic_kitsu" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="mal"
|
android:key="mal"
|
||||||
|
android:summary="@string/loading_"
|
||||||
android:title="@string/mal"
|
android:title="@string/mal"
|
||||||
app:icon="@drawable/ic_mal" />
|
app:icon="@drawable/ic_mal" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="shikimori"
|
android:key="shikimori"
|
||||||
|
android:summary="@string/loading_"
|
||||||
android:title="@string/shikimori"
|
android:title="@string/shikimori"
|
||||||
app:icon="@drawable/ic_shikimori" />
|
app:icon="@drawable/ic_shikimori" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:fragment="org.koitharu.kotatsu.settings.DiscordSettingsFragment"
|
||||||
|
android:key="discord_rpc"
|
||||||
|
android:summary="@string/discord_rpc_summary"
|
||||||
|
android:title="@string/discord_rpc"
|
||||||
|
app:allowDividerAbove="true"
|
||||||
|
app:icon="@drawable/ic_discord" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ json = "20250517"
|
|||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitKtx = "1.2.1"
|
junitKtx = "1.2.1"
|
||||||
kotlin = "2.1.21"
|
kotlin = "2.1.21"
|
||||||
|
kizzyRpc = "ad8f2e32eb"
|
||||||
ksp = "2.1.21-2.0.1"
|
ksp = "2.1.21-2.0.1"
|
||||||
leakcanary = "3.0-alpha-8"
|
leakcanary = "3.0-alpha-8"
|
||||||
lifecycle = "2.9.1"
|
lifecycle = "2.9.1"
|
||||||
@@ -94,6 +95,7 @@ hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", vers
|
|||||||
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" }
|
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" }
|
||||||
json = { module = "org.json:json", version.ref = "json" }
|
json = { module = "org.json:json", version.ref = "json" }
|
||||||
junit = { module = "junit:junit", version.ref = "junit" }
|
junit = { module = "junit:junit", version.ref = "junit" }
|
||||||
|
kizzyrpc = { module = "com.github.dead8309:KizzyRPC", version.ref = "kizzyRpc" }
|
||||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
|
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
|
||||||
kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "coroutines" }
|
kotlinx-coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "coroutines" }
|
||||||
|
|||||||
Reference in New Issue
Block a user