Skip to content

Commit 2acf496

Browse files
committed
feat: Add copy error details in notification
Signed-off-by: LooKeR <iamlooker@proton.me>
1 parent bc2e0c6 commit 2acf496

File tree

4 files changed

+109
-32
lines changed

4 files changed

+109
-32
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@
172172
android:name=".installer.installers.session.SessionInstallerReceiver"
173173
android:exported="false" />
174174

175+
<receiver
176+
android:name=".receivers.CopyErrorReceiver"
177+
android:exported="false">
178+
<intent-filter>
179+
<action android:name="com.looker.droidify.intent.action.COPY_ERROR" />
180+
</intent-filter>
181+
</receiver>
182+
175183
<service
176184
android:name="androidx.work.impl.foreground.SystemForegroundService"
177185
android:foregroundServiceType="dataSync"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.looker.droidify.receivers
2+
3+
import android.content.BroadcastReceiver
4+
import android.content.Context
5+
import android.content.Intent
6+
import com.looker.droidify.utility.common.extension.copyToClipboard
7+
import com.looker.droidify.utility.common.extension.notificationManager
8+
9+
class CopyErrorReceiver : BroadcastReceiver() {
10+
11+
companion object {
12+
const val ACTION_COPY_ERROR = "com.looker.droidify.intent.action.COPY_ERROR"
13+
const val EXTRA_ERROR_DETAILS = "error_details"
14+
const val EXTRA_NOTIFICATION_TAG = "notification_tag"
15+
const val EXTRA_NOTIFICATION_ID = "notification_id"
16+
}
17+
18+
override fun onReceive(context: Context, intent: Intent) {
19+
if (intent.action == ACTION_COPY_ERROR) {
20+
val errorDetails = intent.getStringExtra(EXTRA_ERROR_DETAILS) ?: return
21+
val notificationTag = intent.getStringExtra(EXTRA_NOTIFICATION_TAG)
22+
val notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
23+
24+
context.copyToClipboard(errorDetails)
25+
if (notificationTag != null && notificationId != -1) {
26+
context.notificationManager?.cancel(notificationTag, notificationId)
27+
}
28+
}
29+
}
30+
}

app/src/main/kotlin/com/looker/droidify/service/SyncService.kt

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.looker.droidify.model.ProductItem
2121
import com.looker.droidify.model.Repository
2222
import com.looker.droidify.network.DataSize
2323
import com.looker.droidify.network.percentBy
24+
import com.looker.droidify.receivers.CopyErrorReceiver
2425
import com.looker.droidify.utility.common.Constants
2526
import com.looker.droidify.utility.common.SdkCheck
2627
import com.looker.droidify.utility.common.createNotificationChannel
@@ -283,20 +284,59 @@ class SyncService : ConnectionService<SyncService.Binder>() {
283284
}
284285

