Configure manga tracker for each favourite category
parent
ffad6a4ae6
commit
11fc8b6642
@ -0,0 +1,11 @@
|
||||
package org.koitharu.kotatsu.core.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration9To10 : Migration(9, 10) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE favourite_categories ADD COLUMN `track` INTEGER NOT NULL DEFAULT 1")
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,17 @@
|
||||
package org.koitharu.kotatsu.tracker
|
||||
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.ui.FeedViewModel
|
||||
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
|
||||
|
||||
val trackerModule
|
||||
get() = module {
|
||||
|
||||
single { TrackingRepository(get()) }
|
||||
factory { TrackingRepository(get()) }
|
||||
factory { TrackerNotificationChannels(androidContext(), get()) }
|
||||
|
||||
viewModel { FeedViewModel(get()) }
|
||||
}
|
||||
@ -0,0 +1,143 @@
|
||||
package org.koitharu.kotatsu.tracker.work
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationChannelGroup
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
|
||||
class TrackerNotificationChannels(
|
||||
private val context: Context,
|
||||
private val settings: AppSettings,
|
||||
) {
|
||||
|
||||
private val manager = NotificationManagerCompat.from(context)
|
||||
|
||||
val areNotificationsDisabled: Boolean
|
||||
get() = !manager.areNotificationsEnabled()
|
||||
|
||||
fun updateChannels(categories: Collection<FavouriteCategory>) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return
|
||||
}
|
||||
manager.deleteNotificationChannel(OLD_CHANNEL_ID)
|
||||
val group = createGroup()
|
||||
val existingChannels = group.channels.associateByTo(HashMap()) { it.id }
|
||||
for (category in categories) {
|
||||
val id = getFavouritesChannelId(category.id)
|
||||
if (existingChannels.remove(id)?.name == category.title) {
|
||||
continue
|
||||
}
|
||||
val channel = NotificationChannel(id, category.title, NotificationManager.IMPORTANCE_DEFAULT)
|
||||
channel.group = GROUP_ID
|
||||
manager.createNotificationChannel(channel)
|
||||
}
|
||||
existingChannels.remove(CHANNEL_ID_HISTORY)
|
||||
createHistoryChannel()
|
||||
for (id in existingChannels.keys) {
|
||||
manager.deleteNotificationChannel(id)
|
||||
}
|
||||
}
|
||||
|
||||
fun createChannel(category: FavouriteCategory) {
|
||||
renameChannel(category.id, category.title)
|
||||
}
|
||||
|
||||
fun renameChannel(categoryId: Long, name: String) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return
|
||||
}
|
||||
val id = getFavouritesChannelId(categoryId)
|
||||
val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_DEFAULT)
|
||||
channel.group = createGroup().id
|
||||
manager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
fun deleteChannel(categoryId: Long) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return
|
||||
}
|
||||
manager.deleteNotificationChannel(getFavouritesChannelId(categoryId))
|
||||
}
|
||||
|
||||
fun isFavouriteNotificationsEnabled(category: FavouriteCategory): Boolean {
|
||||
if (!manager.areNotificationsEnabled()) {
|
||||
return false
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = manager.getNotificationChannel(getFavouritesChannelId(category.id))
|
||||
channel != null && channel.importance != NotificationManager.IMPORTANCE_NONE
|
||||
} else {
|
||||
// fallback
|
||||
settings.isTrackerNotificationsEnabled
|
||||
}
|
||||
}
|
||||
|
||||
fun isHistoryNotificationsEnabled(): Boolean {
|
||||
if (!manager.areNotificationsEnabled()) {
|
||||
return false
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = manager.getNotificationChannel(getHistoryChannelId())
|
||||
channel != null && channel.importance != NotificationManager.IMPORTANCE_NONE
|
||||
} else {
|
||||
// fallback
|
||||
settings.isTrackerNotificationsEnabled
|
||||
}
|
||||
}
|
||||
|
||||
fun isNotificationGroupEnabled(): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return settings.isTrackerNotificationsEnabled
|
||||
}
|
||||
val group = manager.getNotificationChannelGroup(GROUP_ID) ?: return true
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && group.isBlocked) {
|
||||
return false
|
||||
}
|
||||
return group.channels.any { it.importance != NotificationManagerCompat.IMPORTANCE_NONE }
|
||||
}
|
||||
|
||||
fun getFavouritesChannelId(categoryId: Long): String {
|
||||
return CHANNEL_ID_PREFIX + categoryId
|
||||
}
|
||||
|
||||
fun getHistoryChannelId(): String {
|
||||
return CHANNEL_ID_HISTORY
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun createGroup(): NotificationChannelGroup {
|
||||
manager.getNotificationChannelGroup(GROUP_ID)?.let {
|
||||
return it
|
||||
}
|
||||
val group = NotificationChannelGroup(GROUP_ID, context.getString(R.string.new_chapters))
|
||||
manager.createNotificationChannelGroup(group)
|
||||
return group
|
||||
}
|
||||
|
||||
private fun createHistoryChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return
|
||||
}
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID_HISTORY,
|
||||
context.getString(R.string.history),
|
||||
NotificationManager.IMPORTANCE_DEFAULT,
|
||||
)
|
||||
channel.group = GROUP_ID
|
||||
manager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val GROUP_ID = "trackers"
|
||||
private const val CHANNEL_ID_PREFIX = "track_fav_"
|
||||
private const val CHANNEL_ID_HISTORY = "track_history"
|
||||
private const val OLD_CHANNEL_ID = "tracking"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package org.koitharu.kotatsu.tracker.work
|
||||
|
||||
import org.koitharu.kotatsu.core.model.MangaTracking
|
||||
|
||||
class TrackingItem(
|
||||
val tracking: MangaTracking,
|
||||
val channelId: String?,
|
||||
) {
|
||||
|
||||
operator fun component1() = tracking
|
||||
|
||||
operator fun component2() = channelId
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as TrackingItem
|
||||
|
||||
if (tracking != other.tracking) return false
|
||||
if (channelId != other.channelId) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = tracking.hashCode()
|
||||
result = 31 * result + channelId.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:windowBackground"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:baselineAligned="false"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:padding="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
style="@style/PreferenceSummaryTextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:maxLines="10"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@android:id/widget_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
@ -1,19 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="tracker_notifications"
|
||||
android:layout="@layout/preference_toggle_header"
|
||||
android:title="@string/notifications_enable" />
|
||||
|
||||
<Preference
|
||||
android:dependency="tracker_notifications"
|
||||
android:key="notifications_sound"
|
||||
android:title="@string/notification_sound" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:dependency="tracker_notifications"
|
||||
android:key="notifications_vibrate"
|
||||
android:title="@string/vibration" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:dependency="tracker_notifications"
|
||||
android:key="notifications_light"
|
||||
android:title="@string/light_indicator" />
|
||||
|
||||
<org.koitharu.kotatsu.settings.utils.LinksPreference
|
||||
android:icon="@drawable/ic_info_outline"
|
||||
android:key="tracker_notifications_info"
|
||||
android:persistent="false"
|
||||
android:selectable="false"
|
||||
android:summary="@string/show_notification_new_chapters_off"
|
||||
app:allowDividerAbove="true"
|
||||
app:isPreferenceVisible="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Loading…
Reference in New Issue