rewrite: DataLoaderService to fix old optimization issues and crashes

(cherry picked from commit 12ed82e608)
This commit is contained in:
Hamza417
2025-07-24 14:33:50 +05:30
parent 3ef08e05bf
commit 9df6f07b95
5 changed files with 158 additions and 402 deletions

View File

@@ -13,7 +13,7 @@ import app.simple.inure.themes.manager.ThemeManager;
public class LineNumberEditText extends TypeFaceEditText implements SharedPreferences.OnSharedPreferenceChangeListener {
private final String newline = System.getProperty("line.separator");
private final String newline = System.lineSeparator();
private Rect rect;
private Paint paint;

View File

@@ -1,57 +1,36 @@
package app.simple.inure.extensions.viewmodels
import android.annotation.SuppressLint
import android.app.Application
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.os.DeadObjectException
import android.os.IBinder
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.lifecycle.viewModelScope
import app.simple.inure.R
import app.simple.inure.apk.utils.PackageUtils
import app.simple.inure.apk.utils.PackageUtils.safeApplicationInfo
import app.simple.inure.services.DataLoaderService
import app.simple.inure.util.ArrayUtils
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
abstract class PackageUtilsViewModel(application: Application) : WrappedViewModel(application) {
private var apps: ArrayList<PackageInfo> = arrayListOf()
private var uninstalledApps: ArrayList<PackageInfo> = arrayListOf()
private var dataLoaderService: DataLoaderService? = null
private var serviceConnection: ServiceConnection? = null
@SuppressLint("StaticFieldLeak") // This is an application context
private var dataLoaderService: DataLoaderService? = null
private var broadcastReceiver: BroadcastReceiver? = null
private var intentFilter: IntentFilter = IntentFilter()
init {
intentFilter.addAction(DataLoaderService.APPS_LOADED)
intentFilter.addAction(DataLoaderService.UNINSTALLED_APPS_LOADED)
intentFilter.addAction(DataLoaderService.INSTALLED_APPS_LOADED)
intentFilter.addAction(DataLoaderService.RELOAD_APPS)
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
dataLoaderService = (service as DataLoaderService.LoaderBinder).getService()
if (dataLoaderService!!.hasDataLoaded()) {
apps = dataLoaderService!!.getInstalledApps()
uninstalledApps = dataLoaderService!!.getUninstalledApps()
onAppsLoaded(apps)
onUninstalledAppsLoaded(uninstalledApps)
} else {
dataLoaderService!!.startLoading()
}
observeServiceFlows()
dataLoaderService?.refresh()
}
override fun onServiceDisconnected(name: ComponentName?) {
@@ -59,65 +38,35 @@ abstract class PackageUtilsViewModel(application: Application) : WrappedViewMode
}
}
@Suppress("UNCHECKED_CAST")
broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
DataLoaderService.APPS_LOADED -> {
apps = dataLoaderService!!.getInstalledApps().clone() as ArrayList<PackageInfo>
uninstalledApps = dataLoaderService!!.getUninstalledApps().clone() as ArrayList<PackageInfo>
applicationContext().bindService(
Intent(applicationContext(), DataLoaderService::class.java),
serviceConnection!!,
Context.BIND_AUTO_CREATE
)
}
onAppsLoaded(apps)
onUninstalledAppsLoaded(uninstalledApps)
}
DataLoaderService.UNINSTALLED_APPS_LOADED -> {
uninstalledApps = dataLoaderService!!.getUninstalledApps().clone() as ArrayList<PackageInfo>
onUninstalledAppsLoaded(uninstalledApps)
}
DataLoaderService.INSTALLED_APPS_LOADED -> {
apps = dataLoaderService!!.getInstalledApps().clone() as ArrayList<PackageInfo>
onAppsLoaded(apps)
}
DataLoaderService.RELOAD_APPS -> {
Log.d("DataLoaderService", "Reloading apps")
dataLoaderService!!.refresh()
}
private fun observeServiceFlows() {
viewModelScope.launch {
dataLoaderService?.installedApps?.collectLatest { apps ->
if (apps != null) {
onAppsLoaded(ArrayUtils.deepCopy(apps))
}
}
}
viewModelScope.launch {
dataLoaderService?.uninstalledApps?.collectLatest { apps ->
if (apps != null) {
onUninstalledAppsLoaded(ArrayUtils.deepCopy(apps))
}
}
}
LocalBroadcastManager.getInstance(applicationContext()).registerReceiver(broadcastReceiver!!, intentFilter)
applicationContext().bindService(
Intent(applicationContext(), DataLoaderService::class.java), serviceConnection!!, Context.BIND_AUTO_CREATE)
}
fun getInstalledApps(): ArrayList<PackageInfo> {
return dataLoaderService!!.getInstalledApps()
}
fun getUninstalledApps(): ArrayList<PackageInfo> {
return dataLoaderService!!.getUninstalledApps()
}
fun getCompleteApps(): List<PackageInfo> {
return getInstalledApps() + getUninstalledApps()
}
fun refreshPackageData() {
dataLoaderService!!.refresh()
}
fun refreshUninstalledPackageData() {
dataLoaderService!!.refreshUninstalled()
}
fun refreshInstalledPackageData() {
dataLoaderService!!.refreshInstalled()
dataLoaderService?.refresh()
}
// Utility and helper functions remain unchanged
protected fun PackageManager.isPackageInstalled(packageName: String): Boolean {
while (true) {
try {
@@ -129,7 +78,7 @@ abstract class PackageUtilsViewModel(application: Application) : WrappedViewMode
return true
} catch (e: PackageManager.NameNotFoundException) {
return false
} catch (e: DeadObjectException) {
} catch (e: android.os.DeadObjectException) {
Log.e("PackageUtilsViewModel", "isPackageInstalled: DeadObjectException")
}
}
@@ -138,9 +87,9 @@ abstract class PackageUtilsViewModel(application: Application) : WrappedViewMode
protected fun PackageManager.isPackageEnabled(packageName: String): Boolean {
return try {
getPackageInfo(packageName)!!.safeApplicationInfo.enabled
} catch (e: PackageManager.NameNotFoundException) {
} catch (_: PackageManager.NameNotFoundException) {
false
} catch (e: NullPointerException) {
} catch (_: NullPointerException) {
false
}
}
@@ -149,17 +98,6 @@ abstract class PackageUtilsViewModel(application: Application) : WrappedViewMode
return isPackageInstalled(packageName) && isPackageEnabled(packageName)
}
protected fun PackageManager.getInstalledPackages(flags: Long = PackageUtils.flags): ArrayList<PackageInfo> {
val packageInfoList = ArrayList<PackageInfo>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageInfoList.addAll(getInstalledPackages(PackageManager.PackageInfoFlags.of(flags)))
} else {
@Suppress("DEPRECATION")
packageInfoList.addAll(getInstalledPackages(flags.toInt()))
}
return ArrayUtils.deepCopy(packageInfoList)
}
protected fun PackageManager.getPackageInfo(packageName: String): PackageInfo? {
try {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -168,7 +106,7 @@ abstract class PackageUtilsViewModel(application: Application) : WrappedViewMode
try {
@Suppress("DEPRECATION")
getPackageInfo(packageName, PackageUtils.flags.toInt())
} catch (e: RuntimeException) {
} catch (_: RuntimeException) {
@Suppress("DEPRECATION")
getPackageInfo(packageName, PackageManager.GET_META_DATA)
}
@@ -176,7 +114,6 @@ abstract class PackageUtilsViewModel(application: Application) : WrappedViewMode
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
return null
}
@@ -192,67 +129,49 @@ abstract class PackageUtilsViewModel(application: Application) : WrappedViewMode
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
return null
}
/**
* Fetches the app's name from the package id of the same application
* @param context of the given environment
* @param applicationInfo is [ApplicationInfo] object containing app's
* information
* @return app's name as [String]
*/
protected fun getApplicationName(context: Context, applicationInfo: ApplicationInfo): String {
while (true) {
try {
return context.packageManager.getApplicationLabel(applicationInfo).toString()
} catch (e: PackageManager.NameNotFoundException) {
} catch (_: PackageManager.NameNotFoundException) {
return context.getString(R.string.unknown)
} catch (e: DeadObjectException) {
} catch (_: android.os.DeadObjectException) {
Log.e("PackageUtilsViewModel", "getApplicationName: DeadObjectException")
}
}
}
protected fun ArrayList<PackageInfo>.loadPackageNames(): ArrayList<PackageInfo> {
forEach {
it.safeApplicationInfo.name = getApplicationName(applicationContext(), it.safeApplicationInfo)
}
return this
}
open fun onUninstalledAppsLoaded(uninstalledApps: ArrayList<PackageInfo>) {
// Log.d("PackageUtilsViewModel", "onUninstalledAppsLoaded: ${uninstalledApps.size}")
// Override in subclasses
}
open fun onAppsLoaded(apps: ArrayList<PackageInfo>) {
// Log.d("PackageUtilsViewModel", "onAppsLoaded: ${apps.size}")
// Override in subclasses
}
open fun getInstalledApps(): ArrayList<PackageInfo> {
return ArrayUtils.deepCopy(dataLoaderService?.installedApps?.value ?: ArrayList())
}
open fun getUninstalledApps(): ArrayList<PackageInfo> {
return ArrayUtils.deepCopy(dataLoaderService?.uninstalledApps?.value ?: ArrayList())
}
override fun onCleared() {
super.onCleared()
try {
serviceConnection?.let {
applicationContext().unbindService(it)
app.applicationContext.unbindService(it)
}
} catch (e: java.lang.IllegalStateException) {
e.printStackTrace()
} catch (e: IllegalArgumentException) {
e.printStackTrace()
}
try {
broadcastReceiver?.let {
LocalBroadcastManager.getInstance(applicationContext()).unregisterReceiver(it)
}
} catch (e: java.lang.IllegalStateException) {
e.printStackTrace()
} catch (e: IllegalArgumentException) {
e.printStackTrace()
} catch (e: NullPointerException) {
} catch (e: Exception) {
e.printStackTrace()
}
}
}
companion object {
private const val TAG = "PackageUtilsViewModel"
}
}