285286
private fun showNotificationError(repository: Repository, exception: Exception) {
286-
val description = getString(
287-
when (exception) {
288-
is RepositoryUpdater.UpdateException -> when (exception.errorType) {
287+
val errorTypeDesc = when (exception) {
288+
is RepositoryUpdater.UpdateException -> getString(
289+
when (exception.errorType) {
289290
RepositoryUpdater.ErrorType.NETWORK -> stringRes.network_error_DESC
290291
RepositoryUpdater.ErrorType.HTTP -> stringRes.http_error_DESC
291292
RepositoryUpdater.ErrorType.VALIDATION -> stringRes.validation_index_error_DESC
292293
RepositoryUpdater.ErrorType.PARSING -> stringRes.parsing_index_error_DESC
293-
}
294+
},
295+
)
296+
297+
else -> getString(stringRes.unknown_error_DESC)
298+
}
299+
300+
val description = buildString {
301+
append(errorTypeDesc)
302+
if (!exception.message.isNullOrBlank()) {
303+
append(": ")
304+
append(exception.message)
305+
}
306+
}
307+
308+
val fullErrorDetails = buildString {
309+
appendLine("Repository: ${repository.name}")
310+
appendLine("Address: ${repository.address}")
311+
appendLine("Error Type: $errorTypeDesc")
312+
appendLine("Message: ${exception.message ?: "N/A"}")
313+
appendLine()
314+
appendLine("Stack Trace:")
315+
appendLine(exception.stackTraceToString())
316+
exception.cause?.let { cause ->
317+
appendLine()
318+
appendLine("Caused by: ${cause.javaClass.name}: ${cause.message}")
319+
appendLine(cause.stackTraceToString())
320+
}
321+
}
322+
323+
val notificationTag = "repository-${repository.id}"
324+
val copyIntent = Intent(this, CopyErrorReceiver::class.java).apply {
325+
action = CopyErrorReceiver.ACTION_COPY_ERROR
326+
putExtra(CopyErrorReceiver.EXTRA_ERROR_DETAILS, fullErrorDetails)
327+
putExtra(CopyErrorReceiver.EXTRA_NOTIFICATION_TAG, notificationTag)
328+
putExtra(CopyErrorReceiver.EXTRA_NOTIFICATION_ID, Constants.NOTIFICATION_ID_SYNCING)
329+
}
294330

295-
else -> stringRes.unknown_error_DESC
296-
},
331+
val copyPendingIntent = PendingIntent.getBroadcast(
332+
this,
333+
repository.id.toInt(),
334+
copyIntent,
335+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
297336
)
337+
298338
notificationManager?.notify(
299-
"repository-${repository.id}",
339+
notificationTag,
300340
Constants.NOTIFICATION_ID_SYNCING,
301341
NotificationCompat
302342
.Builder(this, Constants.NOTIFICATION_CHANNEL_SYNCING)
@@ -307,6 +347,13 @@ class SyncService : ConnectionService<SyncService.Binder>() {
307347
)
308348
.setContentTitle(getString(stringRes.could_not_sync_FORMAT, repository.name))
309349
.setContentText(description)
350+
.setStyle(NotificationCompat.BigTextStyle().bigText(description))
351+
.addAction(
352+
0,
353+
getString(stringRes.copy_error_details),
354+
copyPendingIntent,
355+
)
356+
.setAutoCancel(true)
310357
.build(),
311358
)
312359
}
@@ -403,14 +450,13 @@ class SyncService : ConnectionService<SyncService.Binder>() {
403450
stateNotificationBuilder.setWhen(System.currentTimeMillis())
404451
}
405452

406-
private fun handleNextTask(hasUpdates: Boolean) {
453+
private fun handleNextTask(isIndexModified: Boolean) {
407454
if (currentTask != null) return
408455
if (tasks.isEmpty()) {
409456
if (started != Started.NO) {
410457
lifecycleScope.launch {
411458
val setting = settingsRepository.getInitial()
412459
handleUpdates(
413-
hasUpdates = hasUpdates,
414460
notifyUpdates = setting.notifyUpdate,
415461
autoUpdate = setting.autoUpdate,
416462
skipSignature = setting.ignoreSignature,
@@ -421,7 +467,7 @@ class SyncService : ConnectionService<SyncService.Binder>() {
421467
}
422468
val task = tasks.removeAt(0)
423469
val repository = Database.RepositoryAdapter.get(task.repositoryId)
424-
if (repository == null || !repository.enabled) handleNextTask(hasUpdates)
470+
if (repository == null || !repository.enabled) handleNextTask(isIndexModified)
425471
val lastStarted = started
426472
val newStarted = if (task.manual || lastStarted == Started.MANUAL) {
427473
Started.MANUAL
@@ -441,20 +487,20 @@ class SyncService : ConnectionService<SyncService.Binder>() {
441487
val downloadJob = downloadFile(
442488
task = task,
443489
repository = repository,
444-
hasUpdates = hasUpdates,
490+
isIndexModified = isIndexModified,
445491
unstableUpdates = unstableUpdates,
446492
)
447-
currentTask = CurrentTask(task, downloadJob, hasUpdates, initialState)
493+
currentTask = CurrentTask(task, downloadJob, isIndexModified, initialState)
448494
}
449495
}
450496

451497
private fun CoroutineScope.downloadFile(
452498
task: Task,
453499
repository: Repository,
454-
hasUpdates: Boolean,
500+
isIndexModified: Boolean,
455501
unstableUpdates: Boolean,
456502
): CoroutinesJob = launch(Dispatchers.Default) {
457-
var passedHasUpdates = hasUpdates
503+
var isNewlyModified = isIndexModified
458504
try {
459505
val response = RepositoryUpdater.update(
460506
this@SyncService,
@@ -472,41 +518,34 @@ class SyncService : ConnectionService<SyncService.Binder>() {
472518
)
473519
}
474520
}
475-
passedHasUpdates = when (response) {
521+
isNewlyModified = when (response) {
476522
is Result.Error -> {
477523
response.exception?.let {
478524
it.printStackTrace()
479525
if (task.manual) showNotificationError(repository, it as Exception)
480526
}
481-
response.data == true || hasUpdates
527+
response.data == true || isIndexModified
482528
}
483529

484-
is Result.Success -> response.data || hasUpdates
530+
is Result.Success -> response.data || isIndexModified
485531
}
486532
} finally {
487533
withContext(NonCancellable) {
488534
lock.withLock { currentTask = null }
489-
handleNextTask(passedHasUpdates)
535+
handleNextTask(isNewlyModified)
490536
}
491537
}
492538
}
493539

494540
suspend fun handleUpdates(
495-
hasUpdates: Boolean,
496541
notifyUpdates: Boolean,
497542
autoUpdate: Boolean,
498543
skipSignature: Boolean,
499544
) {
500545
try {
501-
if (!hasUpdates) {
502-
syncState.emit(State.Finish)
503-
val needStop = started == Started.MANUAL
504-
started = Started.NO
505-
if (needStop) stopForegroundCompat()
506-
return
507-
}
508546
val blocked = updateNotificationBlockerFragment?.get()?.isAdded == true
509547
val updates = Database.ProductAdapter.getUpdates(skipSignature)
548+
510549
if (!blocked && updates.isNotEmpty()) {
511550
if (notifyUpdates) {
512551
notificationManager?.notify(
@@ -520,12 +559,11 @@ class SyncService : ConnectionService<SyncService.Binder>() {
520559
updateAllAppsInternal(updates)
521560
}
522561
}
523-
handleUpdates(
524-
hasUpdates = false,
525-
notifyUpdates = notifyUpdates,
526-
autoUpdate = autoUpdate,
527-
skipSignature = skipSignature,
528-
)
562+
563+
syncState.emit(State.Finish)
564+
val needStop = started == Started.MANUAL
565+
started = Started.NO
566+
if (needStop) stopForegroundCompat()
529567
} finally {
530568
withContext(NonCancellable) {
531569
lock.withLock { currentTask = null }

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@
213213

214214
<string name="sync_repositories">Sync repositories</string>
215215
<string name="force_sync_repositories">Force sync repositories</string>
216+
<string name="copy_error_details">Copy error details</string>
216217
<string name="sync_repositories_automatically">Sync repositories automatically</string>
217218
<string name="syncing">Syncing</string>
218219
<string name="syncing_FORMAT">Syncing %s…</string>

0 commit comments

Comments
 (0)