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 @@ - - - - - - - - - - - -