Playhi/FreezeYou

View on GitHub
app/src/main/java/cf/playhi/freezeyou/viewmodel/FreezeActivityViewModel.kt

Summary

Maintainability
A
1 hr
Test Coverage
package cf.playhi.freezeyou.viewmodel

import android.app.Application
import android.content.Intent
import android.os.Parcelable
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import cf.playhi.freezeyou.R
import cf.playhi.freezeyou.fuf.FUFSinglePackage.Companion.ACTION_MODE_FREEZE
import cf.playhi.freezeyou.fuf.FUFSinglePackage.Companion.ACTION_MODE_UNFREEZE
import cf.playhi.freezeyou.fuf.FUFSinglePackage.Companion.ERROR_NO_ERROR_CAUGHT_UNKNOWN_RESULT
import cf.playhi.freezeyou.fuf.FUFSinglePackage.Companion.ERROR_NO_ERROR_SUCCESS
import cf.playhi.freezeyou.fuf.FreezeYouFUFSinglePackage
import cf.playhi.freezeyou.storage.key.DefaultMultiProcessMMKVStorageBooleanKeys.*
import cf.playhi.freezeyou.storage.mmkv.AverageTimeCostsMMKVStorage
import cf.playhi.freezeyou.utils.FUFUtils.realGetFrozenStatus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import java.util.*

class FreezeActivityViewModel(application: Application) : AndroidViewModel(application) {

    private lateinit var mStartedIntent: Intent
    private var mIsFromShortcut = false
    private var mTarget: String? = null
    private var mTasks: String? = null
    private var mPkgName: MutableLiveData<String> =
        MutableLiveData(getApplication<Application>().packageName)
    private var mToastStringId: MutableLiveData<Int> = MutableLiveData()
    private var mFinishMe: MutableLiveData<Boolean> = MutableLiveData(false)
    private var mShowDialog: MutableLiveData<DialogData?> = MutableLiveData()
    private var mPlayAnimator: MutableLiveData<PlayAnimatorData?> = MutableLiveData()
    private var mExecuteResult: MutableLiveData<ExecuteResult> = MutableLiveData()
    private var mAverageTimeCosts: Long = 500

    fun getPkgName(): LiveData<String> {
        return mPkgName
    }

    fun getToastStringId(): LiveData<Int> {
        return mToastStringId
    }

    fun getFinishMe(): LiveData<Boolean> {
        return mFinishMe
    }

    fun getShowDialog(): LiveData<DialogData?> {
        return mShowDialog
    }

    fun getPlayAnimator(): LiveData<PlayAnimatorData?> {
        return mPlayAnimator
    }

    fun getExecuteResult(): LiveData<ExecuteResult> {
        return mExecuteResult
    }

    fun getAverageTimeCosts(): Long {
        return mAverageTimeCosts
    }

    fun loadStartedIntentAndPkgName(startedIntent: Intent) {
        mStartedIntent = startedIntent
        if ("freezeyou" == mStartedIntent.scheme) {
            val dataUri = mStartedIntent.data
            dataUri?.run { getQueryParameter("pkgName")?.let { mPkgName.value = it } }
            mIsFromShortcut = false
        } else {
            mStartedIntent.getStringExtra("pkgName")?.let { mPkgName.value = it }
            mIsFromShortcut = mStartedIntent.getBooleanExtra("fromShortcut", true)
        }
        mTarget = mStartedIntent.getStringExtra("target")
        mTasks = mStartedIntent.getStringExtra("tasks")
    }

    fun go() {
        mPkgName.value.let {
            val target = mTarget
            val tasks = mTasks
            if (it.isNullOrEmpty() || it == getApplication<Application>().packageName) {
                mToastStringId.value = R.string.invalidArguments
                mFinishMe.value = true
                return
            }
            val frozen = realGetFrozenStatus(getApplication(), it, null)
            mAverageTimeCosts = AverageTimeCostsMMKVStorage().getParcelable(
                if (frozen) "Unfreeze" else "Freeze",
                AverageTime::class.java,
                AverageTime()
            )?.averageTimeCost?.let { cost -> if (cost < 200L) 200 else cost } ?: 500

            if (mIsFromShortcut && shortcutAutoFUF.getValue()) {
                if (frozen) {
                    fufAction(
                        it,
                        target,
                        tasks,
                        openImmediatelyAfterUnfreezeUseShortcutAutoFUF.getValue(),
                        true
                    )
                } else {
                    if (needConfirmWhenFreezeUseShortcutAutoFUF.getValue()) {
                        checkOpenAndUFImmediately(
                            it,
                            target,
                            tasks,
                            frozen = false,
                            ignoreAutoRun = true
                        )
                    } else {
                        fufAction(it, target, tasks, runImmediately = false, frozen = false)
                    }
                }
            } else {
                checkOpenAndUFImmediately(
                    it,
                    target,
                    tasks,
                    frozen,
                    !mIsFromShortcut
                )
            }
        }
    }

