diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f9c0005ab..d69ab462b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -337,6 +337,13 @@
+
+
+
+
+
(EXTRA_ERROR) ?: return
+ e.report()
+ }
+
+ companion object {
+
+ private const val EXTRA_ERROR = "err"
+ private const val ACTION_REPORT = "${BuildConfig.APPLICATION_ID}.action.REPORT_ERROR"
+
+ fun getPendingIntent(context: Context, e: Throwable): PendingIntent {
+ val intent = Intent(context, ErrorReporterReceiver::class.java)
+ intent.setAction(ACTION_REPORT)
+ intent.setData(Uri.parse("err://${e.hashCode()}"))
+ intent.putExtra(EXTRA_ERROR, e)
+ return checkNotNull(PendingIntentCompat.getBroadcast(context, 0, intent, 0, false))
+ }
+ }
+}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt
index e1e912caa..831f4514f 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportWorker.kt
@@ -24,6 +24,7 @@ import coil.request.ImageRequest
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.ErrorReporterReceiver
import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull
@@ -91,6 +92,7 @@ class ImportWorker @AssistedInject constructor(
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setDefaults(0)
.setSilent(true)
+ .setAutoCancel(true)
result.onSuccess { manga ->
notification.setLargeIcon(
coil.execute(
@@ -110,10 +112,9 @@ class ImportWorker @AssistedInject constructor(
PendingIntent.FLAG_UPDATE_CURRENT,
false,
),
- ).setAutoCancel(true)
- .setVisibility(
- if (manga.isNsfw) NotificationCompat.VISIBILITY_SECRET else NotificationCompat.VISIBILITY_PUBLIC,
- )
+ ).setVisibility(
+ if (manga.isNsfw) NotificationCompat.VISIBILITY_SECRET else NotificationCompat.VISIBILITY_PUBLIC,
+ )
notification.setContentTitle(applicationContext.getString(R.string.import_completed))
.setContentText(applicationContext.getString(R.string.import_completed_hint))
.setSmallIcon(R.drawable.ic_stat_done)
@@ -123,6 +124,11 @@ class ImportWorker @AssistedInject constructor(
notification.setContentTitle(applicationContext.getString(R.string.error_occurred))
.setContentText(error.getDisplayMessage(applicationContext.resources))
.setSmallIcon(android.R.drawable.stat_notify_error)
+ .addAction(
+ R.drawable.ic_alert_outline,
+ applicationContext.getString(R.string.report),
+ ErrorReporterReceiver.getPendingIntent(applicationContext, error),
+ )
}
return notification.build()
}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt
index b965cdf9c..844e1a815 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorViewModel.kt
@@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
@@ -18,6 +19,7 @@ import org.koitharu.kotatsu.core.parser.MangaIntent
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
+import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.core.util.ext.requireValue
import org.koitharu.kotatsu.list.ui.model.ListModel
@@ -27,7 +29,6 @@ import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga
import org.koitharu.kotatsu.scrobbling.common.ui.selector.model.ScrobblerHint
-import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import javax.inject.Inject
@HiltViewModel
@@ -53,7 +54,7 @@ class ScrobblingSelectorViewModel @Inject constructor(
get() = availableScrobblers[selectedScrobblerIndex.requireValue()]
val content: StateFlow> = combine(
- scrobblerMangaList,
+ scrobblerMangaList.map { it.distinctBy { x -> x.id } },
listError,
hasNextPage,
) { list, error, isHasNextPage ->
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt
index 84d39309f..9e7d3f57f 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt
@@ -98,6 +98,7 @@ class MALRepository @Inject constructor(
.build()
val request = Request.Builder().url(url).get().build()
val response = okHttp.newCall(request).await().parseJson()
+ check(response.has("data")) { "Invalid response: \"$response\"" }
val data = response.getJSONArray("data")
return data.mapJSONNotNull { jsonToManga(it) }
}
diff --git a/app/src/main/res/menu/opt_shelf.xml b/app/src/main/res/menu/opt_shelf.xml
deleted file mode 100644
index 3a88eac5c..000000000
--- a/app/src/main/res/menu/opt_shelf.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-