Loading app/src/main/java/foundation/e/apps/AppInfoFetchViewModel.kt +6 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.BlockedAppRepository import foundation.e.apps.api.fdroid.FdroidRepository import foundation.e.apps.api.fdroid.models.FdroidEntity import foundation.e.apps.api.fused.data.FusedApp Loading @@ -27,6 +28,7 @@ class AppInfoFetchViewModel @Inject constructor( private val fdroidRepository: FdroidRepository, private val gPlayAPIRepository: GPlayAPIRepository, private val dataStoreModule: DataStoreModule, private val blockedAppRepository: BlockedAppRepository, private val gson: Gson ) : ViewModel() { Loading Loading @@ -83,4 +85,8 @@ class AppInfoFetchViewModel @Inject constructor( } } } fun isAppInBlockedList(fusedApp: FusedApp): Boolean { return blockedAppRepository.getBlockedAppPackages().contains(fusedApp.package_name) } } app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +6 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.app.Application import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import dagger.hilt.android.HiltAndroidApp import foundation.e.apps.api.BlockedAppRepository import foundation.e.apps.manager.pkg.PkgManagerBR import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.manager.workmanager.InstallWorkManager Loading @@ -46,6 +47,9 @@ class AppLoungeApplication : Application(), Configuration.Provider { @Inject lateinit var dataStoreModule: DataStoreModule @Inject lateinit var blockedAppRepository: BlockedAppRepository override fun onCreate() { super.onCreate() Loading @@ -60,6 +64,8 @@ class AppLoungeApplication : Application(), Configuration.Provider { dataStoreModule.saveTOCStatus(false, "") } } blockedAppRepository.fetchUpdateOfAppWarningList() } override fun getWorkManagerConfiguration() = Loading app/src/main/java/foundation/e/apps/api/BlockedAppRepository.kt 0 → 100644 +21 −0 Original line number Diff line number Diff line package foundation.e.apps.api import foundation.e.apps.api.cleanapk.data.download.DownloadManager import javax.inject.Inject import javax.inject.Singleton @Singleton class BlockedAppRepository @Inject constructor(private val downloadManager: DownloadManager) { companion object { const val APP_WARNING_LIST_FILE_URL = "https://gitlab.e.foundation/e/os/blocklist-app-lounge/-/raw/main/app-lounge-warning-list.json?inline=false" } fun getBlockedAppPackages(): List<String> { return listOf() } fun fetchUpdateOfAppWarningList() { downloadManager.downloadFile(APP_WARNING_LIST_FILE_URL, "app-lounge-warning-list.json") } } No newline at end of file app/src/main/java/foundation/e/apps/api/cleanapk/data/download/DownloadManager.kt 0 → 100644 +83 −0 Original line number Diff line number Diff line package foundation.e.apps.api.cleanapk.data.download import android.app.DownloadManager import android.net.Uri import android.util.Log import foundation.e.apps.manager.workmanager.InstallAppWorker import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach import java.io.File import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @Singleton class DownloadManager @Inject constructor( private val downloadManager: DownloadManager, @Named("cacheDir") private val cacheDir: String, private val downloadManagerQuery: DownloadManager.Query, ) { private var isDownloading = false fun downloadFile(url: String, fileName: String): Long { val directoryFile = File(cacheDir) val downloadFile = File("$cacheDir/$fileName") if(!directoryFile.exists()) { directoryFile.mkdirs() } // if(downloadFile.exists()) { // downloadFile.delete() // } val request = DownloadManager.Request(Uri.parse(url)) .setTitle("Downloading...") .setDestinationUri(Uri.fromFile(downloadFile)) val downloadId = downloadManager.enqueue(request) isDownloading = true tickerFlow(1.seconds).onEach { checkDownloadProgress(downloadId) } return downloadId } private fun checkDownloadProgress(downloadId: Long, fileName: String = "") { downloadManager.query(downloadManagerQuery.setFilterById(downloadId)) .use { cursor -> if (cursor.moveToFirst()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID)) val status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)) val totalSizeBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) val bytesDownloadedSoFar = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) Log.d( "DownloadManager", "checkDownloadProcess: $fileName=> $bytesDownloadedSoFar/$totalSizeBytes $status" ) if (status == DownloadManager.STATUS_FAILED) { Log.d( "DownloadManager", "Download Failed: $fileName=> $bytesDownloadedSoFar/$totalSizeBytes $status" ) } else if(status == DownloadManager.STATUS_SUCCESSFUL) { Log.d( "DownloadManager", "Download Successful: $fileName=> $bytesDownloadedSoFar/$totalSizeBytes $status" ) } } } } private fun tickerFlow(period: Duration, initialDelay: Duration = Duration.ZERO) = flow { delay(initialDelay) while (isDownloading) { emit(Unit) delay(period) } } } No newline at end of file app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +35 −9 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ import android.os.Bundle import android.text.Html import android.text.format.Formatter import android.util.Log import android.view.Gravity import android.view.View import android.widget.FrameLayout import android.widget.ImageView import android.widget.RelativeLayout import androidx.core.content.ContextCompat Loading @@ -44,11 +46,7 @@ import com.google.android.material.button.MaterialButton import com.google.android.material.snackbar.Snackbar import com.google.android.material.textview.MaterialTextView import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.AppInfoFetchViewModel import foundation.e.apps.MainActivity import foundation.e.apps.MainActivityViewModel import foundation.e.apps.PrivacyInfoViewModel import foundation.e.apps.R import foundation.e.apps.* import foundation.e.apps.api.cleanapk.CleanAPKInterface import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.application.model.ApplicationScreenshotsRVAdapter Loading @@ -65,6 +63,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint class ApplicationFragment : Fragment(R.layout.fragment_application) { Loading Loading @@ -256,6 +255,10 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } } if (appInfoFetchViewModel.isAppInBlockedList(it)) { binding.snackbarLayout.visibility = View.VISIBLE } observeDownloadStatus(view) fetchAppTracker(it) } Loading Loading @@ -414,6 +417,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { text = when { mainActivityViewModel.checkUnsupportedApplication(fusedApp) -> getString(R.string.not_available) appInfoFetchViewModel.isAppInBlockedList(fusedApp) -> getString(R.string.force_installation) fusedApp.isFree -> getString(R.string.install) else -> fusedApp.price } Loading @@ -423,15 +427,19 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } applicationIcon?.let { if (fusedApp.isFree) { mainActivityViewModel.getApplication(fusedApp, it) installApplication(fusedApp, it) } else { if (!mainActivityViewModel.shouldShowPaidAppsSnackBar(fusedApp)) { ApplicationDialogFragment( title = getString(R.string.dialog_title_paid_app, fusedApp.name), message = getString(R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price), message = getString( R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price ), positiveButtonText = getString(R.string.dialog_confirm), positiveButtonAction = { mainActivityViewModel.getApplication(fusedApp, it) installApplication(fusedApp, it) }, cancelButtonText = getString(R.string.dialog_cancel), ).show(childFragmentManager, "ApplicationFragment") Loading @@ -444,6 +452,24 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { appSize.visibility = View.VISIBLE } private fun installApplication( fusedApp: FusedApp, it: ImageView ) { if (appInfoFetchViewModel.isAppInBlockedList(fusedApp)) { ApplicationDialogFragment( title = getString(R.string.this_app_may_not_work_properly), message = getString(R.string.may_not_work_warning_message), positiveButtonText = getString(R.string.ok), positiveButtonAction = { mainActivityViewModel.getApplication(fusedApp, it) } ) } else { mainActivityViewModel.getApplication(fusedApp, it) } } private fun handleUpdatable( installButton: MaterialButton, view: View, Loading Loading
app/src/main/java/foundation/e/apps/AppInfoFetchViewModel.kt +6 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.api.BlockedAppRepository import foundation.e.apps.api.fdroid.FdroidRepository import foundation.e.apps.api.fdroid.models.FdroidEntity import foundation.e.apps.api.fused.data.FusedApp Loading @@ -27,6 +28,7 @@ class AppInfoFetchViewModel @Inject constructor( private val fdroidRepository: FdroidRepository, private val gPlayAPIRepository: GPlayAPIRepository, private val dataStoreModule: DataStoreModule, private val blockedAppRepository: BlockedAppRepository, private val gson: Gson ) : ViewModel() { Loading Loading @@ -83,4 +85,8 @@ class AppInfoFetchViewModel @Inject constructor( } } } fun isAppInBlockedList(fusedApp: FusedApp): Boolean { return blockedAppRepository.getBlockedAppPackages().contains(fusedApp.package_name) } }
app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +6 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.app.Application import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import dagger.hilt.android.HiltAndroidApp import foundation.e.apps.api.BlockedAppRepository import foundation.e.apps.manager.pkg.PkgManagerBR import foundation.e.apps.manager.pkg.PkgManagerModule import foundation.e.apps.manager.workmanager.InstallWorkManager Loading @@ -46,6 +47,9 @@ class AppLoungeApplication : Application(), Configuration.Provider { @Inject lateinit var dataStoreModule: DataStoreModule @Inject lateinit var blockedAppRepository: BlockedAppRepository override fun onCreate() { super.onCreate() Loading @@ -60,6 +64,8 @@ class AppLoungeApplication : Application(), Configuration.Provider { dataStoreModule.saveTOCStatus(false, "") } } blockedAppRepository.fetchUpdateOfAppWarningList() } override fun getWorkManagerConfiguration() = Loading
app/src/main/java/foundation/e/apps/api/BlockedAppRepository.kt 0 → 100644 +21 −0 Original line number Diff line number Diff line package foundation.e.apps.api import foundation.e.apps.api.cleanapk.data.download.DownloadManager import javax.inject.Inject import javax.inject.Singleton @Singleton class BlockedAppRepository @Inject constructor(private val downloadManager: DownloadManager) { companion object { const val APP_WARNING_LIST_FILE_URL = "https://gitlab.e.foundation/e/os/blocklist-app-lounge/-/raw/main/app-lounge-warning-list.json?inline=false" } fun getBlockedAppPackages(): List<String> { return listOf() } fun fetchUpdateOfAppWarningList() { downloadManager.downloadFile(APP_WARNING_LIST_FILE_URL, "app-lounge-warning-list.json") } } No newline at end of file
app/src/main/java/foundation/e/apps/api/cleanapk/data/download/DownloadManager.kt 0 → 100644 +83 −0 Original line number Diff line number Diff line package foundation.e.apps.api.cleanapk.data.download import android.app.DownloadManager import android.net.Uri import android.util.Log import foundation.e.apps.manager.workmanager.InstallAppWorker import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach import java.io.File import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @Singleton class DownloadManager @Inject constructor( private val downloadManager: DownloadManager, @Named("cacheDir") private val cacheDir: String, private val downloadManagerQuery: DownloadManager.Query, ) { private var isDownloading = false fun downloadFile(url: String, fileName: String): Long { val directoryFile = File(cacheDir) val downloadFile = File("$cacheDir/$fileName") if(!directoryFile.exists()) { directoryFile.mkdirs() } // if(downloadFile.exists()) { // downloadFile.delete() // } val request = DownloadManager.Request(Uri.parse(url)) .setTitle("Downloading...") .setDestinationUri(Uri.fromFile(downloadFile)) val downloadId = downloadManager.enqueue(request) isDownloading = true tickerFlow(1.seconds).onEach { checkDownloadProgress(downloadId) } return downloadId } private fun checkDownloadProgress(downloadId: Long, fileName: String = "") { downloadManager.query(downloadManagerQuery.setFilterById(downloadId)) .use { cursor -> if (cursor.moveToFirst()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID)) val status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)) val totalSizeBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) val bytesDownloadedSoFar = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) Log.d( "DownloadManager", "checkDownloadProcess: $fileName=> $bytesDownloadedSoFar/$totalSizeBytes $status" ) if (status == DownloadManager.STATUS_FAILED) { Log.d( "DownloadManager", "Download Failed: $fileName=> $bytesDownloadedSoFar/$totalSizeBytes $status" ) } else if(status == DownloadManager.STATUS_SUCCESSFUL) { Log.d( "DownloadManager", "Download Successful: $fileName=> $bytesDownloadedSoFar/$totalSizeBytes $status" ) } } } } private fun tickerFlow(period: Duration, initialDelay: Duration = Duration.ZERO) = flow { delay(initialDelay) while (isDownloading) { emit(Unit) delay(period) } } } No newline at end of file
app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +35 −9 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ import android.os.Bundle import android.text.Html import android.text.format.Formatter import android.util.Log import android.view.Gravity import android.view.View import android.widget.FrameLayout import android.widget.ImageView import android.widget.RelativeLayout import androidx.core.content.ContextCompat Loading @@ -44,11 +46,7 @@ import com.google.android.material.button.MaterialButton import com.google.android.material.snackbar.Snackbar import com.google.android.material.textview.MaterialTextView import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.AppInfoFetchViewModel import foundation.e.apps.MainActivity import foundation.e.apps.MainActivityViewModel import foundation.e.apps.PrivacyInfoViewModel import foundation.e.apps.R import foundation.e.apps.* import foundation.e.apps.api.cleanapk.CleanAPKInterface import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.application.model.ApplicationScreenshotsRVAdapter Loading @@ -65,6 +63,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint class ApplicationFragment : Fragment(R.layout.fragment_application) { Loading Loading @@ -256,6 +255,10 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } } if (appInfoFetchViewModel.isAppInBlockedList(it)) { binding.snackbarLayout.visibility = View.VISIBLE } observeDownloadStatus(view) fetchAppTracker(it) } Loading Loading @@ -414,6 +417,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { text = when { mainActivityViewModel.checkUnsupportedApplication(fusedApp) -> getString(R.string.not_available) appInfoFetchViewModel.isAppInBlockedList(fusedApp) -> getString(R.string.force_installation) fusedApp.isFree -> getString(R.string.install) else -> fusedApp.price } Loading @@ -423,15 +427,19 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } applicationIcon?.let { if (fusedApp.isFree) { mainActivityViewModel.getApplication(fusedApp, it) installApplication(fusedApp, it) } else { if (!mainActivityViewModel.shouldShowPaidAppsSnackBar(fusedApp)) { ApplicationDialogFragment( title = getString(R.string.dialog_title_paid_app, fusedApp.name), message = getString(R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price), message = getString( R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price ), positiveButtonText = getString(R.string.dialog_confirm), positiveButtonAction = { mainActivityViewModel.getApplication(fusedApp, it) installApplication(fusedApp, it) }, cancelButtonText = getString(R.string.dialog_cancel), ).show(childFragmentManager, "ApplicationFragment") Loading @@ -444,6 +452,24 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { appSize.visibility = View.VISIBLE } private fun installApplication( fusedApp: FusedApp, it: ImageView ) { if (appInfoFetchViewModel.isAppInBlockedList(fusedApp)) { ApplicationDialogFragment( title = getString(R.string.this_app_may_not_work_properly), message = getString(R.string.may_not_work_warning_message), positiveButtonText = getString(R.string.ok), positiveButtonAction = { mainActivityViewModel.getApplication(fusedApp, it) } ) } else { mainActivityViewModel.getApplication(fusedApp, it) } } private fun handleUpdatable( installButton: MaterialButton, view: View, Loading