wklejamy.pl website for sale. You are interested in. Please contact. wklejamy@gmail.com

Click to buy coffe
avatar
Untitled

Guest 90 8th Sep, 2023

MARKUP 5.31 KB
                                           
                         typealias ProgressState = IdentifiableProgressState<String?>
typealias ProgressStateProcess = IdentifiableProgressState<String?>.ProgressProcess

class IdentifiableProgressState<Id> private constructor(
  private val stateFlow: MutableStateFlow<Boolean>,
  val debugName: String? = null
) : StateFlow<Boolean> by stateFlow {

  private val mutex = Mutex()

  companion object {
    private const val DEFAULT_PROGRESS = -1F
    fun create(debugName: String? = null): ProgressState =
      IdentifiableProgressState(
        stateFlow = MutableStateFlow(value = false),
        debugName = debugName
      )

    fun <CustomId> createWithId(debugName: String? = null) =
      IdentifiableProgressState<CustomId>(
        stateFlow = MutableStateFlow(value = false),
        debugName = debugName
      )
  }

  //region Create token

  private val nextFreeToken: AtomicLong = AtomicLong(0)

  private fun generateNextToken(): Long = synchronized(nextFreeToken) {
    nextFreeToken.getAndIncrement()
  }

  //endregion

  //region Progress

  private val _progressPercent = MutableStateFlow(value = 1f)
  val progressPercent = _progressPercent.asStateFlow()

  private val _processes = MutableStateFlow(value = emptyList<Process<Id>>())
  val processes = _processes.asStateFlow()

  //endregion

  //region Register process

  private data class ProcessData<Id>(
    override val token: Long,
    override val id: Id,
    override var progress: Float = DEFAULT_PROGRESS
  ) : Process<Id>

  interface Process<Id> {
    val token: Long
    val id: Id
    val progress: Float
  }

  private val registeredProcesses = mutableMapOf<Long, ProcessData<Id>>()

  private suspend fun registerProcess(token: Long, id: Id) {
    mutex.withLock {
      registeredProcesses[token] = ProcessData(id = id, token = token)
    }
    updateProgress()
  }

  private suspend fun updateProcessProgress(
    token: Long,
    @FloatRange(from = 0.0, to = 1.0) progress: Float
  ) {
    mutex.withLock {
      val process = registeredProcesses[token]
        ?: throw IllegalStateException("This process is not started")
      process.progress = progress
    }
    updateProgress()
  }

  private suspend fun unregisterProcess(token: Long) {
    mutex.withLock {
      registeredProcesses.remove(token)
    }
    updateProgress()
  }

  private suspend fun updateProgress() {
    var isWorking = false
    var percent = 0f
    var processes: List<ProcessData<Id>>
    mutex.withLock {
      processes = registeredProcesses.values.map(ProcessData<Id>::copy)
      val progressedProcesses = processes
        .filter { it.progress != DEFAULT_PROGRESS }
        .map { it.progress }
      val count = progressedProcesses.size
      val sum = progressedProcesses.sum()

      percent = when (count > 0) {
        true -> sum / count
        else -> 0f
      }

      isWorking = processes.isNotEmpty()
    }
    stateFlow.emit(isWorking)
    _processes.emit(processes)
    _progressPercent.emit(percent)
  }

  //endregion

  //region Process

  inner class ProgressProcess internal constructor(
    val token: Long,
    val id: Id
  ) {

    suspend fun reportStart() = registerProcess(token, id)

    suspend fun updateProgress(current: Long, max: Long) {
      val progress = when {
        max <= 0L -> 1f
        current < 0L -> 0f
        current >= max -> 1f
        else -> current.toDouble().div(max).toFloat()
      }
      updateProcessProgress(token, progress)
    }

    suspend fun reportFinish() {
      try {
        unregisterProcess(token)
      } finally {
        if (BuildConfig.DEBUG) {
          val debugMessage = StringBuilder("Finish process: $token")
          if (debugName.isNullOrBlank()) {
            debugMessage.append("-$debugName")
          }
          if (id != null) {
            debugMessage.append("-id:$id")
          }
          Timber.d(debugMessage.toString())
        }
      }
    }

    val workingState: Flow<Boolean>
      get() = processes.map { currentProcesses ->
        currentProcesses.any { it.token == token }
      }

  }

  fun prepareProcess(id: Id): ProgressProcess =
    ProgressProcess(generateNextToken(), id)

  //endregion

}

suspend fun <T> ProgressState.reportWork(
  customName: String? = null,
  block: suspend (reportUpdate: UpdateProcessCallback) -> T
): T {
  // Get token for this process
  val process = prepareProcess(customName)
  return process.reportWork(block)
}

suspend fun <T> IdentifiableProgressState<*>.ProgressProcess.reportWork(
  block: suspend (reportUpdate: UpdateProcessCallback) -> T
): T {
  try {
    reportStart()
    return block(::updateProgress)
  } finally {
    reportFinish()
  }
}

suspend fun IdentifiableProgressState<*>.ProgressProcess.report(isLoading: Boolean) {
  when (isLoading) {
    true -> reportStart()
    false -> reportFinish()
  }
}

typealias UpdateProcessCallback = suspend (current: Long, max: Long) -> Unit
                      
                                       
To share this paste please copy this url and send to your friends
RAW Paste Data
Recent Pastes
Ta strona używa plików cookie w celu usprawnienia i ułatwienia dostępu do serwisu oraz prowadzenia danych statystycznych. Dalsze korzystanie z tej witryny oznacza akceptację tego stanu rzeczy.
Wykorzystywanie plików Cookie
Jak wyłączyć cookies?
ROZUMIEM