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
|
package org.koitharu.kotatsu.tracker
|
||||||
|
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||||
import org.koitharu.kotatsu.tracker.ui.FeedViewModel
|
import org.koitharu.kotatsu.tracker.ui.FeedViewModel
|
||||||
|
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels
|
||||||
|
|
||||||
val trackerModule
|
val trackerModule
|
||||||
get() = module {
|
get() = module {
|
||||||
|
|
||||||
single { TrackingRepository(get()) }
|
factory { TrackingRepository(get()) }
|
||||||
|
factory { TrackerNotificationChannels(androidContext(), get()) }
|
||||||
|
|
||||||
viewModel { FeedViewModel(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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen
|
<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
|
<Preference
|
||||||
|
android:dependency="tracker_notifications"
|
||||||
android:key="notifications_sound"
|
android:key="notifications_sound"
|
||||||
android:title="@string/notification_sound" />
|
android:title="@string/notification_sound" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
android:dependency="tracker_notifications"
|
||||||
android:key="notifications_vibrate"
|
android:key="notifications_vibrate"
|
||||||
android:title="@string/vibration" />
|
android:title="@string/vibration" />
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
|
android:dependency="tracker_notifications"
|
||||||
android:key="notifications_light"
|
android:key="notifications_light"
|
||||||
android:title="@string/light_indicator" />
|
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>
|
</PreferenceScreen>
|
||||||
Loading…
Reference in New Issue