Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 491c85bf authored by Hasib Prince's avatar Hasib Prince
Browse files

App Lounge: conflict resolved

parents c6828cc2 3fcb80e8
Loading
Loading
Loading
Loading
+100 −0
Original line number Diff line number Diff line
package foundation.e.apps

import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import dagger.hilt.android.lifecycle.HiltViewModel
import foundation.e.apps.api.Result
import foundation.e.apps.api.exodus.models.AppPrivacyInfo
import foundation.e.apps.api.exodus.repositories.IAppPrivacyInfoRepository
import foundation.e.apps.api.fused.data.FusedApp
import javax.inject.Inject
import kotlin.math.ceil
import kotlin.math.round

@HiltViewModel
class PrivacyInfoViewModel @Inject constructor(
    private val privacyInfoRepository: IAppPrivacyInfoRepository,
) : ViewModel() {

    fun getAppPrivacyInfoLiveData(fusedApp: FusedApp): LiveData<Result<AppPrivacyInfo>> {
        return liveData {
            emit(fetchEmitAppPrivacyInfo(fusedApp))
        }
    }

    private suspend fun fetchEmitAppPrivacyInfo(
        fusedApp: FusedApp
    ): Result<AppPrivacyInfo> {
        if (fusedApp.trackers.isNotEmpty() && fusedApp.perms.isNotEmpty()) {
            val appInfo = AppPrivacyInfo(fusedApp.trackers, fusedApp.perms)
            return Result.success(appInfo)
        }
        val appPrivacyPrivacyInfoResult =
            privacyInfoRepository.getAppPrivacyInfo(fusedApp.package_name)
        return handleAppPrivacyInfoResult(appPrivacyPrivacyInfoResult, fusedApp)
    }

    private fun handleAppPrivacyInfoResult(
        appPrivacyPrivacyInfoResult: Result<AppPrivacyInfo>,
        fusedApp: FusedApp
    ): Result<AppPrivacyInfo> {
        return if (appPrivacyPrivacyInfoResult.isSuccess()) {
            handleAppPrivacyInfoSuccess(appPrivacyPrivacyInfoResult, fusedApp)
        } else {
            Result.error("Tracker not found!")
        }
    }

    private fun handleAppPrivacyInfoSuccess(
        appPrivacyPrivacyInfoResult: Result<AppPrivacyInfo>,
        fusedApp: FusedApp
    ): Result<AppPrivacyInfo> {
        fusedApp.trackers = appPrivacyPrivacyInfoResult.data?.trackerList ?: listOf()
        if (fusedApp.perms.isEmpty()) {

            fusedApp.perms = appPrivacyPrivacyInfoResult.data?.permissionList ?: listOf()
        }
        return appPrivacyPrivacyInfoResult
    }

    fun getTrackerListText(fusedApp: FusedApp?): String {
        fusedApp?.let {
            if (it.trackers.isNotEmpty()) {
                return it.trackers.joinToString(separator = "") { tracker -> "$tracker<br />" }
            }
        }
        return ""
    }

    fun getPrivacyScore(fusedApp: FusedApp?): Int {
        fusedApp?.let {
            return calculatePrivacyScore(it)
        }
        return -1
    }

    fun calculatePrivacyScore(fusedApp: FusedApp): Int {
        val calculateTrackersScore = calculateTrackersScore(fusedApp.trackers.size)
        val calculatePermissionsScore = calculatePermissionsScore(
            countAndroidPermissions(fusedApp)
        )
        Log.d(
            "PrivacyInfoViewModel",
            "calculatePrivacyScore: ${fusedApp.name}: privacyScore: $calculateTrackersScore permissionScore: $calculatePermissionsScore noOfPermission: ${fusedApp.perms.size}"
        )
        return calculateTrackersScore + calculatePermissionsScore
    }

    private fun countAndroidPermissions(fusedApp: FusedApp) =
        fusedApp.perms.filter { it.contains("android.permission") }.size

    private fun calculateTrackersScore(numberOfTrackers: Int): Int {
        return if (numberOfTrackers > 5) 0 else 9 - numberOfTrackers
    }

    private fun calculatePermissionsScore(numberOfPermission: Int): Int {
        return if (numberOfPermission > 9) 0 else round(0.2 * ceil((10 - numberOfPermission) / 2.0)).toInt()
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -47,5 +47,6 @@ data class FusedApp(
    val isFree: Boolean = true,
    val is_pwa: Boolean = false,
    val url: String = String(),
    var type: Type = Type.NATIVE
    var type: Type = Type.NATIVE,
    var privacyScore: Int = -1
)
+10 −6
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textview.MaterialTextView
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.MainActivityViewModel
import foundation.e.apps.PrivacyInfoViewModel
import foundation.e.apps.R
import foundation.e.apps.api.cleanapk.CleanAPKInterface
import foundation.e.apps.api.fused.data.FusedApp
@@ -73,6 +74,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) {
    lateinit var pkgManagerModule: PkgManagerModule

    private val applicationViewModel: ApplicationViewModel by viewModels()
    private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels()
    private val mainActivityViewModel: MainActivityViewModel by activityViewModels()

    private var applicationIcon: ImageView? = null
@@ -226,7 +228,8 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) {
                    ).show(childFragmentManager, TAG)
                }
                appTrackers.setOnClickListener {
                    var trackers = applicationViewModel.getTrackerListText()
                    var trackers =
                        privacyInfoViewModel.getTrackerListText(applicationViewModel.fusedApp.value)

                    if (trackers.isNotEmpty()) {
                        trackers += "<br /> <br />" + getString(
@@ -244,8 +247,9 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) {
                    ).show(childFragmentManager, TAG)
                }
            }

            observeDownloadStatus(view)
            fetchAppTracker()
            fetchAppTracker(it)
        }
    }

