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

Commit 6bfa83ca authored by Hasib Prince's avatar Hasib Prince
Browse files

refactoring of fragments & adapters

parent 079673f1
Loading
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -19,9 +19,24 @@ class AppPrivacyInfoRepositoryImpl @Inject constructor(
    private val exodusTrackerApi: ExodusTrackerApi,
    private val trackerDao: TrackerDao
) : IAppPrivacyInfoRepository {

    companion object {
        private const val MAX_TRACKER_SCORE = 9
        private const val MIN_TRACKER_SCORE = 0
        private const val MAX_PERMISSION_SCORE = 10
        private const val MIN_PERMISSION_SCORE = 0
        private const val THRESHOLD_OF_NON_ZERO_TRACKER_SCORE = 5
        private const val THRESHOLD_OF_NON_ZERO_PERMISSION_SCORE = 9
        private const val FACTOR_OF_PERMISSION_SCORE = 0.2
        private const val DIVIDER_OF_PERMISSION_SCORE = 2.0
    }

    private var trackers: List<Tracker> = listOf()

    override suspend fun getAppPrivacyInfo(fusedApp: FusedApp, appHandle: String): Result<AppPrivacyInfo> {
    override suspend fun getAppPrivacyInfo(
        fusedApp: FusedApp,
        appHandle: String
    ): Result<AppPrivacyInfo> {
        if (fusedApp.trackers.isNotEmpty() && fusedApp.permsFromExodus.isNotEmpty()) {
            val appInfo = AppPrivacyInfo(fusedApp.trackers, fusedApp.permsFromExodus)
            return Result.success(appInfo)
@@ -29,7 +44,8 @@ class AppPrivacyInfoRepositoryImpl @Inject constructor(

        val appTrackerInfoResult = getResult { exodusTrackerApi.getTrackerInfoOfApp(appHandle) }
        if (appTrackerInfoResult.isSuccess()) {
            val appPrivacyPrivacyInfoResult = handleAppPrivacyInfoResultSuccess(appTrackerInfoResult)
            val appPrivacyPrivacyInfoResult =
                handleAppPrivacyInfoResultSuccess(appTrackerInfoResult)
            updateFusedApp(fusedApp, appPrivacyPrivacyInfoResult)
            return appPrivacyPrivacyInfoResult
        }
@@ -136,10 +152,12 @@ class AppPrivacyInfoRepositoryImpl @Inject constructor(
        fusedApp.permsFromExodus.filter { it.contains("android.permission") }.size

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

    private fun calculatePermissionsScore(numberOfPermission: Int): Int {
        return if (numberOfPermission > 9) 0 else round(0.2 * ceil((10 - numberOfPermission) / 2.0)).toInt()
        return if (numberOfPermission > THRESHOLD_OF_NON_ZERO_PERMISSION_SCORE) MIN_PERMISSION_SCORE else round(
            FACTOR_OF_PERMISSION_SCORE * ceil((MAX_PERMISSION_SCORE - numberOfPermission) / DIVIDER_OF_PERMISSION_SCORE)
        ).toInt()
    }
}
+184 −141
Original line number Diff line number Diff line
@@ -73,8 +73,8 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {

    private val args: ApplicationFragmentArgs by navArgs()
    private val TAG = ApplicationFragment::class.java.simpleName

    private var _binding: FragmentApplicationBinding? = null

    private val binding get() = _binding!!

    /*
@@ -104,6 +104,8 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {

    private var isDetailsLoaded = false

    private lateinit var screenshotsRVAdapter: ApplicationScreenshotsRVAdapter

    @Inject
    lateinit var pkgManagerModule: PkgManagerModule

@@ -139,32 +141,27 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
            refreshDataOrRefreshToken(mainActivityViewModel)
        }

        val startDestination = findNavController().graph.startDestination
        if (startDestination == R.id.applicationFragment) {
            binding.toolbar.setNavigationOnClickListener {
                val action = ApplicationFragmentDirections.actionApplicationFragmentToHomeFragment()
                view.findNavController().navigate(action)
            }
        } else {
            binding.toolbar.setNavigationOnClickListener {
                view.findNavController().navigateUp()
            }
        }

        val notAvailable = getString(R.string.not_available)
        setupToolbar(view)

        val screenshotsRVAdapter = ApplicationScreenshotsRVAdapter(origin)
        binding.recyclerView.apply {
            adapter = screenshotsRVAdapter
            layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
        }
        setupScreenshotRVAdapter()

        binding.applicationLayout.visibility = View.INVISIBLE

        applicationViewModel.fusedApp.observe(viewLifecycleOwner) { resultPair ->
            updateUi(resultPair)
        }

        applicationViewModel.errorMessageLiveData.observe(viewLifecycleOwner) {
            (requireActivity() as MainActivity).showSnackbarMessage(getString(it))
        }
    }

    private fun updateUi(
        resultPair: Pair<FusedApp, ResultStatus>,
    ) {
        if (resultPair.second != ResultStatus.OK) {
            onTimeout()
                return@observe
            return
        }

        /*
@@ -187,59 +184,32 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
        screenshotsRVAdapter.setData(it.other_images_path)

        // Title widgets
            binding.titleInclude.apply {
                applicationIcon = appIcon
                appName.text = it.name
                appInfoFetchViewModel.getAuthorName(it).observe(viewLifecycleOwner) {
                    appAuthor.text = it
                }
                categoryTitle.text = it.category
                if (origin == Origin.CLEANAPK) {
                    appIcon.load(CleanAPKInterface.ASSET_URL + it.icon_image_path)
                } else {
                    appIcon.load(it.icon_image_path)
                }
            }
        updateAppTitlePanel(it)

        binding.downloadInclude.appSize.text = it.appSize

        // Ratings widgets
            binding.ratingsInclude.apply {
                if (it.ratings.usageQualityScore != -1.0) {
                    val rating =
                        applicationViewModel.handleRatingFormat(it.ratings.usageQualityScore)
                    appRating.text =
                        getString(
                            R.string.rating_out_of, rating
                        )
        updateAppRating(it)

                    appRating.setCompoundDrawablesWithIntrinsicBounds(
                        null, null, getRatingDrawable(rating), null
                    )
                    appRating.compoundDrawablePadding = 15
                }
                appRatingLayout.setOnClickListener {
                    ApplicationDialogFragment(
                        R.drawable.ic_star,
                        getString(R.string.rating),
                        getString(R.string.rating_description)
                    ).show(childFragmentManager, TAG)
        updateAppDescriptionText(it)

        // Information widgets
        updateAppInformation(it)

        // Privacy widgets
        updatePrivacyPanel()

        if (appInfoFetchViewModel.isAppInBlockedList(it)) {
            binding.snackbarLayout.visibility = View.VISIBLE
        }
        fetchAppTracker(it)

                appPrivacyScoreLayout.setOnClickListener {
                    ApplicationDialogFragment(
                        R.drawable.ic_lock,
                        getString(R.string.privacy_score),
                        getString(
                            R.string.privacy_description,
                            PRIVACY_SCORE_SOURCE_CODE_URL,
                            EXODUS_URL,
                            PRIVACY_GUIDELINE_URL
                        )
                    ).show(childFragmentManager, TAG)
        mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list ->
            applicationViewModel.updateApplicationStatus(list)
        }
    }

    private fun updateAppDescriptionText(it: FusedApp) {
        binding.appDescription.text =
            Html.fromHtml(it.description, Html.FROM_HTML_MODE_COMPACT)

@@ -248,26 +218,9 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
                ApplicationFragmentDirections.actionApplicationFragmentToDescriptionFragment(it.description)
            view.findNavController().navigate(action)
        }

            // Information widgets
            binding.infoInclude.apply {
                appUpdatedOn.text = getString(
                    R.string.updated_on,
                    if (origin == Origin.CLEANAPK) it.updatedOn else it.last_modified
                )
                appRequires.text = getString(R.string.min_android_version, notAvailable)
                appVersion.text = getString(
                    R.string.version,
                    if (it.latest_version_number == "-1") notAvailable else it.latest_version_number
                )
                appLicense.text = getString(
                    R.string.license,
                    if (it.licence.isBlank() or (it.licence == "unknown")) notAvailable else it.licence
                )
                appPackageName.text = getString(R.string.package_name, it.package_name)
    }

            // Privacy widgets
    private fun updatePrivacyPanel() {
        binding.privacyInclude.apply {
            appPermissions.setOnClickListener { _ ->
                ApplicationDialogFragment(
@@ -299,19 +252,103 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
                ).show(childFragmentManager, TAG)
            }
        }
    }

            if (appInfoFetchViewModel.isAppInBlockedList(it)) {
                binding.snackbarLayout.visibility = View.VISIBLE
    private fun updateAppInformation(
        it: FusedApp,
    ) {
        binding.infoInclude.apply {
            appUpdatedOn.text = getString(
                R.string.updated_on,
                if (origin == Origin.CLEANAPK) it.updatedOn else it.last_modified
            )
            val notAvailable = getString(R.string.not_available)
            appRequires.text = getString(R.string.min_android_version, notAvailable)
            appVersion.text = getString(
                R.string.version,
                if (it.latest_version_number == "-1") notAvailable else it.latest_version_number
            )
            appLicense.text = getString(
                R.string.license,
                if (it.licence.isBlank() or (it.licence == "unknown")) notAvailable else it.licence
            )
            appPackageName.text = getString(R.string.package_name, it.package_name)
        }
    }
            fetchAppTracker(it)

            mainActivityViewModel.downloadList.observe(viewLifecycleOwner) { list ->
                applicationViewModel.updateApplicationStatus(list)
    private fun updateAppRating(it: FusedApp) {
        binding.ratingsInclude.apply {
            if (it.ratings.usageQualityScore != -1.0) {
                val rating =
                    applicationViewModel.handleRatingFormat(it.ratings.usageQualityScore)
                appRating.text =
                    getString(
                        R.string.rating_out_of, rating
                    )

                appRating.setCompoundDrawablesWithIntrinsicBounds(
                    null, null, getRatingDrawable(rating), null
                )
                appRating.compoundDrawablePadding = 15
            }
            appRatingLayout.setOnClickListener {
                ApplicationDialogFragment(
                    R.drawable.ic_star,
                    getString(R.string.rating),
                    getString(R.string.rating_description)
                ).show(childFragmentManager, TAG)
            }

        applicationViewModel.errorMessageLiveData.observe(viewLifecycleOwner) {
            (requireActivity() as MainActivity).showSnackbarMessage(getString(it))
            appPrivacyScoreLayout.setOnClickListener {
                ApplicationDialogFragment(
                    R.drawable.ic_lock,
                    getString(R.string.privacy_score),
                    getString(
                        R.string.privacy_description,
                        PRIVACY_SCORE_SOURCE_CODE_URL,
                        EXODUS_URL,
                        PRIVACY_GUIDELINE_URL
                    )
                ).show(childFragmentManager, TAG)
            }
        }
    }

    private fun updateAppTitlePanel(it: FusedApp) {
        binding.titleInclude.apply {
            applicationIcon = appIcon
            appName.text = it.name
            appInfoFetchViewModel.getAuthorName(it).observe(viewLifecycleOwner) {
                appAuthor.text = it
            }
            categoryTitle.text = it.category
            if (origin == Origin.CLEANAPK) {
                appIcon.load(CleanAPKInterface.ASSET_URL + it.icon_image_path)
            } else {
                appIcon.load(it.icon_image_path)
            }
        }
    }

    private fun setupScreenshotRVAdapter() {
        screenshotsRVAdapter = ApplicationScreenshotsRVAdapter(origin)
        binding.recyclerView.apply {
            adapter = screenshotsRVAdapter
            layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
        }
    }

    private fun setupToolbar(view: View) {
        val startDestination = findNavController().graph.startDestination
        if (startDestination == R.id.applicationFragment) {
            binding.toolbar.setNavigationOnClickListener {
                val action = ApplicationFragmentDirections.actionApplicationFragmentToHomeFragment()
                view.findNavController().navigate(action)
            }
        } else {
            binding.toolbar.setNavigationOnClickListener {
                view.findNavController().navigateUp()
            }
        }
    }

@@ -381,7 +418,12 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
                        downloadPB,
                        appSize
                    )
                    Status.UNAVAILABLE -> handleUnavaiable(installButton, fusedApp, downloadPB, appSize)
                    Status.UNAVAILABLE -> handleUnavaiable(
                        installButton,
                        fusedApp,
                        downloadPB,
                        appSize
                    )
                    Status.QUEUED, Status.AWAITING, Status.DOWNLOADED -> handleQueued(
                        installButton,
                        fusedApp,
@@ -669,7 +711,8 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) {
    }

    private fun updatePrivacyScore() {
        val privacyScore = privacyInfoViewModel.getPrivacyScore(applicationViewModel.fusedApp.value?.first)
        val privacyScore =
            privacyInfoViewModel.getPrivacyScore(applicationViewModel.fusedApp.value?.first)
        if (privacyScore != -1) {
            val appPrivacyScore = binding.ratingsInclude.appPrivacyScore
            appPrivacyScore.text = getString(
+10 −7
Original line number Diff line number Diff line
@@ -78,13 +78,7 @@ class ApplicationListFragment :
        super.onViewCreated(view, savedInstanceState)
        _binding = FragmentApplicationListBinding.bind(view)

        binding.toolbarTitleTV.text = args.translation
        binding.toolbar.apply {
            setNavigationOnClickListener {
                view.findNavController().navigate(R.id.categoriesFragment)
            }
        }

        updateToolbar(view)
        setupRecyclerView(view)
        observeAppListLiveData()

@@ -100,6 +94,15 @@ class ApplicationListFragment :
        }
    }

    private fun updateToolbar(view: View) {
        binding.toolbarTitleTV.text = args.translation
        binding.toolbar.apply {
            setNavigationOnClickListener {
                view.findNavController().navigate(R.id.categoriesFragment)
            }
        }
    }

    private fun setupRecyclerView(view: View) {
        val recyclerView = initRecyclerView()
        findNavController().currentDestination?.id?.let {
+91 −60
Original line number Diff line number Diff line
@@ -123,79 +123,110 @@ class ApplicationListRVAdapter(
                hidePrivacyScore()
            }
            applicationList.setOnClickListener {
                val action = when (currentDestinationId) {
                    R.id.applicationListFragment -> {
                        ApplicationListFragmentDirections.actionApplicationListFragmentToApplicationFragment(
                            searchApp._id,
                            searchApp.package_name,
                            searchApp.origin
                        )
                handleAppItemClick(searchApp, view)
            }
                    R.id.searchFragment -> {
                        SearchFragmentDirections.actionSearchFragmentToApplicationFragment(
                            searchApp._id,
                            searchApp.package_name,
                            searchApp.origin
                        )
            updateAppInfo(searchApp)
            updateRating(searchApp)
            updatePrivacyScore(searchApp, view)
            updateSourceTag(searchApp)
            setAppIcon(searchApp, shimmerDrawable)
            removeIsPurchasedObserver(holder)

            if (appInfoFetchViewModel.isAppInBlockedList(searchApp)) {
                setupShowMoreButton()
            } else {
                mainActivityViewModel.verifyUiFilter(searchApp) {
                    setupInstallButton(searchApp, view, holder)
                }
                    R.id.updatesFragment -> {
                        UpdatesFragmentDirections.actionUpdatesFragmentToApplicationFragment(
                            searchApp._id,
                            searchApp.package_name,
                            searchApp.origin
                        )
            }
                    else -> null

            showCalculatedPrivacyScoreData(searchApp, view)
        }
    }

    private fun ApplicationListItemBinding.setAppIcon(
        searchApp: FusedApp,
        shimmerDrawable: ShimmerDrawable
    ) {
        when (searchApp.origin) {
            Origin.GPLAY -> {
                appIcon.load(searchApp.icon_image_path) {
                    placeholder(shimmerDrawable)
                }
                action?.let { direction -> view.findNavController().navigate(direction) }
            }
            Origin.CLEANAPK -> {
                appIcon.load(CleanAPKInterface.ASSET_URL + searchApp.icon_image_path) {
                    placeholder(shimmerDrawable)
                }
            }
            else -> Log.wtf(TAG, "${searchApp.package_name} is from an unknown origin")
        }
    }

    private fun ApplicationListItemBinding.updateAppInfo(searchApp: FusedApp) {
        appTitle.text = searchApp.name
        appInfoFetchViewModel.getAuthorName(searchApp).observe(lifecycleOwner!!) {
            appAuthor.text = it
        }
    }

    private fun ApplicationListItemBinding.updateRating(searchApp: FusedApp) {
        if (searchApp.ratings.usageQualityScore != -1.0) {
            appRating.text = searchApp.ratings.usageQualityScore.toString()
            appRatingBar.rating = searchApp.ratings.usageQualityScore.toFloat()
        }
    }

    private fun ApplicationListItemBinding.updatePrivacyScore(
        searchApp: FusedApp,
        view: View
    ) {
        if (searchApp.ratings.privacyScore != -1.0) {
            appPrivacyScore.text = view.context.getString(
                R.string.privacy_rating_out_of,
                searchApp.ratings.privacyScore.toInt().toString()
            )
        }
    }

    private fun ApplicationListItemBinding.updateSourceTag(searchApp: FusedApp) {
        if (searchApp.source.isEmpty()) {
            sourceTag.visibility = View.INVISIBLE
        } else {
            sourceTag.visibility = View.VISIBLE
        }
        sourceTag.text = searchApp.source

            when (searchApp.origin) {
                Origin.GPLAY -> {
                    appIcon.load(searchApp.icon_image_path) {
                        placeholder(shimmerDrawable)
                    }
                }
                Origin.CLEANAPK -> {
                    appIcon.load(CleanAPKInterface.ASSET_URL + searchApp.icon_image_path) {
                        placeholder(shimmerDrawable)
    }
                }
                else -> Log.wtf(TAG, "${searchApp.package_name} is from an unknown origin")
            }
            removeIsPurchasedObserver(holder)

            if (appInfoFetchViewModel.isAppInBlockedList(searchApp)) {
                setupShowMoreButton()
            } else {
                mainActivityViewModel.verifyUiFilter(searchApp) {
                    setupInstallButton(searchApp, view, holder)
    private fun handleAppItemClick(
        searchApp: FusedApp,
        view: View
    ) {
        val action = when (currentDestinationId) {
            R.id.applicationListFragment -> {
                ApplicationListFragmentDirections.actionApplicationListFragmentToApplicationFragment(
                    searchApp._id,
                    searchApp.package_name,
                    searchApp.origin
                )
            }
            R.id.searchFragment -> {
                SearchFragmentDirections.actionSearchFragmentToApplicationFragment(
                    searchApp._id,
                    searchApp.package_name,
                    searchApp.origin
                )
            }

            showCalculatedPrivacyScoreData(searchApp, view)
            R.id.updatesFragment -> {
                UpdatesFragmentDirections.actionUpdatesFragmentToApplicationFragment(
                    searchApp._id,
                    searchApp.package_name,
                    searchApp.origin
                )
            }
            else -> null
        }
        action?.let { direction -> view.findNavController().navigate(direction) }
    }

    private fun removeIsPurchasedObserver(holder: ViewHolder) {
+21 −13
Original line number Diff line number Diff line
@@ -59,12 +59,21 @@ class CategoriesRVAdapter :
                    )
                holder.itemView.findNavController().navigate(direction)
            }
            loadCategoryIcon(position)
            categoryTitle.text = oldList[position].title
            updateTag(position)
        }
    }

    private fun CategoriesListItemBinding.loadCategoryIcon(position: Int) {
        if (oldList[position].drawable != -1) {
            categoryIcon.load(oldList[position].drawable)
        } else {
            categoryIcon.load(oldList[position].imageUrl)
        }
            categoryTitle.text = oldList[position].title
    }

    private fun CategoriesListItemBinding.updateTag(position: Int) {
        val tag = oldList[position].tag
        if (tag.displayTag.isNotBlank()) {
            categoryTag.visibility = View.VISIBLE
@@ -74,7 +83,6 @@ class CategoriesRVAdapter :
            categoryTag.text = ""
        }
    }
    }

    override fun getItemCount(): Int {
        return oldList.size
Loading