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

Commit e18cc13b authored by Hasib Prince's avatar Hasib Prince
Browse files

Merge branch '5141_memoryleak_download_progress' into main

parents 50228879 b4e3c6a2
Loading
Loading
Loading
Loading
Loading
+39 −2
Original line number Diff line number Diff line
@@ -19,15 +19,18 @@
package foundation.e.apps.applicationlist

import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.AppProgressViewModel
import foundation.e.apps.MainActivityViewModel
@@ -37,8 +40,12 @@ import foundation.e.apps.api.fused.FusedAPIInterface
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.applicationlist.model.ApplicationListRVAdapter
import foundation.e.apps.databinding.FragmentApplicationListBinding
import foundation.e.apps.home.model.HomeChildRVAdapter
import foundation.e.apps.manager.download.data.DownloadProgress
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.User
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
@@ -107,8 +114,7 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu
                    it,
                    pkgManagerModule,
                    User.valueOf(mainActivityViewModel.userType.value ?: User.UNAVAILABLE.name),
                    viewLifecycleOwner,
                    appProgressViewModel
                    viewLifecycleOwner
                )
            }

@@ -117,6 +123,10 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu
            layoutManager = LinearLayoutManager(view?.context)
        }

        appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) {
            updateProgressOfDownloadingItems(recyclerView, it)
        }

        viewModel.appListLiveData.observe(viewLifecycleOwner) {
            listAdapter?.setData(it)
            if (!isDownloadObserverAdded) {
@@ -141,6 +151,33 @@ class ApplicationListFragment : Fragment(R.layout.fragment_application_list), Fu
        }
    }

    private fun updateProgressOfDownloadingItems(
        recyclerView: RecyclerView,
        it: DownloadProgress
    ) {
        val adapter = recyclerView.adapter as ApplicationListRVAdapter
        lifecycleScope.launch {
            adapter.currentList.forEach { fusedApp ->
                if (fusedApp.status == Status.DOWNLOADING) {
                    val progress = appProgressViewModel.calculateProgress(fusedApp, it)
                    val downloadProgress =
                        ((progress.second / progress.first.toDouble()) * 100).toInt()
                    Log.d(
                        "HomeParentAdapter",
                        "download progress of ===> ${fusedApp.name} : $downloadProgress"
                    )
                    val viewHolder = recyclerView.findViewHolderForAdapterPosition(
                        adapter.currentList.indexOf(fusedApp)
                    )
                    viewHolder?.let {
                        (viewHolder as ApplicationListRVAdapter.ViewHolder).binding.installButton.text =
                            "$downloadProgress%"
                    }
                }
            }
        }
    }

    override fun onPause() {
        isDownloadObserverAdded = false
        binding.shimmerLayout.stopShimmer()
+5 −69
Original line number Diff line number Diff line
@@ -62,7 +62,6 @@ class ApplicationListRVAdapter(
    private val pkgManagerModule: PkgManagerModule,
    private val user: User,
    private val lifecycleOwner: LifecycleOwner,
    private val appProgressViewModel: AppProgressViewModel
) : ListAdapter<FusedApp, ApplicationListRVAdapter.ViewHolder>(ApplicationDiffUtil()) {

    private val TAG = ApplicationListRVAdapter::class.java.simpleName
@@ -76,54 +75,7 @@ class ApplicationListRVAdapter(
        .build()

    inner class ViewHolder(val binding: ApplicationListItemBinding) :
        RecyclerView.ViewHolder(binding.root), LifecycleOwner {

        private val lifecycleRegistry = LifecycleRegistry(this)

        init {
            lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
        }

        fun onAppear() {
            lifecycleRegistry.currentState = Lifecycle.State.RESUMED
        }

        fun onDisappear() {
            lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
        }

        override fun getLifecycle(): Lifecycle {
            return lifecycleRegistry
        }
    }

    override fun onViewAttachedToWindow(holder: ViewHolder) {
        super.onViewAttachedToWindow(holder)
        Log.d(TAG, "onViewAttachedToWindow: Appeared: ${holder.absoluteAdapterPosition}")
        holder.onAppear()
    }

    override fun onViewDetachedFromWindow(holder: ViewHolder) {
        holder.onDisappear()
        Log.d(TAG, "onViewAttachedToWindow: Disappeared: ${holder.absoluteAdapterPosition}")
        super.onViewDetachedFromWindow(holder)
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        Log.d(TAG, "onDetachedFromRecyclerView: ")
        for (i in 0..recyclerView.childCount) {
            val view = recyclerView.getChildAt(i)
            if (view != null) {
                val holder =
                    recyclerView.getChildViewHolder(view)
                holder?.let {
                    appProgressViewModel.downloadProgress.removeObservers(holder as LifecycleOwner)
                    (holder as ViewHolder).onDisappear()
                }
            }
        }
        super.onDetachedFromRecyclerView(recyclerView)
    }
        RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
@@ -206,16 +158,16 @@ class ApplicationListRVAdapter(
            }
            when (searchApp.status) {
                Status.INSTALLED -> {
                    handleInstalled(view, searchApp, holder)
                    handleInstalled(view, searchApp)
                }
                Status.UPDATABLE -> {
                    handleUpdatable(view, searchApp)
                }
                Status.UNAVAILABLE -> {
                    handleUnavailable(view, searchApp, holder)
                    handleUnavailable(view, searchApp)
                }
                Status.QUEUED, Status.AWAITING, Status.DOWNLOADING -> {
                    handleDownloading(view, searchApp, holder)
                    handleDownloading(view, searchApp)
                }
                Status.INSTALLING, Status.UNINSTALLING -> {
                    handleInstalling(view, holder)
@@ -224,7 +176,7 @@ class ApplicationListRVAdapter(
                    handleBlocked(view)
                }
                Status.INSTALLATION_ISSUE -> {
                    handleInstallationIssue(view, searchApp, holder)
                    handleInstallationIssue(view, searchApp)
                }
            }

@@ -235,9 +187,7 @@ class ApplicationListRVAdapter(
    private fun ApplicationListItemBinding.handleInstallationIssue(
        view: View,
        searchApp: FusedApp,
        holder: ViewHolder
    ) {
        appProgressViewModel.downloadProgress.removeObservers(holder)
        installButton.apply {
            isEnabled = true
            text = view.context.getString(R.string.retry)
@@ -318,7 +268,6 @@ class ApplicationListRVAdapter(
    }

    private fun ApplicationListItemBinding.handleInstalling(view: View, holder: ViewHolder) {
        appProgressViewModel.downloadProgress.removeObservers(holder)
        installButton.apply {
            isEnabled = false
            setTextColor(context.getColor(R.color.light_grey))
@@ -331,7 +280,6 @@ class ApplicationListRVAdapter(
    private fun ApplicationListItemBinding.handleDownloading(
        view: View,
        searchApp: FusedApp,
        holder: ViewHolder
    ) {
        installButton.apply {
            isEnabled = true
@@ -340,14 +288,6 @@ class ApplicationListRVAdapter(
            backgroundTintList =
                ContextCompat.getColorStateList(view.context, android.R.color.transparent)
            strokeColor = ContextCompat.getColorStateList(view.context, R.color.colorAccent)
            appProgressViewModel.downloadProgress.observe(holder) {
                appProgressViewModel.viewModelScope.launch {
                    val progress = appProgressViewModel.calculateProgress(searchApp, it)
                    if (progress.second > 0 && progress.second <= progress.first) {
                        text = "${((progress.second / progress.first.toDouble()) * 100).toInt()}%"
                    }
                }
            }
            setOnClickListener {
                cancelDownload(searchApp)
            }
@@ -357,7 +297,6 @@ class ApplicationListRVAdapter(
    private fun ApplicationListItemBinding.handleUnavailable(
        view: View,
        searchApp: FusedApp,
        holder: ViewHolder
    ) {
        installButton.apply {
            isEnabled = true
@@ -366,7 +305,6 @@ class ApplicationListRVAdapter(
            backgroundTintList =
                ContextCompat.getColorStateList(view.context, android.R.color.transparent)
            strokeColor = ContextCompat.getColorStateList(view.context, R.color.colorAccent)
            appProgressViewModel.downloadProgress.removeObservers(holder)
            setOnClickListener {
                installApplication(searchApp, appIcon)
            }
@@ -392,7 +330,6 @@ class ApplicationListRVAdapter(
    private fun ApplicationListItemBinding.handleInstalled(
        view: View,
        searchApp: FusedApp,
        holder: ViewHolder
    ) {
        installButton.apply {
            isEnabled = true
@@ -400,7 +337,6 @@ class ApplicationListRVAdapter(
            setTextColor(Color.WHITE)
            backgroundTintList = ContextCompat.getColorStateList(view.context, R.color.colorAccent)
            strokeColor = ContextCompat.getColorStateList(view.context, R.color.colorAccent)
            appProgressViewModel.downloadProgress.removeObservers(holder)
            setOnClickListener {
                context.startActivity(pkgManagerModule.getLaunchIntent(searchApp.package_name))
            }
+52 −1
Original line number Diff line number Diff line
@@ -24,8 +24,10 @@ import android.widget.ImageView
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.AppProgressViewModel
import foundation.e.apps.MainActivityViewModel
@@ -33,9 +35,13 @@ import foundation.e.apps.R
import foundation.e.apps.api.fused.FusedAPIInterface
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.databinding.FragmentHomeBinding
import foundation.e.apps.home.model.HomeChildRVAdapter
import foundation.e.apps.home.model.HomeParentRVAdapter
import foundation.e.apps.manager.download.data.DownloadProgress
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.User
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
@@ -77,7 +83,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface {
            this,
            pkgManagerModule,
            User.valueOf(mainActivityViewModel.userType.value ?: User.UNAVAILABLE.name),
            mainActivityViewModel, viewLifecycleOwner, appProgressViewModel
            mainActivityViewModel, viewLifecycleOwner
        )

        binding.parentRV.apply {
@@ -90,6 +96,51 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface {
            binding.shimmerLayout.visibility = View.GONE
            binding.parentRV.visibility = View.VISIBLE
        }

        appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) {
            updateProgressOfDownloadingAppItemViews(homeParentRVAdapter, it)
        }
    }

    private fun updateProgressOfDownloadingAppItemViews(
        homeParentRVAdapter: HomeParentRVAdapter,
        downloadProgress: DownloadProgress
    ) {
        homeParentRVAdapter.currentList.forEach { fusedHome ->
            val viewHolder = binding.parentRV.findViewHolderForAdapterPosition(
                homeParentRVAdapter.currentList.indexOf(fusedHome)
            )
            viewHolder?.let { parentViewHolder ->
                val childRV =
                    (parentViewHolder as HomeParentRVAdapter.ViewHolder).binding.childRV
                val adapter = childRV.adapter as HomeChildRVAdapter
                findDownloadingItemsToShowProgress(adapter, downloadProgress, childRV)
            }
        }
    }

    private fun findDownloadingItemsToShowProgress(
        adapter: HomeChildRVAdapter,
        downloadProgress: DownloadProgress,
        childRV: RecyclerView
    ) {
        lifecycleScope.launch {
            adapter.currentList.forEach { fusedApp ->
                if (fusedApp.status == Status.DOWNLOADING) {
                    val progress =
                        appProgressViewModel.calculateProgress(fusedApp, downloadProgress)
                    val downloadProgress =
                        ((progress.second / progress.first.toDouble()) * 100).toInt()
                    val childViewHolder = childRV.findViewHolderForAdapterPosition(
                        adapter.currentList.indexOf(fusedApp)
                    )
                    childViewHolder?.let {
                        (childViewHolder as HomeChildRVAdapter.ViewHolder).binding.installButton.text =
                            "$downloadProgress%"
                    }
                }
            }
        }
    }

    override fun onResume() {
+6 −61
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
package foundation.e.apps.home.model

import android.graphics.Color
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
@@ -50,8 +51,7 @@ import kotlinx.coroutines.launch
class HomeChildRVAdapter(
    private val fusedAPIInterface: FusedAPIInterface,
    private val pkgManagerModule: PkgManagerModule,
    private val user: User,
    private val appProgressViewModel: AppProgressViewModel
    private val user: User
) : ListAdapter<FusedApp, HomeChildRVAdapter.ViewHolder>(HomeChildFusedAppDiffUtil()) {

    private val shimmer = Shimmer.ColorHighlightBuilder()
@@ -63,58 +63,17 @@ class HomeChildRVAdapter(
        .build()

    inner class ViewHolder(val binding: HomeChildListItemBinding) :
        RecyclerView.ViewHolder(binding.root), LifecycleOwner {
        private val lifecycleRegistry = LifecycleRegistry(this)

        init {
            lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
        }

        fun onAppear() {
            lifecycleRegistry.currentState = Lifecycle.State.RESUMED
        }

        fun onDisappear() {
            lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
        }

        override fun getLifecycle(): Lifecycle {
            return lifecycleRegistry
        }
    }

    override fun onViewAttachedToWindow(holder: HomeChildRVAdapter.ViewHolder) {
        super.onViewAttachedToWindow(holder)
        holder.onAppear()
    }

    override fun onViewDetachedFromWindow(holder: HomeChildRVAdapter.ViewHolder) {
        holder.onDisappear()
        super.onViewDetachedFromWindow(holder)
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        for (i in 0..recyclerView.childCount) {
            val view = recyclerView.getChildAt(i)
            if (view != null) {
                val holder = recyclerView.getChildViewHolder(view)
                holder?.let {
                    appProgressViewModel.downloadProgress.removeObservers(holder as LifecycleOwner)
                    (holder as HomeChildRVAdapter.ViewHolder).onDisappear()
                }
            }
        }
        super.onDetachedFromRecyclerView(recyclerView)
    }
        RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
        val viewHolder = ViewHolder(
            HomeChildListItemBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
        return viewHolder
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
@@ -141,9 +100,9 @@ class HomeChildRVAdapter(
                )
                holder.itemView.findNavController().navigate(action)
            }

            when (homeApp.status) {
                Status.INSTALLED -> {
                    appProgressViewModel.downloadProgress.removeObservers(holder)
                    installButton.apply {
                        isEnabled = true
                        text = context.getString(R.string.open)
@@ -158,7 +117,6 @@ class HomeChildRVAdapter(
                    }
                }
                Status.UPDATABLE -> {
                    appProgressViewModel.downloadProgress.removeObservers(holder)
                    installButton.apply {
                        text = context.getString(R.string.update)
                        setTextColor(Color.WHITE)
@@ -172,7 +130,6 @@ class HomeChildRVAdapter(
                    }
                }
                Status.UNAVAILABLE -> {
                    appProgressViewModel.downloadProgress.removeObservers(holder)
                    installButton.apply {
                        text = context.getString(R.string.install)
                        setTextColor(context.getColor(R.color.colorAccent))
@@ -198,22 +155,12 @@ class HomeChildRVAdapter(
                        strokeColor =
                            ContextCompat.getColorStateList(view.context, R.color.colorAccent)

                        appProgressViewModel.downloadProgress.observe(holder) {
                            appProgressViewModel.viewModelScope.launch {
                                val progress = appProgressViewModel.calculateProgress(homeApp, it)
                                if (progress.second > 0 && progress.second <= progress.first) {
                                    text = "${((progress.second / progress.first.toDouble()) * 100).toInt()}%"
                                }
                            }
                        }

                        setOnClickListener {
                            cancelDownload(homeApp)
                        }
                    }
                }
                Status.INSTALLING, Status.UNINSTALLING -> {
                    appProgressViewModel.downloadProgress.removeObservers(holder)
                    installButton.apply {
                        isEnabled = false
                        setTextColor(context.getColor(R.color.light_grey))
@@ -226,7 +173,6 @@ class HomeChildRVAdapter(
                    }
                }
                Status.BLOCKED -> {
                    appProgressViewModel.downloadProgress.removeObservers(holder)
                    installButton.setOnClickListener {
                        val errorMsg = when (user) {
                            User.ANONYMOUS,
@@ -239,7 +185,6 @@ class HomeChildRVAdapter(
                    }
                }
                Status.INSTALLATION_ISSUE -> {
                    appProgressViewModel.downloadProgress.removeObservers(holder)
                    installButton.apply {
                        text = view.context.getString(R.string.retry)
                        setTextColor(context.getColor(R.color.colorAccent))
+6 −3
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ package foundation.e.apps.home.model
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@@ -32,15 +33,16 @@ import foundation.e.apps.api.fused.data.FusedHome
import foundation.e.apps.databinding.HomeParentListItemBinding
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.User
import kotlinx.coroutines.launch

class HomeParentRVAdapter(
    private val fusedAPIInterface: FusedAPIInterface,
    private val pkgManagerModule: PkgManagerModule,
    private val user: User,
    private val mainActivityViewModel: MainActivityViewModel,
    private val lifecycleOwner: LifecycleOwner,
    private val appProgressViewModel: AppProgressViewModel
    private val lifecycleOwner: LifecycleOwner
) : ListAdapter<FusedHome, HomeParentRVAdapter.ViewHolder>(FusedHomeDiffUtil()) {

    private val viewPool = RecyclerView.RecycledViewPool()
@@ -56,7 +58,8 @@ class HomeParentRVAdapter(

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val fusedHome = getItem(position)
        val homeChildRVAdapter = HomeChildRVAdapter(fusedAPIInterface, pkgManagerModule, user, appProgressViewModel)
        val homeChildRVAdapter =
            HomeChildRVAdapter(fusedAPIInterface, pkgManagerModule, user)
        homeChildRVAdapter.setData(fusedHome.list)

        holder.binding.titleTV.text = fusedHome.title
Loading