View File

@@ -1,265 +1,191 @@
package app.simple.inure.services
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ApplicationInfo
import android.content.pm.LauncherApps
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.BadParcelableException
import android.os.Binder
import android.os.Build
import android.os.DeadObjectException
import android.os.DeadSystemException
import android.os.IBinder
import android.os.UserHandle
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import app.simple.inure.R
import app.simple.inure.apk.utils.PackageUtils.safeApplicationInfo
import app.simple.inure.preferences.DevelopmentPreferences
import app.simple.inure.util.ArrayUtils.clone
import app.simple.inure.util.ArrayUtils.toArrayList
import app.simple.inure.util.ConditionUtils.invert
import app.simple.inure.util.NullSafety.isNotNull
import app.simple.inure.utils.DebloatUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.stream.Collectors
class DataLoaderService : Service() {
companion object {
const val UNINSTALLED_APPS_LOADED = "uninstalled_apps_loaded"
const val INSTALLED_APPS_LOADED = "installed_apps_loaded"
const val APPS_LOADED = "apps_loaded"
const val RELOAD_APPS = "reload_apps"
const val RELOAD_QUICK_APPS = "reload_quick_apps"
const val REFRESH = "refresh"
private const val TAG: String = "DataLoaderService"
private const val INSTALLED_FLAGS = PackageManager.GET_META_DATA
@Suppress("DEPRECATION")
private val UNINSTALLED_FLAGS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
INSTALLED_FLAGS or PackageManager.MATCH_UNINSTALLED_PACKAGES
} else {
INSTALLED_FLAGS or PackageManager.GET_UNINSTALLED_PACKAGES
}
const val REFRESH = "app.simple.inure.services.DataLoaderService.REFRESH"
const val RELOAD_QUICK_APPS = "app.simple.inure.services.DataLoaderService.RELOAD_QUICK_APPS"
}
private var apps: ArrayList<PackageInfo> = arrayListOf()
private var uninstalledApps: ArrayList<PackageInfo> = arrayListOf()
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val _installedApps = MutableStateFlow<ArrayList<PackageInfo>?>(null)
val installedApps: StateFlow<ArrayList<PackageInfo>?> = _installedApps
private val _uninstalledApps = MutableStateFlow<ArrayList<PackageInfo>?>(null)
val uninstalledApps: StateFlow<ArrayList<PackageInfo>?> = _uninstalledApps
private var isLoading = false
private var flags = PackageManager.GET_META_DATA
private var broadcastReceiver: BroadcastReceiver? = null
private var intentFilter: IntentFilter = IntentFilter()
private val launcherAppsService: LauncherApps by lazy {
getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps
getSystemService(LAUNCHER_APPS_SERVICE) as LauncherApps
}
private var launcherAppsCallback: LauncherApps.Callback? = null
inner class LoaderBinder : Binder() {
fun getService(): DataLoaderService {
return this@DataLoaderService
}
fun getService(): DataLoaderService = this@DataLoaderService
}
override fun onBind(intent: Intent): IBinder {
return LoaderBinder()
}
override fun onBind(intent: Intent): IBinder = LoaderBinder()
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate: Dataloader service created")
broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
REFRESH -> {
refresh()
}
}
}
}
intentFilter.addAction(REFRESH)
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(broadcastReceiver!!, intentFilter)
if (launcherAppsCallback == null) {
launcherAppsCallback = object : LauncherApps.Callback() {
override fun onPackageRemoved(packageName: String?, user: UserHandle?) {
Log.d(TAG, "onPackageRemoved: $packageName")
refresh()
}
override fun onPackageAdded(packageName: String?, user: UserHandle?) {
Log.d(TAG, "onPackageAdded: $packageName")
refresh()
}
override fun onPackageChanged(packageName: String?, user: UserHandle?) {
Log.d(TAG, "onPackageChanged: $packageName")
refresh()
}
override fun onPackagesAvailable(packageNames: Array<out String>?, user: UserHandle?, replacing: Boolean) {
Log.d(TAG, "onPackagesAvailable: ${packageNames?.contentToString()}")
refresh()
}
override fun onPackagesUnavailable(packageNames: Array<out String>?, user: UserHandle?, replacing: Boolean) {
Log.d(TAG, "onPackagesUnavailable: ${packageNames?.contentToString()}")
refresh()
}
override fun onPackageRemoved(packageName: String?, user: UserHandle?) = refresh()
override fun onPackageAdded(packageName: String?, user: UserHandle?) = refresh()
override fun onPackageChanged(packageName: String?, user: UserHandle?) = refresh()
override fun onPackagesAvailable(packageNames: Array<out String>?, user: UserHandle?, replacing: Boolean) = refresh()
override fun onPackagesUnavailable(packageNames: Array<out String>?, user: UserHandle?, replacing: Boolean) = refresh()
fun refresh() {
if (DevelopmentPreferences.get(DevelopmentPreferences.REFRESH_APPS_LIST_USING_LAUNCHER_SERVICE)) {
this@DataLoaderService.refresh()
}
}
}
// launcherAppsService.registerCallback(launcherAppsCallback!!)
} else {
Log.i(TAG, "onCreate: LauncherApps callback already initialized")
}
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
startLoading()
refresh()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy: Dataloader service destroyed")
LocalBroadcastManager.getInstance(applicationContext).unregisterReceiver(broadcastReceiver!!)
serviceScope.cancel()
if (launcherAppsCallback != null) {
launcherAppsService.unregisterCallback(launcherAppsCallback)
}
}
fun getInstalledApps(): ArrayList<PackageInfo> {
if (apps.isNotNull() && apps.isNotEmpty()) {
@Suppress("UNCHECKED_CAST")
return apps.clone() as ArrayList<PackageInfo>
} else {
apps = loadInstalledApps() as ArrayList<PackageInfo>
return getInstalledApps()
}
}
@Suppress("UNCHECKED_CAST")
fun getUninstalledApps(): ArrayList<PackageInfo> {
return if (uninstalledApps.isNotNull() && uninstalledApps.isNotEmpty()) {
uninstalledApps.clone() as ArrayList<PackageInfo>
} else {
loadUninstalledApps()
uninstalledApps.clone() as ArrayList<PackageInfo>
}
}
fun startLoading() {
fun refresh() {
if (isLoading.invert()) {
Log.i(TAG, "refresh: Loading installed and uninstalled apps")
isLoading = true
CoroutineScope(Dispatchers.IO).launch {
if (apps.isEmpty()) {
apps = loadInstalledApps().clone()
}
if (uninstalledApps.isEmpty()) {
loadUninstalledApps()
}
// onAppsLoaded(apps.toArrayList())
// onUninstalledAppsLoaded(uninstalledApps.toArrayList())
// We will init bloat list here
// Because I couldn't think of any other place
DebloatUtils.initBloatAppsSet()
serviceScope.launch {
val installed = loadInstalledApps()
val uninstalled = loadUninstalledApps()
withContext(Dispatchers.Main) {
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(Intent(APPS_LOADED))
_installedApps.value = installed
_uninstalledApps.value = uninstalled
isLoading = false
}
Log.i(TAG, "refresh: Loaded ${installed.size} installed and ${uninstalled.size} uninstalled apps")
DebloatUtils.initBloatAppsSet()
}
} else {
Log.w(TAG, "refresh: Already loading apps, skipping refresh")
}
}
private suspend fun loadInstalledApps(maxRetries: Int = 3): ArrayList<PackageInfo> {
val result = ArrayList<PackageInfo>()
repeat(maxRetries) { attempt ->
try {
val packageNames = packageManager.getInstalledPackages(0).map { it.packageName }
packageNames.chunked(100).forEach { batch ->
batch.forEach { pkg ->
try {
val info = packageManager.getPackageInfo(pkg, INSTALLED_FLAGS)
result.add(info)
} catch (_: Exception) {
}
}
}
result.forEach {
it.safeApplicationInfo.name = getApplicationName(applicationContext, it.safeApplicationInfo)
}
return result
} catch (e: Exception) {
result.clear()
e.printStackTrace()
if (attempt < maxRetries - 1) {
delay(500L)
}
}
}
return result
}
fun refresh() {
isLoading = false
apps.clear()
uninstalledApps.clear()
startLoading()
}
private suspend fun loadUninstalledApps(maxRetries: Int = 3): ArrayList<PackageInfo> {
val result = ArrayList<PackageInfo>()
fun hasDataLoaded(): Boolean {
return apps.isNotEmpty() && uninstalledApps.isNotEmpty()
}
private fun loadInstalledApps(): MutableList<PackageInfo> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
repeat(maxRetries) { attempt ->
try {
return packageManager.getInstalledPackages(flags).loadPackageNames()
} catch (e: DeadObjectException) {
val packageNames = packageManager.getInstalledPackages(0).map { it.packageName }
packageNames.chunked(100).forEach { batch ->
batch.forEach { pkg ->
try {
val info = packageManager.getPackageInfo(pkg, UNINSTALLED_FLAGS)
if (info.safeApplicationInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
result.add(info)
}
} catch (_: Exception) {
}
}
}
result.forEach {
it.safeApplicationInfo.name = getApplicationName(applicationContext, it.safeApplicationInfo)
}
return result
} catch (e: Exception) {
result.clear()
e.printStackTrace()
return mutableListOf()
} catch (e: DeadSystemException) {
e.printStackTrace()
return mutableListOf()
}
} else {
try {
return packageManager.getInstalledPackages(flags).loadPackageNames()
} catch (e: DeadObjectException) {
e.printStackTrace()
return mutableListOf()
if (attempt < maxRetries - 1) {
delay(500L)
}
}
}
return result
}
@Suppress("DEPRECATION")
private fun loadUninstalledApps() {
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.flags or PackageManager.MATCH_UNINSTALLED_PACKAGES
} else {
this.flags or PackageManager.GET_UNINSTALLED_PACKAGES
}
if (uninstalledApps.isNotEmpty()) return
try {
uninstalledApps = packageManager.getInstalledPackages(flags).stream()
.filter { packageInfo -> packageInfo.safeApplicationInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0 }
.collect(Collectors.toList())
.loadPackageNames()
.toArrayList()
} catch (e: DeadObjectException) {
uninstalledApps = arrayListOf()
e.printStackTrace()
} catch (e: BadParcelableException) {
uninstalledApps = arrayListOf()
e.printStackTrace()
}
}
private fun MutableList<PackageInfo>.loadPackageNames(): MutableList<PackageInfo> {
forEach {
it.safeApplicationInfo.name = getApplicationName(application.applicationContext, it.safeApplicationInfo)
}
return this
}
/**
* Fetches the app's name from the package id of the same application
* @param context of the given environment
* @param applicationInfo is [ApplicationInfo] object containing app's
* information
* @return app's name as [String]
*/
private fun getApplicationName(context: Context, applicationInfo: ApplicationInfo): String? {
return try {
context.packageManager.getApplicationLabel(applicationInfo).toString()
@@ -267,30 +193,4 @@ class DataLoaderService : Service() {
context.getString(R.string.unknown)
}
}
private fun onUninstalledAppsLoaded(uninstalledApps: ArrayList<PackageInfo>) {
Log.d("DataLoaderService", "onUninstalledAppsLoaded: ${uninstalledApps.size}")
LocalBroadcastManager.getInstance(applicationContext)
.sendBroadcast(Intent(UNINSTALLED_APPS_LOADED))
}
private fun onAppsLoaded(apps: ArrayList<PackageInfo>) {
Log.d("DataLoaderService", "onAppsLoaded: ${apps.size}")
LocalBroadcastManager.getInstance(applicationContext)
.sendBroadcast(Intent(INSTALLED_APPS_LOADED))
}
@Suppress("UNCHECKED_CAST")
fun refreshUninstalled() {
uninstalledApps.clear()
loadUninstalledApps()
onUninstalledAppsLoaded(uninstalledApps.clone() as ArrayList<PackageInfo>)
}
@Suppress("UNCHECKED_CAST")
fun refreshInstalled() {
apps.clear()
apps = loadInstalledApps() as ArrayList<PackageInfo>
onAppsLoaded(apps.clone() as ArrayList<PackageInfo>)
}
}
}

