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

Commit 584382cc authored by Hasib Prince's avatar Hasib Prince
Browse files

Conflict resolved: merging main -> 5537-update_all_button

parents fdf465c6 7f70f56e
Loading
Loading
Loading
Loading
Loading
+82 −39
Original line number Diff line number Diff line
@@ -9,19 +9,61 @@ App Lounge use the _Packaging by Features_ approach for packaging the code. A re
```
.
├── api
│   ├── cleanapk
│   ├── fused
│   └── gplay
│   ├── cleanapk
│   │   ├── blockedApps
│   │   └── data
│   │       ├── app
│   │       ├── categories
│   │       ├── download
│   │       ├── home
│   │       └── search
│   ├── database
│   ├── ecloud
│   │   └── modules
│   ├── exodus
│   │   ├── models
│   │   └── repositories
│   ├── fdroid
│   │   └── models
│   ├── fused
│   │   ├── data
│   │   └── utils
│   └── gplay
│       ├── token
│       └── utils
├── application
│   ├── model
│   └── subFrags
├── applicationlist
│   └── model
├── categories
│   └── model
├── di
├── home
│   └── model
├── manager
│   ├── database
│   │   └── fusedDownload
│   ├── download
│   │   └── data
│   ├── fused
│   ├── notification
│   ├── pkg
│   └── workmanager
├── purchase
├── receiver
├── search
├── settings
├── setup
│   ├── signin
│   │   └── google
│   └── tos
├── updates
│   └── manager
└── utils
    ├── enums
    └── modules

```

## API
@@ -30,6 +72,7 @@ App Lounge use the following APIs to offer applications:

