Optimize Notifcation loading

This commit is contained in:
2026-06-10 13:46:18 +05:00
parent 570e6b750b
commit 80bbacc130
2 changed files with 150 additions and 5 deletions
+73
View File
@@ -0,0 +1,73 @@
kotlin version: 2.1.21
error message: Daemon compilation failed: null
java.lang.Exception
at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:69)
at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:65)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:240)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.nio.file.NoSuchFileException: /tmp/kotlin-backups9429064796643384370/222.backup -> /home/shihaam/git/sargit/thijooree/app/build/tmp/kotlin-classes/debug/sh/sar/basedbank/ui/home/TransferFragment$lookupAccount$1.class
at java.base/sun.nio.fs.UnixException.translateToIOException(Unknown Source)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source)
at java.base/sun.nio.fs.UnixFileSystem.move(Unknown Source)
at java.base/sun.nio.fs.UnixFileSystemProvider.move(Unknown Source)
at java.base/java.nio.file.Files.move(Unknown Source)
at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.revertChanges(CompilationTransaction.kt:231)
at org.jetbrains.kotlin.incremental.RecoverableCompilationTransaction.close(CompilationTransaction.kt:256)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:762)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:128)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:678)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1805)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
@@ -13,10 +13,12 @@ import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
@@ -62,6 +64,23 @@ private fun toGroupedList(notifications: List<AppNotification>): List<NotifListI
return result return result
} }
private class NotifDiff(
private val old: List<NotifListItem>,
private val new: List<NotifListItem>
) : DiffUtil.Callback() {
override fun getOldListSize() = old.size
override fun getNewListSize() = new.size
override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
val o = old[oldPos]; val n = new[newPos]
return when {
o is NotifListItem.Header && n is NotifListItem.Header -> o.label == n.label
o is NotifListItem.Entry && n is NotifListItem.Entry -> o.n.id == n.n.id
else -> false
}
}
override fun areContentsTheSame(oldPos: Int, newPos: Int) = old[oldPos] == new[newPos]
}
class NotificationsSheetFragment : BottomSheetDialogFragment() { class NotificationsSheetFragment : BottomSheetDialogFragment() {
var onUnreadCountChanged: ((hasUnread: Boolean) -> Unit)? = null var onUnreadCountChanged: ((hasUnread: Boolean) -> Unit)? = null
@@ -148,11 +167,16 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
} }
} }
private fun setSpinner(show: Boolean) {
tabAdapters.forEach { it?.showLoadingSpinner = show }
}
private fun refreshFromNetwork() { private fun refreshFromNetwork() {
val bmlSessions = app.bmlSessions.toMap() val bmlSessions = app.bmlSessions.toMap()
val mibSessions = app.mibSessions.toMap() val mibSessions = app.mibSessions.toMap()
lifecycleScope.launch { lifecycleScope.launch {
setSpinner(true)
val bmlClient = BmlNotificationsClient() val bmlClient = BmlNotificationsClient()
bmlSessions.forEach { (loginId, session) -> bmlSessions.forEach { (loginId, session) ->
val result = withContext(Dispatchers.IO) { val result = withContext(Dispatchers.IO) {
@@ -196,6 +220,8 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
mibDone[loginId] = hasOverlap || result.nextStart > result.totalCount mibDone[loginId] = hasOverlap || result.nextStart > result.totalCount
} }
} }
if (isAdded) setSpinner(false)
} }
} }
@@ -208,6 +234,7 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
if (!anyLeft) return if (!anyLeft) return
isLoadingMore = true isLoadingMore = true
setSpinner(true)
lifecycleScope.launch { lifecycleScope.launch {
val bmlClient = BmlNotificationsClient() val bmlClient = BmlNotificationsClient()
bmlSessions.forEach { (loginId, session) -> bmlSessions.forEach { (loginId, session) ->
@@ -255,7 +282,10 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
} }
isLoadingMore = false isLoadingMore = false
if (isAdded) refreshAdapters() if (isAdded) {
setSpinner(false)
refreshAdapters()
}
} }
} }
@@ -359,16 +389,35 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
private val displayItems = mutableListOf<NotifListItem>() private val displayItems = mutableListOf<NotifListItem>()
var showLoadingSpinner: Boolean = false
set(value) {
if (field == value) return
field = value
if (displayItems.isEmpty()) {
notifyItemChanged(0)
} else if (value) {
notifyItemInserted(displayItems.size)
} else {
notifyItemRemoved(displayItems.size)
}
}
fun update(filtered: List<AppNotification>) { fun update(filtered: List<AppNotification>) {
val newItems = toGroupedList(filtered)
val diff = DiffUtil.calculateDiff(NotifDiff(displayItems.toList(), newItems))
displayItems.clear() displayItems.clear()
displayItems.addAll(toGroupedList(filtered)) displayItems.addAll(newItems)
notifyDataSetChanged() diff.dispatchUpdatesTo(this)
} }
override fun getItemCount() = if (displayItems.isEmpty()) 1 else displayItems.size override fun getItemCount(): Int {
if (displayItems.isEmpty()) return 1
return displayItems.size + if (showLoadingSpinner) 1 else 0
}
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
if (displayItems.isEmpty()) return 2 // empty if (displayItems.isEmpty()) return if (showLoadingSpinner) 3 else 2
if (showLoadingSpinner && position == displayItems.size) return 3
return when (displayItems[position]) { return when (displayItems[position]) {
is NotifListItem.Header -> 0 is NotifListItem.Header -> 0
is NotifListItem.Entry -> 1 is NotifListItem.Entry -> 1
@@ -379,6 +428,7 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
when (viewType) { when (viewType) {
0 -> HeaderVH(buildHeaderView(parent.context)) 0 -> HeaderVH(buildHeaderView(parent.context))
1 -> ItemVH(buildRowView(parent.context)) 1 -> ItemVH(buildRowView(parent.context))
3 -> SpinnerVH(buildSpinnerView(parent.context))
else -> EmptyVH(buildEmptyView(parent.context)) else -> EmptyVH(buildEmptyView(parent.context))
} }
@@ -439,6 +489,28 @@ class NotificationsSheetFragment : BottomSheetDialogFragment() {
} }
} }
// ── Loading spinner ───────────────────────────────────────────────────────
inner class SpinnerVH(v: View) : RecyclerView.ViewHolder(v)
private fun buildSpinnerView(ctx: android.content.Context): View {
val dp = ctx.resources.displayMetrics.density
val pad = (16 * dp).toInt()
val size = (28 * dp).toInt()
return LinearLayout(ctx).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
setPadding(pad, pad, pad, pad)
addView(ProgressBar(ctx).apply {
layoutParams = LinearLayout.LayoutParams(size, size)
})
}
}
// ── Notification row ────────────────────────────────────────────────────── // ── Notification row ──────────────────────────────────────────────────────
inner class ItemVH(v: View) : RecyclerView.ViewHolder(v) { inner class ItemVH(v: View) : RecyclerView.ViewHolder(v) {