InstalledAppsFragment: Show all installed packages similar to blacklist
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
package com.aurora.store.data.model
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.data.room.update.Update
|
import com.aurora.store.data.room.update.Update
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
@@ -21,5 +24,13 @@ data class MinimalApp(
|
|||||||
fun fromUpdate(update: Update): MinimalApp {
|
fun fromUpdate(update: Update): MinimalApp {
|
||||||
return MinimalApp(update.packageName, update.versionCode, update.displayName)
|
return MinimalApp(update.packageName, update.versionCode, update.displayName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fromPackageInfo(context: Context, packageInfo: PackageInfo): MinimalApp {
|
||||||
|
return MinimalApp(
|
||||||
|
packageInfo.packageName,
|
||||||
|
PackageInfoCompat.getLongVersionCode(packageInfo).toInt(),
|
||||||
|
packageInfo.applicationInfo!!.loadLabel(context.packageManager).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,21 @@ import com.aurora.extensions.isPAndAbove
|
|||||||
import com.aurora.extensions.isTAndAbove
|
import com.aurora.extensions.isTAndAbove
|
||||||
import com.aurora.extensions.isValidApp
|
import com.aurora.extensions.isValidApp
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
object PackageUtil {
|
object PackageUtil {
|
||||||
|
|
||||||
private const val TAG = "PackageUtil"
|
private const val TAG = "PackageUtil"
|
||||||
|
|
||||||
|
fun getAllValidPackages(context: Context): List<PackageInfo> {
|
||||||
|
return context.packageManager.getInstalledPackages(PackageManager.GET_META_DATA)
|
||||||
|
.filter { it.isValidApp(context.packageManager) }
|
||||||
|
.sortedBy {
|
||||||
|
it.applicationInfo!!.loadLabel(context.packageManager).toString()
|
||||||
|
.lowercase(Locale.getDefault())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun isInstalled(context: Context, packageName: String): Boolean {
|
fun isInstalled(context: Context, packageName: String): Boolean {
|
||||||
return try {
|
return try {
|
||||||
getPackageInfo(context, packageName, PackageManager.GET_META_DATA)
|
getPackageInfo(context, packageName, PackageManager.GET_META_DATA)
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* Aurora Store is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Aurora Store is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.epoxy.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
|
import coil3.load
|
||||||
|
import coil3.request.placeholder
|
||||||
|
import coil3.request.transformations
|
||||||
|
import coil3.transform.RoundedCornersTransformation
|
||||||
|
import com.airbnb.epoxy.CallbackProp
|
||||||
|
import com.airbnb.epoxy.ModelProp
|
||||||
|
import com.airbnb.epoxy.ModelView
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.databinding.ViewPackageBinding
|
||||||
|
import com.aurora.store.util.PackageUtil
|
||||||
|
|
||||||
|
@ModelView(
|
||||||
|
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
|
||||||
|
baseModelClass = BaseModel::class
|
||||||
|
)
|
||||||
|
class PackageInfoView @JvmOverloads constructor(
|
||||||
|
context: Context?,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : BaseView<ViewPackageBinding>(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
@ModelProp(options = [ModelProp.Option.IgnoreRequireHashCode])
|
||||||
|
fun packageInfo(packageInfo: PackageInfo) {
|
||||||
|
val appInfo = packageInfo.applicationInfo!!
|
||||||
|
binding.imgIcon.load(PackageUtil.getIconForPackage(context, appInfo.packageName)) {
|
||||||
|
placeholder(R.drawable.bg_placeholder)
|
||||||
|
transformations(RoundedCornersTransformation(25F))
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.txtLine1.text = appInfo.loadLabel(context.packageManager)
|
||||||
|
binding.txtLine2.text = appInfo.packageName
|
||||||
|
binding.txtLine3.text = ("${packageInfo.versionName}.${PackageInfoCompat.getLongVersionCode(packageInfo)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallbackProp
|
||||||
|
fun click(onClickListener: OnClickListener?) {
|
||||||
|
binding.root.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallbackProp
|
||||||
|
fun longClick(onClickListener: OnLongClickListener?) {
|
||||||
|
binding.root.setOnLongClickListener(onClickListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,18 +19,18 @@
|
|||||||
|
|
||||||
package com.aurora.store.view.ui.all
|
package com.aurora.store.view.ui.all
|
||||||
|
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.aurora.gplayapi.data.models.App
|
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.data.event.InstallerEvent
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.model.MinimalApp
|
import com.aurora.store.data.model.MinimalApp
|
||||||
import com.aurora.store.databinding.FragmentAppsBinding
|
import com.aurora.store.databinding.FragmentAppsBinding
|
||||||
import com.aurora.store.view.epoxy.views.HeaderViewModel_
|
import com.aurora.store.view.epoxy.views.HeaderViewModel_
|
||||||
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
|
import com.aurora.store.view.epoxy.views.PackageInfoViewModel_
|
||||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
import com.aurora.store.view.ui.commons.BaseFragment
|
||||||
import com.aurora.store.viewmodel.all.InstalledViewModel
|
import com.aurora.store.viewmodel.all.InstalledViewModel
|
||||||
@@ -54,7 +54,7 @@ class InstalledAppsFragment : BaseFragment<FragmentAppsBinding>() {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.installedApps.collect {
|
viewModel.packages.collect {
|
||||||
updateController(it)
|
updateController(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,10 +75,10 @@ class InstalledAppsFragment : BaseFragment<FragmentAppsBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateController(appList: List<App>?) {
|
private fun updateController(packages: List<PackageInfo>?) {
|
||||||
binding.recycler.withModels {
|
binding.recycler.withModels {
|
||||||
setFilterDuplicates(true)
|
setFilterDuplicates(true)
|
||||||
if (appList == null) {
|
if (packages == null) {
|
||||||
for (i in 1..10) {
|
for (i in 1..10) {
|
||||||
add(
|
add(
|
||||||
AppListViewShimmerModel_()
|
AppListViewShimmerModel_()
|
||||||
@@ -89,16 +89,16 @@ class InstalledAppsFragment : BaseFragment<FragmentAppsBinding>() {
|
|||||||
add(
|
add(
|
||||||
HeaderViewModel_()
|
HeaderViewModel_()
|
||||||
.id("header")
|
.id("header")
|
||||||
.title("${appList.size} apps installed")
|
.title("${packages.size} apps installed")
|
||||||
)
|
)
|
||||||
appList.forEach { app ->
|
packages.forEach { app ->
|
||||||
add(
|
add(
|
||||||
AppListViewModel_()
|
PackageInfoViewModel_()
|
||||||
.id(app.id)
|
.id(app.packageName.hashCode())
|
||||||
.app(app)
|
.packageInfo(app)
|
||||||
.click { _ -> openDetailsFragment(app.packageName, app) }
|
.click { _ -> openDetailsFragment(app.packageName) }
|
||||||
.longClick { _ ->
|
.longClick { _ ->
|
||||||
openAppMenuSheet(MinimalApp.fromApp(app))
|
openAppMenuSheet(MinimalApp.fromPackageInfo(requireContext(), app))
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,16 +24,14 @@ import android.content.pm.PackageInfo
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.extensions.isValidApp
|
|
||||||
import com.aurora.store.data.providers.BlacklistProvider
|
import com.aurora.store.data.providers.BlacklistProvider
|
||||||
|
import com.aurora.store.util.PackageUtil
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.supervisorScope
|
|
||||||
import java.util.Locale
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -55,17 +53,10 @@ class BlacklistViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun fetchApps() {
|
private fun fetchApps() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
try {
|
||||||
try {
|
_packages.value = PackageUtil.getAllValidPackages(context)
|
||||||
_packages.value = context.packageManager.getInstalledPackages(0)
|
} catch (exception: Exception) {
|
||||||
.filter { it.isValidApp(context.packageManager) }
|
Log.e(TAG, "Failed to fetch apps", exception)
|
||||||
.sortedBy {
|
|
||||||
it.applicationInfo!!.loadLabel(context.packageManager).toString()
|
|
||||||
.lowercase(Locale.getDefault())
|
|
||||||
}
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Log.e(TAG, "Failed to fetch apps", exception)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,10 @@
|
|||||||
package com.aurora.store.viewmodel.all
|
package com.aurora.store.viewmodel.all
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.gplayapi.data.models.App
|
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
|
||||||
import com.aurora.store.data.providers.BlacklistProvider
|
|
||||||
import com.aurora.store.util.PackageUtil
|
import com.aurora.store.util.PackageUtil
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
@@ -33,20 +31,17 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.Locale
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class InstalledViewModel @Inject constructor(
|
class InstalledViewModel @Inject constructor(
|
||||||
private val appDetailsHelper: AppDetailsHelper,
|
|
||||||
private val blacklistProvider: BlacklistProvider,
|
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val TAG = InstalledViewModel::class.java.simpleName
|
private val TAG = InstalledViewModel::class.java.simpleName
|
||||||
|
|
||||||
private val _installedApps = MutableStateFlow<List<App>?>(null)
|
private val _packages = MutableStateFlow<List<PackageInfo>?>(null)
|
||||||
val installedApps = _installedApps.asStateFlow()
|
val packages = _packages.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
fetchApps()
|
fetchApps()
|
||||||
@@ -55,16 +50,9 @@ class InstalledViewModel @Inject constructor(
|
|||||||
fun fetchApps() {
|
fun fetchApps() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
PackageUtil.getPackageInfoMap(context).keys.let { packages ->
|
_packages.value = PackageUtil.getAllValidPackages(context)
|
||||||
val filtersPackages = packages.filter { !blacklistProvider.isBlacklisted(it) }
|
|
||||||
|
|
||||||
_installedApps.value = appDetailsHelper.getAppByPackageName(filtersPackages)
|
|
||||||
.filter { it.displayName.isNotEmpty() }
|
|
||||||
.map { it.isInstalled = true; it }
|
|
||||||
.sortedBy { it.displayName.lowercase(Locale.getDefault()) }
|
|
||||||
}
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Failed to get installed apps", exception)
|
Log.e(TAG, "Failed to fetch apps", exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
app/src/main/res/layout/view_package.xml
Normal file
63
app/src/main/res/layout/view_package.xml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Aurora Store
|
||||||
|
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
~
|
||||||
|
~ Aurora Store is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by
|
||||||
|
~ the Free Software Foundation, either version 2 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ Aurora Store is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
~ GNU General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="@dimen/padding_normal"
|
||||||
|
android:paddingTop="@dimen/padding_xsmall"
|
||||||
|
android:paddingEnd="@dimen/padding_small"
|
||||||
|
android:paddingBottom="@dimen/padding_xsmall">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/img_icon"
|
||||||
|
android:layout_width="@dimen/icon_size_medium"
|
||||||
|
android:layout_height="@dimen/icon_size_medium"
|
||||||
|
android:layout_centerVertical="true" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/txt_line1"
|
||||||
|
style="@style/AuroraTextStyle.Line1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/margin_normal"
|
||||||
|
android:layout_toEndOf="@id/img_icon" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/txt_line2"
|
||||||
|
style="@style/AuroraTextStyle.Line2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/txt_line1"
|
||||||
|
android:layout_alignStart="@id/txt_line1"
|
||||||
|
android:layout_alignEnd="@id/txt_line1"
|
||||||
|
android:layout_marginTop="@dimen/margin_xxsmall" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/txt_line3"
|
||||||
|
style="@style/AuroraTextStyle.Line3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/txt_line2"
|
||||||
|
android:layout_alignStart="@id/txt_line1"
|
||||||
|
android:layout_alignEnd="@id/txt_line1"
|
||||||
|
android:layout_marginTop="@dimen/margin_xxsmall" />
|
||||||
|
</RelativeLayout>
|
||||||
Reference in New Issue
Block a user