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

Commit 4d1bddeb authored by Nishant Dande's avatar Nishant Dande
Browse files

Merge branch 'main' into epic103-improve_app_lounge

# Conflicts:
#	app/src/main/java/foundation/e/apps/MainActivity.kt
parents 9e9837f8 d1a46c82
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
image: registry.gitlab.e.foundation/e/os/docker-android-apps-cicd:55-workshop-auto-release
image: registry.gitlab.e.foundation/e/os/docker-android-apps-cicd:master

variables:
  SENTRY_DSN: $SENTRY_DSN
@@ -22,8 +22,10 @@ buildDebug:
      - app/build/outputs/apk/debug/

test:
  allow_failure: true
  stage: debug
  stage: release
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      when: always
  script:
    - ./gradlew test -PtestAccountName="$testAccountName" -PtestAccountPwd="$testAccountPwd" -PtestServerUrl="$testServerUrl"
  artifacts:
+8 −6
Original line number Diff line number Diff line
@@ -10,8 +10,8 @@ plugins {
}

def versionMajor = 2
def versionMinor = 5
def versionPatch = 5
def versionMinor = 6
def versionPatch = 0

def getGitHash = { ->
    def stdOut = new ByteArrayOutputStream()
@@ -119,17 +119,19 @@ android {
    }
    buildFeatures {
        viewBinding true
        aidl true
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = '11'
        jvmTarget = '17'
    }
    lint {
        lintConfig file('lint.xml')
    }
    namespace 'foundation.e.apps'
    kotlin.sourceSets.all {
        languageSettings.optIn("kotlin.RequiresOptIn")
    }
@@ -159,7 +161,7 @@ dependencies {
    // TODO: Add splitinstall-lib to a repo https://gitlab.e.foundation/e/os/backlog/-/issues/628
    api files('libs/splitinstall-lib.jar')

    implementation 'foundation.e.lib:telemetry:0.0.9-alpha'
    implementation 'foundation.e.lib:telemetry:0.0.11-alpha'
    implementation 'foundation.e:gplayapi:3.0.1-2'
    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
+2 −3
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="foundation.e.apps">
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
@@ -46,7 +45,7 @@
        android:usesCleartextTraffic="true">

        <activity
            android:name=".ui.MainActivity"
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
+4 −1
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@
package foundation.e.apps

import android.app.Application
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import androidx.work.ExistingPeriodicWorkPolicy
@@ -56,13 +58,14 @@ class AppLoungeApplication : Application(), Configuration.Provider {
    @Inject
    lateinit var preferenceManagerModule: PreferenceManagerModule

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    override fun onCreate() {
        super.onCreate()

        InstallWorkManager.context = this
        // Register broadcast receiver for package manager
        val pkgManagerBR = object : PkgManagerBR() {}
        registerReceiver(pkgManagerBR, pkgManagerModule.getFilter())
        registerReceiver(pkgManagerBR, pkgManagerModule.getFilter(), RECEIVER_EXPORTED)

        val currentVersion = dataStoreModule.getTOSVersion()
        if (!currentVersion.contentEquals(TOS_VERSION)) {
+52 −81
Original line number Diff line number Diff line
@@ -16,14 +16,10 @@
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package foundation.e.apps.ui
package foundation.e.apps

import android.app.usage.StorageStatsManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.StatFs
import android.os.storage.StorageManager
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
@@ -42,18 +38,17 @@ import com.aurora.gplayapi.exceptions.ApiException
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import foundation.e.apps.R
import foundation.e.apps.data.enums.Status
import foundation.e.apps.data.fusedDownload.models.FusedDownload
import foundation.e.apps.data.login.AuthObject
import foundation.e.apps.data.login.exceptions.GPlayValidationException
import foundation.e.apps.databinding.ActivityMainBinding
import foundation.e.apps.install.updates.UpdatesNotifier
import foundation.e.apps.install.workmanager.InstallWorkManager
import foundation.e.apps.presentation.login.LoginViewModel
import foundation.e.apps.ui.MainActivityViewModel
import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment
import foundation.e.apps.ui.purchase.AppPurchaseFragmentDirections
import foundation.e.apps.ui.settings.SettingsFragment
import foundation.e.apps.ui.setup.signin.SignInViewModel
import foundation.e.apps.utils.SystemInfoProvider
import foundation.e.apps.utils.eventBus.AppEvent
import foundation.e.apps.utils.eventBus.EventBus
@@ -61,12 +56,10 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import java.util.UUID

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private lateinit var signInViewModel: SignInViewModel
    private lateinit var loginViewModel: LoginViewModel
    private lateinit var binding: ActivityMainBinding
    private val TAG = MainActivity::class.java.simpleName
@@ -88,6 +81,7 @@ class MainActivity : AppCompatActivity() {
        var hasInternet = true

        viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]
        signInViewModel = ViewModelProvider(this)[SignInViewModel::class.java]
        loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]

        // navOptions and activityNavController for TOS and SignIn Fragments
@@ -170,19 +164,8 @@ class MainActivity : AppCompatActivity() {
            viewModel.createNotificationChannels()
        }

        // Observe and handle downloads
        viewModel.downloadList.observe(this) { list ->
            list.forEach {
                if (it.status == Status.QUEUED) {
                    handleFusedDownloadQueued(it, viewModel)
                }
            }
        }

        viewModel.purchaseAppLiveData.observe(this) {
            val action =
                AppPurchaseFragmentDirections.actionGlobalAppPurchaseFragment(it.packageName)
            findNavController(R.id.fragment).navigate(action)
            goToAppPurchaseFragment(it)
        }

        viewModel.errorMessage.observe(this) {
@@ -227,10 +210,54 @@ class MainActivity : AppCompatActivity() {
                launch {
                    observeSignatureMissMatchError()
                }

                launch {
                    observerErrorEvent()
                }

                launch {
                    observeAppPurchaseFragment()
                }

                launch {
                    observeNoInternetEvent()
                }
            }
        }
    }

    private suspend fun observeNoInternetEvent() {
        EventBus.events.filter { appEvent ->
            appEvent is AppEvent.NoInternetEvent
        }.collectLatest {
            if (!(it.data as Boolean)) {
                showNoInternet()
            }
        }
    }

    private suspend fun observeAppPurchaseFragment() {
        EventBus.events.filter { appEvent ->
            appEvent is AppEvent.AppPurchaseEvent
        }.collectLatest {
            goToAppPurchaseFragment(it.data as FusedDownload)
        }
    }

    private fun goToAppPurchaseFragment(it: FusedDownload) {
        val action =
            AppPurchaseFragmentDirections.actionGlobalAppPurchaseFragment(it.packageName)
        findNavController(R.id.fragment).navigate(action)
    }

    private suspend fun observerErrorEvent() {
        EventBus.events.filter { appEvent ->
            appEvent is AppEvent.ErrorMessageEvent
        }.collectLatest {
            showSnackbarMessage(getString(it.data as Int))
        }
    }

    private suspend fun observeSignatureMissMatchError() {
        EventBus.events.filter { appEvent ->
            appEvent is AppEvent.SignatureMissMatchError
@@ -281,35 +308,13 @@ class MainActivity : AppCompatActivity() {
        }
    }

    private fun handleFusedDownloadQueued(
        it: FusedDownload,
        viewModel: MainActivityViewModel
    ) {
        lifecycleScope.launch {
            if (!isStorageAvailable(it)) {
                showSnackbarMessage(getString(R.string.not_enough_storage))
                viewModel.updateUnAvailable(it)
                return@launch
            }
            if (viewModel.internetConnection.value == false) {
                showNoInternet()
                viewModel.updateUnAvailable(it)
                return@launch
            }
            viewModel.updateAwaiting(it)
            InstallWorkManager.enqueueWork(it)
            Timber.d("===> onCreate: AWAITING ${it.name}")
        }
    }

    private fun startInstallationOfPurchasedApp(
        viewModel: MainActivityViewModel,
        it: String
        packageName: String
    ) {
        lifecycleScope.launch {
            val fusedDownload = viewModel.updateAwaitingForPurchasedApp(it)
            val fusedDownload = viewModel.updateAwaitingForPurchasedApp(packageName)
            if (fusedDownload != null) {
                InstallWorkManager.enqueueWork(fusedDownload)
                ApplicationDialogFragment(
                    title = getString(R.string.purchase_complete),
                    message = getString(R.string.download_automatically_message),
@@ -333,38 +338,4 @@ class MainActivity : AppCompatActivity() {
        binding.noInternet.visibility = View.VISIBLE
        binding.fragment.visibility = View.GONE
    }

    // TODO: move storage availability code to FileManager Class
    private fun isStorageAvailable(fusedDownload: FusedDownload): Boolean {
        val availableSpace = calculateAvailableDiskSpace()
        return availableSpace > fusedDownload.appSize + (500 * (1000 * 1000))
    }

    private fun calculateAvailableDiskSpace(): Long {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val storageManager = getSystemService(STORAGE_SERVICE) as StorageManager
            val statsManager = getSystemService(STORAGE_STATS_SERVICE) as StorageStatsManager
            val uuid = storageManager.primaryStorageVolume.uuid
            try {
                if (uuid != null) {
                    statsManager.getFreeBytes(UUID.fromString(uuid))
                } else {
                    statsManager.getFreeBytes(StorageManager.UUID_DEFAULT)
                }
            } catch (e: Exception) {
                Timber.e("calculateAvailableDiskSpace: ${e.stackTraceToString()}")
                getAvailableInternalMemorySize()
            }
        } else {
            getAvailableInternalMemorySize()
        }
    }

    private fun getAvailableInternalMemorySize(): Long {
        val path: File = Environment.getDataDirectory()
        val stat = StatFs(path.path)
        val blockSize = stat.blockSizeLong
        val availableBlocks = stat.availableBlocksLong
        return availableBlocks * blockSize
    }
}
Loading