- [GPlayApi](https://gitlab.com/AuroraOSS/gplayapi) from Aurora OSS
- [CleanAPK API](https://info.cleanapk.org/) from CleanAPK
- [Exodus API](https://github.com/Exodus-Privacy/exodus/blob/v1/doc/api.md) from Exodus-Privacy

## Development

+3 −0
Original line number Diff line number Diff line
@@ -125,6 +125,9 @@ dependencies {
//    implementation "com.squareup.moshi:moshi-adapters:1.5.0"
    implementation "com.squareup.okhttp3:okhttp:4.9.2"

    // JSON Converter
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'

    // YAML factory
    implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.2"

+7 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import foundation.e.apps.setup.signin.SignInViewModel
import foundation.e.apps.updates.UpdatesNotifier
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.User
import foundation.e.apps.utils.parentFragment.TimeoutFragment
import foundation.e.apps.utils.modules.CommonUtilsModule
import kotlinx.coroutines.launch
import java.io.File
@@ -140,6 +141,12 @@ class MainActivity : AppCompatActivity() {
                        generateAuthDataBasedOnUserType(user)
                    } else {
                        Log.d(TAG, "Timeout validating auth data!")
                        val lastFragment = navHostFragment.childFragmentManager.fragments[0]
                        if (lastFragment is TimeoutFragment) {
                            Log.d(TAG, "Displaying timeout from MainActivity on fragment: "
                                    + lastFragment::class.java.name)
                            lastFragment.onTimeout()
                        }
                    }
                }
            } else {
+17 −108
Original line number Diff line number Diff line
@@ -18,15 +18,12 @@

package foundation.e.apps

import android.app.Activity
import android.content.Context
import android.content.DialogInterface
import android.graphics.Bitmap
import android.os.Build
import android.os.SystemClock
import android.util.Base64
import android.util.Log
import android.view.KeyEvent
import android.widget.ImageView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
@@ -49,7 +46,6 @@ import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.fused.FusedManagerRepository
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.settings.SettingsFragment
import foundation.e.apps.utils.enums.Origin
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.Type
@@ -94,108 +90,6 @@ class MainActivityViewModel @Inject constructor(
     */
    var firstAuthDataFetchTime = 0L

    /*
     * Alert dialog to show to user if App Lounge times out.
     *
     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404
     */
    private lateinit var timeoutAlertDialog: AlertDialog

    /**
     * Display timeout alert dialog.
     *
     * @param activity Activity class. Basically the MainActivity.
     * @param positiveButtonBlock Code block when "Retry" is pressed.
     * @param openSettings Code block when "Open Settings" button is pressed.
     * This should open the [SettingsFragment] fragment.
     * @param applicationTypeFromPreferences Application type string, can be one of
     * [FusedAPIImpl.APP_TYPE_ANY], [FusedAPIImpl.APP_TYPE_OPEN], [FusedAPIImpl.APP_TYPE_PWA]
     */
    fun displayTimeoutAlertDialog(
        activity: Activity,
        positiveButtonBlock: () -> Unit,
        openSettings: () -> Unit,
        applicationTypeFromPreferences: String,
    ) {
        if (!this::timeoutAlertDialog.isInitialized) {
            timeoutAlertDialog = AlertDialog.Builder(activity).apply {
                setTitle(R.string.timeout_title)
                /*
                 * Prevent dismissing the dialog from pressing outside as it will only
                 * show a blank screen below the dialog.
                 */
                setCancelable(false)
                /*
                 * If user presses back button to close the dialog without selecting anything,
                 * close App Lounge.
                 */
                setOnKeyListener { dialog, keyCode, _ ->
                    if (keyCode == KeyEvent.KEYCODE_BACK) {
                        dialog.dismiss()
                        activity.finish()
                    }
                    true
                }
            }.create()
        }

        timeoutAlertDialog.apply {
            /*
             * Set retry button.
             */
            setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.retry)) { _, _ ->
                positiveButtonBlock()
            }
            /*
             * Set message based on apps from GPlay of cleanapk.
             */
            setMessage(
                activity.getString(
                    when (applicationTypeFromPreferences) {
                        FusedAPIImpl.APP_TYPE_ANY -> R.string.timeout_desc_gplay
                        else -> R.string.timeout_desc_cleanapk
                    }
                )
            )
            /*
             * Show "Open Setting" only for GPlay apps.
             */
            if (applicationTypeFromPreferences == FusedAPIImpl.APP_TYPE_ANY) {
                setButton(
                    DialogInterface.BUTTON_NEUTRAL,
                    activity.getString(R.string.open_settings)
                ) { _, _ ->
                    openSettings()
                }
            }
        }

        timeoutAlertDialog.show()
    }

    /**
     * Returns true if [timeoutAlertDialog] is displaying.
     * Returs false if it is not initialised.
     */
    fun isTimeoutDialogDisplayed(): Boolean {
        return if (this::timeoutAlertDialog.isInitialized) {
            timeoutAlertDialog.isShowing
        } else false
    }

    /**
     * Dismisses the [timeoutAlertDialog] if it is being displayed.
     * Does nothing if it is not being displayed.
     * Caller need not check if the dialog is being displayed.
     */
    fun dismissTimeoutDialog() {
        if (isTimeoutDialogDisplayed()) {
            try {
                timeoutAlertDialog.dismiss()
            } catch (_: Exception) {}
        }
    }

    // Downloads
    val downloadList = fusedManagerRepository.getDownloadLiveList()
    var installInProgress = false
@@ -213,8 +107,10 @@ class MainActivityViewModel @Inject constructor(
    }

    fun setFirstTokenFetchTime() {
        if (firstAuthDataFetchTime == 0L) {
            firstAuthDataFetchTime = SystemClock.uptimeMillis()
        }
    }

    fun isTimeEligibleForTokenRefresh(): Boolean {
        return (SystemClock.uptimeMillis() - firstAuthDataFetchTime) <= timeoutDurationInMillis
@@ -228,6 +124,7 @@ class MainActivityViewModel @Inject constructor(
     * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404
     */
    fun retryFetchingTokenAfterTimeout() {
        firstAuthDataFetchTime = 0
        setFirstTokenFetchTime()
        authValidity.postValue(false)
    }
@@ -248,7 +145,19 @@ class MainActivityViewModel @Inject constructor(
        if (!authRequestRunning) {
            authRequestRunning = true
            viewModelScope.launch {
                fusedAPIRepository.fetchAuthData()
                /*
                 * If getting auth data failed, try getting again.
                 * Sending false in authValidity, triggers observer in MainActivity,
                 * causing it to destroy credentials and try to regenerate auth data.
                 *
                 * Issue:
                 * https://gitlab.e.foundation/e/backlog/-/issues/5413
                 * https://gitlab.e.foundation/e/backlog/-/issues/5404
                 */
                if (!fusedAPIRepository.fetchAuthData()) {
                    authRequestRunning = false
                    authValidity.postValue(false)
                }
            }
        }
    }
+78 −0
Original line number Diff line number Diff line
package foundation.e.apps.api

import foundation.e.apps.utils.enums.ResultStatus

/**
 * Currently defunct, not being used anywhere.
 * Prototype to merge API request and also get rid of Pair, Triple for timeout related cases.
 */
open class JobResult<T> private constructor(val status: ResultStatus) {

    /*
     * Classes for returning multiple data from a function along with a status
     * in the form of ResultStatus.
     * Use the static overloaded create methods (in companion object) to for easy creation.
     *
     * If needed to just pass a single data element with status for API requests,
     * see the static methods success(), error(), loading() (in companion object).
     */
    class of1<A> (val data1: A, status: ResultStatus): JobResult<A>(status)
    class of2<A,B> (val data1: A, val data2: B, status: ResultStatus): JobResult<A>(status)
    class of3<A,B,C> (val data1: A, val data2: B, val data3: C, status: ResultStatus): JobResult<A>(status)

    var message = ""

    /*
     * This is the primary data, mainly for API requests which might send null data.
     * Other data (type B, C ...) are secondary/optional data.
     *
     * For non-null return type, directly use of1, of2, of3 ... classes
     * and directly access data1, data2, data3 ...
     */
    val data: T? get() = when(this) {
        is of1 -> this.data1
        is of2<T, *> -> this.data1
        is of3<T, *, *> -> this.data1
        else -> null
    }

    fun isSuccess(): Boolean {
        return status == ResultStatus.OK
    }

    companion object {
        fun <A> create(data1: A, status: ResultStatus, message: String? = null): of1<A> {
            return of1(data1, status).apply {
                message?.let { this.message = message }
            }
        }
        fun <A,B> create(data1: A, data2: B, status: ResultStatus, message: String? = null): of2<A,B> {
            return of2(data1, data2, status).apply {
                message?.let { this.message = message }
            }
        }
        fun <A,B,C> create(data1: A, data2: B, data3: C, status: ResultStatus, message: String? = null): of3<A,B,C> {
            return of3(data1, data2, data3, status).apply {
                message?.let { this.message = message }
            }
        }

        /*
         * Methods for API
         */
        fun <T> success(data: T): JobResult<T> {
            return of1(data, ResultStatus.OK)
        }
        fun <T> error(message: String, data: T? = null): JobResult<T> {
            val result = if (data == null) JobResult(ResultStatus.UNKNOWN)
            else of1<T>(data, ResultStatus.UNKNOWN)
            return result.apply {
                this.message = message
            }
        }
        /*fun <T> loading(data: T?): JobResult<T> {
            return if (data == null) JobResult(ResultStatus.LOADING)
            else JobResult.of1(data, ResultStatus.LOADING)
        }*/
    }
}
 No newline at end of file
Loading