@@ -463,8 +467,8 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) {
        return permission
    }

    private fun fetchAppTracker() {
        applicationViewModel.fetchAppPrivacyInfo().observe(viewLifecycleOwner) {
    private fun fetchAppTracker(fusedApp: FusedApp) {
        privacyInfoViewModel.getAppPrivacyInfoLiveData(fusedApp).observe(viewLifecycleOwner) {
            updatePrivacyScore()
            binding.applicationLayout.visibility = View.VISIBLE
            binding.progressBar.visibility = View.GONE
@@ -472,7 +476,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) {
    }

    private fun updatePrivacyScore() {
        val privacyScore = applicationViewModel.getPrivacyScore()
        val privacyScore = privacyInfoViewModel.getPrivacyScore(applicationViewModel.fusedApp.value)
        if (privacyScore != -1) {
            val appPrivacyScore = binding.ratingsInclude.appPrivacyScore
            appPrivacyScore.text = getString(
+6 −81
Original line number Diff line number Diff line
@@ -18,17 +18,11 @@

package foundation.e.apps.application

import androidx.lifecycle.LiveData
import androidx.lifecycle.LiveDataScope
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.AuthData
import dagger.hilt.android.lifecycle.HiltViewModel
import foundation.e.apps.api.Result
import foundation.e.apps.api.exodus.models.AppPrivacyInfo
import foundation.e.apps.api.exodus.repositories.IAppPrivacyInfoRepository
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.manager.download.data.DownloadProgress
@@ -39,15 +33,12 @@ import foundation.e.apps.utils.enums.Status
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.math.ceil
import kotlin.math.round

@HiltViewModel
class ApplicationViewModel @Inject constructor(
    downloadProgressLD: DownloadProgressLD,
    private val fusedAPIRepository: FusedAPIRepository,
    private val fusedManagerRepository: FusedManagerRepository,
    private val appPrivacyInfoRepository: IAppPrivacyInfoRepository
    private val fusedManagerRepository: FusedManagerRepository
) : ViewModel() {

    val fusedApp: MutableLiveData<FusedApp> = MutableLiveData()
@@ -92,83 +83,17 @@ class ApplicationViewModel @Inject constructor(
        }
    }

    fun fetchAppPrivacyInfo(): LiveData<Result<AppPrivacyInfo>> {
        return liveData {
            fusedApp.value?.let {
                if (it.trackers.isNotEmpty()) {
                    val appInfo = AppPrivacyInfo(it.trackers, it.perms)
                    emit(Result.success(appInfo))
                    return@liveData
                }
                val trackerResultOfAnApp = appPrivacyInfoRepository.getAppPrivacyInfo(it.package_name)
                handleAppTrackerResult(trackerResultOfAnApp, it)
            }
        }
    }

    private suspend fun LiveDataScope<Result<AppPrivacyInfo>>.handleAppTrackerResult(
        appPrivacyPrivacyInfoResult: Result<AppPrivacyInfo>,
        fusedApp: FusedApp
    ) {
        if (appPrivacyPrivacyInfoResult.isSuccess()) {
            handleAppPrivacyInfoSuccess(appPrivacyPrivacyInfoResult, fusedApp)
        } else {
            emit(Result.error("Tracker not found!"))
        }
    }

    private suspend fun LiveDataScope<Result<AppPrivacyInfo>>.handleAppPrivacyInfoSuccess(
        appPrivacyPrivacyInfoResult: Result<AppPrivacyInfo>,
        fusedApp: FusedApp
    ) {
        fusedApp.trackers = appPrivacyPrivacyInfoResult.data?.trackerList ?: listOf()
        if (fusedApp.perms.isEmpty()) {
            fusedApp.perms = appPrivacyPrivacyInfoResult.data?.permissionList ?: listOf()
        }
        emit(appPrivacyPrivacyInfoResult)
    }

    fun getTrackerListText(): String {
        fusedApp.value?.let {
            if (it.trackers.isNotEmpty()) {
                return it.trackers.joinToString(separator = "") { tracker -> "$tracker<br />" }
            }
        }
        return ""
    }

    fun getPrivacyScore(): Int {
        fusedApp.value?.let {
            return calculatePrivacyScore(it)
        }
        return -1
    }

    private fun calculatePrivacyScore(fusedApp: FusedApp): Int {
        return calculateTrackersScore(fusedApp.trackers.size) + calculatePermissionsScore(
            countAndroidPermissions(fusedApp)
        )
    }

    private fun countAndroidPermissions(fusedApp: FusedApp) =
        fusedApp.perms.filter { it.contains("android.permission") }.size

    private fun calculateTrackersScore(numberOfTrackers: Int): Int {
        return if (numberOfTrackers > 5) 0 else 9 - numberOfTrackers
    }

    private fun calculatePermissionsScore(numberOfPermission: Int): Int {
        return if (numberOfPermission > 9) 0 else round(0.2 * ceil((10 - numberOfPermission) / 2.0)).toInt()
    }

    suspend fun calculateProgress(progress: DownloadProgress): Pair<Long, Long> {
        fusedApp.value?.let { app ->
            val appDownload = fusedManagerRepository.getDownloadList().singleOrNull { it.id.contentEquals(app._id) }
            val appDownload = fusedManagerRepository.getDownloadList()
                .singleOrNull { it.id.contentEquals(app._id) }
            val downloadingMap = progress.totalSizeBytes.filter { item ->
                appDownload?.downloadIdMap?.keys?.contains(item.key) == true
            }
            val totalSizeBytes = downloadingMap.values.sum()
            val downloadedSoFar = progress.bytesDownloadedSoFar.filter { item -> appDownload?.downloadIdMap?.keys?.contains(item.key) == true }.values.sum()
            val downloadedSoFar = progress.bytesDownloadedSoFar.filter { item ->
                appDownload?.downloadIdMap?.keys?.contains(item.key) == true
            }.values.sum()

            return Pair(totalSizeBytes, downloadedSoFar)
        }
+13 −6
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.MainActivityViewModel
import foundation.e.apps.PrivacyInfoViewModel
import foundation.e.apps.R
import foundation.e.apps.api.fused.FusedAPIInterface
import foundation.e.apps.api.fused.data.FusedApp
@@ -48,16 +49,15 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu
    lateinit var pkgManagerModule: PkgManagerModule

    private val viewModel: ApplicationListViewModel by viewModels()
    private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels()
    private val mainActivityViewModel: MainActivityViewModel by activityViewModels()

    private var _binding: FragmentApplicationListBinding? = null
    private val binding get() = _binding!!

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _binding = FragmentApplicationListBinding.bind(view)

        mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { isInternetConnection ->
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mainActivityViewModel.internetConnection.observe(this) { isInternetConnection ->
            mainActivityViewModel.authData.value?.let { authData ->
                if (isInternetConnection) {
                    viewModel.getList(
@@ -69,6 +69,11 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu
                }
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _binding = FragmentApplicationListBinding.bind(view)

        binding.toolbarTitleTV.text = args.translation
        binding.toolbar.apply {
@@ -82,9 +87,11 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu
            findNavController().currentDestination?.id?.let {
                ApplicationListRVAdapter(
                    this,
                    privacyInfoViewModel,
                    it,
                    pkgManagerModule,
                    User.valueOf(mainActivityViewModel.userType.value ?: User.UNAVAILABLE.name)
                    User.valueOf(mainActivityViewModel.userType.value ?: User.UNAVAILABLE.name),
                    viewLifecycleOwner
                )
            }
        recyclerView.apply {
Loading