forked from shihaam/thijooree
Optimize Notifcation loading
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user