View File

@@ -3,17 +3,11 @@ package app.simple.inure.ui.launcher
import android.Manifest
import android.annotation.SuppressLint
import android.app.AppOpsManager
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.IBinder
import android.os.Process
import android.util.Log
import android.view.LayoutInflater
@@ -25,7 +19,6 @@ import androidx.core.app.AppOpsManagerCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import app.simple.inure.BuildConfig
import app.simple.inure.R
import app.simple.inure.apk.utils.PackageUtils.isPackageInstalled
@@ -45,7 +38,6 @@ import app.simple.inure.preferences.MusicPreferences
import app.simple.inure.preferences.SearchPreferences
import app.simple.inure.preferences.SetupPreferences
import app.simple.inure.preferences.TrialPreferences
import app.simple.inure.services.DataLoaderService
import app.simple.inure.ui.panels.Home
import app.simple.inure.util.AppUtils
import app.simple.inure.util.ConditionUtils.invert
@@ -87,12 +79,6 @@ class SplashScreen : ScopedFragment() {
private var isTagsLoaded = false
private var isDebloatLoaded = false
private var serviceConnection: ServiceConnection? = null
private var dataLoaderService: DataLoaderService? = null
private var broadcastReceiver: BroadcastReceiver? = null
private var intentFilter: IntentFilter = IntentFilter()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_splash_screen, container, false)
}
@@ -102,33 +88,6 @@ class SplashScreen : ScopedFragment() {
startPostponedEnterTransition()
clearSearchStates()
intentFilter.addAction(DataLoaderService.APPS_LOADED)
intentFilter.addAction(DataLoaderService.UNINSTALLED_APPS_LOADED)
intentFilter.addAction(DataLoaderService.INSTALLED_APPS_LOADED)
broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == DataLoaderService.APPS_LOADED) {
proceed()
}
}
}
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
dataLoaderService = (service as DataLoaderService.LoaderBinder).getService()
if (dataLoaderService?.hasDataLoaded() == true) {
proceed()
} else {
dataLoaderService?.startLoading()
}
}
override fun onServiceDisconnected(name: ComponentName?) {
dataLoaderService = null
}
}
icon = view.findViewById(R.id.imageView)
loaderImageView = view.findViewById(R.id.loader)
daysLeft = view.findViewById(R.id.days_left)
@@ -167,31 +126,26 @@ class SplashScreen : ScopedFragment() {
}
requireArguments().getBoolean(BundleConstants.skip) -> { // Second check if setup is skipped
startLoaderService()
startLoader()
}
!checkForPermission() -> {
if (SetupPreferences.isDontShowAgain()) { // If setup not skipped open setup
startLoaderService()
startLoader()
} else {
openFragmentSlide(Setup.newInstance())
}
}
else -> { // Load all data
startLoaderService()
startLoader()
}
}
}
}
private fun startLoaderService() {
val intent = Intent(requireContext(), DataLoaderService::class.java)
requireContext().bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
if (BehaviourPreferences.isSkipLoading()) {
proceed()
}
private fun startLoader() {
proceed()
postDelayed(MAX_LOADING_TIME) { // Give the service 7 seconds to load
/**
@@ -438,25 +392,8 @@ class SplashScreen : ScopedFragment() {
}
}
override fun onResume() {
super.onResume()
LocalBroadcastManager.getInstance(requireContext()).registerReceiver(broadcastReceiver!!, intentFilter)
}
override fun onDestroy() {
super.onDestroy()
try {
if (serviceConnection != null) {
requireContext().unbindService(serviceConnection!!)
}
} catch (e: java.lang.IllegalArgumentException) {
e.printStackTrace() // Should crash if moving to another [Setup] fragment
}
if (broadcastReceiver != null) {
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(broadcastReceiver!!)
}
handler.removeCallbacksAndMessages(null)
}

View File

@@ -78,7 +78,7 @@ class Uninstalled : ScopedFragment() {
}
R.drawable.ic_refresh -> {
showLoader(manualOverride = true)
homeViewModel.refreshUninstalledPackageData()
homeViewModel.refreshPackageData()
}
}
}