app/src/main/java/cf/playhi/freezeyou/viewmodel/AutoDiagnosisViewModel.kt
package cf.playhi.freezeyou.viewmodel
import android.Manifest
import android.app.Application
import android.app.NotificationManager
import android.content.Context.*
import android.content.pm.PackageManager
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import androidx.core.app.ActivityCompat
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.storage.key.DefaultSharedPreferenceStorageBooleanKeys.cacheApplicationsIcons
import cf.playhi.freezeyou.utils.AccessibilityUtils.isAccessibilitySettingsOn
import cf.playhi.freezeyou.utils.ApplicationIconUtils.getApplicationIcon
import cf.playhi.freezeyou.utils.ApplicationLabelUtils.getApplicationLabel
import cf.playhi.freezeyou.utils.DevicePolicyManagerUtils.isDeviceOwner
import cf.playhi.freezeyou.utils.FileUtils.clearIconCache
import cf.playhi.freezeyou.utils.ProcessUtils.destroyProcess
import cf.playhi.freezeyou.utils.VersionUtils.isOutdated
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import java.io.DataOutputStream
class AutoDiagnosisViewModel(application: Application) : AndroidViewModel(application) {
private val mutex = Mutex()
private val loadingProgress: MutableLiveData<Int> = MutableLiveData<Int>().apply { value = -1 }
private val problemsList: MutableLiveData<MutableList<Map<String, Any>>> =
MutableLiveData<MutableList<Map<String, Any>>>().apply { value = ArrayList() }
fun getLoadingProgress(): LiveData<Int> {
return loadingProgress
}
fun getProblemsList(): LiveData<MutableList<Map<String, Any>>> {
return problemsList
}
fun refreshDiagnosisData() {
viewModelScope.launch(Dispatchers.IO) {
if (mutex.tryLock("AutoDiagnosisViewModelRefreshingDiagnosisData")) {
loadingProgress.postValue(-1)
problemsList.value!!.clear()
loadingProgress.postValue(5)
checkSystemVersion()
loadingProgress.postValue(10)
checkLongTimeNoUpdate()
loadingProgress.postValue(15)
checkAccessibilityService()
loadingProgress.postValue(20)
checkNotificationListenerPermission()
loadingProgress.postValue(25)
checkNotifyPermission()
loadingProgress.postValue(30)
checkBlueToothPermission()
loadingProgress.postValue(35)
checkIsDeviceOwner()
loadingProgress.postValue(50)
checkRootPermission()
loadingProgress.postValue(55)
doRegenerateSomeCache()
loadingProgress.postValue(90)
checkIsPowerSaveMode()
loadingProgress.postValue(95)
checkIsIgnoringBatteryOptimizations()
loadingProgress.postValue(97)
checkIfNoProblemFound()
loadingProgress.postValue(98)
problemsList.value!!.sortWith { t0: Map<String, Any>, t1: Map<String, Any> ->
val i = (t0["level"] as Int).compareTo(
(t1["level"] as Int)
)
if (i == 0) (t0["id"] as String).compareTo((t1["id"] as String)) else i
}
loadingProgress.postValue(100)
mutex.unlock("AutoDiagnosisViewModelRefreshingDiagnosisData")
}
}
}
private fun generateHashMap(
title: String,
sTitle: String,
id: String,
statusId: Int
): HashMap<String, Any> {
val hashMap = HashMap<String, Any>()
hashMap["title"] = title
hashMap["sTitle"] = sTitle
hashMap["id"] = id
hashMap["status"] = statusId
hashMap["level"] = if (statusId == R.drawable.ic_warning) 0 else 1
return hashMap
}
private fun checkSystemVersion() {
if (Build.VERSION.SDK_INT < 21) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.sysVerLow),
getApplication<Application>().getString(R.string.someFuncUn),
"-50",
R.drawable.ic_warning
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.sysVerLow),
getApplication<Application>().getString(R.string.someFuncUn),
"-1",
R.drawable.ic_done
)
)
}
}
private fun checkAccessibilityService() {
problemsList.value!!.add(
if (isAccessibilitySettingsOn(getApplication())) {
generateHashMap(
getApplication<Application>().getString(R.string.ACBSNotEnabled),
getApplication<Application>().getString(R.string.affect) + " " + getApplication<Application>().getString(
R.string.avoidFreezeForegroundApplications
) + " " + getApplication<Application>().getString(
R.string.scheduledTasks
) + " " + getApplication<Application>().getString(R.string.etc),
"1",
R.drawable.ic_done
)
} else {
generateHashMap(
getApplication<Application>().getString(R.string.ACBSNotEnabled),
getApplication<Application>().getString(R.string.affect) + " " + getApplication<Application>().getString(
R.string.avoidFreezeForegroundApplications
) + " " + getApplication<Application>().getString(
R.string.scheduledTasks
) + " " + getApplication<Application>().getString(R.string.etc),
"1",
R.drawable.ic_warning
)
}
)
}
private fun checkNotificationListenerPermission() {
if (Build.VERSION.SDK_INT >= 21) {
val s = Settings.Secure.getString(
getApplication<Application>().contentResolver,
"enabled_notification_listeners"
)
problemsList.value!!.add(
if (s == null
|| !s.contains("cf.playhi.freezeyou/cf.playhi.freezeyou.MyNotificationListenerService")
) {
generateHashMap(
getApplication<Application>().getString(R.string.noNotificationListenerPermission),
getApplication<Application>().getString(R.string.affect) + " " + getApplication<Application>().getString(
R.string.avoidFreezeNotifyingApplications
),
"2",
R.drawable.ic_warning
)
} else {
generateHashMap(
getApplication<Application>().getString(R.string.noNotificationListenerPermission),
getApplication<Application>().getString(R.string.affect) + " " + getApplication<Application>().getString(
R.string.avoidFreezeNotifyingApplications
),
"2",
R.drawable.ic_done
)
}
)
}
}
private fun checkNotifyPermission() {
val notificationManager =
getApplication<Application>().getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= 24 && !notificationManager.areNotificationsEnabled()) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noNotifyPermission),
getApplication<Application>().getString(R.string.mayCannotNotify),
"6",
R.drawable.ic_warning
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noNotifyPermission),
getApplication<Application>().getString(R.string.mayCannotNotify),
"6",
R.drawable.ic_done
)
)
}
}
private fun checkIsDeviceOwner() {
if (isDeviceOwner(getApplication())) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noMRootPermission),
getApplication<Application>().getString(R.string.someFuncMayRestrict),
"-3",
R.drawable.ic_done
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noMRootPermission),
getApplication<Application>().getString(R.string.someFuncMayRestrict),
"-3",
R.drawable.ic_warning
)
)
}
}
private fun checkLongTimeNoUpdate() {
if (isOutdated(getApplication<Application>())) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.notUpdatedForALongTime),
getApplication<Application>().getString(R.string.someNewFuncMayPub),
"-30",
R.drawable.ic_warning
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.notUpdatedForALongTime),
getApplication<Application>().getString(R.string.someNewFuncMayPub),
"-30",
R.drawable.ic_done
)
)
}
}
private fun checkRootPermission() {
var hasPermission = true
var value = -1
try {
val process = Runtime.getRuntime().exec("su")
val outputStream = DataOutputStream(process.outputStream)
outputStream.writeBytes("exit\n")
outputStream.flush()
value = process.waitFor()
destroyProcess(outputStream, process)
} catch (e: Exception) {
if (e.message!!.toLowerCase().contains("permission denied")
|| e.message!!.toLowerCase().contains("not found")
) {
hasPermission = false
}
}
if (!hasPermission || value != 0) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noRootPermission),
getApplication<Application>().getString(R.string.someFuncMayRestrict),
"-3",
R.drawable.ic_warning
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noRootPermission),
getApplication<Application>().getString(R.string.someFuncMayRestrict),
"-3",
R.drawable.ic_done
)
)
}
}
private fun checkIsIgnoringBatteryOptimizations() {
if (Build.VERSION.SDK_INT >= 23
&& !(getApplication<Application>().getSystemService(POWER_SERVICE) as PowerManager)
.isIgnoringBatteryOptimizations("cf.playhi.freezeyou")
) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noIgnoringBO),
getApplication<Application>().getString(R.string.someFuncMayBeAff),
"4",
R.drawable.ic_warning
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noIgnoringBO),
getApplication<Application>().getString(R.string.someFuncMayBeAff),
"4",
R.drawable.ic_done
)
)
}
}
private fun checkIsPowerSaveMode() {
if (Build.VERSION.SDK_INT >= 21
&& (getApplication<Application>().getSystemService(POWER_SERVICE) as PowerManager).isPowerSaveMode
) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.inPowerSaveMode),
getApplication<Application>().getString(R.string.someFuncMayBeAff),
"5",
R.drawable.ic_warning
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.inPowerSaveMode),
getApplication<Application>().getString(R.string.someFuncMayBeAff),
"5",
R.drawable.ic_done
)
)
}
}
private fun checkBlueToothPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
&& ActivityCompat.checkSelfPermission(
getApplication(),
Manifest.permission.BLUETOOTH_CONNECT
)
!= PackageManager.PERMISSION_GRANTED
) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.bluetoothPermissionIsNotGranted),
getApplication<Application>().getString(R.string.mayCannotUseScheduledTaskToEnableOrDisableBluetooth),
"7",
R.drawable.ic_warning
)
)
} else {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.bluetoothPermissionIsNotGranted),
getApplication<Application>().getString(R.string.mayCannotUseScheduledTaskToEnableOrDisableBluetooth),
"7",
R.drawable.ic_done
)
)
}
}
private fun doRegenerateSomeCache() {
getApplication<Application>().getSharedPreferences("NameOfPackages", MODE_PRIVATE)
.edit().clear().apply()
clearIconCache(getApplication())
val cacheApplicationsIcons = cacheApplicationsIcons.getValue(getApplication())
val pm = getApplication<Application>().packageManager
@Suppress("DEPRECATION")
val installedApplications =
pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES)
val size = installedApplications.size
for (i in 0 until size) {
val applicationInfo = installedApplications[i]
getApplicationLabel(
getApplication(),
pm,
applicationInfo,
applicationInfo.packageName
)
if (cacheApplicationsIcons) {
getApplicationIcon(
getApplication<Application>(), applicationInfo.packageName,
applicationInfo, false, true
)
}
loadingProgress.postValue(40 + (i.toDouble() / size.toDouble() * 50).toInt())
}
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.regenerateSomeCache),
getApplication<Application>().getString(R.string.updateSomeData),
"10",
R.drawable.ic_done
)
)
}
private fun checkIfNoProblemFound() {
if (problemsList.value!!.isEmpty()) {
problemsList.value!!.add(
generateHashMap(
getApplication<Application>().getString(R.string.noProblemsFound),
getApplication<Application>().getString(R.string.everySeemsAllRight),
"-99",
R.drawable.ic_done
)
)
}
}
}