    private fun checkOpenAndUFImmediately(
        pkgName: String,
        target: String?,
        tasks: String?,
        frozen: Boolean,
        ignoreAutoRun: Boolean
    ) {
        if (!ignoreAutoRun && openAndUFImmediately.getValue()) {
            if (frozen) {
                fufAction(pkgName, target, tasks, runImmediately = true, frozen = true)
            } else {
                checkAndStartTaskAndTargetAndActivityOfUnfrozenApp(pkgName, target, tasks)
                mFinishMe.postValue(true)
            }
        } else {
            mShowDialog.value = DialogData(pkgName, target, tasks, frozen, true)
        }
    }

    fun fufAction(
        pkgName: String,
        target: String?,
        tasks: String?,
        runImmediately: Boolean,
        frozen: Boolean
    ) {
        mPlayAnimator.value = PlayAnimatorData(pkgName, !frozen)
        viewModelScope.launch(Dispatchers.IO) {
            val startTime: Long = Date().time
            val freezeYouFUFSinglePackage = FreezeYouFUFSinglePackage(
                getApplication(),
                pkgName,
                if (frozen) ACTION_MODE_UNFREEZE else ACTION_MODE_FREEZE,
                needAskRun = frozen,
                runImmediately = runImmediately,
                tasks = tasks,
                target = target
            )
            val result = freezeYouFUFSinglePackage.commit()
            when (result) {
                ERROR_NO_ERROR_SUCCESS, ERROR_NO_ERROR_CAUGHT_UNKNOWN_RESULT ->
                    recordTimeCost((Date().time - startTime), frozen)
            }
            mExecuteResult.postValue(ExecuteResult(result, freezeYouFUFSinglePackage))
            mFinishMe.postValue(true)
        }
    }

    private fun recordTimeCost(cost: Long, frozen: Boolean) {
        val key = if (frozen) "Unfreeze" else "Freeze"
        AverageTimeCostsMMKVStorage().getParcelable(
            key,
            AverageTime::class.java,
            AverageTime()
        )?.let {
            AverageTimeCostsMMKVStorage().putParcelable(
                key,
                AverageTime(
                    it.timeCosts.let { costs ->
                        if (costs.size >= 5) {
                            costs.removeFirst()
                        }
                        costs.addLast(cost)
                        costs
                    },
                    it.timeCosts.average().toLong()
                )
            )
        }
    }

    fun checkAndStartTaskAndTargetAndActivityOfUnfrozenApp(data: DialogData): Int {
        return checkAndStartTaskAndTargetAndActivityOfUnfrozenApp(
            data.pkgName,
            data.target,
            data.tasks
        )
    }

    private fun checkAndStartTaskAndTargetAndActivityOfUnfrozenApp(
        pkgName: String,
        target: String?,
        tasks: String?
    ): Int {
        return FreezeYouFUFSinglePackage.checkAndStartTaskAndTargetAndActivityOfUnfrozenApp(
            getApplication(), pkgName, target, tasks
        )
    }

}

data class DialogData(
    val pkgName: String,
    val target: String?,
    val tasks: String?,
    val frozen: Boolean,
    var show: Boolean
)

data class PlayAnimatorData(
    val pkgName: String,
    val freezing: Boolean
)

data class ExecuteResult(
    val result: Int,
    val freezeYouFUFSinglePackage: FreezeYouFUFSinglePackage
)

@Parcelize
data class AverageTime(
    val timeCosts: LinkedList<Long> = LinkedList(),
    val averageTimeCost: Long = 500
) : Parcelable