From c13cfdd1639c27b684e4fbcc2ba8d41457a82662 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 19 Jun 2023 12:28:29 +0530 Subject: [PATCH 001/112] Added basic structure for local storage --- modules/.gitignore | 1 + modules/build.gradle | 48 +++++++++ modules/consumer-rules.pro | 0 modules/proguard-rules.pro | 21 ++++ .../app/lounge/ExampleInstrumentedTest.kt | 26 +++++ .../java/app/lounge/PersistentStorageTest.kt | 52 ++++++++++ modules/src/main/AndroidManifest.xml | 4 + .../app/lounge/storage/cache/Persistence.kt | 98 +++++++++++++++++++ .../test/java/app/lounge/ExampleUnitTest.kt | 21 ++++ settings.gradle | 1 + 10 files changed, 272 insertions(+) create mode 100644 modules/.gitignore create mode 100644 modules/build.gradle create mode 100644 modules/consumer-rules.pro create mode 100644 modules/proguard-rules.pro create mode 100644 modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt create mode 100644 modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt create mode 100644 modules/src/main/AndroidManifest.xml create mode 100644 modules/src/main/java/app/lounge/storage/cache/Persistence.kt create mode 100644 modules/src/test/java/app/lounge/ExampleUnitTest.kt diff --git a/modules/.gitignore b/modules/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/modules/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/modules/build.gradle b/modules/build.gradle new file mode 100644 index 000000000..e4d965b9e --- /dev/null +++ b/modules/build.gradle @@ -0,0 +1,48 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +ext { + android_compile_sdk_version = 33 + android_target_sdk_version = 33 + core_version = '1.10.1' +} + +android { + namespace 'app.lounge' + compileSdk android_compile_sdk_version + + defaultConfig { + minSdk 24 + targetSdk android_target_sdk_version + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation "androidx.core:core-ktx:$core_version" + implementation 'androidx.appcompat:appcompat:1.6.1' + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + implementation "org.jetbrains.kotlin:kotlin-reflect:1.8.10" +} \ No newline at end of file diff --git a/modules/consumer-rules.pro b/modules/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/modules/proguard-rules.pro b/modules/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/modules/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt b/modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..c1cfdd165 --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt @@ -0,0 +1,26 @@ +package app.lounge + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 +import app.lounge.storage.cache.PersistedConfiguration + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("app.lounge.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt new file mode 100644 index 000000000..7b41fc947 --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt @@ -0,0 +1,52 @@ +package app.lounge + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import app.lounge.storage.cache.PersistedConfiguration +import app.lounge.storage.cache.PersistenceKey +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.reflect.KClassifier + +@RunWith(AndroidJUnit4::class) +class PersistentStorageTest { + + private lateinit var testConfiguration: PersistedConfiguration + + @Before + fun setupPersistentConfiguration(){ + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + testConfiguration = PersistedConfiguration(appContext) + } + + @Test + fun testOnMethodInvokeReturnCorrectType(){ + PersistenceKey.values().toList().forEach { persistenceKey -> + + val propertyReturnType = testConfiguration.getPropertyReturnType(persistenceKey.name) + val propertyValue = testConfiguration.callMethod(persistenceKey.name) + + when(propertyReturnType){ + Int::class -> Assert.assertNotEquals(propertyValue, 0) + String::class -> Assert.assertNotEquals(propertyValue, null) + Boolean::class -> Assert.assertNotEquals(propertyValue, null) + } + } + } +} + +// Utils function for `Persistence` Testcase only +private inline fun T.callMethod(name: String, vararg args: Any?): Any? = + T::class + .members + .firstOrNull { it.name == name } + ?.call(this, *args) + +private inline fun T.getPropertyReturnType(name: String): KClassifier? = + T::class + .members + .firstOrNull { it.name == name } + ?.returnType + ?.classifier \ No newline at end of file diff --git a/modules/src/main/AndroidManifest.xml b/modules/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/modules/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt new file mode 100644 index 000000000..bdb57b9f7 --- /dev/null +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -0,0 +1,98 @@ +package app.lounge.storage.cache + +import android.content.Context +import kotlin.reflect.KProperty + + +//region Setter Accessors (primarily used for setting) + +val Context.configurations: PersistedConfiguration get() = PersistedConfiguration(context = this) + +//endregion + +// region - Persistence Configuration + +enum class PersistenceKey { + updateInstallAuto, + updateCheckIntervals, + updateAppsFromOtherStores, + showAllApplications, + showPWAApplications, + showFOSSApplications, + // OLD datastore + authData, + email, + oauthtoken, + userType, + tocStatus, + tosversion +} + +class PersistedConfiguration(context: Context) { + var updateInstallAuto by context.persisted(PersistenceKey.updateInstallAuto, false) + var updateCheckIntervals by context.persisted(PersistenceKey.updateCheckIntervals, 24) + var updateAppsFromOtherStores by context.persisted(PersistenceKey.updateAppsFromOtherStores, false) + var showAllApplications by context.persisted(PersistenceKey.showAllApplications, true) + var showPWAApplications by context.persisted(PersistenceKey.showPWAApplications, true) + var showFOSSApplications by context.persisted(PersistenceKey.showFOSSApplications, true) + var authData by context.persisted(PersistenceKey.authData, "") + var email by context.persisted(PersistenceKey.email, "") + var oauthtoken by context.persisted(PersistenceKey.oauthtoken, "") + var userType by context.persisted(PersistenceKey.userType, "") + var tocStatus by context.persisted(PersistenceKey.tocStatus, false) + var tosversion by context.persisted(PersistenceKey.tosversion, "") +} + +// endregion + +//region - Persistence (in shared preferences) + +private class PersistedItem( + context: Context, + val key: PersistenceKey, + var defaultValue: T +) { + + private val sharedPref = + context.getSharedPreferences("Settings", Context.MODE_PRIVATE) + private val sharedPrefKey = "${context.packageName}." + key.name + + + operator fun getValue(thisRef: Any?, property: KProperty<*>): T { + return try { + when (property.returnType.classifier) { + Int::class -> sharedPref.getInt(sharedPrefKey, defaultValue as Int) + Long::class -> sharedPref.getLong(sharedPrefKey, defaultValue as Long) + Boolean::class -> sharedPref.getBoolean(sharedPrefKey, defaultValue as Boolean) + String::class -> sharedPref.getString(sharedPrefKey, defaultValue as String) + else -> IllegalArgumentException( + "TODO: Missing accessor for type -- ${property.returnType.classifier}" + ) + } as T + } catch (e: ClassCastException) { + defaultValue + } + } + + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + sharedPref.edit().apply { + when (value) { + is Int -> putInt(sharedPrefKey, value) + is Long -> putLong(sharedPrefKey, value) + is Boolean -> putBoolean(sharedPrefKey, value) + is String -> putString(sharedPrefKey, value) + else -> IllegalArgumentException( + "TODO: Missing setter for type -- ${property.returnType.classifier}" + ) + } + apply() + } + } +} + + +private fun Context.persisted(key: PersistenceKey, defaultValue: T) : PersistedItem { + return PersistedItem(context = this, key = key, defaultValue = defaultValue) +} + +//endregion \ No newline at end of file diff --git a/modules/src/test/java/app/lounge/ExampleUnitTest.kt b/modules/src/test/java/app/lounge/ExampleUnitTest.kt new file mode 100644 index 000000000..51e5797b9 --- /dev/null +++ b/modules/src/test/java/app/lounge/ExampleUnitTest.kt @@ -0,0 +1,21 @@ +package app.lounge + +import app.lounge.storage.cache.PersistedConfiguration +import app.lounge.storage.cache.PersistenceKey +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + PersistenceKey.values().toList().forEach { persistenceKey -> + println(persistenceKey) + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4f3c63f18..011889897 100644 --- a/settings.gradle +++ b/settings.gradle @@ -62,3 +62,4 @@ dependencyResolutionManagement { } rootProject.name = "App Lounge" include ':app' +include ':modules' -- GitLab From 9667b900e441d8e861eafc3ba426d46518178493 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 21 Jun 2023 19:45:31 +0530 Subject: [PATCH 002/112] Clean up redundant files --- .../app/lounge/ExampleInstrumentedTest.kt | 26 ------------------- .../test/java/app/lounge/ExampleUnitTest.kt | 21 --------------- 2 files changed, 47 deletions(-) delete mode 100644 modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt delete mode 100644 modules/src/test/java/app/lounge/ExampleUnitTest.kt diff --git a/modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt b/modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt deleted file mode 100644 index c1cfdd165..000000000 --- a/modules/src/androidTest/java/app/lounge/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package app.lounge - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 -import app.lounge.storage.cache.PersistedConfiguration - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("app.lounge.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/modules/src/test/java/app/lounge/ExampleUnitTest.kt b/modules/src/test/java/app/lounge/ExampleUnitTest.kt deleted file mode 100644 index 51e5797b9..000000000 --- a/modules/src/test/java/app/lounge/ExampleUnitTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package app.lounge - -import app.lounge.storage.cache.PersistedConfiguration -import app.lounge.storage.cache.PersistenceKey -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - PersistenceKey.values().toList().forEach { persistenceKey -> - println(persistenceKey) - } - } -} \ No newline at end of file -- GitLab From f7165753c619b7f744b0ac086e81af74d7e11ba9 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 21 Jun 2023 22:44:31 +0530 Subject: [PATCH 003/112] Use Java 17 for android module --- modules/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/build.gradle b/modules/build.gradle index e4d965b9e..0d9e06412 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -28,11 +28,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } } -- GitLab From 36159576b9bf8f8fce0646fd3158a36338d918d7 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 21 Jun 2023 22:46:36 +0530 Subject: [PATCH 004/112] Testcase to evaluate value applied for shared preference --- .../java/app/lounge/PersistentStorageTest.kt | 41 ++++++++++++++++++- .../app/lounge/storage/cache/Persistence.kt | 2 +- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt index 7b41fc947..cab78deed 100644 --- a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt +++ b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt @@ -4,6 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import app.lounge.storage.cache.PersistedConfiguration import app.lounge.storage.cache.PersistenceKey +import app.lounge.storage.cache.configurations import org.junit.Assert import org.junit.Before import org.junit.Test @@ -18,7 +19,7 @@ class PersistentStorageTest { @Before fun setupPersistentConfiguration(){ val appContext = InstrumentationRegistry.getInstrumentation().targetContext - testConfiguration = PersistedConfiguration(appContext) + testConfiguration = appContext.configurations } @Test @@ -35,9 +36,35 @@ class PersistentStorageTest { } } } + + @Test + fun testOnSetPersistentKeyReturnsSameExpectedValue() { + PersistenceKey.values().toList().forEach { persistentKey -> + val returnType: KClassifier? = testConfiguration.getPropertyReturnType(persistentKey.name) + when(persistentKey) { + PersistenceKey.updateInstallAuto -> testConfiguration.updateInstallAuto = testBooleanValue + PersistenceKey.updateCheckIntervals -> testConfiguration.updateCheckIntervals = testIntValue + PersistenceKey.updateAppsFromOtherStores -> testConfiguration.updateAppsFromOtherStores = testBooleanValue + PersistenceKey.showAllApplications -> testConfiguration.showAllApplications = testBooleanValue + PersistenceKey.showPWAApplications -> testConfiguration.showPWAApplications = testBooleanValue + PersistenceKey.showFOSSApplications -> testConfiguration.showFOSSApplications = testBooleanValue + PersistenceKey.authData -> testConfiguration.authData = testStringValue + PersistenceKey.email -> testConfiguration.email = testStringValue + PersistenceKey.oauthtoken -> testConfiguration.oauthtoken = testStringValue + PersistenceKey.userType -> testConfiguration.userType = testStringValue + PersistenceKey.tocStatus -> testConfiguration.tocStatus = testBooleanValue + PersistenceKey.tosversion -> testConfiguration.tosversion = testStringValue + } + testConfiguration.evaluateValue(classifier = returnType, key = persistentKey) + } + } } // Utils function for `Persistence` Testcase only +private val testIntValue : Int = (1..10).random() +private const val testStringValue: String = "quick brown fox jump over the lazy dog" +private const val testBooleanValue: Boolean = true + private inline fun T.callMethod(name: String, vararg args: Any?): Any? = T::class .members @@ -49,4 +76,14 @@ private inline fun T.getPropertyReturnType(name: String): KCla .members .firstOrNull { it.name == name } ?.returnType - ?.classifier \ No newline at end of file + ?.classifier +private fun PersistedConfiguration.evaluateValue(classifier: KClassifier?, key: PersistenceKey) { + when(classifier){ + Int::class -> Assert.assertEquals( + "Expected to be `$testIntValue`", testIntValue, this.callMethod(key.name) as Int) + String::class -> Assert.assertEquals( + "Expected to be `$testStringValue`", testStringValue, this.callMethod(key.name) as String) + Boolean::class -> Assert.assertTrue( + "Expected to be `$testBooleanValue`", this.callMethod(key.name) as Boolean) + } +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt index bdb57b9f7..9c0a98302 100644 --- a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -12,7 +12,7 @@ val Context.configurations: PersistedConfiguration get() = PersistedConfiguratio // region - Persistence Configuration -enum class PersistenceKey { +internal enum class PersistenceKey { updateInstallAuto, updateCheckIntervals, updateAppsFromOtherStores, -- GitLab From a11ec9449dee5e7efb1fa8ba3b97f0f26d3fb3d5 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 21 Jun 2023 23:21:56 +0530 Subject: [PATCH 005/112] Testcase to evaluate json string caching and deserialization --- modules/build.gradle | 6 ++-- .../java/app/lounge/PersistentStorageTest.kt | 35 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/modules/build.gradle b/modules/build.gradle index 0d9e06412..6ac8c12d8 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -7,6 +7,8 @@ ext { android_compile_sdk_version = 33 android_target_sdk_version = 33 core_version = '1.10.1' + gson_version = '2.9.0' + kotlin_reflection = '1.8.10' } android { @@ -39,10 +41,10 @@ android { dependencies { implementation "androidx.core:core-ktx:$core_version" - implementation 'androidx.appcompat:appcompat:1.6.1' + implementation "com.google.code.gson:gson:$gson_version" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_reflection" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation "org.jetbrains.kotlin:kotlin-reflect:1.8.10" } \ No newline at end of file diff --git a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt index cab78deed..a51050bdf 100644 --- a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt +++ b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt @@ -5,6 +5,7 @@ import androidx.test.platform.app.InstrumentationRegistry import app.lounge.storage.cache.PersistedConfiguration import app.lounge.storage.cache.PersistenceKey import app.lounge.storage.cache.configurations +import com.google.gson.Gson import org.junit.Assert import org.junit.Before import org.junit.Test @@ -58,12 +59,26 @@ class PersistentStorageTest { testConfiguration.evaluateValue(classifier = returnType, key = persistentKey) } } + + @Test + fun testSetStringJsonReturnSerializedObject() { + testConfiguration.authData = sampleTokensJson + val result: String = testConfiguration.callMethod("authData") as String + + val expectedTokens = Tokens( + aasToken = "ya29.a0AWY7Cknq6ueSCNVN6F7jB", + ac2dmToken = "ABFEt1X6tnDsra6QUsjVsjIWz0T5F", + authToken = "gXKyx_qC5EO64ECheZFonpJOtxbY") + + Assert.assertEquals( + "Tokens should match with $expectedTokens", + expectedTokens, + result.toTokens() + ) + } } // Utils function for `Persistence` Testcase only -private val testIntValue : Int = (1..10).random() -private const val testStringValue: String = "quick brown fox jump over the lazy dog" -private const val testBooleanValue: Boolean = true private inline fun T.callMethod(name: String, vararg args: Any?): Any? = T::class @@ -86,4 +101,16 @@ private fun PersistedConfiguration.evaluateValue(classifier: KClassifier?, key: Boolean::class -> Assert.assertTrue( "Expected to be `$testBooleanValue`", this.callMethod(key.name) as Boolean) } -} \ No newline at end of file +} + +// region test sample data for shared preference verification +private val testIntValue : Int = (1..10).random() +private const val testStringValue: String = "quick brown fox jump over the lazy dog" +private const val testBooleanValue: Boolean = true + +private const val sampleTokensJson = "{\"aasToken\": \"ya29.a0AWY7Cknq6ueSCNVN6F7jB\"," + + "\"ac2dmToken\": \"ABFEt1X6tnDsra6QUsjVsjIWz0T5F\"," + + "\"authToken\": \"gXKyx_qC5EO64ECheZFonpJOtxbY\"}" +data class Tokens(val aasToken: String, val ac2dmToken: String, val authToken: String) +fun String.toTokens() = Gson().fromJson(this, Tokens::class.java) +// endregion \ No newline at end of file -- GitLab From fe62a0a175a4c2d5953e86926cd43823ff94a44c Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 29 Jun 2023 23:08:34 +0530 Subject: [PATCH 006/112] Retrofit generic implementation --- modules/build.gradle | 7 + .../java/app/lounge/networking/FetchError.kt | 50 +++++ .../app/lounge/networking/RetrofitConfig.kt | 29 +++ .../app/lounge/networking/RetrofitFetching.kt | 201 ++++++++++++++++++ .../networking/RetrofitRawResultProcessing.kt | 75 +++++++ 5 files changed, 362 insertions(+) create mode 100644 modules/src/main/java/app/lounge/networking/FetchError.kt create mode 100644 modules/src/main/java/app/lounge/networking/RetrofitConfig.kt create mode 100644 modules/src/main/java/app/lounge/networking/RetrofitFetching.kt create mode 100644 modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt diff --git a/modules/build.gradle b/modules/build.gradle index 6ac8c12d8..849973854 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -9,6 +9,7 @@ ext { core_version = '1.10.1' gson_version = '2.9.0' kotlin_reflection = '1.8.10' + retrofit_version = '2.9.0' } android { @@ -44,6 +45,12 @@ dependencies { implementation "com.google.code.gson:gson:$gson_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_reflection" + // region Retrofit + implementation("com.squareup.retrofit2:retrofit:$retrofit_version") + implementation("com.squareup.retrofit2:converter-gson:$retrofit_version") + implementation("com.squareup.retrofit2:converter-scalars:$retrofit_version") + // endregion + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/modules/src/main/java/app/lounge/networking/FetchError.kt b/modules/src/main/java/app/lounge/networking/FetchError.kt new file mode 100644 index 000000000..271b4ab3b --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/FetchError.kt @@ -0,0 +1,50 @@ +package app.lounge.networking + + +//region Generic Network Error Types + +sealed interface FetchError { + data class Network(val underlyingError: AnyFetchError) : FetchError +} + +/** Supertype for network error types. */ +sealed interface AnyFetchError { + + var description: String + + enum class BadRequest(override var description: String) : AnyFetchError { + Encode("FIXME: Error encoding request! $dumpKeyWord"), + Decode("FIXME: Error decoding request! $dumpKeyWord"), + } + + enum class NotFound(override var description: String) : AnyFetchError { + MissingData("No data found! $dumpKeyWord"), + MissingNetwork("No network! $dumpKeyWord") + } + + data class BadStatusCode (val statusCode: Int, val rawResponse: Any) : AnyFetchError { + override var description: String = + "Bad status code: $statusCode. Raw response: $rawResponse" + } + + /** + * Represents a vague error case typically caused by `UnknownHostException`. + * This error case is encountered if and only if network status cannot be determined + * while the `UnknownHostException` is received. + */ + data class Unknown( + override var description: String = "Unknown Error! $dumpKeyWord" + ) : AnyFetchError + + companion object { + const val dumpKeyWord: String = "dump:-" + + fun make(error: AnyFetchError, addingDump: String) : AnyFetchError { + error.description = error.description + addingDump + return error + } + } + +} + +//endregion diff --git a/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt b/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt new file mode 100644 index 000000000..c4b89b6dc --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt @@ -0,0 +1,29 @@ +package app.lounge.networking + +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import java.util.concurrent.TimeUnit + +/** + * Implement retrofit configuration + * 1. Try to use single instance of configuration + * 2. Generic way to handle success or failure + * 3. API parsing can be clean and testable + * + * NOTE: Try to use naming which define the action for the logic. + * */ + +internal fun Retrofit.Builder.appLounge( + baseURL: String, + shouldFollowRedirects: Boolean, + callTimeoutInSeconds: Long, +) : Retrofit { + return this.baseUrl(baseURL) + .client( + OkHttpClient.Builder() + .callTimeout(callTimeoutInSeconds, TimeUnit.SECONDS) + .followRedirects(shouldFollowRedirects) + .build() + ) + .build() +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/RetrofitFetching.kt b/modules/src/main/java/app/lounge/networking/RetrofitFetching.kt new file mode 100644 index 000000000..6c38171a0 --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/RetrofitFetching.kt @@ -0,0 +1,201 @@ +package app.lounge.networking + +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import java.lang.IllegalStateException +import java.net.UnknownHostException + + +//region Retrofit Asynchronous Networking +interface RetrofitFetching { + val executor: Executor get() = callEnqueue + + val checkNetwork: (() -> Boolean)? get() = null + + interface Executor { + fun fetchAndCallback(endpoint: Call, callback: Callback) + } + + /** + * An object that receives Retrofit `Callback` arguments and determines whether or not + * an error should be returned. Use to return specific error cases for a given network request. + */ + interface ResultProcessing { + /** + * Return `R` if possible to cast given object to `R` or return `null`. + * This lambda is required due to jvm's type-erasing for generic types. + */ + val tryCastResponseBody: (Any?) -> R? + + /** Return result, success/failure, for the received request response */ + val resultFromResponse: (call: Call, response: Response) -> RetrofitResult + + /** Return error object `E` for the given failed request. */ + val errorFromFailureResponse: (call: Call<*>, t: Throwable) -> E + + /** Return error object `E` that contains/represents the given `AnyFetchError` */ + val errorFromNetworkFailure: (AnyFetchError) -> E + + } + + companion object { + /** Creates and returns a network request executor using Retrofit `Call` enqueue. */ + val callEnqueue: Executor + get() { + return object : Executor { + override fun fetchAndCallback(endpoint: Call, callback: Callback) { + endpoint.enqueue(callback) + } + } + } + } +} + +/** + * Fetch for response type `R` and callback in `success` callback. Invokes failure with + * an error subtype of `AnyFetchError` upon failure. + * + * @param usingExecutor Network request executor (set to `this.executor` by default) + * @param endpoint The API endpoint that should be fetched + * @param success Success callback with the response `R` + * @param failure Failure callback with an error case from `AnyFetchError` subtypes + */ +inline fun RetrofitFetching.fetch( + usingExecutor: RetrofitFetching.Executor = executor, + endpoint: Call, + noinline success: (R) -> Unit, + noinline failure: (FetchError) -> Unit +) { + val resultProcessing = RetrofitResultProcessing( + errorFromNetworkFailure = { FetchError.Network(it) }, + hasNetwork = checkNetwork + ) + fetch(usingExecutor, endpoint, resultProcessing, success, failure) +} + +/** + * Fetch for response type `R` and callback in `success` callback. Invokes failure with + * an error subtype of `E : AnyFetchError` upon failure. + * + * @param usingExecutor Network request executor (set to `this.executor` by default) + * @param endpoint The API endpoint that should be fetched + * @param resultProcessing Processes response and finds corresponding error case (if needed) + * @param success Success callback with the response `R` + * @param failure Failure callback with an error case from given error subtype `E` + */ +fun RetrofitFetching.fetch( + usingExecutor: RetrofitFetching.Executor = executor, + endpoint: Call, + resultProcessing: RetrofitFetching.ResultProcessing, + success: (R) -> Unit, + failure: (E) -> Unit +) { + fetch(usingExecutor, endpoint, resultProcessing) { it.invoke(success, failure) } +} + +inline fun RetrofitResult.invoke(success: (R) -> Unit, failure: (E) -> Unit) { + return when (this) { + is RetrofitResult.Success -> success(this.result) + is RetrofitResult.Failure -> failure(this.error) + } +} + +sealed class RetrofitResult { + data class Success(val result: R) : RetrofitResult() + data class Failure(val error: E) : RetrofitResult() +} + +private fun fetch( + usingExecutor: RetrofitFetching.Executor, + endpoint: Call, + resultProcessing: RetrofitFetching.ResultProcessing, + callback: (RetrofitResult) -> Unit, +) { + usingExecutor.fetchAndCallback(endpoint, object : Callback { + override fun onFailure(call: Call, t: Throwable) { + callback(RetrofitResult.Failure(resultProcessing.errorFromFailureResponse(call, t))) + } + + override fun onResponse(call: Call, response: Response) { + callback(resultProcessing.resultFromResponse(call, response)) + } + }) +} + +//endregion + +//region Retrofit Standard Result Processing + +/** Returns result processing object for given response type `R` and error type `E` */ +open class RetrofitResultProcessing( + override val tryCastResponseBody: (Any?) -> R?, + override val errorFromNetworkFailure: (AnyFetchError) -> E, + hasNetwork: (() -> Boolean)? = null, +) : RetrofitFetching.ResultProcessing { + + companion object { + inline operator fun invoke( + noinline errorFromNetworkFailure: (AnyFetchError) -> E, + noinline hasNetwork: (() -> Boolean)? = null + ) : RetrofitResultProcessing { + return RetrofitResultProcessing( + tryCastResponseBody = { + if (it == null && R::class.java == Unit::class.java) { Unit as R } + else { it as? R } + }, + errorFromNetworkFailure = errorFromNetworkFailure, hasNetwork = hasNetwork + ) + } + } + + override var errorFromFailureResponse: (call: Call<*>, t: Throwable) -> E = { call, t -> + val error: AnyFetchError = when(t) { + is UnknownHostException -> errorForUnknownHostException + is IllegalStateException -> errorForIllegalStateException + + //TODO: Check other cases + else -> AnyFetchError.Unknown() + } + errorFromNetworkFailure( + AnyFetchError.make(error = error, addingDump = "(Throwable): $t\n(Call): $call") + ) + } + + override var resultFromResponse: + (Call, Response) -> RetrofitResult = { call, response -> + if (response.isSuccessful) { + tryCastResponseBody(response.body())?.let { body -> + RetrofitResult.Success(body) + } ?: RetrofitResult.Failure( + errorFromNetworkFailure( + AnyFetchError.make( + error = AnyFetchError.NotFound.MissingData, + addingDump = "(Response): $response\n(Call): $call" + ) + ) + ) + } else { + RetrofitResult.Failure( + errorFromNetworkFailure(AnyFetchError.BadStatusCode(response.code(), response)) + ) + } + } + + //region Exception to AnyFetchError mapping + + var errorForUnknownHostException: AnyFetchError = if (hasNetwork != null) { + if (hasNetwork()) AnyFetchError.BadRequest.Encode + else AnyFetchError.NotFound.MissingNetwork + } else { + // Cannot distinguish the error case from `MissingNetwork` and `Encode` error. + AnyFetchError.Unknown() + } + + var errorForIllegalStateException: AnyFetchError = AnyFetchError.BadRequest.Decode + + //endregion + +} + +//endregion \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt b/modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt new file mode 100644 index 000000000..f983bf653 --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt @@ -0,0 +1,75 @@ +package app.lounge.networking + +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +interface RawResponseProcessing { + val onResponse: (Call, Response) -> RetrofitResult +} + + +fun RetrofitFetching.fetch( + usingExecutor: RetrofitFetching.Executor = executor, + endpoint: Call, + processing: Processing, + success: (R) -> Unit, + failure: (E) -> Unit +) where Processing : RawResponseProcessing, + Processing : RetrofitFetching.ResultProcessing { + fetch(usingExecutor, endpoint, processing) { it.invoke(success, failure) } +} + + +private fun fetch( + usingExecutor: RetrofitFetching.Executor, + endpoint: Call, + processing: Processing, + callback: (RetrofitResult) -> Unit, +) where Processing : RawResponseProcessing, + Processing : RetrofitFetching.ResultProcessing { + usingExecutor.fetchAndCallback(endpoint, object : Callback { + override fun onFailure(call: Call, t: Throwable) { + callback(RetrofitResult.Failure(processing.errorFromFailureResponse(call, t))) + } + + override fun onResponse(call: Call, response: Response) { + callback(processing.onResponse(call, response)) + } + }) +} + +class RetrofitRawResultProcessing( + override val onResponse: (Call<*>, Response<*>) -> RetrofitResult, + override val tryCastResponseBody: (Any?) -> R?, + override val errorFromNetworkFailure: (AnyFetchError) -> E, + hasNetwork: (() -> Boolean)? = null, +) : RetrofitResultProcessing(tryCastResponseBody, errorFromNetworkFailure, hasNetwork), + RawResponseProcessing { + + override var resultFromResponse: (Call, Response) -> RetrofitResult = { + call, response -> + when(val customResult = onResponse(call, response)) { + is RetrofitResult.Success -> customResult + is RetrofitResult.Failure -> super.resultFromResponse(call, response) + } + } + + companion object { + inline operator fun invoke( + noinline onResponse: (Call<*>, Response<*>) -> RetrofitResult, + noinline errorFromNetworkFailure: (AnyFetchError) -> E, + noinline hasNetwork: (() -> Boolean)? = null + ) : RetrofitRawResultProcessing { + return RetrofitRawResultProcessing( + onResponse = onResponse, + tryCastResponseBody = { + if (it == null && R::class.java == Unit::class.java) { Unit as R } + else { it as? R } + }, + errorFromNetworkFailure = errorFromNetworkFailure, hasNetwork = hasNetwork + ) + } + } + +} -- GitLab From 96956a3817de342f915c5037b4ded3a21eb9c593 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 29 Jun 2023 23:09:11 +0530 Subject: [PATCH 007/112] Added google and anonymous user structure --- .../src/main/java/app/lounge/users/Google.kt | 8 +++ .../src/main/java/app/lounge/users/Session.kt | 11 ++++ .../app/lounge/users/anonymous/Anonymous.kt | 35 +++++++++++ .../lounge/users/anonymous/AnonymousAPI.kt | 33 +++++++++++ .../users/anonymous/RetrofitAnonymousAPI.kt | 58 +++++++++++++++++++ 5 files changed, 145 insertions(+) create mode 100644 modules/src/main/java/app/lounge/users/Google.kt create mode 100644 modules/src/main/java/app/lounge/users/Session.kt create mode 100644 modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt create mode 100644 modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt create mode 100644 modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt diff --git a/modules/src/main/java/app/lounge/users/Google.kt b/modules/src/main/java/app/lounge/users/Google.kt new file mode 100644 index 000000000..48ce6c2dd --- /dev/null +++ b/modules/src/main/java/app/lounge/users/Google.kt @@ -0,0 +1,8 @@ +package app.lounge.users + +/** + * Implement API related to Google login flow only. + * 1. Login api for google users. + * 2. Parsing google user data. For now use typealias object, later we will refactor with generic type + * 3. Add unit test cases for the api functions. + * */ \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/Session.kt b/modules/src/main/java/app/lounge/users/Session.kt new file mode 100644 index 000000000..5885edea2 --- /dev/null +++ b/modules/src/main/java/app/lounge/users/Session.kt @@ -0,0 +1,11 @@ +package app.lounge.users + +/** + * Handle user login session in the file. + * This should be the only file which communicate with application regarding user data + * + * 1. Application facing api will be part of this file. + * 2. Manage user session for Anonymous, Google, PWA users. + * 3. In case of invalid user data, timeout etc. inform application about session failure. + * 4. If possible add session api testcases. + */ \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt new file mode 100644 index 000000000..8c888f012 --- /dev/null +++ b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt @@ -0,0 +1,35 @@ +package app.lounge.users.anonymous + +/** + * Implement API related to Anonymous login flow only. + * 1. Login api for Anonymous users. + * 2. Parsing Anonymous user data. For now use typealias object, later we will refactor with generic type + * 3. Add unit test cases for the api functions. + * */ + +interface Anonymous { + + val api: AnonymousAPI + + companion object { + + fun anonymousRequestFor( + api: AnonymousAPI = AnonymousAPI.create() + ) : Anonymous { + return object : Anonymous { + override val api: AnonymousAPI = api + } + } + } + + // pass input from this function + fun login( + success : () -> Unit, + failure : () -> Unit + ) { + api.performLogin( + success = success, + failure = failure + ) + } +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt new file mode 100644 index 000000000..5ef1a8ae1 --- /dev/null +++ b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt @@ -0,0 +1,33 @@ +package app.lounge.users.anonymous + +import app.lounge.BuildConfig + +interface AnonymousAPI { + + companion object { + private const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" + private const val loginBaseURL: String = "GooglePlayApi.URL_SYNC" + + fun create(baseURL: String = tokenBaseURL) : AnonymousAPI { + return RetrofitAnonymousAPI( + baseURL = baseURL, + anonymousUserEndpointFollowsRedirects = true, + callTimeoutInSeconds = 30 + ) + } + } + + fun performLogin( + success : () -> Unit, + failure : () -> Unit + ) + + object Header { + val authData: Map get() { + return mapOf( + Pair("User-Agent", BuildConfig.BUILD_TYPE), // CommonUtilsFunctions.getAppBuildInfo() + ) + } + } + +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt new file mode 100644 index 000000000..1b9b296f0 --- /dev/null +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -0,0 +1,58 @@ +package app.lounge.users.anonymous + +import app.lounge.networking.RetrofitFetching +import app.lounge.networking.RetrofitRawResultProcessing +import app.lounge.networking.RetrofitResult +import app.lounge.networking.appLounge +import app.lounge.networking.fetch +import okhttp3.RequestBody +import retrofit2.Call +import retrofit2.Retrofit +import retrofit2.http.Body +import retrofit2.http.HeaderMap +import retrofit2.http.POST + +internal class RetrofitAnonymousAPI( + baseURL: String, + anonymousUserEndpointFollowsRedirects: Boolean, + callTimeoutInSeconds: Long, +): AnonymousAPI, RetrofitFetching { + + private val anonymousUserEndPoint: AnonymousUserEndPointEndpoint = Retrofit.Builder().appLounge( + baseURL = baseURL, + shouldFollowRedirects = anonymousUserEndpointFollowsRedirects, + callTimeoutInSeconds = callTimeoutInSeconds + ).create(AnonymousUserEndPointEndpoint::class.java) + + interface AnonymousUserEndPointEndpoint { + + @POST + fun authDataRequest( + @HeaderMap headers: Map = AnonymousAPI.Header.authData, + @Body requestBody: RequestBody? + ): Call + } + + override fun performLogin(success: () -> Unit, failure: () -> Unit) { + /*fetch( + endpoint = anonymousUserEndPoint.authDataRequest(requestBody = null), + processing = , + )*/ + } + + + /*private val cardTerminalResultProcessing = RetrofitRawResultProcessing< + CardTerminalRetrofitCallRequestResponse, + CardTerminalRetrofitCallFailure, + CardTerminalRetrofitCallResponse>( + errorFromNetworkFailure = { CardTerminalRetrofitCallFailure.Network(it) }, + onResponse = { _, response -> + RetrofitResult.Success(CardTerminalRetrofitCallResponse("")) + } + )*/ + +} + +internal typealias CardTerminalRetrofitCallResponse = String +internal typealias CardTerminalRetrofitCallRequestResponse = String +internal typealias CardTerminalRetrofitCallFailure = String -- GitLab From 8d4b61adf8130e646fa9b03341d45d067df7dba0 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 30 Jun 2023 20:23:36 +0530 Subject: [PATCH 008/112] Clean up redundant code --- .../networking/RetrofitRawResultProcessing.kt | 75 ------------------- .../users/anonymous/RetrofitAnonymousAPI.kt | 18 ----- 2 files changed, 93 deletions(-) delete mode 100644 modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt diff --git a/modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt b/modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt deleted file mode 100644 index f983bf653..000000000 --- a/modules/src/main/java/app/lounge/networking/RetrofitRawResultProcessing.kt +++ /dev/null @@ -1,75 +0,0 @@ -package app.lounge.networking - -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response - -interface RawResponseProcessing { - val onResponse: (Call, Response) -> RetrofitResult -} - - -fun RetrofitFetching.fetch( - usingExecutor: RetrofitFetching.Executor = executor, - endpoint: Call, - processing: Processing, - success: (R) -> Unit, - failure: (E) -> Unit -) where Processing : RawResponseProcessing, - Processing : RetrofitFetching.ResultProcessing { - fetch(usingExecutor, endpoint, processing) { it.invoke(success, failure) } -} - - -private fun fetch( - usingExecutor: RetrofitFetching.Executor, - endpoint: Call, - processing: Processing, - callback: (RetrofitResult) -> Unit, -) where Processing : RawResponseProcessing, - Processing : RetrofitFetching.ResultProcessing { - usingExecutor.fetchAndCallback(endpoint, object : Callback { - override fun onFailure(call: Call, t: Throwable) { - callback(RetrofitResult.Failure(processing.errorFromFailureResponse(call, t))) - } - - override fun onResponse(call: Call, response: Response) { - callback(processing.onResponse(call, response)) - } - }) -} - -class RetrofitRawResultProcessing( - override val onResponse: (Call<*>, Response<*>) -> RetrofitResult, - override val tryCastResponseBody: (Any?) -> R?, - override val errorFromNetworkFailure: (AnyFetchError) -> E, - hasNetwork: (() -> Boolean)? = null, -) : RetrofitResultProcessing(tryCastResponseBody, errorFromNetworkFailure, hasNetwork), - RawResponseProcessing { - - override var resultFromResponse: (Call, Response) -> RetrofitResult = { - call, response -> - when(val customResult = onResponse(call, response)) { - is RetrofitResult.Success -> customResult - is RetrofitResult.Failure -> super.resultFromResponse(call, response) - } - } - - companion object { - inline operator fun invoke( - noinline onResponse: (Call<*>, Response<*>) -> RetrofitResult, - noinline errorFromNetworkFailure: (AnyFetchError) -> E, - noinline hasNetwork: (() -> Boolean)? = null - ) : RetrofitRawResultProcessing { - return RetrofitRawResultProcessing( - onResponse = onResponse, - tryCastResponseBody = { - if (it == null && R::class.java == Unit::class.java) { Unit as R } - else { it as? R } - }, - errorFromNetworkFailure = errorFromNetworkFailure, hasNetwork = hasNetwork - ) - } - } - -} diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt index 1b9b296f0..8fe81ae67 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -1,10 +1,7 @@ package app.lounge.users.anonymous import app.lounge.networking.RetrofitFetching -import app.lounge.networking.RetrofitRawResultProcessing -import app.lounge.networking.RetrofitResult import app.lounge.networking.appLounge -import app.lounge.networking.fetch import okhttp3.RequestBody import retrofit2.Call import retrofit2.Retrofit @@ -40,19 +37,4 @@ internal class RetrofitAnonymousAPI( )*/ } - - /*private val cardTerminalResultProcessing = RetrofitRawResultProcessing< - CardTerminalRetrofitCallRequestResponse, - CardTerminalRetrofitCallFailure, - CardTerminalRetrofitCallResponse>( - errorFromNetworkFailure = { CardTerminalRetrofitCallFailure.Network(it) }, - onResponse = { _, response -> - RetrofitResult.Success(CardTerminalRetrofitCallResponse("")) - } - )*/ - } - -internal typealias CardTerminalRetrofitCallResponse = String -internal typealias CardTerminalRetrofitCallRequestResponse = String -internal typealias CardTerminalRetrofitCallFailure = String -- GitLab From 344c0e46445114c60848d0a1924c6b6224e38d64 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sun, 2 Jul 2023 22:54:34 +0530 Subject: [PATCH 009/112] Modify the Retrofit config for Anonymous User data --- modules/build.gradle | 2 + .../java/app/lounge/AnonymousUserAPITest.kt | 79 +++++++++++++++++++ modules/src/main/AndroidManifest.xml | 2 + .../java/app/lounge/extension/Extension.kt | 7 ++ .../app/lounge/networking/RetrofitConfig.kt | 11 +++ .../app/lounge/users/anonymous/Anonymous.kt | 12 ++- .../lounge/users/anonymous/AnonymousAPI.kt | 28 ++++--- .../users/anonymous/RetrofitAnonymousAPI.kt | 39 +++++++-- 8 files changed, 158 insertions(+), 22 deletions(-) create mode 100644 modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt create mode 100644 modules/src/main/java/app/lounge/extension/Extension.kt diff --git a/modules/build.gradle b/modules/build.gradle index 849973854..68e0af9c5 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -10,6 +10,7 @@ ext { gson_version = '2.9.0' kotlin_reflection = '1.8.10' retrofit_version = '2.9.0' + retrofit_interceptor_version = '5.0.0-alpha.2' } android { @@ -49,6 +50,7 @@ dependencies { implementation("com.squareup.retrofit2:retrofit:$retrofit_version") implementation("com.squareup.retrofit2:converter-gson:$retrofit_version") implementation("com.squareup.retrofit2:converter-scalars:$retrofit_version") + implementation("com.squareup.okhttp3:logging-interceptor:$retrofit_interceptor_version") // endregion testImplementation 'junit:junit:4.13.2' diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt new file mode 100644 index 000000000..916c6fce4 --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt @@ -0,0 +1,79 @@ +package app.lounge + +import app.lounge.users.anonymous.AnonymousAPI +import app.lounge.users.anonymous.AnonymousAuthDataRequestBody +import org.junit.Assert +import org.junit.Test +import java.util.Properties + +class AnonymousUserAPITest { + + private val testAnonymousAPI = AnonymousAPI.create( + baseURL = AnonymousAPI.tokenBaseURL + ) + + private val userAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" + + private val systemProperties = Properties().apply { + setProperty("UserReadableName", "coral-default") + setProperty("Build.HARDWARE", "coral") + setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") + setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") + setProperty("Build.BRAND", "google") + setProperty("Build.DEVICE", "coral") + setProperty("Build.VERSION.SDK_INT", "32") + setProperty("Build.VERSION.RELEASE", "12") + setProperty("Build.MODEL", "Pixel 4 XL") + setProperty("Build.MANUFACTURER", "Google") + setProperty("Build.PRODUCT", "coral") + setProperty("Build.ID", "SQ3A.220705.004") + setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") + setProperty("TouchScreen", "3") + setProperty("Keyboard", "1") + setProperty("Navigation", "1") + setProperty("ScreenLayout", "2") + setProperty("HasHardKeyboard", "false") + setProperty("HasFiveWayNavigation", "false") + setProperty("Screen.Density", "560") + setProperty("Screen.Width", "1440") + setProperty("Screen.Height", "2984") + setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") + setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") + setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") + setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") + setProperty("GL.Version", "196610") + setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") + setProperty("Client", "android-google") + setProperty("GSF.version", "223616055") + setProperty("Vending.version", "82151710") + setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") + setProperty("Roaming", "mobile-notroaming") + setProperty("TimeZone", "UTC-10") + setProperty("CellOperator", "310") + setProperty("SimOperator", "38") + } + + + private val requestBodyData = AnonymousAuthDataRequestBody( + properties = systemProperties, + userAgent = userAgent + ) + + @Test + fun testAPI() { + testAnonymousAPI.requestAuthData( + anonymousAuthDataRequestBody = requestBodyData, + success = { + print(it) + Assert.assertEquals("Hello", it) + }, + failure = { + print(it) + Assert.assertEquals("Hello", it.toString()) + } + ) + + Thread.sleep(30000) + } + +} \ No newline at end of file diff --git a/modules/src/main/AndroidManifest.xml b/modules/src/main/AndroidManifest.xml index a5918e68a..2a7393a69 100644 --- a/modules/src/main/AndroidManifest.xml +++ b/modules/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + + \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/extension/Extension.kt b/modules/src/main/java/app/lounge/extension/Extension.kt new file mode 100644 index 000000000..133934a78 --- /dev/null +++ b/modules/src/main/java/app/lounge/extension/Extension.kt @@ -0,0 +1,7 @@ +package app.lounge.extension + +import com.google.gson.Gson +import java.util.Properties + +/** Convert Properties parameter to byte array*/ +fun Properties.toByteArray() = Gson().toJson(this).toByteArray() \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt b/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt index c4b89b6dc..fb6290cdf 100644 --- a/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt +++ b/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt @@ -1,7 +1,9 @@ package app.lounge.networking import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit /** @@ -19,11 +21,20 @@ internal fun Retrofit.Builder.appLounge( callTimeoutInSeconds: Long, ) : Retrofit { return this.baseUrl(baseURL) + .addConverterFactory(GsonConverterFactory.create()) .client( OkHttpClient.Builder() + .addNetworkInterceptor(interceptor) .callTimeout(callTimeoutInSeconds, TimeUnit.SECONDS) .followRedirects(shouldFollowRedirects) .build() ) .build() +} + +private val interceptor = run { + val httpLoggingInterceptor = HttpLoggingInterceptor() + httpLoggingInterceptor.apply { + httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY + } } \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt index 8c888f012..9ca6f9067 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt @@ -1,5 +1,7 @@ package app.lounge.users.anonymous +import app.lounge.networking.FetchError + /** * Implement API related to Anonymous login flow only. * 1. Login api for Anonymous users. @@ -23,11 +25,13 @@ interface Anonymous { } // pass input from this function - fun login( - success : () -> Unit, - failure : () -> Unit + fun requestAuthData( + anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, + success : (LoginResponse) -> Unit, + failure : (FetchError) -> Unit ) { - api.performLogin( + api.requestAuthData( + anonymousAuthDataRequestBody = anonymousAuthDataRequestBody, success = success, failure = failure ) diff --git a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt index 5ef1a8ae1..9da4d4368 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt @@ -1,12 +1,15 @@ package app.lounge.users.anonymous import app.lounge.BuildConfig +import app.lounge.networking.FetchError +import com.google.gson.Gson +import java.util.Properties interface AnonymousAPI { companion object { - private const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" - private const val loginBaseURL: String = "GooglePlayApi.URL_SYNC" + const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" + const val loginBaseURL: String = "GooglePlayApi.URL_SYNC" fun create(baseURL: String = tokenBaseURL) : AnonymousAPI { return RetrofitAnonymousAPI( @@ -17,17 +20,22 @@ interface AnonymousAPI { } } - fun performLogin( - success : () -> Unit, - failure : () -> Unit + fun requestAuthData( + anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, + success : (LoginResponse) -> Unit, + failure : (FetchError) -> Unit ) object Header { - val authData: Map get() { - return mapOf( - Pair("User-Agent", BuildConfig.BUILD_TYPE), // CommonUtilsFunctions.getAppBuildInfo() - ) + val authData: (() -> String) -> Map = { + mapOf(Pair("User-Agent", it.invoke())) } } -} \ No newline at end of file +} + +/** AnonymousAuthDataRequestBody */ +data class AnonymousAuthDataRequestBody( + val properties: Properties, + val userAgent: String +) diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt index 8fe81ae67..8b2d3f608 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -1,13 +1,18 @@ package app.lounge.users.anonymous +import app.lounge.extension.toByteArray +import app.lounge.networking.FetchError import app.lounge.networking.RetrofitFetching import app.lounge.networking.appLounge +import app.lounge.networking.fetch +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import retrofit2.Call import retrofit2.Retrofit import retrofit2.http.Body import retrofit2.http.HeaderMap import retrofit2.http.POST +import retrofit2.http.Url internal class RetrofitAnonymousAPI( baseURL: String, @@ -25,16 +30,34 @@ internal class RetrofitAnonymousAPI( @POST fun authDataRequest( - @HeaderMap headers: Map = AnonymousAPI.Header.authData, - @Body requestBody: RequestBody? - ): Call + @Url url: String = "https://eu.gtoken.ecloud.global", + @HeaderMap headers: Map, + @Body requestBody: RequestBody + ): Call } - override fun performLogin(success: () -> Unit, failure: () -> Unit) { - /*fetch( - endpoint = anonymousUserEndPoint.authDataRequest(requestBody = null), - processing = , - )*/ + override fun requestAuthData( + anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, + success: (LoginResponse) -> Unit, + failure: (FetchError) -> Unit + ) { + val requestBody = RequestBody.create( + "application/json".toMediaTypeOrNull(), + anonymousAuthDataRequestBody.properties.toByteArray() + ) + + fetch( + endpoint = anonymousUserEndPoint.authDataRequest( + requestBody = requestBody, + headers = AnonymousAPI.Header.authData { + anonymousAuthDataRequestBody.userAgent + } + ), + success = success, + failure = failure + ) } } + +typealias LoginResponse = String -- GitLab From 4078ebdf29438eafde23752668c8fcf7f2920a4d Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 4 Jul 2023 00:04:36 +0530 Subject: [PATCH 010/112] Modify Java 18, Added Google Play API dependency --- modules/build.gradle | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/build.gradle b/modules/build.gradle index 68e0af9c5..17b61982a 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -11,6 +11,8 @@ ext { kotlin_reflection = '1.8.10' retrofit_version = '2.9.0' retrofit_interceptor_version = '5.0.0-alpha.2' + google_play_api = '3.0.1' + protobuf_java = '3.19.3' } android { @@ -32,11 +34,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_18 + targetCompatibility JavaVersion.VERSION_18 } kotlinOptions { - jvmTarget = '17' + jvmTarget = '18' } } @@ -53,6 +55,11 @@ dependencies { implementation("com.squareup.okhttp3:logging-interceptor:$retrofit_interceptor_version") // endregion + // region Google Play API + implementation("foundation.e:gplayapi:$google_play_api") + implementation("com.google.protobuf:protobuf-java:$protobuf_java") + // endregion + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' -- GitLab From e9c487724c974a3ed0c2a85ca7ba240c37e7278d Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 4 Jul 2023 00:05:33 +0530 Subject: [PATCH 011/112] Implemented AuthData request api for anonymous user --- .../src/main/java/app/lounge/users/anonymous/Anonymous.kt | 4 ++-- .../main/java/app/lounge/users/anonymous/AnonymousAPI.kt | 4 +--- .../app/lounge/users/anonymous/RetrofitAnonymousAPI.kt | 7 ++++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt index 9ca6f9067..4dd0ef0e6 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt @@ -16,7 +16,7 @@ interface Anonymous { companion object { fun anonymousRequestFor( - api: AnonymousAPI = AnonymousAPI.create() + api: AnonymousAPI ) : Anonymous { return object : Anonymous { override val api: AnonymousAPI = api @@ -27,7 +27,7 @@ interface Anonymous { // pass input from this function fun requestAuthData( anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, - success : (LoginResponse) -> Unit, + success : (AuthDataResponse) -> Unit, failure : (FetchError) -> Unit ) { api.requestAuthData( diff --git a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt index 9da4d4368..83473fdf0 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt @@ -1,8 +1,6 @@ package app.lounge.users.anonymous -import app.lounge.BuildConfig import app.lounge.networking.FetchError -import com.google.gson.Gson import java.util.Properties interface AnonymousAPI { @@ -22,7 +20,7 @@ interface AnonymousAPI { fun requestAuthData( anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, - success : (LoginResponse) -> Unit, + success : (AuthDataResponse) -> Unit, failure : (FetchError) -> Unit ) diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt index 8b2d3f608..4cf90c452 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -5,6 +5,7 @@ import app.lounge.networking.FetchError import app.lounge.networking.RetrofitFetching import app.lounge.networking.appLounge import app.lounge.networking.fetch +import com.aurora.gplayapi.data.models.AuthData import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import retrofit2.Call @@ -33,12 +34,12 @@ internal class RetrofitAnonymousAPI( @Url url: String = "https://eu.gtoken.ecloud.global", @HeaderMap headers: Map, @Body requestBody: RequestBody - ): Call + ): Call } override fun requestAuthData( anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, - success: (LoginResponse) -> Unit, + success: (AuthDataResponse) -> Unit, failure: (FetchError) -> Unit ) { val requestBody = RequestBody.create( @@ -60,4 +61,4 @@ internal class RetrofitAnonymousAPI( } -typealias LoginResponse = String +typealias AuthDataResponse = AuthData \ No newline at end of file -- GitLab From 5ce05c407e7a005860dbd88735ba34852cdd5c9f Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 4 Jul 2023 00:06:15 +0530 Subject: [PATCH 012/112] Implement testcase for anonymous use api --- .../lounge/networking/AnonymousUserAPITest.kt | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt diff --git a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt new file mode 100644 index 000000000..2b4eb65f3 --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt @@ -0,0 +1,84 @@ +package app.lounge.networking + +import app.lounge.users.anonymous.Anonymous +import app.lounge.users.anonymous.AnonymousAPI +import app.lounge.users.anonymous.AnonymousAuthDataRequestBody +import app.lounge.users.anonymous.AuthDataResponse +import org.junit.Test +import java.util.Properties + +class AnonymousUserAPITest { + + private val testAnonymousAPIForToken = Anonymous.anonymousRequestFor( + api = AnonymousAPI.create(AnonymousAPI.tokenBaseURL) + ) + +// private val testAnonymousAPIForLogin = Anonymous.anonymousRequestFor( +// api = AnonymousAPI.create(AnonymousAPI.loginBaseURL) +// ) + + private val requestBodyData = AnonymousAuthDataRequestBody( + properties = testSystemProperties, + userAgent = testUserAgent + ) + + @Test + fun testOnSuccessReturnsAuthData() { + var receivedData: AuthDataResponse? + testAnonymousAPIForToken.requestAuthData( + anonymousAuthDataRequestBody = requestBodyData, + success = { response -> + receivedData = response + assert(receivedData is AuthDataResponse) { "Assert!! Success must return data" } + }, + failure = {} + ) + Thread.sleep(3000) + } + +} + +// region TestData + +private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" + +private val testSystemProperties = Properties().apply { + setProperty("UserReadableName", "coral-default") + setProperty("Build.HARDWARE", "coral") + setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") + setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") + setProperty("Build.BRAND", "google") + setProperty("Build.DEVICE", "coral") + setProperty("Build.VERSION.SDK_INT", "32") + setProperty("Build.VERSION.RELEASE", "12") + setProperty("Build.MODEL", "Pixel 4 XL") + setProperty("Build.MANUFACTURER", "Google") + setProperty("Build.PRODUCT", "coral") + setProperty("Build.ID", "SQ3A.220705.004") + setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") + setProperty("TouchScreen", "3") + setProperty("Keyboard", "1") + setProperty("Navigation", "1") + setProperty("ScreenLayout", "2") + setProperty("HasHardKeyboard", "false") + setProperty("HasFiveWayNavigation", "false") + setProperty("Screen.Density", "560") + setProperty("Screen.Width", "1440") + setProperty("Screen.Height", "2984") + setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") + setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") + setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") + setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") + setProperty("GL.Version", "196610") + setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") + setProperty("Client", "android-google") + setProperty("GSF.version", "223616055") + setProperty("Vending.version", "82151710") + setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") + setProperty("Roaming", "mobile-notroaming") + setProperty("TimeZone", "UTC-10") + setProperty("CellOperator", "310") + setProperty("SimOperator", "38") +} + +// endregion \ No newline at end of file -- GitLab From b03c240cfeb8077598c241a034a2b2d6358aecad Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 4 Jul 2023 22:20:13 +0530 Subject: [PATCH 013/112] Added Anonymous login api --- modules/build.gradle | 6 +- .../java/app/lounge/AnonymousUserAPITest.kt | 79 ------------------- .../lounge/networking/AnonymousUserAPITest.kt | 57 +++++++++---- .../app/lounge/users/anonymous/Anonymous.kt | 19 ++++- .../lounge/users/anonymous/AnonymousAPI.kt | 17 +++- .../users/anonymous/RetrofitAnonymousAPI.kt | 40 +++++++--- 6 files changed, 111 insertions(+), 107 deletions(-) delete mode 100644 modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt diff --git a/modules/build.gradle b/modules/build.gradle index 17b61982a..02731152a 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -34,11 +34,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_18 - targetCompatibility JavaVersion.VERSION_18 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '18' + jvmTarget = '17' } } diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt deleted file mode 100644 index 916c6fce4..000000000 --- a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt +++ /dev/null @@ -1,79 +0,0 @@ -package app.lounge - -import app.lounge.users.anonymous.AnonymousAPI -import app.lounge.users.anonymous.AnonymousAuthDataRequestBody -import org.junit.Assert -import org.junit.Test -import java.util.Properties - -class AnonymousUserAPITest { - - private val testAnonymousAPI = AnonymousAPI.create( - baseURL = AnonymousAPI.tokenBaseURL - ) - - private val userAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" - - private val systemProperties = Properties().apply { - setProperty("UserReadableName", "coral-default") - setProperty("Build.HARDWARE", "coral") - setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") - setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") - setProperty("Build.BRAND", "google") - setProperty("Build.DEVICE", "coral") - setProperty("Build.VERSION.SDK_INT", "32") - setProperty("Build.VERSION.RELEASE", "12") - setProperty("Build.MODEL", "Pixel 4 XL") - setProperty("Build.MANUFACTURER", "Google") - setProperty("Build.PRODUCT", "coral") - setProperty("Build.ID", "SQ3A.220705.004") - setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") - setProperty("TouchScreen", "3") - setProperty("Keyboard", "1") - setProperty("Navigation", "1") - setProperty("ScreenLayout", "2") - setProperty("HasHardKeyboard", "false") - setProperty("HasFiveWayNavigation", "false") - setProperty("Screen.Density", "560") - setProperty("Screen.Width", "1440") - setProperty("Screen.Height", "2984") - setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") - setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") - setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") - setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") - setProperty("GL.Version", "196610") - setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") - setProperty("Client", "android-google") - setProperty("GSF.version", "223616055") - setProperty("Vending.version", "82151710") - setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") - setProperty("Roaming", "mobile-notroaming") - setProperty("TimeZone", "UTC-10") - setProperty("CellOperator", "310") - setProperty("SimOperator", "38") - } - - - private val requestBodyData = AnonymousAuthDataRequestBody( - properties = systemProperties, - userAgent = userAgent - ) - - @Test - fun testAPI() { - testAnonymousAPI.requestAuthData( - anonymousAuthDataRequestBody = requestBodyData, - success = { - print(it) - Assert.assertEquals("Hello", it) - }, - failure = { - print(it) - Assert.assertEquals("Hello", it.toString()) - } - ) - - Thread.sleep(30000) - } - -} \ No newline at end of file diff --git a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt index 2b4eb65f3..7deb467ce 100644 --- a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt @@ -3,7 +3,9 @@ package app.lounge.networking import app.lounge.users.anonymous.Anonymous import app.lounge.users.anonymous.AnonymousAPI import app.lounge.users.anonymous.AnonymousAuthDataRequestBody +import app.lounge.users.anonymous.AnonymousLoginRequestBody import app.lounge.users.anonymous.AuthDataResponse +import org.junit.Assert import org.junit.Test import java.util.Properties @@ -13,27 +15,49 @@ class AnonymousUserAPITest { api = AnonymousAPI.create(AnonymousAPI.tokenBaseURL) ) -// private val testAnonymousAPIForLogin = Anonymous.anonymousRequestFor( -// api = AnonymousAPI.create(AnonymousAPI.loginBaseURL) -// ) + private val testAnonymousAPIForLogin = Anonymous.anonymousRequestFor( + api = AnonymousAPI.create(AnonymousAPI.loginBaseURL) + ) private val requestBodyData = AnonymousAuthDataRequestBody( properties = testSystemProperties, userAgent = testUserAgent ) + + companion object { + lateinit var receivedData: AuthDataResponse + } + @Test - fun testOnSuccessReturnsAuthData() { - var receivedData: AuthDataResponse? - testAnonymousAPIForToken.requestAuthData( - anonymousAuthDataRequestBody = requestBodyData, - success = { response -> - receivedData = response - assert(receivedData is AuthDataResponse) { "Assert!! Success must return data" } - }, - failure = {} - ) - Thread.sleep(3000) + fun test1OnSuccessReturnsAuthData() { + await { + testAnonymousAPIForToken.requestAuthData( + anonymousAuthDataRequestBody = requestBodyData, + success = { response -> + receivedData = response + assert(receivedData is AuthDataResponse) { "Assert!! Success must return data" } + }, + failure = {} + ) + } + } + + @Test + fun test2OnSuccessReturnsLoginData(){ + await { + testAnonymousAPIForLogin.requestLogin( + anonymousLoginRequestBody = AnonymousLoginRequestBody( + authDataResponse = receivedData + ), + success = { response -> + Assert.assertEquals("Hello", response.toString()) + }, + failure = { + Assert.assertEquals("Hello", it.toString()) + } + ) + } } } @@ -81,4 +105,9 @@ private val testSystemProperties = Properties().apply { setProperty("SimOperator", "38") } +fun await(callback: () -> Unit) { + callback.invoke() + Thread.sleep(3000) +} + // endregion \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt index 4dd0ef0e6..c53d75237 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt @@ -1,6 +1,8 @@ package app.lounge.users.anonymous import app.lounge.networking.FetchError +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.models.PlayResponse /** * Implement API related to Anonymous login flow only. @@ -36,4 +38,19 @@ interface Anonymous { failure = failure ) } -} \ No newline at end of file + + fun requestLogin( + anonymousLoginRequestBody: AnonymousLoginRequestBody, + success : (LoginResponse) -> Unit, + failure : (FetchError) -> Unit + ) { + api.performUserLogin( + anonymousLoginRequestBody = anonymousLoginRequestBody, + success = success, + failure = failure + ) + } +} + +typealias AuthDataResponse = AuthData +typealias LoginResponse = PlayResponse \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt index 83473fdf0..fa237e9f5 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt @@ -1,13 +1,15 @@ package app.lounge.users.anonymous import app.lounge.networking.FetchError +import com.aurora.gplayapi.GooglePlayApi +import com.aurora.gplayapi.data.providers.HeaderProvider import java.util.Properties interface AnonymousAPI { companion object { const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" - const val loginBaseURL: String = "GooglePlayApi.URL_SYNC" + const val loginBaseURL: String = GooglePlayApi.URL_SYNC+"/" fun create(baseURL: String = tokenBaseURL) : AnonymousAPI { return RetrofitAnonymousAPI( @@ -24,6 +26,12 @@ interface AnonymousAPI { failure : (FetchError) -> Unit ) + fun performUserLogin( + anonymousLoginRequestBody: AnonymousLoginRequestBody, + success : (LoginResponse) -> Unit, + failure : (FetchError) -> Unit + ) + object Header { val authData: (() -> String) -> Map = { mapOf(Pair("User-Agent", it.invoke())) @@ -37,3 +45,10 @@ data class AnonymousAuthDataRequestBody( val properties: Properties, val userAgent: String ) + +/** AnonymousLoginRequestBody */ +data class AnonymousLoginRequestBody( + val authDataResponse: AuthDataResponse, +) { + val header = HeaderProvider.getAuthHeaders(authDataResponse) +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt index 4cf90c452..d65a6633f 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -5,9 +5,10 @@ import app.lounge.networking.FetchError import app.lounge.networking.RetrofitFetching import app.lounge.networking.appLounge import app.lounge.networking.fetch -import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.providers.HeaderProvider import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody import retrofit2.Call import retrofit2.Retrofit import retrofit2.http.Body @@ -31,10 +32,16 @@ internal class RetrofitAnonymousAPI( @POST fun authDataRequest( - @Url url: String = "https://eu.gtoken.ecloud.global", + @Url url: String = AnonymousAPI.tokenBaseURL, @HeaderMap headers: Map, @Body requestBody: RequestBody ): Call + + @POST + fun loginUser( + @Url url: String = AnonymousAPI.loginBaseURL, + @HeaderMap headers: Map + ): Call } override fun requestAuthData( @@ -42,10 +49,14 @@ internal class RetrofitAnonymousAPI( success: (AuthDataResponse) -> Unit, failure: (FetchError) -> Unit ) { - val requestBody = RequestBody.create( - "application/json".toMediaTypeOrNull(), - anonymousAuthDataRequestBody.properties.toByteArray() - ) + val requestBody: RequestBody = + anonymousAuthDataRequestBody.properties.toByteArray().let { result -> + result.toRequestBody( + contentType = "application/json".toMediaTypeOrNull(), + offset = 0, + byteCount = result.size + ) + } fetch( endpoint = anonymousUserEndPoint.authDataRequest( @@ -59,6 +70,17 @@ internal class RetrofitAnonymousAPI( ) } -} - -typealias AuthDataResponse = AuthData \ No newline at end of file + override fun performUserLogin( + anonymousLoginRequestBody: AnonymousLoginRequestBody, + success: (LoginResponse) -> Unit, + failure: (FetchError) -> Unit + ) { + fetch( + endpoint = anonymousUserEndPoint.loginUser( + headers = anonymousLoginRequestBody.header + ), + success = success, + failure = failure + ) + } +} \ No newline at end of file -- GitLab From ca249997b95b3f979781ff9e15ac4612d519510e Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 5 Jul 2023 23:36:00 +0530 Subject: [PATCH 014/112] Handle auth data validation and timeout error --- .../lounge/networking/AnonymousUserAPITest.kt | 73 ++++++++++++++----- .../app/lounge/networking/RetrofitConfig.kt | 2 + .../app/lounge/users/anonymous/Anonymous.kt | 4 +- .../lounge/users/anonymous/AnonymousAPI.kt | 13 +++- .../users/anonymous/RetrofitAnonymousAPI.kt | 8 +- 5 files changed, 70 insertions(+), 30 deletions(-) diff --git a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt index 7deb467ce..e39bb9f48 100644 --- a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt @@ -12,11 +12,18 @@ import java.util.Properties class AnonymousUserAPITest { private val testAnonymousAPIForToken = Anonymous.anonymousRequestFor( - api = AnonymousAPI.create(AnonymousAPI.tokenBaseURL) + api = AnonymousAPI.create(baseURL = AnonymousAPI.tokenBaseURL) ) private val testAnonymousAPIForLogin = Anonymous.anonymousRequestFor( - api = AnonymousAPI.create(AnonymousAPI.loginBaseURL) + api = AnonymousAPI.create(baseURL = AnonymousAPI.loginBaseURL) + ) + + private val testAnonymousAPIForLoginTimeout = Anonymous.anonymousRequestFor( + api = AnonymousAPI.create( + baseURL = AnonymousAPI.loginBaseURL, + callTimeoutInSeconds = 1 + ) ) private val requestBodyData = AnonymousAuthDataRequestBody( @@ -24,9 +31,8 @@ class AnonymousUserAPITest { userAgent = testUserAgent ) - companion object { - lateinit var receivedData: AuthDataResponse + var receivedData: AuthDataResponse? = null } @Test @@ -34,32 +40,59 @@ class AnonymousUserAPITest { await { testAnonymousAPIForToken.requestAuthData( anonymousAuthDataRequestBody = requestBodyData, - success = { response -> - receivedData = response - assert(receivedData is AuthDataResponse) { "Assert!! Success must return data" } - }, - failure = {} + success = { response -> receivedData = response }, + failure = { } ) } + assert(receivedData is AuthDataResponse) { "Assert!! Success must return data" } } @Test fun test2OnSuccessReturnsLoginData(){ await { - testAnonymousAPIForLogin.requestLogin( - anonymousLoginRequestBody = AnonymousLoginRequestBody( - authDataResponse = receivedData - ), - success = { response -> - Assert.assertEquals("Hello", response.toString()) - }, - failure = { - Assert.assertEquals("Hello", it.toString()) - } - ) + receivedData?.let { authData -> + authData.dfeCookie = "null" + + testAnonymousAPIForLogin.requestLogin( + anonymousLoginRequestBody = AnonymousLoginRequestBody( + authDataResponse = authData + ), + success = { response -> + Assert.assertNotNull( + "Assert!! `response` must have data", + response + ) + }, + failure = {} + ) + } ?: run { + assert(receivedData == null) { "Assert!! `receivedData` must not have null" } + } } } + @Test + fun test3OnTimeoutFailureReturnsError(){ + await { + receivedData?.let { authData -> + authData.dfeCookie = "null" + testAnonymousAPIForLoginTimeout.requestLogin( + anonymousLoginRequestBody = AnonymousLoginRequestBody( + authDataResponse = authData + ), + success = {}, + failure = { + assert(it.toString().contains("timeout")) { + "Assert!! Failure callback must handle timeout error" + } + } + ) + } ?: run { + assert(receivedData == null) { "Assert!! `receivedData` must have data" } + } + + } + } } // region TestData diff --git a/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt b/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt index fb6290cdf..8c1314f69 100644 --- a/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt +++ b/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt @@ -26,6 +26,8 @@ internal fun Retrofit.Builder.appLounge( OkHttpClient.Builder() .addNetworkInterceptor(interceptor) .callTimeout(callTimeoutInSeconds, TimeUnit.SECONDS) + .connectTimeout(callTimeoutInSeconds, TimeUnit.SECONDS) + .readTimeout(callTimeoutInSeconds, TimeUnit.SECONDS) .followRedirects(shouldFollowRedirects) .build() ) diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt index c53d75237..3b1a2cbd0 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt @@ -2,7 +2,7 @@ package app.lounge.users.anonymous import app.lounge.networking.FetchError import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.models.PlayResponse +import okhttp3.ResponseBody /** * Implement API related to Anonymous login flow only. @@ -53,4 +53,4 @@ interface Anonymous { } typealias AuthDataResponse = AuthData -typealias LoginResponse = PlayResponse \ No newline at end of file +typealias LoginResponse = ResponseBody \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt index fa237e9f5..9d70ab61d 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt @@ -9,13 +9,13 @@ interface AnonymousAPI { companion object { const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" - const val loginBaseURL: String = GooglePlayApi.URL_SYNC+"/" + const val loginBaseURL: String = GooglePlayApi.URL_BASE - fun create(baseURL: String = tokenBaseURL) : AnonymousAPI { + fun create(baseURL: String = tokenBaseURL, callTimeoutInSeconds: Long = 10) : AnonymousAPI { return RetrofitAnonymousAPI( baseURL = baseURL, anonymousUserEndpointFollowsRedirects = true, - callTimeoutInSeconds = 30 + callTimeoutInSeconds = callTimeoutInSeconds ) } } @@ -38,6 +38,11 @@ interface AnonymousAPI { } } + object Path { + const val authData = "/" + const val sync = "/fdfe/apps/contentSync" + } + } /** AnonymousAuthDataRequestBody */ @@ -50,5 +55,5 @@ data class AnonymousAuthDataRequestBody( data class AnonymousLoginRequestBody( val authDataResponse: AuthDataResponse, ) { - val header = HeaderProvider.getAuthHeaders(authDataResponse) + val header = HeaderProvider.getDefaultHeaders(authDataResponse) } \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt index d65a6633f..d2d2aac97 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -9,10 +9,12 @@ import com.aurora.gplayapi.data.providers.HeaderProvider import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody +import org.json.JSONObject import retrofit2.Call import retrofit2.Retrofit import retrofit2.http.Body import retrofit2.http.HeaderMap +import retrofit2.http.Headers import retrofit2.http.POST import retrofit2.http.Url @@ -30,16 +32,14 @@ internal class RetrofitAnonymousAPI( interface AnonymousUserEndPointEndpoint { - @POST + @POST(AnonymousAPI.Path.authData) fun authDataRequest( - @Url url: String = AnonymousAPI.tokenBaseURL, @HeaderMap headers: Map, @Body requestBody: RequestBody ): Call - @POST + @POST(AnonymousAPI.Path.sync) fun loginUser( - @Url url: String = AnonymousAPI.loginBaseURL, @HeaderMap headers: Map ): Call } -- GitLab From 614472bc599f93d20b663f7a0545c31ebe851970 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 5 Jul 2023 23:41:46 +0530 Subject: [PATCH 015/112] Rename UserLogin api to AuthDataValidation --- .../app/lounge/networking/AnonymousUserAPITest.kt | 10 +++++----- .../java/app/lounge/users/anonymous/Anonymous.kt | 12 ++++++------ .../app/lounge/users/anonymous/AnonymousAPI.kt | 8 ++++---- .../lounge/users/anonymous/RetrofitAnonymousAPI.kt | 14 +++++--------- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt index e39bb9f48..897a93889 100644 --- a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt @@ -3,7 +3,7 @@ package app.lounge.networking import app.lounge.users.anonymous.Anonymous import app.lounge.users.anonymous.AnonymousAPI import app.lounge.users.anonymous.AnonymousAuthDataRequestBody -import app.lounge.users.anonymous.AnonymousLoginRequestBody +import app.lounge.users.anonymous.AnonymousAuthDataValidationRequestBody import app.lounge.users.anonymous.AuthDataResponse import org.junit.Assert import org.junit.Test @@ -53,8 +53,8 @@ class AnonymousUserAPITest { receivedData?.let { authData -> authData.dfeCookie = "null" - testAnonymousAPIForLogin.requestLogin( - anonymousLoginRequestBody = AnonymousLoginRequestBody( + testAnonymousAPIForLogin.requestAuthDataValidation( + anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( authDataResponse = authData ), success = { response -> @@ -76,8 +76,8 @@ class AnonymousUserAPITest { await { receivedData?.let { authData -> authData.dfeCookie = "null" - testAnonymousAPIForLoginTimeout.requestLogin( - anonymousLoginRequestBody = AnonymousLoginRequestBody( + testAnonymousAPIForLoginTimeout.requestAuthDataValidation( + anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( authDataResponse = authData ), success = {}, diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt index 3b1a2cbd0..8054cc998 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt @@ -39,13 +39,13 @@ interface Anonymous { ) } - fun requestLogin( - anonymousLoginRequestBody: AnonymousLoginRequestBody, - success : (LoginResponse) -> Unit, + fun requestAuthDataValidation( + anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody, + success : (AuthDataValidationResponse) -> Unit, failure : (FetchError) -> Unit ) { - api.performUserLogin( - anonymousLoginRequestBody = anonymousLoginRequestBody, + api.requestAuthDataValidation( + anonymousAuthDataValidationRequestBody = anonymousAuthDataValidationRequestBody, success = success, failure = failure ) @@ -53,4 +53,4 @@ interface Anonymous { } typealias AuthDataResponse = AuthData -typealias LoginResponse = ResponseBody \ No newline at end of file +typealias AuthDataValidationResponse = ResponseBody \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt index 9d70ab61d..44f9ad4e6 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt @@ -26,9 +26,9 @@ interface AnonymousAPI { failure : (FetchError) -> Unit ) - fun performUserLogin( - anonymousLoginRequestBody: AnonymousLoginRequestBody, - success : (LoginResponse) -> Unit, + fun requestAuthDataValidation( + anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody, + success : (AuthDataValidationResponse) -> Unit, failure : (FetchError) -> Unit ) @@ -52,7 +52,7 @@ data class AnonymousAuthDataRequestBody( ) /** AnonymousLoginRequestBody */ -data class AnonymousLoginRequestBody( +data class AnonymousAuthDataValidationRequestBody( val authDataResponse: AuthDataResponse, ) { val header = HeaderProvider.getDefaultHeaders(authDataResponse) diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt index d2d2aac97..49bf4c321 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -5,18 +5,14 @@ import app.lounge.networking.FetchError import app.lounge.networking.RetrofitFetching import app.lounge.networking.appLounge import app.lounge.networking.fetch -import com.aurora.gplayapi.data.providers.HeaderProvider import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody -import org.json.JSONObject import retrofit2.Call import retrofit2.Retrofit import retrofit2.http.Body import retrofit2.http.HeaderMap -import retrofit2.http.Headers import retrofit2.http.POST -import retrofit2.http.Url internal class RetrofitAnonymousAPI( baseURL: String, @@ -41,7 +37,7 @@ internal class RetrofitAnonymousAPI( @POST(AnonymousAPI.Path.sync) fun loginUser( @HeaderMap headers: Map - ): Call + ): Call } override fun requestAuthData( @@ -70,14 +66,14 @@ internal class RetrofitAnonymousAPI( ) } - override fun performUserLogin( - anonymousLoginRequestBody: AnonymousLoginRequestBody, - success: (LoginResponse) -> Unit, + override fun requestAuthDataValidation( + anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody, + success: (AuthDataValidationResponse) -> Unit, failure: (FetchError) -> Unit ) { fetch( endpoint = anonymousUserEndPoint.loginUser( - headers = anonymousLoginRequestBody.header + headers = anonymousAuthDataValidationRequestBody.header ), success = success, failure = failure -- GitLab From 8357ebe7fe25aa03da4030dee2d4820f3c6530cb Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 5 Jul 2023 23:48:55 +0530 Subject: [PATCH 016/112] AnonymousUserAPITest is handled in Anonymous Login Flow MR --- .../java/app/lounge/AnonymousUserAPITest.kt | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt deleted file mode 100644 index 916c6fce4..000000000 --- a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt +++ /dev/null @@ -1,79 +0,0 @@ -package app.lounge - -import app.lounge.users.anonymous.AnonymousAPI -import app.lounge.users.anonymous.AnonymousAuthDataRequestBody -import org.junit.Assert -import org.junit.Test -import java.util.Properties - -class AnonymousUserAPITest { - - private val testAnonymousAPI = AnonymousAPI.create( - baseURL = AnonymousAPI.tokenBaseURL - ) - - private val userAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" - - private val systemProperties = Properties().apply { - setProperty("UserReadableName", "coral-default") - setProperty("Build.HARDWARE", "coral") - setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") - setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") - setProperty("Build.BRAND", "google") - setProperty("Build.DEVICE", "coral") - setProperty("Build.VERSION.SDK_INT", "32") - setProperty("Build.VERSION.RELEASE", "12") - setProperty("Build.MODEL", "Pixel 4 XL") - setProperty("Build.MANUFACTURER", "Google") - setProperty("Build.PRODUCT", "coral") - setProperty("Build.ID", "SQ3A.220705.004") - setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") - setProperty("TouchScreen", "3") - setProperty("Keyboard", "1") - setProperty("Navigation", "1") - setProperty("ScreenLayout", "2") - setProperty("HasHardKeyboard", "false") - setProperty("HasFiveWayNavigation", "false") - setProperty("Screen.Density", "560") - setProperty("Screen.Width", "1440") - setProperty("Screen.Height", "2984") - setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") - setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") - setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") - setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") - setProperty("GL.Version", "196610") - setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") - setProperty("Client", "android-google") - setProperty("GSF.version", "223616055") - setProperty("Vending.version", "82151710") - setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") - setProperty("Roaming", "mobile-notroaming") - setProperty("TimeZone", "UTC-10") - setProperty("CellOperator", "310") - setProperty("SimOperator", "38") - } - - - private val requestBodyData = AnonymousAuthDataRequestBody( - properties = systemProperties, - userAgent = userAgent - ) - - @Test - fun testAPI() { - testAnonymousAPI.requestAuthData( - anonymousAuthDataRequestBody = requestBodyData, - success = { - print(it) - Assert.assertEquals("Hello", it) - }, - failure = { - print(it) - Assert.assertEquals("Hello", it.toString()) - } - ) - - Thread.sleep(30000) - } - -} \ No newline at end of file -- GitLab From 8c7bd53fb73456cf0531496ba18747bbe5da5b3e Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 6 Jul 2023 00:04:04 +0530 Subject: [PATCH 017/112] Update as per code review comment --- .../java/app/lounge/extension/Extension.kt | 5 ++++- ...{RetrofitFetching.kt => NetworkFetching.kt} | 18 +++++++++--------- .../users/anonymous/RetrofitAnonymousAPI.kt | 6 +++--- 3 files changed, 16 insertions(+), 13 deletions(-) rename modules/src/main/java/app/lounge/networking/{RetrofitFetching.kt => NetworkFetching.kt} (94%) diff --git a/modules/src/main/java/app/lounge/extension/Extension.kt b/modules/src/main/java/app/lounge/extension/Extension.kt index 133934a78..17a68e188 100644 --- a/modules/src/main/java/app/lounge/extension/Extension.kt +++ b/modules/src/main/java/app/lounge/extension/Extension.kt @@ -3,5 +3,8 @@ package app.lounge.extension import com.google.gson.Gson import java.util.Properties -/** Convert Properties parameter to byte array*/ +/** + * Convert Properties parameter to byte array + * @return Byte Array of Properties + * */ fun Properties.toByteArray() = Gson().toJson(this).toByteArray() \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/RetrofitFetching.kt b/modules/src/main/java/app/lounge/networking/NetworkFetching.kt similarity index 94% rename from modules/src/main/java/app/lounge/networking/RetrofitFetching.kt rename to modules/src/main/java/app/lounge/networking/NetworkFetching.kt index 6c38171a0..a8493d686 100644 --- a/modules/src/main/java/app/lounge/networking/RetrofitFetching.kt +++ b/modules/src/main/java/app/lounge/networking/NetworkFetching.kt @@ -8,7 +8,7 @@ import java.net.UnknownHostException //region Retrofit Asynchronous Networking -interface RetrofitFetching { +interface NetworkFetching { val executor: Executor get() = callEnqueue val checkNetwork: (() -> Boolean)? get() = null @@ -61,8 +61,8 @@ interface RetrofitFetching { * @param success Success callback with the response `R` * @param failure Failure callback with an error case from `AnyFetchError` subtypes */ -inline fun RetrofitFetching.fetch( - usingExecutor: RetrofitFetching.Executor = executor, +inline fun NetworkFetching.fetch( + usingExecutor: NetworkFetching.Executor = executor, endpoint: Call, noinline success: (R) -> Unit, noinline failure: (FetchError) -> Unit @@ -84,10 +84,10 @@ inline fun RetrofitFetching.fetch( * @param success Success callback with the response `R` * @param failure Failure callback with an error case from given error subtype `E` */ -fun RetrofitFetching.fetch( - usingExecutor: RetrofitFetching.Executor = executor, +fun NetworkFetching.fetch( + usingExecutor: NetworkFetching.Executor = executor, endpoint: Call, - resultProcessing: RetrofitFetching.ResultProcessing, + resultProcessing: NetworkFetching.ResultProcessing, success: (R) -> Unit, failure: (E) -> Unit ) { @@ -107,9 +107,9 @@ sealed class RetrofitResult { } private fun fetch( - usingExecutor: RetrofitFetching.Executor, + usingExecutor: NetworkFetching.Executor, endpoint: Call, - resultProcessing: RetrofitFetching.ResultProcessing, + resultProcessing: NetworkFetching.ResultProcessing, callback: (RetrofitResult) -> Unit, ) { usingExecutor.fetchAndCallback(endpoint, object : Callback { @@ -132,7 +132,7 @@ open class RetrofitResultProcessing( override val tryCastResponseBody: (Any?) -> R?, override val errorFromNetworkFailure: (AnyFetchError) -> E, hasNetwork: (() -> Boolean)? = null, -) : RetrofitFetching.ResultProcessing { +) : NetworkFetching.ResultProcessing { companion object { inline operator fun invoke( diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt index 8b2d3f608..68d90b84c 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt @@ -2,7 +2,7 @@ package app.lounge.users.anonymous import app.lounge.extension.toByteArray import app.lounge.networking.FetchError -import app.lounge.networking.RetrofitFetching +import app.lounge.networking.NetworkFetching import app.lounge.networking.appLounge import app.lounge.networking.fetch import okhttp3.MediaType.Companion.toMediaTypeOrNull @@ -18,7 +18,7 @@ internal class RetrofitAnonymousAPI( baseURL: String, anonymousUserEndpointFollowsRedirects: Boolean, callTimeoutInSeconds: Long, -): AnonymousAPI, RetrofitFetching { +): AnonymousAPI, NetworkFetching { private val anonymousUserEndPoint: AnonymousUserEndPointEndpoint = Retrofit.Builder().appLounge( baseURL = baseURL, @@ -30,7 +30,7 @@ internal class RetrofitAnonymousAPI( @POST fun authDataRequest( - @Url url: String = "https://eu.gtoken.ecloud.global", + @Url url: String = AnonymousAPI.tokenBaseURL, @HeaderMap headers: Map, @Body requestBody: RequestBody ): Call -- GitLab From 90ea86542ebcf07e6316bffcaed9b385534af8cc Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 6 Jul 2023 21:16:48 +0530 Subject: [PATCH 018/112] Modifiy the codebase as per review comment --- .../java/app/lounge/networking/AnonymousUserAPITest.kt | 4 ---- .../src/main/java/app/lounge/users/anonymous/Anonymous.kt | 7 ------- 2 files changed, 11 deletions(-) diff --git a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt index 897a93889..428b3d632 100644 --- a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt @@ -95,8 +95,6 @@ class AnonymousUserAPITest { } } -// region TestData - private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" private val testSystemProperties = Properties().apply { @@ -142,5 +140,3 @@ fun await(callback: () -> Unit) { callback.invoke() Thread.sleep(3000) } - -// endregion \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt index 8054cc998..2192e021e 100644 --- a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt +++ b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt @@ -4,13 +4,6 @@ import app.lounge.networking.FetchError import com.aurora.gplayapi.data.models.AuthData import okhttp3.ResponseBody -/** - * Implement API related to Anonymous login flow only. - * 1. Login api for Anonymous users. - * 2. Parsing Anonymous user data. For now use typealias object, later we will refactor with generic type - * 3. Add unit test cases for the api functions. - * */ - interface Anonymous { val api: AnonymousAPI -- GitLab From cd686a0ca4a1881802d74f0f5352adb43fe11913 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 6 Jul 2023 22:24:06 +0530 Subject: [PATCH 019/112] Fix CI build issue --- modules/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/build.gradle b/modules/build.gradle index 02731152a..0323af5d3 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -34,11 +34,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '17' + jvmTarget = '11' } } -- GitLab From 344a8921f0f18cdd1a24f92fc40a0c392360ef98 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 6 Jul 2023 22:47:35 +0530 Subject: [PATCH 020/112] Fix build integration issue --- modules/build.gradle | 6 +++--- .../src/main/java/app/lounge/storage/cache/Persistence.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/build.gradle b/modules/build.gradle index 6ac8c12d8..43b102cc5 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -30,11 +30,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '17' + jvmTarget = '11' } } diff --git a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt index 9c0a98302..2130a092b 100644 --- a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -47,7 +47,7 @@ class PersistedConfiguration(context: Context) { //region - Persistence (in shared preferences) -private class PersistedItem( +internal class PersistedItem( context: Context, val key: PersistenceKey, var defaultValue: T @@ -91,7 +91,7 @@ private class PersistedItem( } -private fun Context.persisted(key: PersistenceKey, defaultValue: T) : PersistedItem { +internal fun Context.persisted(key: PersistenceKey, defaultValue: T) : PersistedItem { return PersistedItem(context = this, key = key, defaultValue = defaultValue) } -- GitLab From 6d2b03df7a3608cdc20f60191c62b6e382d43a5e Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 6 Jul 2023 22:53:08 +0530 Subject: [PATCH 021/112] Modify Persistent name as per review comment --- .../java/app/lounge/PersistentStorageTest.kt | 6 +-- .../app/lounge/storage/cache/Persistence.kt | 49 +++++++------------ 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt index a51050bdf..6161b1cfb 100644 --- a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt +++ b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt @@ -2,7 +2,7 @@ package app.lounge import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import app.lounge.storage.cache.PersistedConfiguration +import app.lounge.storage.cache.PersistentConfiguration import app.lounge.storage.cache.PersistenceKey import app.lounge.storage.cache.configurations import com.google.gson.Gson @@ -15,7 +15,7 @@ import kotlin.reflect.KClassifier @RunWith(AndroidJUnit4::class) class PersistentStorageTest { - private lateinit var testConfiguration: PersistedConfiguration + private lateinit var testConfiguration: PersistentConfiguration @Before fun setupPersistentConfiguration(){ @@ -92,7 +92,7 @@ private inline fun T.getPropertyReturnType(name: String): KCla .firstOrNull { it.name == name } ?.returnType ?.classifier -private fun PersistedConfiguration.evaluateValue(classifier: KClassifier?, key: PersistenceKey) { +private fun PersistentConfiguration.evaluateValue(classifier: KClassifier?, key: PersistenceKey) { when(classifier){ Int::class -> Assert.assertEquals( "Expected to be `$testIntValue`", testIntValue, this.callMethod(key.name) as Int) diff --git a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt index 2130a092b..6c089a458 100644 --- a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -4,13 +4,7 @@ import android.content.Context import kotlin.reflect.KProperty -//region Setter Accessors (primarily used for setting) - -val Context.configurations: PersistedConfiguration get() = PersistedConfiguration(context = this) - -//endregion - -// region - Persistence Configuration +val Context.configurations: PersistentConfiguration get() = PersistentConfiguration(context = this) internal enum class PersistenceKey { updateInstallAuto, @@ -28,26 +22,22 @@ internal enum class PersistenceKey { tosversion } -class PersistedConfiguration(context: Context) { - var updateInstallAuto by context.persisted(PersistenceKey.updateInstallAuto, false) - var updateCheckIntervals by context.persisted(PersistenceKey.updateCheckIntervals, 24) - var updateAppsFromOtherStores by context.persisted(PersistenceKey.updateAppsFromOtherStores, false) - var showAllApplications by context.persisted(PersistenceKey.showAllApplications, true) - var showPWAApplications by context.persisted(PersistenceKey.showPWAApplications, true) - var showFOSSApplications by context.persisted(PersistenceKey.showFOSSApplications, true) - var authData by context.persisted(PersistenceKey.authData, "") - var email by context.persisted(PersistenceKey.email, "") - var oauthtoken by context.persisted(PersistenceKey.oauthtoken, "") - var userType by context.persisted(PersistenceKey.userType, "") - var tocStatus by context.persisted(PersistenceKey.tocStatus, false) - var tosversion by context.persisted(PersistenceKey.tosversion, "") +class PersistentConfiguration(context: Context) { + var updateInstallAuto by context.persistent(PersistenceKey.updateInstallAuto, false) + var updateCheckIntervals by context.persistent(PersistenceKey.updateCheckIntervals, 24) + var updateAppsFromOtherStores by context.persistent(PersistenceKey.updateAppsFromOtherStores, false) + var showAllApplications by context.persistent(PersistenceKey.showAllApplications, true) + var showPWAApplications by context.persistent(PersistenceKey.showPWAApplications, true) + var showFOSSApplications by context.persistent(PersistenceKey.showFOSSApplications, true) + var authData by context.persistent(PersistenceKey.authData, "") + var email by context.persistent(PersistenceKey.email, "") + var oauthtoken by context.persistent(PersistenceKey.oauthtoken, "") + var userType by context.persistent(PersistenceKey.userType, "") + var tocStatus by context.persistent(PersistenceKey.tocStatus, false) + var tosversion by context.persistent(PersistenceKey.tosversion, "") } -// endregion - -//region - Persistence (in shared preferences) - -internal class PersistedItem( +internal class PersistentItem( context: Context, val key: PersistenceKey, var defaultValue: T @@ -90,9 +80,6 @@ internal class PersistedItem( } } - -internal fun Context.persisted(key: PersistenceKey, defaultValue: T) : PersistedItem { - return PersistedItem(context = this, key = key, defaultValue = defaultValue) -} - -//endregion \ No newline at end of file +internal fun Context.persistent(key: PersistenceKey, defaultValue: T) : PersistentItem { + return PersistentItem(context = this, key = key, defaultValue = defaultValue) +} \ No newline at end of file -- GitLab From 82e7a42b75948cc0473082097feb4942ef346f44 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 6 Jul 2023 22:47:35 +0530 Subject: [PATCH 022/112] Fix build integration issue --- modules/src/main/java/app/lounge/storage/cache/Persistence.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt index 9c0a98302..2130a092b 100644 --- a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -47,7 +47,7 @@ class PersistedConfiguration(context: Context) { //region - Persistence (in shared preferences) -private class PersistedItem( +internal class PersistedItem( context: Context, val key: PersistenceKey, var defaultValue: T @@ -91,7 +91,7 @@ private class PersistedItem( } -private fun Context.persisted(key: PersistenceKey, defaultValue: T) : PersistedItem { +internal fun Context.persisted(key: PersistenceKey, defaultValue: T) : PersistedItem { return PersistedItem(context = this, key = key, defaultValue = defaultValue) } -- GitLab From 8c7f3eb191fcc5af22bb1b2d09a6a8f7339f0a02 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 6 Jul 2023 22:53:08 +0530 Subject: [PATCH 023/112] Modify Persistent name as per review comment --- .../java/app/lounge/PersistentStorageTest.kt | 6 +-- .../app/lounge/storage/cache/Persistence.kt | 49 +++++++------------ 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt index a51050bdf..6161b1cfb 100644 --- a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt +++ b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt @@ -2,7 +2,7 @@ package app.lounge import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import app.lounge.storage.cache.PersistedConfiguration +import app.lounge.storage.cache.PersistentConfiguration import app.lounge.storage.cache.PersistenceKey import app.lounge.storage.cache.configurations import com.google.gson.Gson @@ -15,7 +15,7 @@ import kotlin.reflect.KClassifier @RunWith(AndroidJUnit4::class) class PersistentStorageTest { - private lateinit var testConfiguration: PersistedConfiguration + private lateinit var testConfiguration: PersistentConfiguration @Before fun setupPersistentConfiguration(){ @@ -92,7 +92,7 @@ private inline fun T.getPropertyReturnType(name: String): KCla .firstOrNull { it.name == name } ?.returnType ?.classifier -private fun PersistedConfiguration.evaluateValue(classifier: KClassifier?, key: PersistenceKey) { +private fun PersistentConfiguration.evaluateValue(classifier: KClassifier?, key: PersistenceKey) { when(classifier){ Int::class -> Assert.assertEquals( "Expected to be `$testIntValue`", testIntValue, this.callMethod(key.name) as Int) diff --git a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt index 2130a092b..6c089a458 100644 --- a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -4,13 +4,7 @@ import android.content.Context import kotlin.reflect.KProperty -//region Setter Accessors (primarily used for setting) - -val Context.configurations: PersistedConfiguration get() = PersistedConfiguration(context = this) - -//endregion - -// region - Persistence Configuration +val Context.configurations: PersistentConfiguration get() = PersistentConfiguration(context = this) internal enum class PersistenceKey { updateInstallAuto, @@ -28,26 +22,22 @@ internal enum class PersistenceKey { tosversion } -class PersistedConfiguration(context: Context) { - var updateInstallAuto by context.persisted(PersistenceKey.updateInstallAuto, false) - var updateCheckIntervals by context.persisted(PersistenceKey.updateCheckIntervals, 24) - var updateAppsFromOtherStores by context.persisted(PersistenceKey.updateAppsFromOtherStores, false) - var showAllApplications by context.persisted(PersistenceKey.showAllApplications, true) - var showPWAApplications by context.persisted(PersistenceKey.showPWAApplications, true) - var showFOSSApplications by context.persisted(PersistenceKey.showFOSSApplications, true) - var authData by context.persisted(PersistenceKey.authData, "") - var email by context.persisted(PersistenceKey.email, "") - var oauthtoken by context.persisted(PersistenceKey.oauthtoken, "") - var userType by context.persisted(PersistenceKey.userType, "") - var tocStatus by context.persisted(PersistenceKey.tocStatus, false) - var tosversion by context.persisted(PersistenceKey.tosversion, "") +class PersistentConfiguration(context: Context) { + var updateInstallAuto by context.persistent(PersistenceKey.updateInstallAuto, false) + var updateCheckIntervals by context.persistent(PersistenceKey.updateCheckIntervals, 24) + var updateAppsFromOtherStores by context.persistent(PersistenceKey.updateAppsFromOtherStores, false) + var showAllApplications by context.persistent(PersistenceKey.showAllApplications, true) + var showPWAApplications by context.persistent(PersistenceKey.showPWAApplications, true) + var showFOSSApplications by context.persistent(PersistenceKey.showFOSSApplications, true) + var authData by context.persistent(PersistenceKey.authData, "") + var email by context.persistent(PersistenceKey.email, "") + var oauthtoken by context.persistent(PersistenceKey.oauthtoken, "") + var userType by context.persistent(PersistenceKey.userType, "") + var tocStatus by context.persistent(PersistenceKey.tocStatus, false) + var tosversion by context.persistent(PersistenceKey.tosversion, "") } -// endregion - -//region - Persistence (in shared preferences) - -internal class PersistedItem( +internal class PersistentItem( context: Context, val key: PersistenceKey, var defaultValue: T @@ -90,9 +80,6 @@ internal class PersistedItem( } } - -internal fun Context.persisted(key: PersistenceKey, defaultValue: T) : PersistedItem { - return PersistedItem(context = this, key = key, defaultValue = defaultValue) -} - -//endregion \ No newline at end of file +internal fun Context.persistent(key: PersistenceKey, defaultValue: T) : PersistentItem { + return PersistentItem(context = this, key = key, defaultValue = defaultValue) +} \ No newline at end of file -- GitLab From 3eb894861a48bb91ced0d4476507112d21701623 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 7 Jul 2023 00:03:01 +0530 Subject: [PATCH 024/112] Modifed FetchError as per review comments --- .../lounge/networking/AnonymousUserAPITest.kt | 37 ++++++++----------- .../java/app/lounge/networking/FetchError.kt | 23 +++++------- .../app/lounge/networking/NetworkFetching.kt | 34 +++++++++-------- 3 files changed, 43 insertions(+), 51 deletions(-) diff --git a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt index 428b3d632..ab364f291 100644 --- a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt @@ -5,7 +5,7 @@ import app.lounge.users.anonymous.AnonymousAPI import app.lounge.users.anonymous.AnonymousAuthDataRequestBody import app.lounge.users.anonymous.AnonymousAuthDataValidationRequestBody import app.lounge.users.anonymous.AuthDataResponse -import org.junit.Assert +import app.lounge.users.anonymous.AuthDataValidationResponse import org.junit.Test import java.util.Properties @@ -32,7 +32,7 @@ class AnonymousUserAPITest { ) companion object { - var receivedData: AuthDataResponse? = null + var authData: AuthDataResponse? = null } @Test @@ -40,58 +40,51 @@ class AnonymousUserAPITest { await { testAnonymousAPIForToken.requestAuthData( anonymousAuthDataRequestBody = requestBodyData, - success = { response -> receivedData = response }, + success = { response -> authData = response }, failure = { } ) } - assert(receivedData is AuthDataResponse) { "Assert!! Success must return data" } + assert(authData is AuthDataResponse) { "Assert!! Success must return data" } } @Test fun test2OnSuccessReturnsLoginData(){ + var response: AuthDataValidationResponse? = null await { - receivedData?.let { authData -> + authData?.let { authData -> authData.dfeCookie = "null" testAnonymousAPIForLogin.requestAuthDataValidation( anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( authDataResponse = authData ), - success = { response -> - Assert.assertNotNull( - "Assert!! `response` must have data", - response - ) - }, + success = { result -> response = result }, failure = {} ) - } ?: run { - assert(receivedData == null) { "Assert!! `receivedData` must not have null" } } } + assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } + assert(response is AuthDataValidationResponse) { "Assert!! `response` must have data" } } @Test fun test3OnTimeoutFailureReturnsError(){ + var failure: FetchError? = null await { - receivedData?.let { authData -> + authData?.let { authData -> authData.dfeCookie = "null" testAnonymousAPIForLoginTimeout.requestAuthDataValidation( anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( authDataResponse = authData ), success = {}, - failure = { - assert(it.toString().contains("timeout")) { - "Assert!! Failure callback must handle timeout error" - } - } + failure = { error -> failure = error } ) - } ?: run { - assert(receivedData == null) { "Assert!! `receivedData` must have data" } } - } + assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } + failure?.description?.contains("timeout") + ?.let { assert(it) { "Assert!! Timeout Failure callback must call" } } } } diff --git a/modules/src/main/java/app/lounge/networking/FetchError.kt b/modules/src/main/java/app/lounge/networking/FetchError.kt index 271b4ab3b..a02f19392 100644 --- a/modules/src/main/java/app/lounge/networking/FetchError.kt +++ b/modules/src/main/java/app/lounge/networking/FetchError.kt @@ -1,28 +1,25 @@ package app.lounge.networking -//region Generic Network Error Types - sealed interface FetchError { - data class Network(val underlyingError: AnyFetchError) : FetchError -} - -/** Supertype for network error types. */ -sealed interface AnyFetchError { var description: String - enum class BadRequest(override var description: String) : AnyFetchError { + enum class BadRequest(override var description: String) : FetchError { Encode("FIXME: Error encoding request! $dumpKeyWord"), Decode("FIXME: Error decoding request! $dumpKeyWord"), } - enum class NotFound(override var description: String) : AnyFetchError { + enum class NotFound(override var description: String) : FetchError { MissingData("No data found! $dumpKeyWord"), MissingNetwork("No network! $dumpKeyWord") } - data class BadStatusCode (val statusCode: Int, val rawResponse: Any) : AnyFetchError { + enum class InterruptedIO(override var description: String) : FetchError { + Timeout("Timeout Error! $dumpKeyWord") + } + + data class BadStatusCode (val statusCode: Int, val rawResponse: Any) : FetchError { override var description: String = "Bad status code: $statusCode. Raw response: $rawResponse" } @@ -34,17 +31,15 @@ sealed interface AnyFetchError { */ data class Unknown( override var description: String = "Unknown Error! $dumpKeyWord" - ) : AnyFetchError + ) : FetchError companion object { const val dumpKeyWord: String = "dump:-" - fun make(error: AnyFetchError, addingDump: String) : AnyFetchError { + fun make(error: FetchError, addingDump: String = "") : FetchError { error.description = error.description + addingDump return error } } } - -//endregion diff --git a/modules/src/main/java/app/lounge/networking/NetworkFetching.kt b/modules/src/main/java/app/lounge/networking/NetworkFetching.kt index a8493d686..ccbc0f8d6 100644 --- a/modules/src/main/java/app/lounge/networking/NetworkFetching.kt +++ b/modules/src/main/java/app/lounge/networking/NetworkFetching.kt @@ -3,6 +3,7 @@ package app.lounge.networking import retrofit2.Call import retrofit2.Callback import retrofit2.Response +import java.io.InterruptedIOException import java.lang.IllegalStateException import java.net.UnknownHostException @@ -35,7 +36,7 @@ interface NetworkFetching { val errorFromFailureResponse: (call: Call<*>, t: Throwable) -> E /** Return error object `E` that contains/represents the given `AnyFetchError` */ - val errorFromNetworkFailure: (AnyFetchError) -> E + val errorFromNetworkFailure: (FetchError) -> E } @@ -68,7 +69,7 @@ inline fun NetworkFetching.fetch( noinline failure: (FetchError) -> Unit ) { val resultProcessing = RetrofitResultProcessing( - errorFromNetworkFailure = { FetchError.Network(it) }, + errorFromNetworkFailure = { FetchError.make(it) }, hasNetwork = checkNetwork ) fetch(usingExecutor, endpoint, resultProcessing, success, failure) @@ -130,13 +131,13 @@ private fun fetch( /** Returns result processing object for given response type `R` and error type `E` */ open class RetrofitResultProcessing( override val tryCastResponseBody: (Any?) -> R?, - override val errorFromNetworkFailure: (AnyFetchError) -> E, + override val errorFromNetworkFailure: (FetchError) -> E, hasNetwork: (() -> Boolean)? = null, ) : NetworkFetching.ResultProcessing { companion object { inline operator fun invoke( - noinline errorFromNetworkFailure: (AnyFetchError) -> E, + noinline errorFromNetworkFailure: (FetchError) -> E, noinline hasNetwork: (() -> Boolean)? = null ) : RetrofitResultProcessing { return RetrofitResultProcessing( @@ -150,15 +151,16 @@ open class RetrofitResultProcessing( } override var errorFromFailureResponse: (call: Call<*>, t: Throwable) -> E = { call, t -> - val error: AnyFetchError = when(t) { + val error: FetchError = when(t) { is UnknownHostException -> errorForUnknownHostException is IllegalStateException -> errorForIllegalStateException + is InterruptedIOException -> errorForInterruptedIOException //TODO: Check other cases - else -> AnyFetchError.Unknown() + else -> FetchError.Unknown() } errorFromNetworkFailure( - AnyFetchError.make(error = error, addingDump = "(Throwable): $t\n(Call): $call") + FetchError.make(error = error, addingDump = "(Throwable): $t\n(Call): $call") ) } @@ -169,30 +171,32 @@ open class RetrofitResultProcessing( RetrofitResult.Success(body) } ?: RetrofitResult.Failure( errorFromNetworkFailure( - AnyFetchError.make( - error = AnyFetchError.NotFound.MissingData, + FetchError.make( + error = FetchError.NotFound.MissingData, addingDump = "(Response): $response\n(Call): $call" ) ) ) } else { RetrofitResult.Failure( - errorFromNetworkFailure(AnyFetchError.BadStatusCode(response.code(), response)) + errorFromNetworkFailure(FetchError.BadStatusCode(response.code(), response)) ) } } //region Exception to AnyFetchError mapping - var errorForUnknownHostException: AnyFetchError = if (hasNetwork != null) { - if (hasNetwork()) AnyFetchError.BadRequest.Encode - else AnyFetchError.NotFound.MissingNetwork + var errorForUnknownHostException: FetchError = if (hasNetwork != null) { + if (hasNetwork()) FetchError.BadRequest.Encode + else FetchError.NotFound.MissingNetwork } else { // Cannot distinguish the error case from `MissingNetwork` and `Encode` error. - AnyFetchError.Unknown() + FetchError.Unknown() } - var errorForIllegalStateException: AnyFetchError = AnyFetchError.BadRequest.Decode + var errorForIllegalStateException: FetchError = FetchError.BadRequest.Decode + + var errorForInterruptedIOException: FetchError = FetchError.InterruptedIO.Timeout //endregion -- GitLab From dc40dc7f09e4acb316f53ea358acf02649f0a468 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sun, 16 Jul 2023 21:16:39 +0530 Subject: [PATCH 025/112] Implement network api using coroutine --- app/build.gradle | 2 + modules/build.gradle | 20 +- .../java/app/lounge/NetworkFetchingAPITest.kt | 149 +++++++++++++ .../main/java/app/lounge/di/NetworkModule.kt | 59 +++++ .../src/main/java/app/lounge/model/Model.kt | 24 +++ .../java/app/lounge/networking/FetchError.kt | 50 ----- .../app/lounge/networking/NetworkFetching.kt | 204 +----------------- .../networking/NetworkFetchingRetrofitImpl.kt | 88 ++++++++ .../app/lounge/networking/RetrofitConfig.kt | 40 ---- .../src/main/java/app/lounge/users/Google.kt | 8 - .../src/main/java/app/lounge/users/Session.kt | 11 - .../app/lounge/users/anonymous/Anonymous.kt | 39 ---- .../lounge/users/anonymous/AnonymousAPI.kt | 41 ---- .../users/anonymous/RetrofitAnonymousAPI.kt | 63 ------ 14 files changed, 349 insertions(+), 449 deletions(-) create mode 100644 modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt create mode 100644 modules/src/main/java/app/lounge/di/NetworkModule.kt create mode 100644 modules/src/main/java/app/lounge/model/Model.kt delete mode 100644 modules/src/main/java/app/lounge/networking/FetchError.kt create mode 100644 modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt delete mode 100644 modules/src/main/java/app/lounge/networking/RetrofitConfig.kt delete mode 100644 modules/src/main/java/app/lounge/users/Google.kt delete mode 100644 modules/src/main/java/app/lounge/users/Session.kt delete mode 100644 modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt delete mode 100644 modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt delete mode 100644 modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt diff --git a/app/build.gradle b/app/build.gradle index 1d0b1c0d7..f0e0d5e26 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -147,6 +147,8 @@ allOpen { dependencies { + implementation project(path: ':modules') + // TODO: Add splitinstall-lib to a repo https://gitlab.e.foundation/e/os/backlog/-/issues/628 api files('libs/splitinstall-lib.jar') diff --git a/modules/build.gradle b/modules/build.gradle index 4ef71300d..d36cf010a 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -1,6 +1,8 @@ plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' + id("com.google.dagger.hilt.android") } ext { @@ -8,9 +10,14 @@ ext { android_target_sdk_version = 33 core_version = '1.10.1' gson_version = '2.9.0' - kotlin_reflection = '1.8.10' + kotlin_reflection = '1.8.20' retrofit_version = '2.9.0' retrofit_interceptor_version = '5.0.0-alpha.2' + kotlin_coroutines_core = '1.7.2' + google_play_api = '3.0.1' + protobuf_java = '3.19.3' + javax_version = '1' + dagger_hilt_version = '2.46.1' } android { @@ -45,13 +52,20 @@ dependencies { implementation "androidx.core:core-ktx:$core_version" implementation "com.google.code.gson:gson:$gson_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_reflection" + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_core") - // region Retrofit implementation("com.squareup.retrofit2:retrofit:$retrofit_version") implementation("com.squareup.retrofit2:converter-gson:$retrofit_version") implementation("com.squareup.retrofit2:converter-scalars:$retrofit_version") implementation("com.squareup.okhttp3:logging-interceptor:$retrofit_interceptor_version") - // endregion + + implementation("foundation.e:gplayapi:$google_play_api") + implementation("com.google.protobuf:protobuf-java:$protobuf_java") + + implementation("javax.inject:javax.inject:$javax_version") + + implementation("com.google.dagger:hilt-android:$dagger_hilt_version") + kapt("com.google.dagger:hilt-android-compiler:$dagger_hilt_version") testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt b/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt new file mode 100644 index 000000000..d2b92e219 --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt @@ -0,0 +1,149 @@ +package app.lounge + +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AnonymousAuthDataValidationRequestBody +import app.lounge.model.AuthDataResponse +import app.lounge.model.AuthDataValidationResponse +import app.lounge.networking.NetworkFetching +import app.lounge.networking.NetworkFetchingRetrofitAPI +import app.lounge.networking.NetworkFetchingRetrofitImpl +import kotlinx.coroutines.runBlocking +import okhttp3.OkHttpClient +import org.junit.Test +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.io.InterruptedIOException +import java.util.Properties +import java.util.concurrent.TimeUnit + +class NetworkFetchingAPITest { + + private val networkFetchingToken: NetworkFetching = NetworkFetchingRetrofitImpl( + Retrofit.Builder() + .baseUrl(NetworkFetchingRetrofitAPI.tokenBaseURL) + .addConverterFactory(GsonConverterFactory.create()) + .client( + OkHttpClient.Builder() + .callTimeout(10, TimeUnit.SECONDS) + .build() + ) + .build() + ) + + private val networkFetchingGoogle: NetworkFetching = NetworkFetchingRetrofitImpl( + Retrofit.Builder() + .baseUrl(NetworkFetchingRetrofitAPI.googlePlayBaseURL) + .addConverterFactory(GsonConverterFactory.create()) + .client( + OkHttpClient.Builder() + .callTimeout(10, TimeUnit.SECONDS) + .build() + ) + .build() + ) + + private val networkFetchingTimeoutGoogle: NetworkFetching = NetworkFetchingRetrofitImpl( + Retrofit.Builder() + .baseUrl(NetworkFetchingRetrofitAPI.googlePlayBaseURL) + .addConverterFactory(GsonConverterFactory.create()) + .client( + OkHttpClient.Builder() + .callTimeout(10, TimeUnit.MILLISECONDS) + .build() + ) + .build() + ) + + private val requestBodyData = AnonymousAuthDataRequestBody( + properties = testSystemProperties, + userAgent = testUserAgent + ) + + companion object { + var authData: AuthDataResponse? = null + } + + @Test + fun test1OnSuccessReturnsAuthData() = runBlocking { + authData = networkFetchingToken.requestAuthData( + anonymousAuthDataRequestBody = requestBodyData, + ) + assert(authData is AuthDataResponse) { "Assert!! Success must return data" } + } + + @Test + fun test2OnSuccessReturnsLoginData() = runBlocking { + var result: AuthDataValidationResponse? = null + authData?.let { authData -> + authData.dfeCookie = "null" + + result = networkFetchingGoogle.requestAuthDataValidation( + anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( + authDataResponse = authData + ) + ) + } + + assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } + assert(result is AuthDataValidationResponse) { "Assert!! `response` must have data" } + } + + @Test + fun test3OnTimeoutFailureReturnsError(): Unit = runBlocking { + var failure: Exception = Exception("No Error") + authData?.let { authData -> + authData.dfeCookie = "null" + + try { + networkFetchingTimeoutGoogle.requestAuthDataValidation( + anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( + authDataResponse = authData + ) + ) + } catch (e: InterruptedIOException) { failure = e } + } + assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } + assert(failure is InterruptedIOException) { "Assert!! Timeout Failure callback must call" } + } +} + +private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" + +private val testSystemProperties = Properties().apply { + setProperty("UserReadableName", "coral-default") + setProperty("Build.HARDWARE", "coral") + setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") + setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") + setProperty("Build.BRAND", "google") + setProperty("Build.DEVICE", "coral") + setProperty("Build.VERSION.SDK_INT", "32") + setProperty("Build.VERSION.RELEASE", "12") + setProperty("Build.MODEL", "Pixel 4 XL") + setProperty("Build.MANUFACTURER", "Google") + setProperty("Build.PRODUCT", "coral") + setProperty("Build.ID", "SQ3A.220705.004") + setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") + setProperty("TouchScreen", "3") + setProperty("Keyboard", "1") + setProperty("Navigation", "1") + setProperty("ScreenLayout", "2") + setProperty("HasHardKeyboard", "false") + setProperty("HasFiveWayNavigation", "false") + setProperty("Screen.Density", "560") + setProperty("Screen.Width", "1440") + setProperty("Screen.Height", "2984") + setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") + setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") + setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") + setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") + setProperty("GL.Version", "196610") + setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") + setProperty("Client", "android-google") + setProperty("GSF.version", "223616055") + setProperty("Vending.version", "82151710") + setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") + setProperty("Roaming", "mobile-notroaming") + setProperty("TimeZone", "UTC-10") + setProperty("CellOperator", "310") + setProperty("SimOperator", "38") +} diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt new file mode 100644 index 000000000..1763676d2 --- /dev/null +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -0,0 +1,59 @@ +package app.lounge.di + +import app.lounge.networking.NetworkFetchingRetrofitAPI +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit +import javax.inject.Named +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object NetworkModule { + + private const val HTTP_TIMEOUT = 10L + + @Provides + @Singleton + internal fun providesRetrofit( + okHttpClient: OkHttpClient, + baseUrl: String = NetworkFetchingRetrofitAPI.tokenBaseURL + ) : Retrofit { + return Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .client(okHttpClient) + .build() + } + + @Provides + @Singleton + @Named("privateOkHttpClient") + internal fun providesOkHttpClient( + httpLogger: HttpLoggingInterceptor + ): OkHttpClient { + return OkHttpClient.Builder() + .addNetworkInterceptor(httpLogger) + .callTimeout(HTTP_TIMEOUT, TimeUnit.SECONDS) + .build() + } + + @Provides + @Singleton + internal fun providesHttpLogger() : HttpLoggingInterceptor { + return run { + val httpLoggingInterceptor = HttpLoggingInterceptor() + httpLoggingInterceptor.apply { + httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY + } + } + } + +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/model/Model.kt b/modules/src/main/java/app/lounge/model/Model.kt new file mode 100644 index 000000000..ba4263416 --- /dev/null +++ b/modules/src/main/java/app/lounge/model/Model.kt @@ -0,0 +1,24 @@ +package app.lounge.model + +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.providers.HeaderProvider +import okhttp3.ResponseBody +import java.util.Properties + + +/** AnonymousAuthDataRequestBody */ +data class AnonymousAuthDataRequestBody( + val properties: Properties, + val userAgent: String +) + +/** AnonymousLoginRequestBody */ +data class AnonymousAuthDataValidationRequestBody( + val authDataResponse: AuthDataResponse, +) { + val header = HeaderProvider.getDefaultHeaders(authDataResponse) +} + +typealias AuthDataResponse = AuthData + +typealias AuthDataValidationResponse = ResponseBody \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/FetchError.kt b/modules/src/main/java/app/lounge/networking/FetchError.kt deleted file mode 100644 index 271b4ab3b..000000000 --- a/modules/src/main/java/app/lounge/networking/FetchError.kt +++ /dev/null @@ -1,50 +0,0 @@ -package app.lounge.networking - - -//region Generic Network Error Types - -sealed interface FetchError { - data class Network(val underlyingError: AnyFetchError) : FetchError -} - -/** Supertype for network error types. */ -sealed interface AnyFetchError { - - var description: String - - enum class BadRequest(override var description: String) : AnyFetchError { - Encode("FIXME: Error encoding request! $dumpKeyWord"), - Decode("FIXME: Error decoding request! $dumpKeyWord"), - } - - enum class NotFound(override var description: String) : AnyFetchError { - MissingData("No data found! $dumpKeyWord"), - MissingNetwork("No network! $dumpKeyWord") - } - - data class BadStatusCode (val statusCode: Int, val rawResponse: Any) : AnyFetchError { - override var description: String = - "Bad status code: $statusCode. Raw response: $rawResponse" - } - - /** - * Represents a vague error case typically caused by `UnknownHostException`. - * This error case is encountered if and only if network status cannot be determined - * while the `UnknownHostException` is received. - */ - data class Unknown( - override var description: String = "Unknown Error! $dumpKeyWord" - ) : AnyFetchError - - companion object { - const val dumpKeyWord: String = "dump:-" - - fun make(error: AnyFetchError, addingDump: String) : AnyFetchError { - error.description = error.description + addingDump - return error - } - } - -} - -//endregion diff --git a/modules/src/main/java/app/lounge/networking/NetworkFetching.kt b/modules/src/main/java/app/lounge/networking/NetworkFetching.kt index a8493d686..c544c7aac 100644 --- a/modules/src/main/java/app/lounge/networking/NetworkFetching.kt +++ b/modules/src/main/java/app/lounge/networking/NetworkFetching.kt @@ -1,201 +1,17 @@ package app.lounge.networking -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response -import java.lang.IllegalStateException -import java.net.UnknownHostException +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AnonymousAuthDataValidationRequestBody +import app.lounge.model.AuthDataResponse +import app.lounge.model.AuthDataValidationResponse - -//region Retrofit Asynchronous Networking interface NetworkFetching { - val executor: Executor get() = callEnqueue - - val checkNetwork: (() -> Boolean)? get() = null - - interface Executor { - fun fetchAndCallback(endpoint: Call, callback: Callback) - } - - /** - * An object that receives Retrofit `Callback` arguments and determines whether or not - * an error should be returned. Use to return specific error cases for a given network request. - */ - interface ResultProcessing { - /** - * Return `R` if possible to cast given object to `R` or return `null`. - * This lambda is required due to jvm's type-erasing for generic types. - */ - val tryCastResponseBody: (Any?) -> R? - - /** Return result, success/failure, for the received request response */ - val resultFromResponse: (call: Call, response: Response) -> RetrofitResult - - /** Return error object `E` for the given failed request. */ - val errorFromFailureResponse: (call: Call<*>, t: Throwable) -> E - - /** Return error object `E` that contains/represents the given `AnyFetchError` */ - val errorFromNetworkFailure: (AnyFetchError) -> E - - } - - companion object { - /** Creates and returns a network request executor using Retrofit `Call` enqueue. */ - val callEnqueue: Executor - get() { - return object : Executor { - override fun fetchAndCallback(endpoint: Call, callback: Callback) { - endpoint.enqueue(callback) - } - } - } - } -} - -/** - * Fetch for response type `R` and callback in `success` callback. Invokes failure with - * an error subtype of `AnyFetchError` upon failure. - * - * @param usingExecutor Network request executor (set to `this.executor` by default) - * @param endpoint The API endpoint that should be fetched - * @param success Success callback with the response `R` - * @param failure Failure callback with an error case from `AnyFetchError` subtypes - */ -inline fun NetworkFetching.fetch( - usingExecutor: NetworkFetching.Executor = executor, - endpoint: Call, - noinline success: (R) -> Unit, - noinline failure: (FetchError) -> Unit -) { - val resultProcessing = RetrofitResultProcessing( - errorFromNetworkFailure = { FetchError.Network(it) }, - hasNetwork = checkNetwork - ) - fetch(usingExecutor, endpoint, resultProcessing, success, failure) -} - -/** - * Fetch for response type `R` and callback in `success` callback. Invokes failure with - * an error subtype of `E : AnyFetchError` upon failure. - * - * @param usingExecutor Network request executor (set to `this.executor` by default) - * @param endpoint The API endpoint that should be fetched - * @param resultProcessing Processes response and finds corresponding error case (if needed) - * @param success Success callback with the response `R` - * @param failure Failure callback with an error case from given error subtype `E` - */ -fun NetworkFetching.fetch( - usingExecutor: NetworkFetching.Executor = executor, - endpoint: Call, - resultProcessing: NetworkFetching.ResultProcessing, - success: (R) -> Unit, - failure: (E) -> Unit -) { - fetch(usingExecutor, endpoint, resultProcessing) { it.invoke(success, failure) } -} - -inline fun RetrofitResult.invoke(success: (R) -> Unit, failure: (E) -> Unit) { - return when (this) { - is RetrofitResult.Success -> success(this.result) - is RetrofitResult.Failure -> failure(this.error) - } -} - -sealed class RetrofitResult { - data class Success(val result: R) : RetrofitResult() - data class Failure(val error: E) : RetrofitResult() -} - -private fun fetch( - usingExecutor: NetworkFetching.Executor, - endpoint: Call, - resultProcessing: NetworkFetching.ResultProcessing, - callback: (RetrofitResult) -> Unit, -) { - usingExecutor.fetchAndCallback(endpoint, object : Callback { - override fun onFailure(call: Call, t: Throwable) { - callback(RetrofitResult.Failure(resultProcessing.errorFromFailureResponse(call, t))) - } - - override fun onResponse(call: Call, response: Response) { - callback(resultProcessing.resultFromResponse(call, response)) - } - }) -} - -//endregion - -//region Retrofit Standard Result Processing - -/** Returns result processing object for given response type `R` and error type `E` */ -open class RetrofitResultProcessing( - override val tryCastResponseBody: (Any?) -> R?, - override val errorFromNetworkFailure: (AnyFetchError) -> E, - hasNetwork: (() -> Boolean)? = null, -) : NetworkFetching.ResultProcessing { - - companion object { - inline operator fun invoke( - noinline errorFromNetworkFailure: (AnyFetchError) -> E, - noinline hasNetwork: (() -> Boolean)? = null - ) : RetrofitResultProcessing { - return RetrofitResultProcessing( - tryCastResponseBody = { - if (it == null && R::class.java == Unit::class.java) { Unit as R } - else { it as? R } - }, - errorFromNetworkFailure = errorFromNetworkFailure, hasNetwork = hasNetwork - ) - } - } - - override var errorFromFailureResponse: (call: Call<*>, t: Throwable) -> E = { call, t -> - val error: AnyFetchError = when(t) { - is UnknownHostException -> errorForUnknownHostException - is IllegalStateException -> errorForIllegalStateException - - //TODO: Check other cases - else -> AnyFetchError.Unknown() - } - errorFromNetworkFailure( - AnyFetchError.make(error = error, addingDump = "(Throwable): $t\n(Call): $call") - ) - } - - override var resultFromResponse: - (Call, Response) -> RetrofitResult = { call, response -> - if (response.isSuccessful) { - tryCastResponseBody(response.body())?.let { body -> - RetrofitResult.Success(body) - } ?: RetrofitResult.Failure( - errorFromNetworkFailure( - AnyFetchError.make( - error = AnyFetchError.NotFound.MissingData, - addingDump = "(Response): $response\n(Call): $call" - ) - ) - ) - } else { - RetrofitResult.Failure( - errorFromNetworkFailure(AnyFetchError.BadStatusCode(response.code(), response)) - ) - } - } - - //region Exception to AnyFetchError mapping - - var errorForUnknownHostException: AnyFetchError = if (hasNetwork != null) { - if (hasNetwork()) AnyFetchError.BadRequest.Encode - else AnyFetchError.NotFound.MissingNetwork - } else { - // Cannot distinguish the error case from `MissingNetwork` and `Encode` error. - AnyFetchError.Unknown() - } - - var errorForIllegalStateException: AnyFetchError = AnyFetchError.BadRequest.Decode - - //endregion + suspend fun requestAuthData( + anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody + ) : AuthDataResponse + suspend fun requestAuthDataValidation( + anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody + ) : AuthDataValidationResponse } -//endregion \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt b/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt new file mode 100644 index 000000000..04d3ddd7c --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt @@ -0,0 +1,88 @@ +package app.lounge.networking + +import app.lounge.extension.toByteArray +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AnonymousAuthDataValidationRequestBody +import app.lounge.model.AuthDataResponse +import app.lounge.model.AuthDataValidationResponse +import com.aurora.gplayapi.GooglePlayApi +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import retrofit2.Retrofit +import retrofit2.http.Body +import retrofit2.http.HeaderMap +import retrofit2.http.POST +import javax.inject.Inject +import javax.inject.Singleton + +interface NetworkFetchingRetrofitAPI { + + companion object { + const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" + const val googlePlayBaseURL: String = GooglePlayApi.URL_BASE + } + + @POST(Path.authData) + suspend fun authDataRequest( + @HeaderMap headers: Map, + @Body requestBody: RequestBody + ): AuthDataResponse + + @POST(Path.sync) + suspend fun validateAuthentication( + @HeaderMap headers: Map + ): AuthDataValidationResponse + + + + object Header { + val authData: (() -> String) -> Map = { + mapOf(Pair("User-Agent", it.invoke())) + } + } + + private object Path { + const val authData = "/" + const val sync = "/fdfe/apps/contentSync" + } + +} + +@Singleton +class NetworkFetchingRetrofitImpl @Inject constructor( + retrofit: Retrofit +) : NetworkFetching { + + private val networkFetchingRetrofitAPI = retrofit.create( + NetworkFetchingRetrofitAPI::class.java + ) + + override suspend fun requestAuthData( + anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody + ): AuthDataResponse { + val requestBody: RequestBody = + anonymousAuthDataRequestBody.properties.toByteArray().let { result -> + result.toRequestBody( + contentType = "application/json".toMediaTypeOrNull(), + offset = 0, + byteCount = result.size + ) + } + return networkFetchingRetrofitAPI.authDataRequest( + requestBody = requestBody, + headers = NetworkFetchingRetrofitAPI.Header.authData { + anonymousAuthDataRequestBody.userAgent + } + ) + } + + override suspend fun requestAuthDataValidation( + anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody + ): AuthDataValidationResponse { + return networkFetchingRetrofitAPI.validateAuthentication( + headers = anonymousAuthDataValidationRequestBody.header + ) + } + +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt b/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt deleted file mode 100644 index fb6290cdf..000000000 --- a/modules/src/main/java/app/lounge/networking/RetrofitConfig.kt +++ /dev/null @@ -1,40 +0,0 @@ -package app.lounge.networking - -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import java.util.concurrent.TimeUnit - -/** - * Implement retrofit configuration - * 1. Try to use single instance of configuration - * 2. Generic way to handle success or failure - * 3. API parsing can be clean and testable - * - * NOTE: Try to use naming which define the action for the logic. - * */ - -internal fun Retrofit.Builder.appLounge( - baseURL: String, - shouldFollowRedirects: Boolean, - callTimeoutInSeconds: Long, -) : Retrofit { - return this.baseUrl(baseURL) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .addNetworkInterceptor(interceptor) - .callTimeout(callTimeoutInSeconds, TimeUnit.SECONDS) - .followRedirects(shouldFollowRedirects) - .build() - ) - .build() -} - -private val interceptor = run { - val httpLoggingInterceptor = HttpLoggingInterceptor() - httpLoggingInterceptor.apply { - httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY - } -} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/Google.kt b/modules/src/main/java/app/lounge/users/Google.kt deleted file mode 100644 index 48ce6c2dd..000000000 --- a/modules/src/main/java/app/lounge/users/Google.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.lounge.users - -/** - * Implement API related to Google login flow only. - * 1. Login api for google users. - * 2. Parsing google user data. For now use typealias object, later we will refactor with generic type - * 3. Add unit test cases for the api functions. - * */ \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/Session.kt b/modules/src/main/java/app/lounge/users/Session.kt deleted file mode 100644 index 5885edea2..000000000 --- a/modules/src/main/java/app/lounge/users/Session.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.lounge.users - -/** - * Handle user login session in the file. - * This should be the only file which communicate with application regarding user data - * - * 1. Application facing api will be part of this file. - * 2. Manage user session for Anonymous, Google, PWA users. - * 3. In case of invalid user data, timeout etc. inform application about session failure. - * 4. If possible add session api testcases. - */ \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt b/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt deleted file mode 100644 index 9ca6f9067..000000000 --- a/modules/src/main/java/app/lounge/users/anonymous/Anonymous.kt +++ /dev/null @@ -1,39 +0,0 @@ -package app.lounge.users.anonymous - -import app.lounge.networking.FetchError - -/** - * Implement API related to Anonymous login flow only. - * 1. Login api for Anonymous users. - * 2. Parsing Anonymous user data. For now use typealias object, later we will refactor with generic type - * 3. Add unit test cases for the api functions. - * */ - -interface Anonymous { - - val api: AnonymousAPI - - companion object { - - fun anonymousRequestFor( - api: AnonymousAPI = AnonymousAPI.create() - ) : Anonymous { - return object : Anonymous { - override val api: AnonymousAPI = api - } - } - } - - // pass input from this function - fun requestAuthData( - anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, - success : (LoginResponse) -> Unit, - failure : (FetchError) -> Unit - ) { - api.requestAuthData( - anonymousAuthDataRequestBody = anonymousAuthDataRequestBody, - success = success, - failure = failure - ) - } -} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt deleted file mode 100644 index 9da4d4368..000000000 --- a/modules/src/main/java/app/lounge/users/anonymous/AnonymousAPI.kt +++ /dev/null @@ -1,41 +0,0 @@ -package app.lounge.users.anonymous - -import app.lounge.BuildConfig -import app.lounge.networking.FetchError -import com.google.gson.Gson -import java.util.Properties - -interface AnonymousAPI { - - companion object { - const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" - const val loginBaseURL: String = "GooglePlayApi.URL_SYNC" - - fun create(baseURL: String = tokenBaseURL) : AnonymousAPI { - return RetrofitAnonymousAPI( - baseURL = baseURL, - anonymousUserEndpointFollowsRedirects = true, - callTimeoutInSeconds = 30 - ) - } - } - - fun requestAuthData( - anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, - success : (LoginResponse) -> Unit, - failure : (FetchError) -> Unit - ) - - object Header { - val authData: (() -> String) -> Map = { - mapOf(Pair("User-Agent", it.invoke())) - } - } - -} - -/** AnonymousAuthDataRequestBody */ -data class AnonymousAuthDataRequestBody( - val properties: Properties, - val userAgent: String -) diff --git a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt b/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt deleted file mode 100644 index 68d90b84c..000000000 --- a/modules/src/main/java/app/lounge/users/anonymous/RetrofitAnonymousAPI.kt +++ /dev/null @@ -1,63 +0,0 @@ -package app.lounge.users.anonymous - -import app.lounge.extension.toByteArray -import app.lounge.networking.FetchError -import app.lounge.networking.NetworkFetching -import app.lounge.networking.appLounge -import app.lounge.networking.fetch -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody -import retrofit2.Call -import retrofit2.Retrofit -import retrofit2.http.Body -import retrofit2.http.HeaderMap -import retrofit2.http.POST -import retrofit2.http.Url - -internal class RetrofitAnonymousAPI( - baseURL: String, - anonymousUserEndpointFollowsRedirects: Boolean, - callTimeoutInSeconds: Long, -): AnonymousAPI, NetworkFetching { - - private val anonymousUserEndPoint: AnonymousUserEndPointEndpoint = Retrofit.Builder().appLounge( - baseURL = baseURL, - shouldFollowRedirects = anonymousUserEndpointFollowsRedirects, - callTimeoutInSeconds = callTimeoutInSeconds - ).create(AnonymousUserEndPointEndpoint::class.java) - - interface AnonymousUserEndPointEndpoint { - - @POST - fun authDataRequest( - @Url url: String = AnonymousAPI.tokenBaseURL, - @HeaderMap headers: Map, - @Body requestBody: RequestBody - ): Call - } - - override fun requestAuthData( - anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody, - success: (LoginResponse) -> Unit, - failure: (FetchError) -> Unit - ) { - val requestBody = RequestBody.create( - "application/json".toMediaTypeOrNull(), - anonymousAuthDataRequestBody.properties.toByteArray() - ) - - fetch( - endpoint = anonymousUserEndPoint.authDataRequest( - requestBody = requestBody, - headers = AnonymousAPI.Header.authData { - anonymousAuthDataRequestBody.userAgent - } - ), - success = success, - failure = failure - ) - } - -} - -typealias LoginResponse = String -- GitLab From 029ac0ba7423be1320928961cb309a5a7b7adac0 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 18 Jul 2023 22:49:45 +0530 Subject: [PATCH 026/112] Refine networking module for different endpoint --- .../java/app/lounge/NetworkFetchingAPITest.kt | 76 +++++----- .../lounge/networking/AnonymousUserAPITest.kt | 135 ------------------ .../main/java/app/lounge/di/NetworkModule.kt | 49 ++++++- .../networking/NetworkFetchingRetrofitImpl.kt | 14 +- 4 files changed, 85 insertions(+), 189 deletions(-) delete mode 100644 modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt diff --git a/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt b/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt index d2b92e219..46ef8cc8f 100644 --- a/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt @@ -18,47 +18,6 @@ import java.util.concurrent.TimeUnit class NetworkFetchingAPITest { - private val networkFetchingToken: NetworkFetching = NetworkFetchingRetrofitImpl( - Retrofit.Builder() - .baseUrl(NetworkFetchingRetrofitAPI.tokenBaseURL) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .callTimeout(10, TimeUnit.SECONDS) - .build() - ) - .build() - ) - - private val networkFetchingGoogle: NetworkFetching = NetworkFetchingRetrofitImpl( - Retrofit.Builder() - .baseUrl(NetworkFetchingRetrofitAPI.googlePlayBaseURL) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .callTimeout(10, TimeUnit.SECONDS) - .build() - ) - .build() - ) - - private val networkFetchingTimeoutGoogle: NetworkFetching = NetworkFetchingRetrofitImpl( - Retrofit.Builder() - .baseUrl(NetworkFetchingRetrofitAPI.googlePlayBaseURL) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .callTimeout(10, TimeUnit.MILLISECONDS) - .build() - ) - .build() - ) - - private val requestBodyData = AnonymousAuthDataRequestBody( - properties = testSystemProperties, - userAgent = testUserAgent - ) - companion object { var authData: AuthDataResponse? = null } @@ -77,7 +36,7 @@ class NetworkFetchingAPITest { authData?.let { authData -> authData.dfeCookie = "null" - result = networkFetchingGoogle.requestAuthDataValidation( + result = networkFetchingToken.requestAuthDataValidation( anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( authDataResponse = authData ) @@ -105,6 +64,39 @@ class NetworkFetchingAPITest { assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } assert(failure is InterruptedIOException) { "Assert!! Timeout Failure callback must call" } } + + private fun retrofitTestConfig( + baseUrl: String, + timeoutInMillisecond: Long = 10000L + ): Retrofit = Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .client( + OkHttpClient.Builder() + .callTimeout(timeoutInMillisecond, TimeUnit.MILLISECONDS) + .build() + ) + .build() + + private val eCloudTest = retrofitTestConfig(NetworkFetchingRetrofitAPI.tokenBaseURL) + private val googleTest = retrofitTestConfig(NetworkFetchingRetrofitAPI.googlePlayBaseURL) + private val googleTestTimeout = retrofitTestConfig( + NetworkFetchingRetrofitAPI.googlePlayBaseURL, 50L) + + private val networkFetchingToken: NetworkFetching = NetworkFetchingRetrofitImpl( + eCloud = eCloudTest, + google = googleTest + ) + + private val networkFetchingTimeoutGoogle: NetworkFetching = NetworkFetchingRetrofitImpl( + eCloud = eCloudTest, + google = googleTestTimeout + ) + + private val requestBodyData = AnonymousAuthDataRequestBody( + properties = testSystemProperties, + userAgent = testUserAgent + ) } private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" diff --git a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt deleted file mode 100644 index ab364f291..000000000 --- a/modules/src/androidTest/java/app/lounge/networking/AnonymousUserAPITest.kt +++ /dev/null @@ -1,135 +0,0 @@ -package app.lounge.networking - -import app.lounge.users.anonymous.Anonymous -import app.lounge.users.anonymous.AnonymousAPI -import app.lounge.users.anonymous.AnonymousAuthDataRequestBody -import app.lounge.users.anonymous.AnonymousAuthDataValidationRequestBody -import app.lounge.users.anonymous.AuthDataResponse -import app.lounge.users.anonymous.AuthDataValidationResponse -import org.junit.Test -import java.util.Properties - -class AnonymousUserAPITest { - - private val testAnonymousAPIForToken = Anonymous.anonymousRequestFor( - api = AnonymousAPI.create(baseURL = AnonymousAPI.tokenBaseURL) - ) - - private val testAnonymousAPIForLogin = Anonymous.anonymousRequestFor( - api = AnonymousAPI.create(baseURL = AnonymousAPI.loginBaseURL) - ) - - private val testAnonymousAPIForLoginTimeout = Anonymous.anonymousRequestFor( - api = AnonymousAPI.create( - baseURL = AnonymousAPI.loginBaseURL, - callTimeoutInSeconds = 1 - ) - ) - - private val requestBodyData = AnonymousAuthDataRequestBody( - properties = testSystemProperties, - userAgent = testUserAgent - ) - - companion object { - var authData: AuthDataResponse? = null - } - - @Test - fun test1OnSuccessReturnsAuthData() { - await { - testAnonymousAPIForToken.requestAuthData( - anonymousAuthDataRequestBody = requestBodyData, - success = { response -> authData = response }, - failure = { } - ) - } - assert(authData is AuthDataResponse) { "Assert!! Success must return data" } - } - - @Test - fun test2OnSuccessReturnsLoginData(){ - var response: AuthDataValidationResponse? = null - await { - authData?.let { authData -> - authData.dfeCookie = "null" - - testAnonymousAPIForLogin.requestAuthDataValidation( - anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( - authDataResponse = authData - ), - success = { result -> response = result }, - failure = {} - ) - } - } - assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } - assert(response is AuthDataValidationResponse) { "Assert!! `response` must have data" } - } - - @Test - fun test3OnTimeoutFailureReturnsError(){ - var failure: FetchError? = null - await { - authData?.let { authData -> - authData.dfeCookie = "null" - testAnonymousAPIForLoginTimeout.requestAuthDataValidation( - anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( - authDataResponse = authData - ), - success = {}, - failure = { error -> failure = error } - ) - } - } - assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } - failure?.description?.contains("timeout") - ?.let { assert(it) { "Assert!! Timeout Failure callback must call" } } - } -} - -private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" - -private val testSystemProperties = Properties().apply { - setProperty("UserReadableName", "coral-default") - setProperty("Build.HARDWARE", "coral") - setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") - setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") - setProperty("Build.BRAND", "google") - setProperty("Build.DEVICE", "coral") - setProperty("Build.VERSION.SDK_INT", "32") - setProperty("Build.VERSION.RELEASE", "12") - setProperty("Build.MODEL", "Pixel 4 XL") - setProperty("Build.MANUFACTURER", "Google") - setProperty("Build.PRODUCT", "coral") - setProperty("Build.ID", "SQ3A.220705.004") - setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") - setProperty("TouchScreen", "3") - setProperty("Keyboard", "1") - setProperty("Navigation", "1") - setProperty("ScreenLayout", "2") - setProperty("HasHardKeyboard", "false") - setProperty("HasFiveWayNavigation", "false") - setProperty("Screen.Density", "560") - setProperty("Screen.Width", "1440") - setProperty("Screen.Height", "2984") - setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") - setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") - setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") - setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") - setProperty("GL.Version", "196610") - setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") - setProperty("Client", "android-google") - setProperty("GSF.version", "223616055") - setProperty("Vending.version", "82151710") - setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") - setProperty("Roaming", "mobile-notroaming") - setProperty("TimeZone", "UTC-10") - setProperty("CellOperator", "310") - setProperty("SimOperator", "38") -} - -fun await(callback: () -> Unit) { - callback.invoke() - Thread.sleep(3000) -} diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 1763676d2..d2ea451e8 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -1,7 +1,8 @@ package app.lounge.di +import app.lounge.networking.NetworkFetching import app.lounge.networking.NetworkFetchingRetrofitAPI -import dagger.Binds +import app.lounge.networking.NetworkFetchingRetrofitImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -18,13 +19,11 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) internal object NetworkModule { - private const val HTTP_TIMEOUT = 10L + private const val HTTP_TIMEOUT_IN_SECOND = 10L - @Provides - @Singleton - internal fun providesRetrofit( + private fun retrofit( okHttpClient: OkHttpClient, - baseUrl: String = NetworkFetchingRetrofitAPI.tokenBaseURL + baseUrl: String ) : Retrofit { return Retrofit.Builder() .baseUrl(baseUrl) @@ -33,6 +32,30 @@ internal object NetworkModule { .build() } + @Provides + @Singleton + @Named("ECloudRetrofit") + internal fun provideECloudRetrofit( + okHttpClient: OkHttpClient + ): Retrofit { + return retrofit( + okHttpClient = okHttpClient, + baseUrl = NetworkFetchingRetrofitAPI.tokenBaseURL + ) + } + + @Provides + @Singleton + @Named("GoogleRetrofit") + internal fun provideGoogleRetrofit( + okHttpClient: OkHttpClient + ): Retrofit { + return retrofit( + okHttpClient = okHttpClient, + baseUrl = NetworkFetchingRetrofitAPI.googlePlayBaseURL + ) + } + @Provides @Singleton @Named("privateOkHttpClient") @@ -41,7 +64,7 @@ internal object NetworkModule { ): OkHttpClient { return OkHttpClient.Builder() .addNetworkInterceptor(httpLogger) - .callTimeout(HTTP_TIMEOUT, TimeUnit.SECONDS) + .callTimeout(HTTP_TIMEOUT_IN_SECOND, TimeUnit.SECONDS) .build() } @@ -56,4 +79,16 @@ internal object NetworkModule { } } + @Provides + @Singleton + fun provideNetworkFetching( + @Named("ECloudRetrofit") ecloud: Retrofit, + @Named("GoogleRetrofit") google: Retrofit, + ) : NetworkFetching { + return NetworkFetchingRetrofitImpl( + eCloud = ecloud, + google = google + ) + } + } \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt b/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt index 04d3ddd7c..6621e4010 100644 --- a/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt +++ b/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt @@ -51,10 +51,15 @@ interface NetworkFetchingRetrofitAPI { @Singleton class NetworkFetchingRetrofitImpl @Inject constructor( - retrofit: Retrofit + val eCloud: Retrofit, + val google: Retrofit, ) : NetworkFetching { - private val networkFetchingRetrofitAPI = retrofit.create( + private val eCloudNetworkFetchingRetrofitAPI = eCloud.create( + NetworkFetchingRetrofitAPI::class.java + ) + + private val googleNetworkFetchingRetrofitAPI = google.create( NetworkFetchingRetrofitAPI::class.java ) @@ -69,7 +74,7 @@ class NetworkFetchingRetrofitImpl @Inject constructor( byteCount = result.size ) } - return networkFetchingRetrofitAPI.authDataRequest( + return eCloudNetworkFetchingRetrofitAPI.authDataRequest( requestBody = requestBody, headers = NetworkFetchingRetrofitAPI.Header.authData { anonymousAuthDataRequestBody.userAgent @@ -80,9 +85,8 @@ class NetworkFetchingRetrofitImpl @Inject constructor( override suspend fun requestAuthDataValidation( anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody ): AuthDataValidationResponse { - return networkFetchingRetrofitAPI.validateAuthentication( + return googleNetworkFetchingRetrofitAPI.validateAuthentication( headers = anonymousAuthDataValidationRequestBody.header ) } - } \ No newline at end of file -- GitLab From bcbdf18d9ccfa24cf6d71bfe079302b640e27f29 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 21 Jul 2023 19:16:29 +0530 Subject: [PATCH 027/112] Network module integration --- .../java/foundation/e/apps/di/LoginModule.kt | 10 +++++ .../login/repository/LoginRepository.kt | 13 ++++++ .../login/repository/LoginRepositoryImpl.kt | 18 ++++++++ .../domain/login/usecase/UserLoginUseCase.kt | 37 +++++++++++++++++ .../e/apps/presentation/login/LoginState.kt | 8 ++++ .../login/LoginViewModel.kt | 41 ++++++++++++++++++- .../java/foundation/e/apps/ui/MainActivity.kt | 4 +- .../apps/ui/parentFragment/TimeoutFragment.kt | 2 +- .../e/apps/ui/settings/SettingsFragment.kt | 2 +- .../e/apps/ui/setup/signin/SignInFragment.kt | 2 +- .../signin/google/GoogleSignInFragment.kt | 2 +- .../java/foundation/e/apps/utils/Resource.kt | 8 ++++ 12 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt create mode 100644 app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt rename app/src/main/java/foundation/e/apps/{data => presentation}/login/LoginViewModel.kt (76%) create mode 100644 app/src/main/java/foundation/e/apps/utils/Resource.kt diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt index f59a084b8..9708c9a8e 100644 --- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt +++ b/app/src/main/java/foundation/e/apps/di/LoginModule.kt @@ -24,6 +24,8 @@ import dagger.hilt.components.SingletonComponent import foundation.e.apps.data.login.LoginSourceCleanApk import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.login.LoginSourceInterface +import foundation.e.apps.domain.login.repository.LoginRepositoryImpl +import foundation.e.apps.domain.login.usecase.UserLoginUseCase @InstallIn(SingletonComponent::class) @Module @@ -36,4 +38,12 @@ object LoginModule { ): List { return listOf(gPlay, cleanApk) } + + @Provides + fun provideLoginUserCase( + loginRepositoryImpl: LoginRepositoryImpl + ): UserLoginUseCase { + return UserLoginUseCase(loginRepositoryImpl) + } + } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt new file mode 100644 index 000000000..a35c1bc98 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -0,0 +1,13 @@ +package foundation.e.apps.domain.login.repository + +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AuthDataResponse + + +interface LoginRepository { + + suspend fun anonymousUser( + authDataRequestBody: AnonymousAuthDataRequestBody + ): AuthDataResponse + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt new file mode 100644 index 000000000..4e4b30127 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -0,0 +1,18 @@ +package foundation.e.apps.domain.login.repository + +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AuthDataResponse +import app.lounge.networking.NetworkFetching +import javax.inject.Inject + +class LoginRepositoryImpl @Inject constructor( + private val networkFetching: NetworkFetching +): LoginRepository { + + override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthDataResponse { + return networkFetching.requestAuthData( + anonymousAuthDataRequestBody = authDataRequestBody + ) + } + +} diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt new file mode 100644 index 000000000..e2e8b1813 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -0,0 +1,37 @@ +package foundation.e.apps.domain.login.usecase + +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AuthDataResponse +import foundation.e.apps.domain.login.repository.LoginRepository +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import retrofit2.HttpException +import java.io.IOException +import java.util.Properties +import javax.inject.Inject + +class UserLoginUseCase @Inject constructor( + private val loginRepository: LoginRepository, +) { + + operator fun invoke( + properties: Properties, + userAgent: String + ): Flow> = flow { + try { + emit(Resource.Loading()) + val userResponse: AuthDataResponse = loginRepository.anonymousUser( + authDataRequestBody = AnonymousAuthDataRequestBody( + properties = properties, + userAgent = userAgent + ) + ) + emit(Resource.Success(userResponse)) + } catch(e: HttpException) { + emit(Resource.Error(e.localizedMessage ?: "An unexpected error occured")) + } catch(e: IOException) { + emit(Resource.Error("Couldn't reach server. Check your internet connection.")) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt new file mode 100644 index 000000000..400fb86bc --- /dev/null +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt @@ -0,0 +1,8 @@ +package foundation.e.apps.presentation.login + + +data class LoginState( + val isLoading: Boolean = false, + val isLoggedIn: Boolean = false, + val error: String = "" +) diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt similarity index 76% rename from app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt rename to app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 25c54362e..c209288af 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -15,15 +15,24 @@ * along with this program. If not, see . */ -package foundation.e.apps.data.login +package foundation.e.apps.presentation.login +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.enums.User +import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.data.login.LoginSourceRepository +import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.ui.parentFragment.LoadingViewModel +import foundation.e.apps.utils.Resource +import foundation.e.apps.utils.SystemInfoProvider +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import java.util.Properties import javax.inject.Inject /** @@ -33,6 +42,7 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, + private val userLoginUseCase: UserLoginUseCase ) : ViewModel() { /** @@ -131,4 +141,33 @@ class LoginViewModel @Inject constructor( authObjects.postValue(listOf()) } } + + + private val _loginState: MutableLiveData = MutableLiveData() + val loginState: LiveData = _loginState + + + fun authenticateAnonymousUser( + properties: Properties, + userAgent: String = SystemInfoProvider.getAppBuildInfo() + ) { + userLoginUseCase( + properties = properties, + userAgent = userAgent + ).onEach { result -> + when (result) { + is Resource.Success -> { + _loginState.value = LoginState(isLoggedIn = true) + } + is Resource.Error -> { + _loginState.value = LoginState( + error = result.message ?: "An unexpected error occured" + ) + } + is Resource.Loading -> { + _loginState.value = LoginState(isLoading = true) + } + } + }.launchIn(viewModelScope) + } } diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt index a2426b41f..096c4b98f 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt @@ -46,7 +46,7 @@ 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.LoginViewModel +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.install.updates.UpdatesNotifier @@ -68,7 +68,6 @@ 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 @@ -90,7 +89,6 @@ 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 diff --git a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt index 5173da6fe..9a7f8eb10 100644 --- a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt @@ -34,7 +34,7 @@ import foundation.e.apps.R import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceGPlay -import foundation.e.apps.data.login.LoginViewModel +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.data.login.exceptions.CleanApkException import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.login.exceptions.GPlayLoginException diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index d3c507cad..4a717877d 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -40,7 +40,7 @@ import foundation.e.apps.BuildConfig import foundation.e.apps.R import foundation.e.apps.data.enums.User import foundation.e.apps.data.fused.UpdatesDao -import foundation.e.apps.data.login.LoginViewModel +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.databinding.CustomPreferenceBinding import foundation.e.apps.install.updates.UpdatesWorkManager import foundation.e.apps.ui.MainActivityViewModel diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt index 213e84148..7ed9eb765 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt @@ -7,7 +7,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R -import foundation.e.apps.data.login.LoginViewModel +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.databinding.FragmentSignInBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate import foundation.e.apps.utils.showGoogleSignInAlertDialog diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt index 8dc3081b3..ed964d86c 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt @@ -32,7 +32,7 @@ import androidx.navigation.findNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.gplay.utils.AC2DMUtil -import foundation.e.apps.data.login.LoginViewModel +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.databinding.FragmentGoogleSigninBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate diff --git a/app/src/main/java/foundation/e/apps/utils/Resource.kt b/app/src/main/java/foundation/e/apps/utils/Resource.kt new file mode 100644 index 000000000..f598e91e5 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/Resource.kt @@ -0,0 +1,8 @@ +package foundation.e.apps.utils + + +sealed class Resource(val data: T? = null, val message: String? = null) { + class Success(data: T) : Resource(data) + class Error(message: String, data: T? = null) : Resource(data, message) + class Loading(data: T? = null) : Resource(data) +} -- GitLab From a6b83e0bf08fe5f3d5657c516aa5d2fb58215fe2 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 24 Jul 2023 22:58:00 +0530 Subject: [PATCH 028/112] Update the code as per review comment --- ...PITest.kt => UserAuthenticationAPITest.kt} | 43 +++++++++++++------ .../main/java/app/lounge/di/NetworkModule.kt | 5 +-- .../app/lounge/networking/RetrofitHandler.kt | 35 +++++++++++++++ ...tworkFetching.kt => UserAuthentication.kt} | 6 +-- ...l.kt => UserAuthenticationRetrofitImpl.kt} | 41 ++++++++++-------- 5 files changed, 92 insertions(+), 38 deletions(-) rename modules/src/androidTest/java/app/lounge/{NetworkFetchingAPITest.kt => UserAuthenticationAPITest.kt} (90%) create mode 100644 modules/src/main/java/app/lounge/networking/RetrofitHandler.kt rename modules/src/main/java/app/lounge/networking/{NetworkFetching.kt => UserAuthentication.kt} (79%) rename modules/src/main/java/app/lounge/networking/{NetworkFetchingRetrofitImpl.kt => UserAuthenticationRetrofitImpl.kt} (68%) diff --git a/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt b/modules/src/androidTest/java/app/lounge/UserAuthenticationAPITest.kt similarity index 90% rename from modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt rename to modules/src/androidTest/java/app/lounge/UserAuthenticationAPITest.kt index d2b92e219..b5fc794cb 100644 --- a/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/UserAuthenticationAPITest.kt @@ -4,9 +4,10 @@ import app.lounge.model.AnonymousAuthDataRequestBody import app.lounge.model.AnonymousAuthDataValidationRequestBody import app.lounge.model.AuthDataResponse import app.lounge.model.AuthDataValidationResponse -import app.lounge.networking.NetworkFetching -import app.lounge.networking.NetworkFetchingRetrofitAPI -import app.lounge.networking.NetworkFetchingRetrofitImpl +import app.lounge.networking.NetworkResult +import app.lounge.networking.UserAuthentication +import app.lounge.networking.UserAuthenticationRetrofitAPI +import app.lounge.networking.UserAuthenticationRetrofitImpl import kotlinx.coroutines.runBlocking import okhttp3.OkHttpClient import org.junit.Test @@ -16,11 +17,11 @@ import java.io.InterruptedIOException import java.util.Properties import java.util.concurrent.TimeUnit -class NetworkFetchingAPITest { +class UserAuthenticationAPITest { - private val networkFetchingToken: NetworkFetching = NetworkFetchingRetrofitImpl( + private val userAuthenticationToken: UserAuthentication = UserAuthenticationRetrofitImpl( Retrofit.Builder() - .baseUrl(NetworkFetchingRetrofitAPI.tokenBaseURL) + .baseUrl(UserAuthenticationRetrofitAPI.tokenBaseURL) .addConverterFactory(GsonConverterFactory.create()) .client( OkHttpClient.Builder() @@ -30,9 +31,9 @@ class NetworkFetchingAPITest { .build() ) - private val networkFetchingGoogle: NetworkFetching = NetworkFetchingRetrofitImpl( + private val userAuthenticationGoogle: UserAuthentication = UserAuthenticationRetrofitImpl( Retrofit.Builder() - .baseUrl(NetworkFetchingRetrofitAPI.googlePlayBaseURL) + .baseUrl(UserAuthenticationRetrofitAPI.googlePlayBaseURL) .addConverterFactory(GsonConverterFactory.create()) .client( OkHttpClient.Builder() @@ -42,9 +43,9 @@ class NetworkFetchingAPITest { .build() ) - private val networkFetchingTimeoutGoogle: NetworkFetching = NetworkFetchingRetrofitImpl( + private val userAuthenticationTimeoutGoogle: UserAuthentication = UserAuthenticationRetrofitImpl( Retrofit.Builder() - .baseUrl(NetworkFetchingRetrofitAPI.googlePlayBaseURL) + .baseUrl(UserAuthenticationRetrofitAPI.googlePlayBaseURL) .addConverterFactory(GsonConverterFactory.create()) .client( OkHttpClient.Builder() @@ -65,9 +66,14 @@ class NetworkFetchingAPITest { @Test fun test1OnSuccessReturnsAuthData() = runBlocking { - authData = networkFetchingToken.requestAuthData( + val response = userAuthenticationToken.requestAuthData( anonymousAuthDataRequestBody = requestBodyData, ) + when(response){ + is NetworkResult.Success -> authData = response.data + else -> {} + } + assert(authData is AuthDataResponse) { "Assert!! Success must return data" } } @@ -77,11 +83,15 @@ class NetworkFetchingAPITest { authData?.let { authData -> authData.dfeCookie = "null" - result = networkFetchingGoogle.requestAuthDataValidation( + val response = userAuthenticationGoogle.requestAuthDataValidation( anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( authDataResponse = authData ) ) + when(response){ + is NetworkResult.Success -> result = response.data + else -> {} + } } assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } @@ -90,16 +100,21 @@ class NetworkFetchingAPITest { @Test fun test3OnTimeoutFailureReturnsError(): Unit = runBlocking { - var failure: Exception = Exception("No Error") + var failure: Throwable = Exception("No Error") authData?.let { authData -> authData.dfeCookie = "null" try { - networkFetchingTimeoutGoogle.requestAuthDataValidation( + val response = userAuthenticationTimeoutGoogle.requestAuthDataValidation( anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( authDataResponse = authData ) ) + + when(response){ + is NetworkResult.Error -> failure = response.exception + else -> {} + } } catch (e: InterruptedIOException) { failure = e } } assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 1763676d2..52667088e 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -1,7 +1,6 @@ package app.lounge.di -import app.lounge.networking.NetworkFetchingRetrofitAPI -import dagger.Binds +import app.lounge.networking.UserAuthenticationRetrofitAPI import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -24,7 +23,7 @@ internal object NetworkModule { @Singleton internal fun providesRetrofit( okHttpClient: OkHttpClient, - baseUrl: String = NetworkFetchingRetrofitAPI.tokenBaseURL + baseUrl: String = UserAuthenticationRetrofitAPI.tokenBaseURL ) : Retrofit { return Retrofit.Builder() .baseUrl(baseUrl) diff --git a/modules/src/main/java/app/lounge/networking/RetrofitHandler.kt b/modules/src/main/java/app/lounge/networking/RetrofitHandler.kt new file mode 100644 index 000000000..804dc542e --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/RetrofitHandler.kt @@ -0,0 +1,35 @@ +package app.lounge.networking + +import retrofit2.Response + +sealed interface NetworkResult { + data class Success(val data: T) : NetworkResult + data class Error( + val exception: Throwable, + val code: Int, + val errorMessage: String, + ) : NetworkResult +} + +suspend fun fetch(call: suspend () -> Response): NetworkResult { + try { + val response = call() + if (response.isSuccessful) { + response.body()?.let { result -> + return NetworkResult.Success(result) + } + } + + return NetworkResult.Error( + exception = Exception(response.message()), + code = response.code(), + errorMessage = " ${response.code()} ${response.message()}" + ) + } catch (exception: Exception) { + return NetworkResult.Error( + exception = exception, + code = exception.hashCode(), + errorMessage = exception.toString() + ) + } +} diff --git a/modules/src/main/java/app/lounge/networking/NetworkFetching.kt b/modules/src/main/java/app/lounge/networking/UserAuthentication.kt similarity index 79% rename from modules/src/main/java/app/lounge/networking/NetworkFetching.kt rename to modules/src/main/java/app/lounge/networking/UserAuthentication.kt index c544c7aac..2eeed0f13 100644 --- a/modules/src/main/java/app/lounge/networking/NetworkFetching.kt +++ b/modules/src/main/java/app/lounge/networking/UserAuthentication.kt @@ -5,13 +5,13 @@ import app.lounge.model.AnonymousAuthDataValidationRequestBody import app.lounge.model.AuthDataResponse import app.lounge.model.AuthDataValidationResponse -interface NetworkFetching { +interface UserAuthentication { suspend fun requestAuthData( anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody - ) : AuthDataResponse + ) : NetworkResult suspend fun requestAuthDataValidation( anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody - ) : AuthDataValidationResponse + ) : NetworkResult } diff --git a/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt b/modules/src/main/java/app/lounge/networking/UserAuthenticationRetrofitImpl.kt similarity index 68% rename from modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt rename to modules/src/main/java/app/lounge/networking/UserAuthenticationRetrofitImpl.kt index 04d3ddd7c..ae87eaded 100644 --- a/modules/src/main/java/app/lounge/networking/NetworkFetchingRetrofitImpl.kt +++ b/modules/src/main/java/app/lounge/networking/UserAuthenticationRetrofitImpl.kt @@ -9,6 +9,7 @@ import com.aurora.gplayapi.GooglePlayApi import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody +import retrofit2.Response import retrofit2.Retrofit import retrofit2.http.Body import retrofit2.http.HeaderMap @@ -16,7 +17,7 @@ import retrofit2.http.POST import javax.inject.Inject import javax.inject.Singleton -interface NetworkFetchingRetrofitAPI { +interface UserAuthenticationRetrofitAPI { companion object { const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" @@ -27,12 +28,12 @@ interface NetworkFetchingRetrofitAPI { suspend fun authDataRequest( @HeaderMap headers: Map, @Body requestBody: RequestBody - ): AuthDataResponse + ): Response @POST(Path.sync) suspend fun validateAuthentication( @HeaderMap headers: Map - ): AuthDataValidationResponse + ): Response @@ -50,17 +51,17 @@ interface NetworkFetchingRetrofitAPI { } @Singleton -class NetworkFetchingRetrofitImpl @Inject constructor( +class UserAuthenticationRetrofitImpl @Inject constructor( retrofit: Retrofit -) : NetworkFetching { +) : UserAuthentication { - private val networkFetchingRetrofitAPI = retrofit.create( - NetworkFetchingRetrofitAPI::class.java + private val userAuthenticationRetrofitAPI = retrofit.create( + UserAuthenticationRetrofitAPI::class.java ) override suspend fun requestAuthData( anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody - ): AuthDataResponse { + ): NetworkResult { val requestBody: RequestBody = anonymousAuthDataRequestBody.properties.toByteArray().let { result -> result.toRequestBody( @@ -69,20 +70,24 @@ class NetworkFetchingRetrofitImpl @Inject constructor( byteCount = result.size ) } - return networkFetchingRetrofitAPI.authDataRequest( - requestBody = requestBody, - headers = NetworkFetchingRetrofitAPI.Header.authData { - anonymousAuthDataRequestBody.userAgent - } - ) + return fetch { + userAuthenticationRetrofitAPI.authDataRequest( + requestBody = requestBody, + headers = UserAuthenticationRetrofitAPI.Header.authData { + anonymousAuthDataRequestBody.userAgent + } + ) + } } override suspend fun requestAuthDataValidation( anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody - ): AuthDataValidationResponse { - return networkFetchingRetrofitAPI.validateAuthentication( - headers = anonymousAuthDataValidationRequestBody.header - ) + ): NetworkResult { + return fetch { + userAuthenticationRetrofitAPI.validateAuthentication( + headers = anonymousAuthDataValidationRequestBody.header + ) + } } } \ No newline at end of file -- GitLab From bbc8d6898835fa63187b07ab42fbb6137d8864c3 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 26 Jul 2023 22:37:40 +0530 Subject: [PATCH 029/112] Added unit test case --- app/build.gradle | 12 ++++ .../login/repository/LoginRepositoryImpl.kt | 10 +++- .../usecase/repository/LoginRepositoryTest.kt | 59 +++++++++++++++++++ .../java/app/lounge/NetworkFetchingAPITest.kt | 4 +- 4 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt diff --git a/app/build.gradle b/app/build.gradle index f0e0d5e26..e6e74159d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -133,6 +133,12 @@ android { kotlin.sourceSets.all { languageSettings.optIn("kotlin.RequiresOptIn") } + + testOptions { + unitTests { + includeAndroidResources = true + } + } } kapt { @@ -164,6 +170,7 @@ dependencies { implementation "androidx.datastore:datastore-preferences:1.0.0" implementation 'com.facebook.shimmer:shimmer:0.5.0' implementation 'androidx.core:core-google-shortcuts:1.0.0' + implementation 'androidx.test.ext:junit-ktx:1.1.5' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' testImplementation "com.google.truth:truth:1.1.3" testImplementation 'junit:junit:4.13.2' @@ -252,4 +259,9 @@ dependencies { // elib implementation 'foundation.e:elib:0.0.1-alpha11' + + + testImplementation 'org.mockito:mockito-core:5.0.0' + testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' + testImplementation 'org.robolectric:robolectric:4.9' } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index 4e4b30127..37001fb5d 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -1,18 +1,24 @@ package foundation.e.apps.domain.login.repository +import android.content.Context import app.lounge.model.AnonymousAuthDataRequestBody import app.lounge.model.AuthDataResponse import app.lounge.networking.NetworkFetching +import app.lounge.storage.cache.configurations +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class LoginRepositoryImpl @Inject constructor( - private val networkFetching: NetworkFetching + private val networkFetching: NetworkFetching, + @ApplicationContext val applicationContext: Context ): LoginRepository { override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthDataResponse { return networkFetching.requestAuthData( anonymousAuthDataRequestBody = authDataRequestBody - ) + ).also { + applicationContext.configurations.authData = it.toString() + } } } diff --git a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt new file mode 100644 index 000000000..beaf715fd --- /dev/null +++ b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt @@ -0,0 +1,59 @@ +package foundation.e.apps.usecase.repository + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AuthDataResponse +import app.lounge.networking.NetworkFetching +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.domain.login.repository.LoginRepository +import foundation.e.apps.domain.login.repository.LoginRepositoryImpl +import kotlinx.coroutines.test.runTest +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.robolectric.RobolectricTestRunner +import retrofit2.Response +import java.util.Properties + +@RunWith(RobolectricTestRunner::class) +class LoginRepositoryTest { + + @Mock + lateinit var networkAPI: NetworkFetching + + lateinit var instrumentationContext: Context + + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + instrumentationContext= ApplicationProvider.getApplicationContext() + } + + @Test + fun testRequestAuthData() = runTest { + Mockito.`when`(networkAPI.requestAuthData( + requestBodyData + )).thenReturn(AuthData("nisdande@murena.io", "")) + + val sut = LoginRepositoryImpl( + networkAPI, instrumentationContext) + val result = sut.anonymousUser(requestBodyData) + Assert.assertEquals(true, result is AuthDataResponse) + Assert.assertEquals("nisdande@murena.io", result.email) + } +} + +private val requestBodyData = AnonymousAuthDataRequestBody( + properties = Properties(), + userAgent = "testUserAgent" +) \ No newline at end of file diff --git a/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt b/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt index 46ef8cc8f..0d155607e 100644 --- a/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/NetworkFetchingAPITest.kt @@ -99,9 +99,9 @@ class NetworkFetchingAPITest { ) } -private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" +const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" -private val testSystemProperties = Properties().apply { +val testSystemProperties = Properties().apply { setProperty("UserReadableName", "coral-default") setProperty("Build.HARDWARE", "coral") setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") -- GitLab From de0ae7a019395ce60a82a5d6413daede15556c2c Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 13:09:34 +0530 Subject: [PATCH 030/112] Remove redundant code --- .../app/lounge/UserAuthenticationAPITest.kt | 164 ------------------ .../main/java/app/lounge/di/NetworkModule.kt | 3 +- .../java/app/lounge/extension/Extension.kt | 10 -- .../src/main/java/app/lounge/model/Model.kt | 24 --- .../lounge/networking/UserAuthentication.kt | 17 -- .../UserAuthenticationRetrofitImpl.kt | 93 ---------- 6 files changed, 1 insertion(+), 310 deletions(-) delete mode 100644 modules/src/androidTest/java/app/lounge/UserAuthenticationAPITest.kt delete mode 100644 modules/src/main/java/app/lounge/extension/Extension.kt delete mode 100644 modules/src/main/java/app/lounge/model/Model.kt delete mode 100644 modules/src/main/java/app/lounge/networking/UserAuthentication.kt delete mode 100644 modules/src/main/java/app/lounge/networking/UserAuthenticationRetrofitImpl.kt diff --git a/modules/src/androidTest/java/app/lounge/UserAuthenticationAPITest.kt b/modules/src/androidTest/java/app/lounge/UserAuthenticationAPITest.kt deleted file mode 100644 index b5fc794cb..000000000 --- a/modules/src/androidTest/java/app/lounge/UserAuthenticationAPITest.kt +++ /dev/null @@ -1,164 +0,0 @@ -package app.lounge - -import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AnonymousAuthDataValidationRequestBody -import app.lounge.model.AuthDataResponse -import app.lounge.model.AuthDataValidationResponse -import app.lounge.networking.NetworkResult -import app.lounge.networking.UserAuthentication -import app.lounge.networking.UserAuthenticationRetrofitAPI -import app.lounge.networking.UserAuthenticationRetrofitImpl -import kotlinx.coroutines.runBlocking -import okhttp3.OkHttpClient -import org.junit.Test -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import java.io.InterruptedIOException -import java.util.Properties -import java.util.concurrent.TimeUnit - -class UserAuthenticationAPITest { - - private val userAuthenticationToken: UserAuthentication = UserAuthenticationRetrofitImpl( - Retrofit.Builder() - .baseUrl(UserAuthenticationRetrofitAPI.tokenBaseURL) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .callTimeout(10, TimeUnit.SECONDS) - .build() - ) - .build() - ) - - private val userAuthenticationGoogle: UserAuthentication = UserAuthenticationRetrofitImpl( - Retrofit.Builder() - .baseUrl(UserAuthenticationRetrofitAPI.googlePlayBaseURL) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .callTimeout(10, TimeUnit.SECONDS) - .build() - ) - .build() - ) - - private val userAuthenticationTimeoutGoogle: UserAuthentication = UserAuthenticationRetrofitImpl( - Retrofit.Builder() - .baseUrl(UserAuthenticationRetrofitAPI.googlePlayBaseURL) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .callTimeout(10, TimeUnit.MILLISECONDS) - .build() - ) - .build() - ) - - private val requestBodyData = AnonymousAuthDataRequestBody( - properties = testSystemProperties, - userAgent = testUserAgent - ) - - companion object { - var authData: AuthDataResponse? = null - } - - @Test - fun test1OnSuccessReturnsAuthData() = runBlocking { - val response = userAuthenticationToken.requestAuthData( - anonymousAuthDataRequestBody = requestBodyData, - ) - when(response){ - is NetworkResult.Success -> authData = response.data - else -> {} - } - - assert(authData is AuthDataResponse) { "Assert!! Success must return data" } - } - - @Test - fun test2OnSuccessReturnsLoginData() = runBlocking { - var result: AuthDataValidationResponse? = null - authData?.let { authData -> - authData.dfeCookie = "null" - - val response = userAuthenticationGoogle.requestAuthDataValidation( - anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( - authDataResponse = authData - ) - ) - when(response){ - is NetworkResult.Success -> result = response.data - else -> {} - } - } - - assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } - assert(result is AuthDataValidationResponse) { "Assert!! `response` must have data" } - } - - @Test - fun test3OnTimeoutFailureReturnsError(): Unit = runBlocking { - var failure: Throwable = Exception("No Error") - authData?.let { authData -> - authData.dfeCookie = "null" - - try { - val response = userAuthenticationTimeoutGoogle.requestAuthDataValidation( - anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( - authDataResponse = authData - ) - ) - - when(response){ - is NetworkResult.Error -> failure = response.exception - else -> {} - } - } catch (e: InterruptedIOException) { failure = e } - } - assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } - assert(failure is InterruptedIOException) { "Assert!! Timeout Failure callback must call" } - } -} - -private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" - -private val testSystemProperties = Properties().apply { - setProperty("UserReadableName", "coral-default") - setProperty("Build.HARDWARE", "coral") - setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") - setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") - setProperty("Build.BRAND", "google") - setProperty("Build.DEVICE", "coral") - setProperty("Build.VERSION.SDK_INT", "32") - setProperty("Build.VERSION.RELEASE", "12") - setProperty("Build.MODEL", "Pixel 4 XL") - setProperty("Build.MANUFACTURER", "Google") - setProperty("Build.PRODUCT", "coral") - setProperty("Build.ID", "SQ3A.220705.004") - setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") - setProperty("TouchScreen", "3") - setProperty("Keyboard", "1") - setProperty("Navigation", "1") - setProperty("ScreenLayout", "2") - setProperty("HasHardKeyboard", "false") - setProperty("HasFiveWayNavigation", "false") - setProperty("Screen.Density", "560") - setProperty("Screen.Width", "1440") - setProperty("Screen.Height", "2984") - setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") - setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") - setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") - setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") - setProperty("GL.Version", "196610") - setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") - setProperty("Client", "android-google") - setProperty("GSF.version", "223616055") - setProperty("Vending.version", "82151710") - setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") - setProperty("Roaming", "mobile-notroaming") - setProperty("TimeZone", "UTC-10") - setProperty("CellOperator", "310") - setProperty("SimOperator", "38") -} diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 52667088e..5251de66f 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -1,6 +1,5 @@ package app.lounge.di -import app.lounge.networking.UserAuthenticationRetrofitAPI import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -23,7 +22,7 @@ internal object NetworkModule { @Singleton internal fun providesRetrofit( okHttpClient: OkHttpClient, - baseUrl: String = UserAuthenticationRetrofitAPI.tokenBaseURL + baseUrl: String ) : Retrofit { return Retrofit.Builder() .baseUrl(baseUrl) diff --git a/modules/src/main/java/app/lounge/extension/Extension.kt b/modules/src/main/java/app/lounge/extension/Extension.kt deleted file mode 100644 index 17a68e188..000000000 --- a/modules/src/main/java/app/lounge/extension/Extension.kt +++ /dev/null @@ -1,10 +0,0 @@ -package app.lounge.extension - -import com.google.gson.Gson -import java.util.Properties - -/** - * Convert Properties parameter to byte array - * @return Byte Array of Properties - * */ -fun Properties.toByteArray() = Gson().toJson(this).toByteArray() \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/model/Model.kt b/modules/src/main/java/app/lounge/model/Model.kt deleted file mode 100644 index ba4263416..000000000 --- a/modules/src/main/java/app/lounge/model/Model.kt +++ /dev/null @@ -1,24 +0,0 @@ -package app.lounge.model - -import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.providers.HeaderProvider -import okhttp3.ResponseBody -import java.util.Properties - - -/** AnonymousAuthDataRequestBody */ -data class AnonymousAuthDataRequestBody( - val properties: Properties, - val userAgent: String -) - -/** AnonymousLoginRequestBody */ -data class AnonymousAuthDataValidationRequestBody( - val authDataResponse: AuthDataResponse, -) { - val header = HeaderProvider.getDefaultHeaders(authDataResponse) -} - -typealias AuthDataResponse = AuthData - -typealias AuthDataValidationResponse = ResponseBody \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/UserAuthentication.kt b/modules/src/main/java/app/lounge/networking/UserAuthentication.kt deleted file mode 100644 index 2eeed0f13..000000000 --- a/modules/src/main/java/app/lounge/networking/UserAuthentication.kt +++ /dev/null @@ -1,17 +0,0 @@ -package app.lounge.networking - -import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AnonymousAuthDataValidationRequestBody -import app.lounge.model.AuthDataResponse -import app.lounge.model.AuthDataValidationResponse - -interface UserAuthentication { - suspend fun requestAuthData( - anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody - ) : NetworkResult - - suspend fun requestAuthDataValidation( - anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody - ) : NetworkResult -} - diff --git a/modules/src/main/java/app/lounge/networking/UserAuthenticationRetrofitImpl.kt b/modules/src/main/java/app/lounge/networking/UserAuthenticationRetrofitImpl.kt deleted file mode 100644 index ae87eaded..000000000 --- a/modules/src/main/java/app/lounge/networking/UserAuthenticationRetrofitImpl.kt +++ /dev/null @@ -1,93 +0,0 @@ -package app.lounge.networking - -import app.lounge.extension.toByteArray -import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AnonymousAuthDataValidationRequestBody -import app.lounge.model.AuthDataResponse -import app.lounge.model.AuthDataValidationResponse -import com.aurora.gplayapi.GooglePlayApi -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import retrofit2.Response -import retrofit2.Retrofit -import retrofit2.http.Body -import retrofit2.http.HeaderMap -import retrofit2.http.POST -import javax.inject.Inject -import javax.inject.Singleton - -interface UserAuthenticationRetrofitAPI { - - companion object { - const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" - const val googlePlayBaseURL: String = GooglePlayApi.URL_BASE - } - - @POST(Path.authData) - suspend fun authDataRequest( - @HeaderMap headers: Map, - @Body requestBody: RequestBody - ): Response - - @POST(Path.sync) - suspend fun validateAuthentication( - @HeaderMap headers: Map - ): Response - - - - object Header { - val authData: (() -> String) -> Map = { - mapOf(Pair("User-Agent", it.invoke())) - } - } - - private object Path { - const val authData = "/" - const val sync = "/fdfe/apps/contentSync" - } - -} - -@Singleton -class UserAuthenticationRetrofitImpl @Inject constructor( - retrofit: Retrofit -) : UserAuthentication { - - private val userAuthenticationRetrofitAPI = retrofit.create( - UserAuthenticationRetrofitAPI::class.java - ) - - override suspend fun requestAuthData( - anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody - ): NetworkResult { - val requestBody: RequestBody = - anonymousAuthDataRequestBody.properties.toByteArray().let { result -> - result.toRequestBody( - contentType = "application/json".toMediaTypeOrNull(), - offset = 0, - byteCount = result.size - ) - } - return fetch { - userAuthenticationRetrofitAPI.authDataRequest( - requestBody = requestBody, - headers = UserAuthenticationRetrofitAPI.Header.authData { - anonymousAuthDataRequestBody.userAgent - } - ) - } - } - - override suspend fun requestAuthDataValidation( - anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody - ): NetworkResult { - return fetch { - userAuthenticationRetrofitAPI.validateAuthentication( - headers = anonymousAuthDataValidationRequestBody.header - ) - } - } - -} \ No newline at end of file -- GitLab From 054f1424416995c828ffe08cc86d2f07d0b444ac Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 14:39:38 +0530 Subject: [PATCH 031/112] Add Copyright text --- app/build.gradle | 20 +++++++++++++++++-- modules/build.gradle | 19 ++++++++++++++++++ .../main/java/app/lounge/di/NetworkModule.kt | 19 ++++++++++++++++++ .../app/lounge/networking/RetrofitHandler.kt | 19 ++++++++++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f0e0d5e26..15d44becc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + plugins { id 'com.android.application' @@ -147,8 +165,6 @@ allOpen { dependencies { - implementation project(path: ':modules') - // TODO: Add splitinstall-lib to a repo https://gitlab.e.foundation/e/os/backlog/-/issues/628 api files('libs/splitinstall-lib.jar') diff --git a/modules/build.gradle b/modules/build.gradle index d36cf010a..48fab303e 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 5251de66f..e64cde0cf 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package app.lounge.di import dagger.Module diff --git a/modules/src/main/java/app/lounge/networking/RetrofitHandler.kt b/modules/src/main/java/app/lounge/networking/RetrofitHandler.kt index 804dc542e..9138167f7 100644 --- a/modules/src/main/java/app/lounge/networking/RetrofitHandler.kt +++ b/modules/src/main/java/app/lounge/networking/RetrofitHandler.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package app.lounge.networking import retrofit2.Response -- GitLab From d3400d52dc4bfe8dab349541aad7f7f3ed5d86f5 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 14:41:11 +0530 Subject: [PATCH 032/112] revert unwnated changes --- app/build.gradle | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 15d44becc..1d0b1c0d7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,21 +1,3 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - plugins { id 'com.android.application' -- GitLab From bd29d531b49f49cceeac27673b86a2fce07587fd Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 14:54:44 +0530 Subject: [PATCH 033/112] Implement Anonymous user network api --- .../java/app/lounge/AnonymousUserAPITest.kt | 158 ++++++++++++++++++ .../main/java/app/lounge/di/NetworkModule.kt | 47 +++++- .../java/app/lounge/extension/Extension.kt | 29 ++++ .../src/main/java/app/lounge/model/Model.kt | 37 ++++ .../AnonymousAnonymousUserRetrofitImpl.kt | 118 +++++++++++++ .../app/lounge/networking/AnonymousUser.kt | 36 ++++ 6 files changed, 420 insertions(+), 5 deletions(-) create mode 100644 modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt create mode 100644 modules/src/main/java/app/lounge/extension/Extension.kt create mode 100644 modules/src/main/java/app/lounge/model/Model.kt create mode 100644 modules/src/main/java/app/lounge/networking/AnonymousAnonymousUserRetrofitImpl.kt create mode 100644 modules/src/main/java/app/lounge/networking/AnonymousUser.kt diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt new file mode 100644 index 000000000..2651e08db --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt @@ -0,0 +1,158 @@ +package app.lounge + +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AnonymousAuthDataValidationRequestBody +import app.lounge.model.AuthDataResponse +import app.lounge.model.AuthDataValidationResponse +import app.lounge.networking.NetworkResult +import app.lounge.networking.UserAuthentication +import app.lounge.networking.UserAuthenticationRetrofitAPI +import app.lounge.networking.UserAuthenticationRetrofitImpl +import kotlinx.coroutines.runBlocking +import okhttp3.OkHttpClient +import org.junit.Test +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.io.InterruptedIOException +import java.util.Properties +import java.util.concurrent.TimeUnit + +class UserAuthenticationAPITest { + + companion object { + var authData: AuthDataResponse? = null + } + + @Test + fun test1OnSuccessReturnsAuthData() = runBlocking { + val response = userAuthentication.requestAuthData( + anonymousAuthDataRequestBody = requestBodyData, + ) + when(response){ + is NetworkResult.Success -> authData = response.data + else -> {} + } + + assert(authData is AuthDataResponse) { "Assert!! Success must return data" } + } + + @Test + fun test2OnSuccessReturnsLoginData() = runBlocking { + var result: AuthDataValidationResponse? = null + authData?.let { authData -> + authData.dfeCookie = "null" + + val response = userAuthentication.requestAuthDataValidation( + anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( + authDataResponse = authData + ) + ) + when(response){ + is NetworkResult.Success -> result = response.data + else -> {} + } + } + + assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } + assert(result is AuthDataValidationResponse) { "Assert!! `response` must have data" } + } + + @Test + fun test3OnTimeoutFailureReturnsError(): Unit = runBlocking { + var failure: Throwable = Exception("No Error") + authData?.let { authData -> + authData.dfeCookie = "null" + + try { + val response = userAuthenticationGoogleTimeout.requestAuthDataValidation( + anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( + authDataResponse = authData + ) + ) + + when(response){ + is NetworkResult.Error -> failure = response.exception + else -> {} + } + } catch (e: InterruptedIOException) { failure = e } + } + assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } + assert(failure is InterruptedIOException) { "Assert!! Timeout Failure callback must call" } + } + + + private fun retrofitTestConfig( + baseUrl: String, + timeoutInMillisecond: Long = 10000L + ): Retrofit = Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .client( + OkHttpClient.Builder() + .callTimeout(timeoutInMillisecond, TimeUnit.MILLISECONDS) + .build() + ) + .build() + + private val eCloudTest = retrofitTestConfig(UserAuthenticationRetrofitAPI.tokenBaseURL) + private val googleTest = retrofitTestConfig(UserAuthenticationRetrofitAPI.googlePlayBaseURL) + private val googleTestTimeout = retrofitTestConfig( + UserAuthenticationRetrofitAPI.googlePlayBaseURL, 50L) + + private val userAuthentication: UserAuthentication = UserAuthenticationRetrofitImpl( + eCloud = eCloudTest, + google = googleTest + ) + + private val userAuthenticationGoogleTimeout: UserAuthentication = UserAuthenticationRetrofitImpl( + eCloud = eCloudTest, + google = googleTestTimeout + ) + + private val requestBodyData = AnonymousAuthDataRequestBody( + properties = testSystemProperties, + userAgent = testUserAgent + ) +} + +private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" + +private val testSystemProperties = Properties().apply { + setProperty("UserReadableName", "coral-default") + setProperty("Build.HARDWARE", "coral") + setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") + setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") + setProperty("Build.BRAND", "google") + setProperty("Build.DEVICE", "coral") + setProperty("Build.VERSION.SDK_INT", "32") + setProperty("Build.VERSION.RELEASE", "12") + setProperty("Build.MODEL", "Pixel 4 XL") + setProperty("Build.MANUFACTURER", "Google") + setProperty("Build.PRODUCT", "coral") + setProperty("Build.ID", "SQ3A.220705.004") + setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") + setProperty("TouchScreen", "3") + setProperty("Keyboard", "1") + setProperty("Navigation", "1") + setProperty("ScreenLayout", "2") + setProperty("HasHardKeyboard", "false") + setProperty("HasFiveWayNavigation", "false") + setProperty("Screen.Density", "560") + setProperty("Screen.Width", "1440") + setProperty("Screen.Height", "2984") + setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") + setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") + setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") + setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") + setProperty("GL.Version", "196610") + setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") + setProperty("Client", "android-google") + setProperty("GSF.version", "223616055") + setProperty("Vending.version", "82151710") + setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") + setProperty("Roaming", "mobile-notroaming") + setProperty("TimeZone", "UTC-10") + setProperty("CellOperator", "310") + setProperty("SimOperator", "38") +} + diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 5251de66f..fb2c40163 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -1,5 +1,8 @@ package app.lounge.di +import app.lounge.networking.AnonymousUser +import app.lounge.networking.AnonymousUserRetrofitAPI +import app.lounge.networking.AnonymousAnonymousUserRetrofitImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -16,11 +19,9 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) internal object NetworkModule { - private const val HTTP_TIMEOUT = 10L + private const val HTTP_TIMEOUT_IN_SECOND = 10L - @Provides - @Singleton - internal fun providesRetrofit( + private fun retrofit( okHttpClient: OkHttpClient, baseUrl: String ) : Retrofit { @@ -31,6 +32,30 @@ internal object NetworkModule { .build() } + @Provides + @Singleton + @Named("ECloudRetrofit") + internal fun provideECloudRetrofit( + okHttpClient: OkHttpClient + ): Retrofit { + return retrofit( + okHttpClient = okHttpClient, + baseUrl = AnonymousUserRetrofitAPI.tokenBaseURL + ) + } + + @Provides + @Singleton + @Named("GoogleRetrofit") + internal fun provideGoogleRetrofit( + okHttpClient: OkHttpClient + ): Retrofit { + return retrofit( + okHttpClient = okHttpClient, + baseUrl = AnonymousUserRetrofitAPI.googlePlayBaseURL + ) + } + @Provides @Singleton @Named("privateOkHttpClient") @@ -39,7 +64,7 @@ internal object NetworkModule { ): OkHttpClient { return OkHttpClient.Builder() .addNetworkInterceptor(httpLogger) - .callTimeout(HTTP_TIMEOUT, TimeUnit.SECONDS) + .callTimeout(HTTP_TIMEOUT_IN_SECOND, TimeUnit.SECONDS) .build() } @@ -54,4 +79,16 @@ internal object NetworkModule { } } + @Provides + @Singleton + fun provideNetworkFetching( + @Named("ECloudRetrofit") ecloud: Retrofit, + @Named("GoogleRetrofit") google: Retrofit, + ) : AnonymousUser { + return AnonymousAnonymousUserRetrofitImpl( + eCloud = ecloud, + google = google + ) + } + } \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/extension/Extension.kt b/modules/src/main/java/app/lounge/extension/Extension.kt new file mode 100644 index 000000000..98ac278b0 --- /dev/null +++ b/modules/src/main/java/app/lounge/extension/Extension.kt @@ -0,0 +1,29 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +package app.lounge.extension + +import com.google.gson.Gson +import java.util.Properties + +/** + * Convert Properties parameter to byte array + * @return Byte Array of Properties + * */ +fun Properties.toByteArray() = Gson().toJson(this).toByteArray() \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/model/Model.kt b/modules/src/main/java/app/lounge/model/Model.kt new file mode 100644 index 000000000..acb1eb88c --- /dev/null +++ b/modules/src/main/java/app/lounge/model/Model.kt @@ -0,0 +1,37 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package app.lounge.model + +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.providers.HeaderProvider +import java.util.Properties + + +/** AnonymousAuthDataRequestBody */ +data class AnonymousAuthDataRequestBody( + val properties: Properties, + val userAgent: String +) + +/** AnonymousLoginRequestBody */ +data class AnonymousAuthDataValidationRequestBody( + val authDataResponse: AuthData, +) { + val header = HeaderProvider.getDefaultHeaders(authDataResponse) +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/AnonymousAnonymousUserRetrofitImpl.kt b/modules/src/main/java/app/lounge/networking/AnonymousAnonymousUserRetrofitImpl.kt new file mode 100644 index 000000000..24e65bb46 --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/AnonymousAnonymousUserRetrofitImpl.kt @@ -0,0 +1,118 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +package app.lounge.networking + +import app.lounge.extension.toByteArray +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AnonymousAuthDataValidationRequestBody +import com.aurora.gplayapi.GooglePlayApi +import com.aurora.gplayapi.data.models.AuthData +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.ResponseBody +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.http.Body +import retrofit2.http.HeaderMap +import retrofit2.http.POST +import javax.inject.Inject +import javax.inject.Singleton + +interface AnonymousUserRetrofitAPI { + + companion object { + const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" + const val googlePlayBaseURL: String = GooglePlayApi.URL_BASE + } + + @POST(Path.authData) + suspend fun authDataRequest( + @HeaderMap headers: Map, + @Body requestBody: RequestBody + ): Response + + @POST(Path.sync) + suspend fun validateAuthentication( + @HeaderMap headers: Map + ): Response + + + + object Header { + val authData: (() -> String) -> Map = { + mapOf(Pair("User-Agent", it.invoke())) + } + } + + private object Path { + const val authData = "/" + const val sync = "/fdfe/apps/contentSync" + } + +} + +@Singleton +class AnonymousAnonymousUserRetrofitImpl @Inject constructor( + val eCloud: Retrofit, + val google: Retrofit +) : AnonymousUser { + + private val eCloudNetworkFetchingRetrofitAPI = eCloud.create( + AnonymousUserRetrofitAPI::class.java + ) + + private val googleNetworkFetchingRetrofitAPI = google.create( + AnonymousUserRetrofitAPI::class.java + ) + + + override suspend fun requestAuthData( + anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody + ): NetworkResult { + val requestBody: RequestBody = + anonymousAuthDataRequestBody.properties.toByteArray().let { result -> + result.toRequestBody( + contentType = "application/json".toMediaTypeOrNull(), + offset = 0, + byteCount = result.size + ) + } + return fetch { + eCloudNetworkFetchingRetrofitAPI.authDataRequest( + requestBody = requestBody, + headers = AnonymousUserRetrofitAPI.Header.authData { + anonymousAuthDataRequestBody.userAgent + } + ) + } + } + + override suspend fun requestAuthDataValidation( + anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody + ): NetworkResult { + return fetch { + googleNetworkFetchingRetrofitAPI.validateAuthentication( + headers = anonymousAuthDataValidationRequestBody.header + ) + } + } + +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/AnonymousUser.kt b/modules/src/main/java/app/lounge/networking/AnonymousUser.kt new file mode 100644 index 000000000..00ee7c54d --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/AnonymousUser.kt @@ -0,0 +1,36 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +package app.lounge.networking + +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.model.AnonymousAuthDataValidationRequestBody +import com.aurora.gplayapi.data.models.AuthData +import okhttp3.ResponseBody + +interface AnonymousUser { + suspend fun requestAuthData( + anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody + ) : NetworkResult + + suspend fun requestAuthDataValidation( + anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody + ) : NetworkResult +} + -- GitLab From 44ea3b245b08b78791430bbd1550aa8d5c9d83d9 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 14:58:21 +0530 Subject: [PATCH 034/112] Deleted test files --- .../java/app/lounge/AnonymousUserAPITest.kt | 158 ------------------ 1 file changed, 158 deletions(-) delete mode 100644 modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt deleted file mode 100644 index 2651e08db..000000000 --- a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt +++ /dev/null @@ -1,158 +0,0 @@ -package app.lounge - -import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AnonymousAuthDataValidationRequestBody -import app.lounge.model.AuthDataResponse -import app.lounge.model.AuthDataValidationResponse -import app.lounge.networking.NetworkResult -import app.lounge.networking.UserAuthentication -import app.lounge.networking.UserAuthenticationRetrofitAPI -import app.lounge.networking.UserAuthenticationRetrofitImpl -import kotlinx.coroutines.runBlocking -import okhttp3.OkHttpClient -import org.junit.Test -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import java.io.InterruptedIOException -import java.util.Properties -import java.util.concurrent.TimeUnit - -class UserAuthenticationAPITest { - - companion object { - var authData: AuthDataResponse? = null - } - - @Test - fun test1OnSuccessReturnsAuthData() = runBlocking { - val response = userAuthentication.requestAuthData( - anonymousAuthDataRequestBody = requestBodyData, - ) - when(response){ - is NetworkResult.Success -> authData = response.data - else -> {} - } - - assert(authData is AuthDataResponse) { "Assert!! Success must return data" } - } - - @Test - fun test2OnSuccessReturnsLoginData() = runBlocking { - var result: AuthDataValidationResponse? = null - authData?.let { authData -> - authData.dfeCookie = "null" - - val response = userAuthentication.requestAuthDataValidation( - anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( - authDataResponse = authData - ) - ) - when(response){ - is NetworkResult.Success -> result = response.data - else -> {} - } - } - - assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } - assert(result is AuthDataValidationResponse) { "Assert!! `response` must have data" } - } - - @Test - fun test3OnTimeoutFailureReturnsError(): Unit = runBlocking { - var failure: Throwable = Exception("No Error") - authData?.let { authData -> - authData.dfeCookie = "null" - - try { - val response = userAuthenticationGoogleTimeout.requestAuthDataValidation( - anonymousAuthDataValidationRequestBody = AnonymousAuthDataValidationRequestBody( - authDataResponse = authData - ) - ) - - when(response){ - is NetworkResult.Error -> failure = response.exception - else -> {} - } - } catch (e: InterruptedIOException) { failure = e } - } - assert(authData is AuthDataResponse) { "Assert!! AuthData must be present" } - assert(failure is InterruptedIOException) { "Assert!! Timeout Failure callback must call" } - } - - - private fun retrofitTestConfig( - baseUrl: String, - timeoutInMillisecond: Long = 10000L - ): Retrofit = Retrofit.Builder() - .baseUrl(baseUrl) - .addConverterFactory(GsonConverterFactory.create()) - .client( - OkHttpClient.Builder() - .callTimeout(timeoutInMillisecond, TimeUnit.MILLISECONDS) - .build() - ) - .build() - - private val eCloudTest = retrofitTestConfig(UserAuthenticationRetrofitAPI.tokenBaseURL) - private val googleTest = retrofitTestConfig(UserAuthenticationRetrofitAPI.googlePlayBaseURL) - private val googleTestTimeout = retrofitTestConfig( - UserAuthenticationRetrofitAPI.googlePlayBaseURL, 50L) - - private val userAuthentication: UserAuthentication = UserAuthenticationRetrofitImpl( - eCloud = eCloudTest, - google = googleTest - ) - - private val userAuthenticationGoogleTimeout: UserAuthentication = UserAuthenticationRetrofitImpl( - eCloud = eCloudTest, - google = googleTestTimeout - ) - - private val requestBodyData = AnonymousAuthDataRequestBody( - properties = testSystemProperties, - userAgent = testUserAgent - ) -} - -private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" - -private val testSystemProperties = Properties().apply { - setProperty("UserReadableName", "coral-default") - setProperty("Build.HARDWARE", "coral") - setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") - setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") - setProperty("Build.BRAND", "google") - setProperty("Build.DEVICE", "coral") - setProperty("Build.VERSION.SDK_INT", "32") - setProperty("Build.VERSION.RELEASE", "12") - setProperty("Build.MODEL", "Pixel 4 XL") - setProperty("Build.MANUFACTURER", "Google") - setProperty("Build.PRODUCT", "coral") - setProperty("Build.ID", "SQ3A.220705.004") - setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") - setProperty("TouchScreen", "3") - setProperty("Keyboard", "1") - setProperty("Navigation", "1") - setProperty("ScreenLayout", "2") - setProperty("HasHardKeyboard", "false") - setProperty("HasFiveWayNavigation", "false") - setProperty("Screen.Density", "560") - setProperty("Screen.Width", "1440") - setProperty("Screen.Height", "2984") - setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") - setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") - setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") - setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") - setProperty("GL.Version", "196610") - setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") - setProperty("Client", "android-google") - setProperty("GSF.version", "223616055") - setProperty("Vending.version", "82151710") - setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") - setProperty("Roaming", "mobile-notroaming") - setProperty("TimeZone", "UTC-10") - setProperty("CellOperator", "310") - setProperty("SimOperator", "38") -} - -- GitLab From 3326606c2dfc84a117f409f8ab872fe14760075a Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 15:12:43 +0530 Subject: [PATCH 035/112] Refine anonymous user codebase --- .../java/app/lounge/AnonymousUserAPITest.kt | 100 ++++++++++++++++++ .../main/java/app/lounge/di/NetworkModule.kt | 22 +--- .../src/main/java/app/lounge/model/Model.kt | 11 +- .../app/lounge/networking/AnonymousUser.kt | 6 -- ...itImpl.kt => AnonymousUserRetrofitImpl.kt} | 36 +------ 5 files changed, 109 insertions(+), 66 deletions(-) create mode 100644 modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt rename modules/src/main/java/app/lounge/networking/{AnonymousAnonymousUserRetrofitImpl.kt => AnonymousUserRetrofitImpl.kt} (69%) diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt new file mode 100644 index 000000000..130bee5b5 --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt @@ -0,0 +1,100 @@ +package app.lounge + +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.networking.AnonymousUser +import app.lounge.networking.AnonymousUserRetrofitAPI +import app.lounge.networking.AnonymousUserRetrofitImpl +import app.lounge.networking.NetworkResult +import com.aurora.gplayapi.data.models.AuthData +import kotlinx.coroutines.runBlocking +import okhttp3.OkHttpClient +import org.junit.Test +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.Properties +import java.util.concurrent.TimeUnit + +class AnonymousUserAPITest { + + companion object { + var authData: AuthData? = null + } + + @Test + fun testOnSuccessReturnsAuthData() = runBlocking { + val response = anonymousUser.requestAuthData( + anonymousAuthDataRequestBody = requestBodyData, + ) + when(response){ + is NetworkResult.Success -> authData = response.data + else -> {} + } + + assert(authData is AuthData) { "Assert!! Success must return data" } + } + + + private fun retrofitTestConfig( + baseUrl: String, + timeoutInMillisecond: Long = 10000L + ): Retrofit = Retrofit.Builder() + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .client( + OkHttpClient.Builder() + .callTimeout(timeoutInMillisecond, TimeUnit.MILLISECONDS) + .build() + ) + .build() + + private val eCloudTest = retrofitTestConfig(AnonymousUserRetrofitAPI.tokenBaseURL) + + private val anonymousUser: AnonymousUser = AnonymousUserRetrofitImpl(eCloud = eCloudTest) + + private val requestBodyData = AnonymousAuthDataRequestBody( + properties = testSystemProperties, + userAgent = testUserAgent + ) +} + +private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" + +private val testSystemProperties = Properties().apply { + setProperty("UserReadableName", "coral-default") + setProperty("Build.HARDWARE", "coral") + setProperty("Build.RADIO", "g8150-00123-220402-B-8399852") + setProperty("Build.FINGERPRINT","google/coral/coral:12/SQ3A.220705.003.A1/8672226:user/release-keys") + setProperty("Build.BRAND", "google") + setProperty("Build.DEVICE", "coral") + setProperty("Build.VERSION.SDK_INT", "32") + setProperty("Build.VERSION.RELEASE", "12") + setProperty("Build.MODEL", "Pixel 4 XL") + setProperty("Build.MANUFACTURER", "Google") + setProperty("Build.PRODUCT", "coral") + setProperty("Build.ID", "SQ3A.220705.004") + setProperty("Build.BOOTLOADER", "c2f2-0.4-8351033") + setProperty("TouchScreen", "3") + setProperty("Keyboard", "1") + setProperty("Navigation", "1") + setProperty("ScreenLayout", "2") + setProperty("HasHardKeyboard", "false") + setProperty("HasFiveWayNavigation", "false") + setProperty("Screen.Density", "560") + setProperty("Screen.Width", "1440") + setProperty("Screen.Height", "2984") + setProperty("Platforms", "arm64-v8a,armeabi-v7a,armeabi") + setProperty("Features", "android.hardware.sensor.proximity,com.verizon.hardware.telephony.lte,com.verizon.hardware.telephony.ehrpd,android.hardware.sensor.accelerometer,android.software.controls,android.hardware.faketouch,com.google.android.feature.D2D_CABLE_MIGRATION_FEATURE,android.hardware.telephony.euicc,android.hardware.reboot_escrow,android.hardware.usb.accessory,android.hardware.telephony.cdma,android.software.backup,android.hardware.touchscreen,android.hardware.touchscreen.multitouch,android.software.print,org.lineageos.weather,android.software.activities_on_secondary_displays,android.hardware.wifi.rtt,com.google.android.feature.PIXEL_2017_EXPERIENCE,android.software.voice_recognizers,android.software.picture_in_picture,android.hardware.sensor.gyroscope,android.hardware.audio.low_latency,android.software.vulkan.deqp.level,android.software.cant_save_state,com.google.android.feature.PIXEL_2018_EXPERIENCE,android.hardware.security.model.compatible,com.google.android.feature.PIXEL_2019_EXPERIENCE,android.hardware.opengles.aep,org.lineageos.livedisplay,org.lineageos.profiles,android.hardware.bluetooth,android.hardware.camera.autofocus,android.hardware.telephony.gsm,android.hardware.telephony.ims,android.software.incremental_delivery,android.software.sip.voip,android.hardware.se.omapi.ese,android.software.opengles.deqp.level,android.hardware.usb.host,android.hardware.audio.output,android.software.verified_boot,android.hardware.camera.flash,android.hardware.camera.front,android.hardware.sensor.hifi_sensors,com.google.android.apps.photos.PIXEL_2019_PRELOAD,android.hardware.se.omapi.uicc,android.hardware.strongbox_keystore,android.hardware.screen.portrait,android.hardware.nfc,com.google.android.feature.TURBO_PRELOAD,com.nxp.mifare,android.hardware.sensor.stepdetector,android.software.home_screen,android.hardware.context_hub,android.hardware.microphone,android.software.autofill,org.lineageos.hardware,org.lineageos.globalactions,android.software.securely_removes_users,com.google.android.feature.PIXEL_EXPERIENCE,android.hardware.bluetooth_le,android.hardware.sensor.compass,com.google.android.feature.GOOGLE_FI_BUNDLED,android.hardware.touchscreen.multitouch.jazzhand,android.hardware.sensor.barometer,android.software.app_widgets,android.hardware.telephony.carrierlock,android.software.input_methods,android.hardware.sensor.light,android.hardware.vulkan.version,android.software.companion_device_setup,android.software.device_admin,com.google.android.feature.WELLBEING,android.hardware.wifi.passpoint,android.hardware.camera,org.lineageos.trust,android.hardware.device_unique_attestation,android.hardware.screen.landscape,android.software.device_id_attestation,com.google.android.feature.AER_OPTIMIZED,android.hardware.ram.normal,org.lineageos.android,com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE,android.software.managed_users,android.software.webview,android.hardware.sensor.stepcounter,android.hardware.camera.capability.manual_post_processing,android.hardware.camera.any,android.hardware.camera.capability.raw,android.hardware.vulkan.compute,android.software.connectionservice,android.hardware.touchscreen.multitouch.distinct,android.hardware.location.network,android.software.cts,android.software.sip,android.hardware.camera.capability.manual_sensor,android.software.app_enumeration,android.hardware.camera.level.full,android.hardware.identity_credential,android.hardware.wifi.direct,android.software.live_wallpaper,android.software.ipsec_tunnels,org.lineageos.settings,android.hardware.sensor.assist,android.hardware.audio.pro,android.hardware.nfc.hcef,android.hardware.nfc.uicc,android.hardware.location.gps,android.sofware.nfc.beam,android.software.midi,android.hardware.nfc.any,android.hardware.nfc.ese,android.hardware.nfc.hce,android.hardware.wifi,android.hardware.location,android.hardware.vulkan.level,android.hardware.wifi.aware,android.software.secure_lock_screen,android.hardware.biometrics.face,android.hardware.telephony,android.software.file_based_encryption") + setProperty("Locales", "af,af_ZA,am,am_ET,ar,ar_EG,ar_XB,as,ast_ES,az,be,bg,bg_BG,bn,bs,ca,ca_ES,cs,cs_CZ,cy,da,da_DK,de,de_DE,el,el_GR,en,en_AU,en_CA,en_GB,en_IN,en_US,en_XA,en_XC,es,es_419,es_ES,es_US,et,eu,fa,fa_IR,fi,fi_FI,fil,fil_PH,fr,fr_CA,fr_FR,gd,gl,gu,hi,hi_IN,hr,hr_HR,hu,hu_HU,hy,in,in_ID,is,it,it_IT,iw,iw_IL,ja,ja_JP,ka,kk,km,kn,ko,ko_KR,ky,lo,lt,lt_LT,lv,lv_LV,mk,ml,mn,mr,ms,ms_MY,my,nb,nb_NO,ne,nl,nl_NL,or,pa,pl,pl_PL,pt,pt_BR,pt_PT,ro,ro_RO,ru,ru_RU,si,sk,sk_SK,sl,sl_SI,sq,sr,sr_Latn,sr_RS,sv,sv_SE,sw,sw_TZ,ta,te,th,th_TH,tr,tr_TR,uk,uk_UA,ur,uz,vi,vi_VN,zh_CN,zh_HK,zh_TW,zu,zu_ZA") + setProperty("SharedLibraries", "android.test.base,android.test.mock,com.vzw.apnlib,android.hidl.manager-V1.0-java,qti-telephony-hidl-wrapper,libfastcvopt.so,google-ril,qti-telephony-utils,com.android.omadm.radioconfig,libcdsprpc.so,android.hidl.base-V1.0-java,com.qualcomm.qmapbridge,libairbrush-pixel.so,com.google.android.camera.experimental2019,libOpenCL-pixel.so,libadsprpc.so,com.android.location.provider,android.net.ipsec.ike,com.android.future.usb.accessory,libsdsprpc.so,android.ext.shared,javax.obex,izat.xt.srv,com.google.android.gms,lib_aion_buffer.so,com.qualcomm.uimremoteclientlibrary,libqdMetaData.so,com.qualcomm.uimremoteserverlibrary,com.qualcomm.qcrilhook,android.test.runner,org.apache.http.legacy,com.google.android.camera.extensions,com.google.android.hardwareinfo,com.android.cts.ctsshim.shared_library,com.android.nfc_extras,com.android.media.remotedisplay,com.android.mediadrm.signer,com.qualcomm.qti.imscmservice-V2.0-java,qti-telephony-hidl-wrapper-prd,com.qualcomm.qti.imscmservice-V2.1-java,com.qualcomm.qti.imscmservice-V2.2-java") + setProperty("GL.Version", "196610") + setProperty("GL.Extensions", ",GL_AMD_compressed_ATC_texture,GL_AMD_performance_monitor,GL_ANDROID_extension_pack_es31a,GL_APPLE_texture_2D_limited_npot,GL_ARB_vertex_buffer_object,GL_ARM_shader_framebuffer_fetch_depth_stencil,GL_EXT_EGL_image_array,GL_EXT_EGL_image_external_wrap_modes,GL_EXT_EGL_image_storage,GL_EXT_YUV_target,GL_EXT_blend_func_extended,GL_EXT_blit_framebuffer_params,GL_EXT_buffer_storage,GL_EXT_clip_control,GL_EXT_clip_cull_distance,GL_EXT_color_buffer_float,GL_EXT_color_buffer_half_float,GL_EXT_copy_image,GL_EXT_debug_label,GL_EXT_debug_marker,GL_EXT_discard_framebuffer,GL_EXT_disjoint_timer_query,GL_EXT_draw_buffers_indexed,GL_EXT_external_buffer,GL_EXT_fragment_invocation_density,GL_EXT_geometry_shader,GL_EXT_gpu_shader5,GL_EXT_memory_object,GL_EXT_memory_object_fd,GL_EXT_multisampled_render_to_texture,GL_EXT_multisampled_render_to_texture2,GL_EXT_primitive_bounding_box,GL_EXT_protected_textures,GL_EXT_read_format_bgra,GL_EXT_robustness,GL_EXT_sRGB,GL_EXT_sRGB_write_control,GL_EXT_shader_framebuffer_fetch,GL_EXT_shader_io_blocks,GL_EXT_shader_non_constant_global_initializers,GL_EXT_tessellation_shader,GL_EXT_texture_border_clamp,GL_EXT_texture_buffer,GL_EXT_texture_cube_map_array,GL_EXT_texture_filter_anisotropic,GL_EXT_texture_format_BGRA8888,GL_EXT_texture_format_sRGB_override,GL_EXT_texture_norm16,GL_EXT_texture_sRGB_R8,GL_EXT_texture_sRGB_decode,GL_EXT_texture_type_2_10_10_10_REV,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_debug,GL_KHR_no_error,GL_KHR_robust_buffer_access_behavior,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_NV_shader_noperspective_interpolation,GL_OES_EGL_image,GL_OES_EGL_image_external,GL_OES_EGL_image_external_essl3,GL_OES_EGL_sync,GL_OES_blend_equation_separate,GL_OES_blend_func_separate,GL_OES_blend_subtract,GL_OES_compressed_ETC1_RGB8_texture,GL_OES_compressed_paletted_texture,GL_OES_depth24,GL_OES_depth_texture,GL_OES_depth_texture_cube_map,GL_OES_draw_texture,GL_OES_element_index_uint,GL_OES_framebuffer_object,GL_OES_get_program_binary,GL_OES_matrix_palette,GL_OES_packed_depth_stencil,GL_OES_point_size_array,GL_OES_point_sprite,GL_OES_read_format,GL_OES_rgb8_rgba8,GL_OES_sample_shading,GL_OES_sample_variables,GL_OES_shader_image_atomic,GL_OES_shader_multisample_interpolation,GL_OES_standard_derivatives,GL_OES_stencil_wrap,GL_OES_surfaceless_context,GL_OES_texture_3D,GL_OES_texture_compression_astc,GL_OES_texture_cube_map,GL_OES_texture_env_crossbar,GL_OES_texture_float,GL_OES_texture_float_linear,GL_OES_texture_half_float,GL_OES_texture_half_float_linear,GL_OES_texture_mirrored_repeat,GL_OES_texture_npot,GL_OES_texture_stencil8,GL_OES_texture_storage_multisample_2d_array,GL_OES_texture_view,GL_OES_vertex_array_object,GL_OES_vertex_half_float,GL_OVR_multiview,GL_OVR_multiview2,GL_OVR_multiview_multisampled_render_to_texture,GL_QCOM_YUV_texture_gather,GL_QCOM_alpha_test,GL_QCOM_extended_get,GL_QCOM_motion_estimation,GL_QCOM_shader_framebuffer_fetch_noncoherent,GL_QCOM_shader_framebuffer_fetch_rate,GL_QCOM_texture_foveated,GL_QCOM_texture_foveated_subsampled_layout,GL_QCOM_tiled_rendering,GL_QCOM_validate_shader_binary") + setProperty("Client", "android-google") + setProperty("GSF.version", "223616055") + setProperty("Vending.version", "82151710") + setProperty("Vending.versionString", "21.5.17-21 [0] [PR] 326734551") + setProperty("Roaming", "mobile-notroaming") + setProperty("TimeZone", "UTC-10") + setProperty("CellOperator", "310") + setProperty("SimOperator", "38") +} + diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 8c11f0d04..c1c7f65cc 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -21,7 +21,7 @@ package app.lounge.di import app.lounge.networking.AnonymousUser import app.lounge.networking.AnonymousUserRetrofitAPI -import app.lounge.networking.AnonymousAnonymousUserRetrofitImpl +import app.lounge.networking.AnonymousUserRetrofitImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -63,18 +63,6 @@ internal object NetworkModule { ) } - @Provides - @Singleton - @Named("GoogleRetrofit") - internal fun provideGoogleRetrofit( - okHttpClient: OkHttpClient - ): Retrofit { - return retrofit( - okHttpClient = okHttpClient, - baseUrl = AnonymousUserRetrofitAPI.googlePlayBaseURL - ) - } - @Provides @Singleton @Named("privateOkHttpClient") @@ -101,12 +89,10 @@ internal object NetworkModule { @Provides @Singleton fun provideNetworkFetching( - @Named("ECloudRetrofit") ecloud: Retrofit, - @Named("GoogleRetrofit") google: Retrofit, + @Named("ECloudRetrofit") ecloud: Retrofit ) : AnonymousUser { - return AnonymousAnonymousUserRetrofitImpl( - eCloud = ecloud, - google = google + return AnonymousUserRetrofitImpl( + eCloud = ecloud ) } diff --git a/modules/src/main/java/app/lounge/model/Model.kt b/modules/src/main/java/app/lounge/model/Model.kt index acb1eb88c..e77a91db4 100644 --- a/modules/src/main/java/app/lounge/model/Model.kt +++ b/modules/src/main/java/app/lounge/model/Model.kt @@ -18,8 +18,6 @@ package app.lounge.model -import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.providers.HeaderProvider import java.util.Properties @@ -27,11 +25,4 @@ import java.util.Properties data class AnonymousAuthDataRequestBody( val properties: Properties, val userAgent: String -) - -/** AnonymousLoginRequestBody */ -data class AnonymousAuthDataValidationRequestBody( - val authDataResponse: AuthData, -) { - val header = HeaderProvider.getDefaultHeaders(authDataResponse) -} \ No newline at end of file +) \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/AnonymousUser.kt b/modules/src/main/java/app/lounge/networking/AnonymousUser.kt index 00ee7c54d..48acc7f5f 100644 --- a/modules/src/main/java/app/lounge/networking/AnonymousUser.kt +++ b/modules/src/main/java/app/lounge/networking/AnonymousUser.kt @@ -20,17 +20,11 @@ package app.lounge.networking import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AnonymousAuthDataValidationRequestBody import com.aurora.gplayapi.data.models.AuthData -import okhttp3.ResponseBody interface AnonymousUser { suspend fun requestAuthData( anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody ) : NetworkResult - - suspend fun requestAuthDataValidation( - anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody - ) : NetworkResult } diff --git a/modules/src/main/java/app/lounge/networking/AnonymousAnonymousUserRetrofitImpl.kt b/modules/src/main/java/app/lounge/networking/AnonymousUserRetrofitImpl.kt similarity index 69% rename from modules/src/main/java/app/lounge/networking/AnonymousAnonymousUserRetrofitImpl.kt rename to modules/src/main/java/app/lounge/networking/AnonymousUserRetrofitImpl.kt index 24e65bb46..967aadaa2 100644 --- a/modules/src/main/java/app/lounge/networking/AnonymousAnonymousUserRetrofitImpl.kt +++ b/modules/src/main/java/app/lounge/networking/AnonymousUserRetrofitImpl.kt @@ -21,13 +21,10 @@ package app.lounge.networking import app.lounge.extension.toByteArray import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AnonymousAuthDataValidationRequestBody -import com.aurora.gplayapi.GooglePlayApi import com.aurora.gplayapi.data.models.AuthData import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.ResponseBody import retrofit2.Response import retrofit2.Retrofit import retrofit2.http.Body @@ -40,7 +37,6 @@ interface AnonymousUserRetrofitAPI { companion object { const val tokenBaseURL: String = "https://eu.gtoken.ecloud.global" - const val googlePlayBaseURL: String = GooglePlayApi.URL_BASE } @POST(Path.authData) @@ -49,13 +45,6 @@ interface AnonymousUserRetrofitAPI { @Body requestBody: RequestBody ): Response - @POST(Path.sync) - suspend fun validateAuthentication( - @HeaderMap headers: Map - ): Response - - - object Header { val authData: (() -> String) -> Map = { mapOf(Pair("User-Agent", it.invoke())) @@ -64,26 +53,19 @@ interface AnonymousUserRetrofitAPI { private object Path { const val authData = "/" - const val sync = "/fdfe/apps/contentSync" } } @Singleton -class AnonymousAnonymousUserRetrofitImpl @Inject constructor( - val eCloud: Retrofit, - val google: Retrofit +class AnonymousUserRetrofitImpl @Inject constructor( + val eCloud: Retrofit ) : AnonymousUser { - private val eCloudNetworkFetchingRetrofitAPI = eCloud.create( - AnonymousUserRetrofitAPI::class.java - ) - - private val googleNetworkFetchingRetrofitAPI = google.create( + private val eCloudRetrofitAPI = eCloud.create( AnonymousUserRetrofitAPI::class.java ) - override suspend fun requestAuthData( anonymousAuthDataRequestBody: AnonymousAuthDataRequestBody ): NetworkResult { @@ -96,7 +78,7 @@ class AnonymousAnonymousUserRetrofitImpl @Inject constructor( ) } return fetch { - eCloudNetworkFetchingRetrofitAPI.authDataRequest( + eCloudRetrofitAPI.authDataRequest( requestBody = requestBody, headers = AnonymousUserRetrofitAPI.Header.authData { anonymousAuthDataRequestBody.userAgent @@ -105,14 +87,4 @@ class AnonymousAnonymousUserRetrofitImpl @Inject constructor( } } - override suspend fun requestAuthDataValidation( - anonymousAuthDataValidationRequestBody: AnonymousAuthDataValidationRequestBody - ): NetworkResult { - return fetch { - googleNetworkFetchingRetrofitAPI.validateAuthentication( - headers = anonymousAuthDataValidationRequestBody.header - ) - } - } - } \ No newline at end of file -- GitLab From bbc9afb1e3c4592ea38758c6e191ca2993febb5e Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 19:20:16 +0530 Subject: [PATCH 036/112] Move anonymous code to login package --- modules/build.gradle | 5 +++++ .../src/androidTest/java/app/lounge/AnonymousUserAPITest.kt | 6 +++--- modules/src/main/java/app/lounge/di/NetworkModule.kt | 6 +++--- .../lounge/{networking => login/anonymous}/AnonymousUser.kt | 3 ++- .../anonymous}/AnonymousUserRetrofitImpl.kt | 6 ++++-- 5 files changed, 17 insertions(+), 9 deletions(-) rename modules/src/main/java/app/lounge/{networking => login/anonymous}/AnonymousUser.kt (92%) rename modules/src/main/java/app/lounge/{networking => login/anonymous}/AnonymousUserRetrofitImpl.kt (95%) diff --git a/modules/build.gradle b/modules/build.gradle index 48fab303e..9189068ef 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -87,6 +87,11 @@ dependencies { kapt("com.google.dagger:hilt-android-compiler:$dagger_hilt_version") testImplementation 'junit:junit:4.13.2' + testImplementation "androidx.test:core:1.5.0" + testImplementation 'org.mockito:mockito-core:5.0.0' + testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' + testImplementation 'org.robolectric:robolectric:4.9' + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.2" androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } \ No newline at end of file diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt index 130bee5b5..020dade3d 100644 --- a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt @@ -1,9 +1,9 @@ package app.lounge import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.networking.AnonymousUser -import app.lounge.networking.AnonymousUserRetrofitAPI -import app.lounge.networking.AnonymousUserRetrofitImpl +import app.lounge.login.anonymous.AnonymousUser +import app.lounge.login.anonymous.AnonymousUserRetrofitAPI +import app.lounge.login.anonymous.AnonymousUserRetrofitImpl import app.lounge.networking.NetworkResult import com.aurora.gplayapi.data.models.AuthData import kotlinx.coroutines.runBlocking diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index c1c7f65cc..485648920 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -19,9 +19,9 @@ package app.lounge.di -import app.lounge.networking.AnonymousUser -import app.lounge.networking.AnonymousUserRetrofitAPI -import app.lounge.networking.AnonymousUserRetrofitImpl +import app.lounge.login.anonymous.AnonymousUser +import app.lounge.login.anonymous.AnonymousUserRetrofitAPI +import app.lounge.login.anonymous.AnonymousUserRetrofitImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/modules/src/main/java/app/lounge/networking/AnonymousUser.kt b/modules/src/main/java/app/lounge/login/anonymous/AnonymousUser.kt similarity index 92% rename from modules/src/main/java/app/lounge/networking/AnonymousUser.kt rename to modules/src/main/java/app/lounge/login/anonymous/AnonymousUser.kt index 48acc7f5f..d02bb05c4 100644 --- a/modules/src/main/java/app/lounge/networking/AnonymousUser.kt +++ b/modules/src/main/java/app/lounge/login/anonymous/AnonymousUser.kt @@ -17,9 +17,10 @@ */ -package app.lounge.networking +package app.lounge.login.anonymous import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.networking.NetworkResult import com.aurora.gplayapi.data.models.AuthData interface AnonymousUser { diff --git a/modules/src/main/java/app/lounge/networking/AnonymousUserRetrofitImpl.kt b/modules/src/main/java/app/lounge/login/anonymous/AnonymousUserRetrofitImpl.kt similarity index 95% rename from modules/src/main/java/app/lounge/networking/AnonymousUserRetrofitImpl.kt rename to modules/src/main/java/app/lounge/login/anonymous/AnonymousUserRetrofitImpl.kt index 967aadaa2..0721d8bfd 100644 --- a/modules/src/main/java/app/lounge/networking/AnonymousUserRetrofitImpl.kt +++ b/modules/src/main/java/app/lounge/login/anonymous/AnonymousUserRetrofitImpl.kt @@ -17,10 +17,12 @@ */ -package app.lounge.networking +package app.lounge.login.anonymous import app.lounge.extension.toByteArray import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.networking.NetworkResult +import app.lounge.networking.fetch import com.aurora.gplayapi.data.models.AuthData import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody @@ -60,7 +62,7 @@ interface AnonymousUserRetrofitAPI { @Singleton class AnonymousUserRetrofitImpl @Inject constructor( val eCloud: Retrofit -) : AnonymousUser { +) : AnonymousUser { private val eCloudRetrofitAPI = eCloud.create( AnonymousUserRetrofitAPI::class.java -- GitLab From 3f23ea0bcec83a42de115b73cd3d59aac966982e Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 27 Jul 2023 19:23:40 +0530 Subject: [PATCH 037/112] Remove unwanted dependencies --- modules/build.gradle | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/build.gradle b/modules/build.gradle index 9189068ef..48fab303e 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -87,11 +87,6 @@ dependencies { kapt("com.google.dagger:hilt-android-compiler:$dagger_hilt_version") testImplementation 'junit:junit:4.13.2' - testImplementation "androidx.test:core:1.5.0" - testImplementation 'org.mockito:mockito-core:5.0.0' - testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' - testImplementation 'org.robolectric:robolectric:4.9' - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.2" androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } \ No newline at end of file -- GitLab From b2bfdee79f6914125f35853e1e6c205c40424ce2 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 28 Jul 2023 12:37:06 +0530 Subject: [PATCH 038/112] Rename Anonymous user porvider in NetworkModule --- modules/src/main/java/app/lounge/di/NetworkModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 485648920..63d354cf1 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -88,7 +88,7 @@ internal object NetworkModule { @Provides @Singleton - fun provideNetworkFetching( + fun provideAnonymousUser( @Named("ECloudRetrofit") ecloud: Retrofit ) : AnonymousUser { return AnonymousUserRetrofitImpl( -- GitLab From 7d34835954a8e3fe8150fbef8a2840aeaec53bac Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 1 Aug 2023 23:29:20 +0530 Subject: [PATCH 039/112] Adde testcase for anonymous user view model --- .../domain/login/usecase/UserLoginUseCase.kt | 8 +- .../apps/presentation/login/LoginViewModel.kt | 36 +++--- app/src/test/java/foundation/e/apps/Shared.kt | 15 +++ .../presentation/login/LoginViewModelTest.kt | 108 ++++++++++++++++++ .../usecase/repository/LoginRepositoryTest.kt | 34 ++---- 5 files changed, 155 insertions(+), 46 deletions(-) create mode 100644 app/src/test/java/foundation/e/apps/Shared.kt create mode 100644 app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index e2e8b1813..fafa16e6a 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -4,8 +4,8 @@ import app.lounge.model.AnonymousAuthDataRequestBody import app.lounge.model.AuthDataResponse import foundation.e.apps.domain.login.repository.LoginRepository import foundation.e.apps.utils.Resource -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.single import retrofit2.HttpException import java.io.IOException import java.util.Properties @@ -15,10 +15,10 @@ class UserLoginUseCase @Inject constructor( private val loginRepository: LoginRepository, ) { - operator fun invoke( + suspend operator fun invoke( properties: Properties, userAgent: String - ): Flow> = flow { + ): Resource = flow { try { emit(Resource.Loading()) val userResponse: AuthDataResponse = loginRepository.anonymousUser( @@ -33,5 +33,5 @@ class UserLoginUseCase @Inject constructor( } catch(e: IOException) { emit(Resource.Error("Couldn't reach server. Check your internet connection.")) } - } + }.single() } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index c209288af..e4fec4e76 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -29,8 +29,6 @@ import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.ui.parentFragment.LoadingViewModel import foundation.e.apps.utils.Resource import foundation.e.apps.utils.SystemInfoProvider -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import java.util.Properties import javax.inject.Inject @@ -151,23 +149,25 @@ class LoginViewModel @Inject constructor( properties: Properties, userAgent: String = SystemInfoProvider.getAppBuildInfo() ) { - userLoginUseCase( - properties = properties, - userAgent = userAgent - ).onEach { result -> - when (result) { - is Resource.Success -> { - _loginState.value = LoginState(isLoggedIn = true) - } - is Resource.Error -> { - _loginState.value = LoginState( - error = result.message ?: "An unexpected error occured" - ) - } - is Resource.Loading -> { - _loginState.value = LoginState(isLoading = true) + viewModelScope.launch { + userLoginUseCase( + properties = properties, + userAgent = userAgent + ).also { result -> + when (result) { + is Resource.Success -> { + _loginState.value = LoginState(isLoggedIn = true) + } + is Resource.Error -> { + _loginState.value = LoginState( + error = result.message ?: "An unexpected error occured" + ) + } + is Resource.Loading -> { + _loginState.value = LoginState(isLoading = true) + } } } - }.launchIn(viewModelScope) + } } } diff --git a/app/src/test/java/foundation/e/apps/Shared.kt b/app/src/test/java/foundation/e/apps/Shared.kt new file mode 100644 index 000000000..0057d504f --- /dev/null +++ b/app/src/test/java/foundation/e/apps/Shared.kt @@ -0,0 +1,15 @@ +package foundation.e.apps + +import app.lounge.model.AnonymousAuthDataRequestBody +import com.aurora.gplayapi.data.models.AuthData +import java.util.Properties + + +val testAnonymousRequestBodyData = AnonymousAuthDataRequestBody( + properties = Properties(), + userAgent = "testUserAgent" +) + +val testAnonymousResponseData = AuthData("eOS@murena.io", "") + +const val loginFailureMessage = "Fail to login" \ No newline at end of file diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt new file mode 100644 index 000000000..03760d35e --- /dev/null +++ b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt @@ -0,0 +1,108 @@ +package foundation.e.apps.presentation.login + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import foundation.e.apps.testAnonymousRequestBodyData +import foundation.e.apps.data.login.LoginSourceRepository +import foundation.e.apps.domain.login.usecase.UserLoginUseCase +import foundation.e.apps.loginFailureMessage +import foundation.e.apps.testAnonymousResponseData +import foundation.e.apps.util.getOrAwaitValue +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + + +@OptIn(ExperimentalCoroutinesApi::class) +class LoginViewModelTest { + + private val testDispatcher = StandardTestDispatcher() + + @get:Rule + val instantTaskExecutorRule = InstantTaskExecutorRule() + + @Mock + lateinit var mockUserLoginUseCase: UserLoginUseCase + + @Mock + lateinit var loginRepository: LoginSourceRepository + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + Dispatchers.setMain(testDispatcher) + } + + @Test + fun testAnonymousUserLoginStateForSuccess() = runTest{ + Mockito.`when`(mockUserLoginUseCase.invoke( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + )).thenReturn(Resource.Success(testAnonymousResponseData)) + + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + loginViewModel.authenticateAnonymousUser( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + ) + testDispatcher.scheduler.advanceUntilIdle() + val result = loginViewModel.loginState.getOrAwaitValue() + Assert.assertEquals(true, result.isLoggedIn) + Assert.assertEquals(false, result.isLoading) + } + + @Test + fun testAnonymousUserLoginStateForFailure() = runTest{ + Mockito.`when`(mockUserLoginUseCase.invoke( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + )).thenReturn(Resource.Error(loginFailureMessage)) + + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + loginViewModel.authenticateAnonymousUser( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + ) + testDispatcher.scheduler.advanceUntilIdle() + val result = loginViewModel.loginState.getOrAwaitValue() + Assert.assertEquals(false, result.isLoggedIn) + Assert.assertEquals(false, result.isLoading) + Assert.assertEquals(loginFailureMessage, result.error) + } + + @Test + fun testAnonymousUserLoginStateForLoading() = runTest{ + Mockito.`when`(mockUserLoginUseCase.invoke( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + )).thenReturn(Resource.Loading()) + + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + loginViewModel.authenticateAnonymousUser( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + ) + testDispatcher.scheduler.advanceUntilIdle() + val result = loginViewModel.loginState.getOrAwaitValue() + Assert.assertEquals(true, result.isLoading) + Assert.assertEquals(false, result.isLoggedIn) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + +} + diff --git a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt index beaf715fd..77631b4f1 100644 --- a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt @@ -2,27 +2,20 @@ package foundation.e.apps.usecase.repository import android.content.Context import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.rules.ActivityScenarioRule -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import app.lounge.model.AnonymousAuthDataRequestBody import app.lounge.model.AuthDataResponse import app.lounge.networking.NetworkFetching -import com.aurora.gplayapi.data.models.AuthData -import foundation.e.apps.domain.login.repository.LoginRepository +import foundation.e.apps.testAnonymousRequestBodyData import foundation.e.apps.domain.login.repository.LoginRepositoryImpl +import foundation.e.apps.testAnonymousResponseData import kotlinx.coroutines.test.runTest import org.junit.Assert import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner -import retrofit2.Response -import java.util.Properties @RunWith(RobolectricTestRunner::class) class LoginRepositoryTest { @@ -30,7 +23,7 @@ class LoginRepositoryTest { @Mock lateinit var networkAPI: NetworkFetching - lateinit var instrumentationContext: Context + private lateinit var instrumentationContext: Context @Before @@ -41,19 +34,12 @@ class LoginRepositoryTest { @Test fun testRequestAuthData() = runTest { - Mockito.`when`(networkAPI.requestAuthData( - requestBodyData - )).thenReturn(AuthData("nisdande@murena.io", "")) - - val sut = LoginRepositoryImpl( - networkAPI, instrumentationContext) - val result = sut.anonymousUser(requestBodyData) - Assert.assertEquals(true, result is AuthDataResponse) - Assert.assertEquals("nisdande@murena.io", result.email) + Mockito.`when`(networkAPI.requestAuthData(testAnonymousRequestBodyData)) + .thenReturn(testAnonymousResponseData) + + val loginRepository = LoginRepositoryImpl(networkAPI, instrumentationContext) + val result: AuthDataResponse = loginRepository.anonymousUser(testAnonymousRequestBodyData) + Assert.assertNotNull(result) + Assert.assertEquals("eOS@murena.io", result.email) } } - -private val requestBodyData = AnonymousAuthDataRequestBody( - properties = Properties(), - userAgent = "testUserAgent" -) \ No newline at end of file -- GitLab From 2abcc41eac3f23972626120a3b3c1669ed4f72c1 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 4 Aug 2023 23:54:24 +0530 Subject: [PATCH 040/112] Update codebase with necessary changes --- app/build.gradle | 2 ++ .../login/repository/LoginRepository.kt | 4 ++-- .../login/repository/LoginRepositoryImpl.kt | 22 +++++++++++++------ .../domain/login/usecase/UserLoginUseCase.kt | 12 +++++----- .../usecase/repository/LoginRepositoryTest.kt | 22 ++++++++++--------- 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b451b98d0..6098ff118 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,6 +153,8 @@ allOpen { dependencies { + implementation project(':modules') + // TODO: Add splitinstall-lib to a repo https://gitlab.e.foundation/e/os/backlog/-/issues/628 api files('libs/splitinstall-lib.jar') diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt index a35c1bc98..e80c9203a 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -1,13 +1,13 @@ package foundation.e.apps.domain.login.repository import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AuthDataResponse +import com.aurora.gplayapi.data.models.AuthData interface LoginRepository { suspend fun anonymousUser( authDataRequestBody: AnonymousAuthDataRequestBody - ): AuthDataResponse + ): AuthData } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index 37001fb5d..004c32680 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -1,23 +1,31 @@ package foundation.e.apps.domain.login.repository import android.content.Context +import app.lounge.login.anonymous.AnonymousUser import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AuthDataResponse -import app.lounge.networking.NetworkFetching +import app.lounge.networking.NetworkResult import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class LoginRepositoryImpl @Inject constructor( - private val networkFetching: NetworkFetching, + private val networkFetching: AnonymousUser, @ApplicationContext val applicationContext: Context ): LoginRepository { - override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthDataResponse { - return networkFetching.requestAuthData( + override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthData { + val result = networkFetching.requestAuthData( anonymousAuthDataRequestBody = authDataRequestBody - ).also { - applicationContext.configurations.authData = it.toString() + ) + + when(result) { + is NetworkResult.Error -> + throw Exception(result.errorMessage, result.exception) + is NetworkResult.Success -> { + applicationContext.configurations.authData = result.data.toString() + return result.data + } } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index fafa16e6a..9b0ea9a1b 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -1,7 +1,7 @@ package foundation.e.apps.domain.login.usecase import app.lounge.model.AnonymousAuthDataRequestBody -import app.lounge.model.AuthDataResponse +import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.domain.login.repository.LoginRepository import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.flow @@ -18,20 +18,18 @@ class UserLoginUseCase @Inject constructor( suspend operator fun invoke( properties: Properties, userAgent: String - ): Resource = flow { + ): Resource = flow { try { emit(Resource.Loading()) - val userResponse: AuthDataResponse = loginRepository.anonymousUser( + val userResponse = loginRepository.anonymousUser( authDataRequestBody = AnonymousAuthDataRequestBody( properties = properties, userAgent = userAgent ) ) emit(Resource.Success(userResponse)) - } catch(e: HttpException) { - emit(Resource.Error(e.localizedMessage ?: "An unexpected error occured")) - } catch(e: IOException) { - emit(Resource.Error("Couldn't reach server. Check your internet connection.")) + } catch(e: Exception) { + emit(Resource.Error(e.localizedMessage)) } }.single() } \ No newline at end of file diff --git a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt index 77631b4f1..d16c32773 100644 --- a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt @@ -2,11 +2,6 @@ package foundation.e.apps.usecase.repository import android.content.Context import androidx.test.core.app.ApplicationProvider -import app.lounge.model.AuthDataResponse -import app.lounge.networking.NetworkFetching -import foundation.e.apps.testAnonymousRequestBodyData -import foundation.e.apps.domain.login.repository.LoginRepositoryImpl -import foundation.e.apps.testAnonymousResponseData import kotlinx.coroutines.test.runTest import org.junit.Assert import org.junit.Before @@ -17,11 +12,18 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations import org.robolectric.RobolectricTestRunner +import foundation.e.apps.domain.login.repository.LoginRepositoryImpl +import foundation.e.apps.testAnonymousRequestBodyData +import foundation.e.apps.testAnonymousResponseData + +import app.lounge.login.anonymous.AnonymousUser +import app.lounge.networking.NetworkResult + @RunWith(RobolectricTestRunner::class) class LoginRepositoryTest { @Mock - lateinit var networkAPI: NetworkFetching + lateinit var anonymousUser: AnonymousUser private lateinit var instrumentationContext: Context @@ -34,11 +36,11 @@ class LoginRepositoryTest { @Test fun testRequestAuthData() = runTest { - Mockito.`when`(networkAPI.requestAuthData(testAnonymousRequestBodyData)) - .thenReturn(testAnonymousResponseData) + Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) + .thenReturn(NetworkResult.Success(testAnonymousResponseData)) - val loginRepository = LoginRepositoryImpl(networkAPI, instrumentationContext) - val result: AuthDataResponse = loginRepository.anonymousUser(testAnonymousRequestBodyData) + val loginRepository = LoginRepositoryImpl(anonymousUser, instrumentationContext) + val result = loginRepository.anonymousUser(testAnonymousRequestBodyData) Assert.assertNotNull(result) Assert.assertEquals("eOS@murena.io", result.email) } -- GitLab From 43b8e755bcaf99a58937d7ed3e1219a6afafd034 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sat, 5 Aug 2023 23:24:47 +0530 Subject: [PATCH 041/112] Refine test case for domain and ui layer --- app/src/test/java/foundation/e/apps/Shared.kt | 4 ++- .../presentation/login/LoginViewModelTest.kt | 6 ++--- .../usecase/repository/LoginRepositoryTest.kt | 27 ++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/app/src/test/java/foundation/e/apps/Shared.kt b/app/src/test/java/foundation/e/apps/Shared.kt index 0057d504f..986e5f1cd 100644 --- a/app/src/test/java/foundation/e/apps/Shared.kt +++ b/app/src/test/java/foundation/e/apps/Shared.kt @@ -12,4 +12,6 @@ val testAnonymousRequestBodyData = AnonymousAuthDataRequestBody( val testAnonymousResponseData = AuthData("eOS@murena.io", "") -const val loginFailureMessage = "Fail to login" \ No newline at end of file +const val loginFailureMessage = "Fail to login" + +val testFailureException: Exception = Exception(loginFailureMessage) \ No newline at end of file diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt index 03760d35e..93f65c438 100644 --- a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt +++ b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt @@ -45,7 +45,7 @@ class LoginViewModelTest { } @Test - fun testAnonymousUserLoginStateForSuccess() = runTest{ + fun testOnSuccessReturnLogInStateTrue() = runTest{ Mockito.`when`(mockUserLoginUseCase.invoke( properties = testAnonymousRequestBodyData.properties, userAgent = testAnonymousRequestBodyData.userAgent @@ -63,7 +63,7 @@ class LoginViewModelTest { } @Test - fun testAnonymousUserLoginStateForFailure() = runTest{ + fun testOnFailureReturnLogInStateFalseWithError() = runTest{ Mockito.`when`(mockUserLoginUseCase.invoke( properties = testAnonymousRequestBodyData.properties, userAgent = testAnonymousRequestBodyData.userAgent @@ -82,7 +82,7 @@ class LoginViewModelTest { } @Test - fun testAnonymousUserLoginStateForLoading() = runTest{ + fun testOnLoadingReturnLogInStateFalse() = runTest{ Mockito.`when`(mockUserLoginUseCase.invoke( properties = testAnonymousRequestBodyData.properties, userAgent = testAnonymousRequestBodyData.userAgent diff --git a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt index d16c32773..9d3fbda3f 100644 --- a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt @@ -18,6 +18,8 @@ import foundation.e.apps.testAnonymousResponseData import app.lounge.login.anonymous.AnonymousUser import app.lounge.networking.NetworkResult +import foundation.e.apps.loginFailureMessage +import foundation.e.apps.testFailureException @RunWith(RobolectricTestRunner::class) class LoginRepositoryTest { @@ -35,13 +37,32 @@ class LoginRepositoryTest { } @Test - fun testRequestAuthData() = runTest { + fun testOnSuccessReturnAuthData() = runTest { Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) .thenReturn(NetworkResult.Success(testAnonymousResponseData)) - val loginRepository = LoginRepositoryImpl(anonymousUser, instrumentationContext) - val result = loginRepository.anonymousUser(testAnonymousRequestBodyData) + val result = LoginRepositoryImpl(anonymousUser, instrumentationContext) + .run { + anonymousUser(testAnonymousRequestBodyData) + } + Assert.assertNotNull(result) Assert.assertEquals("eOS@murena.io", result.email) } + + @Test + fun testOnFailureReturnErrorWithException() = runTest { + Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) + .thenReturn(NetworkResult.Error( + exception = testFailureException, + code = 1, + errorMessage = loginFailureMessage + )) + runCatching { + LoginRepositoryImpl(anonymousUser, instrumentationContext) + .run { anonymousUser(testAnonymousRequestBodyData) } + }.onFailure { error -> + Assert.assertEquals(testFailureException.message, error.message) + } + } } -- GitLab From a1d79950c070d67f7fe13646f3cf000f51a478cf Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sat, 5 Aug 2023 23:27:34 +0530 Subject: [PATCH 042/112] Added copyright text --- .../login/repository/LoginRepository.kt | 19 +++++++++++++++++++ .../login/repository/LoginRepositoryImpl.kt | 19 +++++++++++++++++++ .../domain/login/usecase/UserLoginUseCase.kt | 19 +++++++++++++++++++ .../e/apps/presentation/login/LoginState.kt | 19 +++++++++++++++++++ .../java/foundation/e/apps/utils/Resource.kt | 19 +++++++++++++++++++ app/src/test/java/foundation/e/apps/Shared.kt | 19 +++++++++++++++++++ .../presentation/login/LoginViewModelTest.kt | 19 +++++++++++++++++++ .../usecase/repository/LoginRepositoryTest.kt | 19 +++++++++++++++++++ 8 files changed, 152 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt index e80c9203a..94533699a 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps.domain.login.repository import app.lounge.model.AnonymousAuthDataRequestBody diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index 004c32680..b5f36c1be 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps.domain.login.repository import android.content.Context diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index 9b0ea9a1b..ee936b572 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps.domain.login.usecase import app.lounge.model.AnonymousAuthDataRequestBody diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt index 400fb86bc..b7e3d0f90 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps.presentation.login diff --git a/app/src/main/java/foundation/e/apps/utils/Resource.kt b/app/src/main/java/foundation/e/apps/utils/Resource.kt index f598e91e5..7c468ab47 100644 --- a/app/src/main/java/foundation/e/apps/utils/Resource.kt +++ b/app/src/main/java/foundation/e/apps/utils/Resource.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps.utils diff --git a/app/src/test/java/foundation/e/apps/Shared.kt b/app/src/test/java/foundation/e/apps/Shared.kt index 986e5f1cd..b82344dfa 100644 --- a/app/src/test/java/foundation/e/apps/Shared.kt +++ b/app/src/test/java/foundation/e/apps/Shared.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps import app.lounge.model.AnonymousAuthDataRequestBody diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt index 93f65c438..cc14f35c2 100644 --- a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt +++ b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps.presentation.login import androidx.arch.core.executor.testing.InstantTaskExecutorRule diff --git a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt index 9d3fbda3f..25a9f06d5 100644 --- a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt @@ -1,3 +1,22 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + package foundation.e.apps.usecase.repository import android.content.Context -- GitLab From 57611451366b4025d735f7098a0465780b6f798c Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 8 Aug 2023 09:16:01 +0530 Subject: [PATCH 043/112] Updated codebse as per review comment --- .../java/app/lounge/AnonymousUserAPITest.kt | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt index 020dade3d..494cf0c28 100644 --- a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package app.lounge import app.lounge.model.AnonymousAuthDataRequestBody @@ -27,7 +45,7 @@ class AnonymousUserAPITest { ) when(response){ is NetworkResult.Success -> authData = response.data - else -> {} + is NetworkResult.Error -> { } } assert(authData is AuthData) { "Assert!! Success must return data" } -- GitLab From 436461d75f831d9757eac1de4793907f5602522f Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 9 Aug 2023 21:57:24 +0530 Subject: [PATCH 044/112] Implement domain layer for anonymous users --- app/build.gradle | 6 ++ .../login/repository/LoginRepository.kt | 29 +++++++ .../login/repository/LoginRepositoryImpl.kt | 49 +++++++++++ .../domain/login/usecase/UserLoginUseCase.kt | 51 +++++++++++ .../java/foundation/e/apps/utils/Resource.kt | 25 ++++++ app/src/test/java/foundation/e/apps/Shared.kt | 34 ++++++++ .../login/repository/LoginRepositoryTest.kt | 84 +++++++++++++++++++ 7 files changed, 278 insertions(+) create mode 100644 app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt create mode 100644 app/src/main/java/foundation/e/apps/utils/Resource.kt create mode 100644 app/src/test/java/foundation/e/apps/Shared.kt create mode 100644 app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt diff --git a/app/build.gradle b/app/build.gradle index 1d0b1c0d7..26137f69f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -147,6 +147,8 @@ allOpen { dependencies { + implementation project(':modules') + // TODO: Add splitinstall-lib to a repo https://gitlab.e.foundation/e/os/backlog/-/issues/628 api files('libs/splitinstall-lib.jar') @@ -250,4 +252,8 @@ dependencies { // elib implementation 'foundation.e:elib:0.0.1-alpha11' + + testImplementation 'org.mockito:mockito-core:5.0.0' + testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' + testImplementation 'org.robolectric:robolectric:4.9' } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt new file mode 100644 index 000000000..19ff48c34 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -0,0 +1,29 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.repository + +import app.lounge.model.AnonymousAuthDataRequestBody +import com.aurora.gplayapi.data.models.AuthData + +interface LoginRepository { + + suspend fun anonymousUser( + authDataRequestBody: AnonymousAuthDataRequestBody + ): AuthData +} diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt new file mode 100644 index 000000000..ad61667d8 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -0,0 +1,49 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.repository + +import android.content.Context +import app.lounge.login.anonymous.AnonymousUser +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.networking.NetworkResult +import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +class LoginRepositoryImpl @Inject constructor( + private val networkFetching: AnonymousUser, + @ApplicationContext val applicationContext: Context +) : LoginRepository { + + override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthData { + val result = networkFetching.requestAuthData( + anonymousAuthDataRequestBody = authDataRequestBody + ) + + when (result) { + is NetworkResult.Error -> + throw Exception(result.errorMessage, result.exception) + is NetworkResult.Success -> { + applicationContext.configurations.authData = result.data.toString() + return result.data + } + } + } +} diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt new file mode 100644 index 000000000..a62ee1961 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -0,0 +1,51 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.usecase + +import app.lounge.model.AnonymousAuthDataRequestBody +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.domain.login.repository.LoginRepository +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.single +import java.util.Properties +import javax.inject.Inject + +class UserLoginUseCase @Inject constructor( + private val loginRepository: LoginRepository, +) { + + suspend operator fun invoke( + properties: Properties, + userAgent: String + ): Resource = flow { + try { + emit(Resource.Loading()) + val userResponse = loginRepository.anonymousUser( + authDataRequestBody = AnonymousAuthDataRequestBody( + properties = properties, + userAgent = userAgent + ) + ) + emit(Resource.Success(userResponse)) + } catch (e: Exception) { + emit(Resource.Error(e.localizedMessage)) + } + }.single() +} diff --git a/app/src/main/java/foundation/e/apps/utils/Resource.kt b/app/src/main/java/foundation/e/apps/utils/Resource.kt new file mode 100644 index 000000000..7a471a5f1 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/Resource.kt @@ -0,0 +1,25 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.utils + +sealed class Resource(val data: T? = null, val message: String? = null) { + class Success(data: T) : Resource(data) + class Error(message: String, data: T? = null) : Resource(data, message) + class Loading(data: T? = null) : Resource(data) +} diff --git a/app/src/test/java/foundation/e/apps/Shared.kt b/app/src/test/java/foundation/e/apps/Shared.kt new file mode 100644 index 000000000..2199b514f --- /dev/null +++ b/app/src/test/java/foundation/e/apps/Shared.kt @@ -0,0 +1,34 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps + +import app.lounge.model.AnonymousAuthDataRequestBody +import com.aurora.gplayapi.data.models.AuthData +import java.util.Properties + +val testAnonymousRequestBodyData = AnonymousAuthDataRequestBody( + properties = Properties(), + userAgent = "testUserAgent" +) + +val testAnonymousResponseData = AuthData("eOS@murena.io", "") + +const val loginFailureMessage = "Fail to login" + +val testFailureException: Exception = Exception(loginFailureMessage) diff --git a/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt new file mode 100644 index 000000000..3d1cf6c27 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.repository + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import app.lounge.login.anonymous.AnonymousUser +import app.lounge.networking.NetworkResult +import foundation.e.apps.loginFailureMessage +import foundation.e.apps.testAnonymousRequestBodyData +import foundation.e.apps.testAnonymousResponseData +import foundation.e.apps.testFailureException +import kotlinx.coroutines.test.runTest +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class LoginRepositoryTest { + + @Mock + lateinit var anonymousUser: AnonymousUser + + private lateinit var instrumentationContext: Context + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + instrumentationContext = ApplicationProvider.getApplicationContext() + } + + @Test + fun testOnSuccessReturnAuthData() = runTest { + Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) + .thenReturn(NetworkResult.Success(testAnonymousResponseData)) + + val result = LoginRepositoryImpl(anonymousUser, instrumentationContext) + .run { + anonymousUser(testAnonymousRequestBodyData) + } + + Assert.assertNotNull(result) + Assert.assertEquals("eOS@murena.io", result.email) + } + + @Test + fun testOnFailureReturnErrorWithException() = runTest { + Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) + .thenReturn( + NetworkResult.Error( + exception = testFailureException, + code = 1, + errorMessage = loginFailureMessage + ) + ) + runCatching { + LoginRepositoryImpl(anonymousUser, instrumentationContext) + .run { anonymousUser(testAnonymousRequestBodyData) } + }.onFailure { error -> + Assert.assertEquals(testFailureException.message, error.message) + } + } +} -- GitLab From c99026301943a3ced54d2dcd0421879de566cb85 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 9 Aug 2023 22:17:10 +0530 Subject: [PATCH 045/112] Modify variable names --- .../e/apps/domain/login/repository/LoginRepositoryImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index ad61667d8..e11159b2b 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -28,12 +28,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class LoginRepositoryImpl @Inject constructor( - private val networkFetching: AnonymousUser, + private val anonymousUser: AnonymousUser, @ApplicationContext val applicationContext: Context ) : LoginRepository { override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthData { - val result = networkFetching.requestAuthData( + val result = anonymousUser.requestAuthData( anonymousAuthDataRequestBody = authDataRequestBody ) -- GitLab From 086312e957ef7a887d5ae3feee2e7338e516ddbf Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 11 Aug 2023 22:04:09 +0530 Subject: [PATCH 046/112] Modified code comment and Resource state structure --- .../domain/login/usecase/UserLoginUseCase.kt | 4 ++-- .../java/foundation/e/apps/utils/Resource.kt | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index a62ee1961..dd0d2af07 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -31,12 +31,12 @@ class UserLoginUseCase @Inject constructor( private val loginRepository: LoginRepository, ) { - suspend operator fun invoke( + suspend fun anonymousUser( properties: Properties, userAgent: String ): Resource = flow { try { - emit(Resource.Loading()) + emit(Resource.Loading) val userResponse = loginRepository.anonymousUser( authDataRequestBody = AnonymousAuthDataRequestBody( properties = properties, diff --git a/app/src/main/java/foundation/e/apps/utils/Resource.kt b/app/src/main/java/foundation/e/apps/utils/Resource.kt index 7a471a5f1..81bee55fc 100644 --- a/app/src/main/java/foundation/e/apps/utils/Resource.kt +++ b/app/src/main/java/foundation/e/apps/utils/Resource.kt @@ -18,8 +18,23 @@ package foundation.e.apps.utils -sealed class Resource(val data: T? = null, val message: String? = null) { - class Success(data: T) : Resource(data) - class Error(message: String, data: T? = null) : Resource(data, message) - class Loading(data: T? = null) : Resource(data) +/** + * Class represents the different states of a resource for user case layer + */ +sealed class Resource { + /** + * Represents a successful state of the resource with data. + * @param data The data associated with the resource. + */ + class Success(val data: T) : Resource() + /** + * Represents an error state of the resource with an error message. + * @param message The error message associated with the resource. + */ + class Error(message: String) : Resource() + + /** + * Represents a loading state of the resource. + */ + object Loading : Resource() } -- GitLab From ddce8136cf7747638eb55602ee5f762726c9c298 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sat, 19 Aug 2023 16:22:36 +0530 Subject: [PATCH 047/112] Remove anonymousUser request parameter from api --- .../login/repository/LoginRepository.kt | 5 +-- .../login/repository/LoginRepositoryImpl.kt | 14 +++++-- .../domain/login/usecase/UserLoginUseCase.kt | 14 +------ app/src/test/java/foundation/e/apps/Shared.kt | 15 +++---- .../login/repository/LoginRepositoryTest.kt | 39 ++++++++++--------- 5 files changed, 42 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt index 19ff48c34..45edb8773 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -18,12 +18,9 @@ package foundation.e.apps.domain.login.repository -import app.lounge.model.AnonymousAuthDataRequestBody import com.aurora.gplayapi.data.models.AuthData interface LoginRepository { - suspend fun anonymousUser( - authDataRequestBody: AnonymousAuthDataRequestBody - ): AuthData + suspend fun anonymousUser(): AuthData } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index e11159b2b..58e970348 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -25,16 +25,24 @@ import app.lounge.networking.NetworkResult import app.lounge.storage.cache.configurations import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.utils.SystemInfoProvider +import java.util.Properties import javax.inject.Inject class LoginRepositoryImpl @Inject constructor( + @ApplicationContext val applicationContext: Context, + private val properties: Properties, private val anonymousUser: AnonymousUser, - @ApplicationContext val applicationContext: Context ) : LoginRepository { - override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthData { + private val userAgent: String by lazy { SystemInfoProvider.getAppBuildInfo() } + + override suspend fun anonymousUser(): AuthData { val result = anonymousUser.requestAuthData( - anonymousAuthDataRequestBody = authDataRequestBody + anonymousAuthDataRequestBody = AnonymousAuthDataRequestBody( + properties = properties, + userAgent = userAgent + ) ) when (result) { diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index dd0d2af07..33cee4402 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -18,31 +18,21 @@ package foundation.e.apps.domain.login.usecase -import app.lounge.model.AnonymousAuthDataRequestBody import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.domain.login.repository.LoginRepository import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.single -import java.util.Properties import javax.inject.Inject class UserLoginUseCase @Inject constructor( private val loginRepository: LoginRepository, ) { - suspend fun anonymousUser( - properties: Properties, - userAgent: String - ): Resource = flow { + suspend fun anonymousUser(): Resource = flow { try { emit(Resource.Loading) - val userResponse = loginRepository.anonymousUser( - authDataRequestBody = AnonymousAuthDataRequestBody( - properties = properties, - userAgent = userAgent - ) - ) + val userResponse = loginRepository.anonymousUser() emit(Resource.Success(userResponse)) } catch (e: Exception) { emit(Resource.Error(e.localizedMessage)) diff --git a/app/src/test/java/foundation/e/apps/Shared.kt b/app/src/test/java/foundation/e/apps/Shared.kt index 2199b514f..95f108bec 100644 --- a/app/src/test/java/foundation/e/apps/Shared.kt +++ b/app/src/test/java/foundation/e/apps/Shared.kt @@ -20,15 +20,16 @@ package foundation.e.apps import app.lounge.model.AnonymousAuthDataRequestBody import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.utils.SystemInfoProvider import java.util.Properties -val testAnonymousRequestBodyData = AnonymousAuthDataRequestBody( +const val testEmailAddress: String = "eOS@murena.io" +const val loginFailureMessage = "Fail to login" +val testFailureException: Exception = Exception(loginFailureMessage) + +val testAnonymousRequestData = AnonymousAuthDataRequestBody( properties = Properties(), - userAgent = "testUserAgent" + userAgent = SystemInfoProvider.getAppBuildInfo() ) -val testAnonymousResponseData = AuthData("eOS@murena.io", "") - -const val loginFailureMessage = "Fail to login" - -val testFailureException: Exception = Exception(loginFailureMessage) +val testAnonymousResponseData = AuthData(testEmailAddress, "") diff --git a/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt index 3d1cf6c27..5e9f368b0 100644 --- a/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt @@ -23,8 +23,9 @@ import androidx.test.core.app.ApplicationProvider import app.lounge.login.anonymous.AnonymousUser import app.lounge.networking.NetworkResult import foundation.e.apps.loginFailureMessage -import foundation.e.apps.testAnonymousRequestBodyData +import foundation.e.apps.testAnonymousRequestData import foundation.e.apps.testAnonymousResponseData +import foundation.e.apps.testEmailAddress import foundation.e.apps.testFailureException import kotlinx.coroutines.test.runTest import org.junit.Assert @@ -42,41 +43,41 @@ class LoginRepositoryTest { @Mock lateinit var anonymousUser: AnonymousUser - private lateinit var instrumentationContext: Context + private lateinit var context: Context @Before fun setUp() { MockitoAnnotations.openMocks(this) - instrumentationContext = ApplicationProvider.getApplicationContext() + context = ApplicationProvider.getApplicationContext() } @Test fun testOnSuccessReturnAuthData() = runTest { - Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) - .thenReturn(NetworkResult.Success(testAnonymousResponseData)) + Mockito.`when`( + anonymousUser.requestAuthData(testAnonymousRequestData) + ).thenReturn(NetworkResult.Success(testAnonymousResponseData)) - val result = LoginRepositoryImpl(anonymousUser, instrumentationContext) - .run { - anonymousUser(testAnonymousRequestBodyData) - } + val result = LoginRepositoryImpl(context, testAnonymousRequestData.properties, anonymousUser) + .anonymousUser() Assert.assertNotNull(result) - Assert.assertEquals("eOS@murena.io", result.email) + Assert.assertEquals(testEmailAddress, result.email) } @Test fun testOnFailureReturnErrorWithException() = runTest { - Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) - .thenReturn( - NetworkResult.Error( - exception = testFailureException, - code = 1, - errorMessage = loginFailureMessage - ) + Mockito.`when`( + anonymousUser.requestAuthData(testAnonymousRequestData) + ).thenReturn( + NetworkResult.Error( + exception = testFailureException, + code = 1, + errorMessage = loginFailureMessage ) + ) runCatching { - LoginRepositoryImpl(anonymousUser, instrumentationContext) - .run { anonymousUser(testAnonymousRequestBodyData) } + LoginRepositoryImpl(context, testAnonymousRequestData.properties, anonymousUser) + .run { anonymousUser() } }.onFailure { error -> Assert.assertEquals(testFailureException.message, error.message) } -- GitLab From e4219815da5875b4ffa833e198d3ad05fe687f51 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sat, 19 Aug 2023 23:00:33 +0530 Subject: [PATCH 048/112] Return flow from usecases to handle all emit on ui --- .../e/apps/domain/login/usecase/UserLoginUseCase.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index 33cee4402..359d42bee 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -21,15 +21,15 @@ package foundation.e.apps.domain.login.usecase import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.domain.login.repository.LoginRepository import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.single import javax.inject.Inject class UserLoginUseCase @Inject constructor( private val loginRepository: LoginRepository, ) { - suspend fun anonymousUser(): Resource = flow { + fun anonymousUser(): Flow> = flow { try { emit(Resource.Loading) val userResponse = loginRepository.anonymousUser() @@ -37,5 +37,5 @@ class UserLoginUseCase @Inject constructor( } catch (e: Exception) { emit(Resource.Error(e.localizedMessage)) } - }.single() + } } -- GitLab From 9dce99b567b059daab5f2cf746788f8de8d1a66f Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 21 Aug 2023 23:45:00 +0530 Subject: [PATCH 049/112] Updated viewmodel with flow and handled testcases --- app/build.gradle | 2 + .../apps/presentation/login/LoginViewModel.kt | 14 ++- .../presentation/login/LoginViewModelTest.kt | 47 +++------- .../usecase/repository/LoginRepositoryTest.kt | 87 ------------------- 4 files changed, 21 insertions(+), 129 deletions(-) delete mode 100644 app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt diff --git a/app/build.gradle b/app/build.gradle index 9ce0bc8cd..81ed596b6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -137,6 +137,7 @@ android { testOptions { unitTests { includeAndroidResources = true + returnDefaultValues = true } } } @@ -262,4 +263,5 @@ dependencies { testImplementation 'org.mockito:mockito-core:5.0.0' testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' testImplementation 'org.robolectric:robolectric:4.9' + testImplementation 'org.json:json:20180813' // Added to avoid SystemInfoProvider.getAppBuildInfo() mock error } diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index e4fec4e76..5655020e3 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -29,6 +29,8 @@ import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.ui.parentFragment.LoadingViewModel import foundation.e.apps.utils.Resource import foundation.e.apps.utils.SystemInfoProvider +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import java.util.Properties import javax.inject.Inject @@ -145,15 +147,9 @@ class LoginViewModel @Inject constructor( val loginState: LiveData = _loginState - fun authenticateAnonymousUser( - properties: Properties, - userAgent: String = SystemInfoProvider.getAppBuildInfo() - ) { + fun authenticateAnonymousUser() { viewModelScope.launch { - userLoginUseCase( - properties = properties, - userAgent = userAgent - ).also { result -> + userLoginUseCase.anonymousUser().onEach {result -> when (result) { is Resource.Success -> { _loginState.value = LoginState(isLoggedIn = true) @@ -167,7 +163,7 @@ class LoginViewModel @Inject constructor( _loginState.value = LoginState(isLoading = true) } } - } + }.collect() } } } diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt index cc14f35c2..16b2d5d77 100644 --- a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt +++ b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt @@ -16,11 +16,9 @@ * along with this program. If not, see . */ - package foundation.e.apps.presentation.login import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import foundation.e.apps.testAnonymousRequestBodyData import foundation.e.apps.data.login.LoginSourceRepository import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.loginFailureMessage @@ -29,6 +27,7 @@ import foundation.e.apps.util.getOrAwaitValue import foundation.e.apps.utils.Resource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest @@ -42,14 +41,13 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations - @OptIn(ExperimentalCoroutinesApi::class) class LoginViewModelTest { private val testDispatcher = StandardTestDispatcher() @get:Rule - val instantTaskExecutorRule = InstantTaskExecutorRule() + val rule = InstantTaskExecutorRule() @Mock lateinit var mockUserLoginUseCase: UserLoginUseCase @@ -64,17 +62,12 @@ class LoginViewModelTest { } @Test - fun testOnSuccessReturnLogInStateTrue() = runTest{ - Mockito.`when`(mockUserLoginUseCase.invoke( - properties = testAnonymousRequestBodyData.properties, - userAgent = testAnonymousRequestBodyData.userAgent - )).thenReturn(Resource.Success(testAnonymousResponseData)) + fun testOnSuccessReturnLogInStateTrue() = runTest { + Mockito.`when`(mockUserLoginUseCase.anonymousUser()) + .thenReturn(flowOf(Resource.Success(testAnonymousResponseData))) val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) - loginViewModel.authenticateAnonymousUser( - properties = testAnonymousRequestBodyData.properties, - userAgent = testAnonymousRequestBodyData.userAgent - ) + loginViewModel.authenticateAnonymousUser() testDispatcher.scheduler.advanceUntilIdle() val result = loginViewModel.loginState.getOrAwaitValue() Assert.assertEquals(true, result.isLoggedIn) @@ -82,17 +75,12 @@ class LoginViewModelTest { } @Test - fun testOnFailureReturnLogInStateFalseWithError() = runTest{ - Mockito.`when`(mockUserLoginUseCase.invoke( - properties = testAnonymousRequestBodyData.properties, - userAgent = testAnonymousRequestBodyData.userAgent - )).thenReturn(Resource.Error(loginFailureMessage)) + fun testOnFailureReturnLogInStateFalseWithError() = runTest { + Mockito.`when`(mockUserLoginUseCase.anonymousUser()) + .thenReturn(flowOf(Resource.Error(loginFailureMessage, null))) val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) - loginViewModel.authenticateAnonymousUser( - properties = testAnonymousRequestBodyData.properties, - userAgent = testAnonymousRequestBodyData.userAgent - ) + loginViewModel.authenticateAnonymousUser() testDispatcher.scheduler.advanceUntilIdle() val result = loginViewModel.loginState.getOrAwaitValue() Assert.assertEquals(false, result.isLoggedIn) @@ -101,17 +89,12 @@ class LoginViewModelTest { } @Test - fun testOnLoadingReturnLogInStateFalse() = runTest{ - Mockito.`when`(mockUserLoginUseCase.invoke( - properties = testAnonymousRequestBodyData.properties, - userAgent = testAnonymousRequestBodyData.userAgent - )).thenReturn(Resource.Loading()) + fun testOnLoadingReturnLogInStateFalse() = runTest { + Mockito.`when`(mockUserLoginUseCase.anonymousUser()) + .thenReturn(flowOf(Resource.Loading(null))) val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) - loginViewModel.authenticateAnonymousUser( - properties = testAnonymousRequestBodyData.properties, - userAgent = testAnonymousRequestBodyData.userAgent - ) + loginViewModel.authenticateAnonymousUser() testDispatcher.scheduler.advanceUntilIdle() val result = loginViewModel.loginState.getOrAwaitValue() Assert.assertEquals(true, result.isLoading) @@ -122,6 +105,4 @@ class LoginViewModelTest { fun tearDown() { Dispatchers.resetMain() } - } - diff --git a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt deleted file mode 100644 index 25a9f06d5..000000000 --- a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -package foundation.e.apps.usecase.repository - -import android.content.Context -import androidx.test.core.app.ApplicationProvider -import kotlinx.coroutines.test.runTest -import org.junit.Assert -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.MockitoAnnotations -import org.robolectric.RobolectricTestRunner - -import foundation.e.apps.domain.login.repository.LoginRepositoryImpl -import foundation.e.apps.testAnonymousRequestBodyData -import foundation.e.apps.testAnonymousResponseData - -import app.lounge.login.anonymous.AnonymousUser -import app.lounge.networking.NetworkResult -import foundation.e.apps.loginFailureMessage -import foundation.e.apps.testFailureException - -@RunWith(RobolectricTestRunner::class) -class LoginRepositoryTest { - - @Mock - lateinit var anonymousUser: AnonymousUser - - private lateinit var instrumentationContext: Context - - - @Before - fun setUp() { - MockitoAnnotations.openMocks(this) - instrumentationContext= ApplicationProvider.getApplicationContext() - } - - @Test - fun testOnSuccessReturnAuthData() = runTest { - Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) - .thenReturn(NetworkResult.Success(testAnonymousResponseData)) - - val result = LoginRepositoryImpl(anonymousUser, instrumentationContext) - .run { - anonymousUser(testAnonymousRequestBodyData) - } - - Assert.assertNotNull(result) - Assert.assertEquals("eOS@murena.io", result.email) - } - - @Test - fun testOnFailureReturnErrorWithException() = runTest { - Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) - .thenReturn(NetworkResult.Error( - exception = testFailureException, - code = 1, - errorMessage = loginFailureMessage - )) - runCatching { - LoginRepositoryImpl(anonymousUser, instrumentationContext) - .run { anonymousUser(testAnonymousRequestBodyData) } - }.onFailure { error -> - Assert.assertEquals(testFailureException.message, error.message) - } - } -} -- GitLab From eeab7fa22fe9ffb65ee287954574e3a4ed708cf0 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 21 Aug 2023 23:47:12 +0530 Subject: [PATCH 050/112] Fix Klint issues --- app/src/main/java/foundation/e/apps/di/LoginModule.kt | 1 - .../java/foundation/e/apps/presentation/login/LoginState.kt | 2 -- .../foundation/e/apps/presentation/login/LoginViewModel.kt | 6 +----- app/src/main/java/foundation/e/apps/ui/MainActivity.kt | 3 +-- .../foundation/e/apps/ui/parentFragment/TimeoutFragment.kt | 2 +- .../java/foundation/e/apps/ui/settings/SettingsFragment.kt | 2 +- .../foundation/e/apps/ui/setup/signin/SignInFragment.kt | 2 +- .../e/apps/ui/setup/signin/google/GoogleSignInFragment.kt | 2 +- 8 files changed, 6 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt index 9708c9a8e..3df9857af 100644 --- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt +++ b/app/src/main/java/foundation/e/apps/di/LoginModule.kt @@ -45,5 +45,4 @@ object LoginModule { ): UserLoginUseCase { return UserLoginUseCase(loginRepositoryImpl) } - } diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt index b7e3d0f90..23f54bc34 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt @@ -16,10 +16,8 @@ * along with this program. If not, see . */ - package foundation.e.apps.presentation.login - data class LoginState( val isLoading: Boolean = false, val isLoggedIn: Boolean = false, diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 5655020e3..18da1ca6e 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -28,11 +28,9 @@ import foundation.e.apps.data.login.LoginSourceRepository import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.ui.parentFragment.LoadingViewModel import foundation.e.apps.utils.Resource -import foundation.e.apps.utils.SystemInfoProvider import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import java.util.Properties import javax.inject.Inject /** @@ -142,14 +140,12 @@ class LoginViewModel @Inject constructor( } } - private val _loginState: MutableLiveData = MutableLiveData() val loginState: LiveData = _loginState - fun authenticateAnonymousUser() { viewModelScope.launch { - userLoginUseCase.anonymousUser().onEach {result -> + userLoginUseCase.anonymousUser().onEach { result -> when (result) { is Resource.Success -> { _loginState.value = LoginState(isLoggedIn = true) diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt index 096c4b98f..82e5f4ce1 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt @@ -46,15 +46,14 @@ 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.presentation.login.LoginViewModel 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.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 diff --git a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt index 9a7f8eb10..9e395ae2a 100644 --- a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt @@ -34,13 +34,13 @@ import foundation.e.apps.R import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceGPlay -import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.data.login.exceptions.CleanApkException import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.data.login.exceptions.UnknownSourceException import foundation.e.apps.databinding.DialogErrorLogBinding +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.MainActivityViewModel import timber.log.Timber diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index 4a717877d..9865ff7cd 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -40,9 +40,9 @@ import foundation.e.apps.BuildConfig import foundation.e.apps.R import foundation.e.apps.data.enums.User import foundation.e.apps.data.fused.UpdatesDao -import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.databinding.CustomPreferenceBinding import foundation.e.apps.install.updates.UpdatesWorkManager +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.utils.SystemInfoProvider import timber.log.Timber diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt index 7ed9eb765..e9ce89eb3 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt @@ -7,9 +7,9 @@ import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R -import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.databinding.FragmentSignInBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.utils.showGoogleSignInAlertDialog @AndroidEntryPoint diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt index ed964d86c..3ecc77843 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt @@ -32,9 +32,9 @@ import androidx.navigation.findNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.gplay.utils.AC2DMUtil -import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.databinding.FragmentGoogleSigninBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate +import foundation.e.apps.presentation.login.LoginViewModel @AndroidEntryPoint class GoogleSignInFragment : Fragment(R.layout.fragment_google_signin) { -- GitLab From 878aabc5e2b7f9ccd69ad493d2def679d465c127 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 21 Aug 2023 23:58:02 +0530 Subject: [PATCH 051/112] Apply new anonymous api --- .../domain/login/repository/LoginRepositoryImpl.kt | 2 ++ .../e/apps/ui/setup/signin/SignInFragment.kt | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index 58e970348..da99361cd 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -25,6 +25,7 @@ import app.lounge.networking.NetworkResult import app.lounge.storage.cache.configurations import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.data.enums.User import foundation.e.apps.utils.SystemInfoProvider import java.util.Properties import javax.inject.Inject @@ -50,6 +51,7 @@ class LoginRepositoryImpl @Inject constructor( throw Exception(result.errorMessage, result.exception) is NetworkResult.Success -> { applicationContext.configurations.authData = result.data.toString() + applicationContext.configurations.userType = User.ANONYMOUS.name return result.data } } diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt index e9ce89eb3..34e8d51b6 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt @@ -33,10 +33,7 @@ class SignInFragment : Fragment(R.layout.fragment_sign_in) { } binding.anonymousBT.setOnClickListener { - viewModel.initialAnonymousLogin { - view.findNavController() - .safeNavigate(R.id.signInFragment, R.id.action_signInFragment_to_homeFragment) - } + viewModel.authenticateAnonymousUser() } binding.noGoogleBT.setOnClickListener { @@ -45,6 +42,15 @@ class SignInFragment : Fragment(R.layout.fragment_sign_in) { .safeNavigate(R.id.signInFragment, R.id.action_signInFragment_to_homeFragment) } } + + viewModel.loginState.observe(this.viewLifecycleOwner) { + if (it.isLoggedIn) + view.findNavController() + .safeNavigate( + R.id.signInFragment, + R.id.action_signInFragment_to_homeFragment + ) + } } override fun onDestroyView() { -- GitLab From c2754ac073e56585fb7baadc7680f5c2a6e4698e Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 1 Sep 2023 09:52:30 +0530 Subject: [PATCH 052/112] Update module AGP version --- modules/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/build.gradle b/modules/build.gradle index 48fab303e..26f426ebb 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -58,11 +58,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '11' + jvmTarget = '17' } } -- GitLab From b9c6e36df9b7820f1282a10fcf751d66d2c9ecc0 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 4 Sep 2023 22:45:16 +0530 Subject: [PATCH 053/112] Refine anoymous user flow for app lounge --- .../java/foundation/e/apps/MainActivity.kt | 39 ++--- .../java/foundation/e/apps/data/enums/User.kt | 10 +- .../java/foundation/e/apps/di/LoginModule.kt | 31 +++- .../common/repository/CommonRepository.kt | 32 ++++ .../common/repository/CommonRepositoryImpl.kt | 53 +++++++ .../login/repository/LoginRepository.kt | 1 + .../login/repository/LoginRepositoryImpl.kt | 6 +- .../domain/login/usecase/UserLoginUseCase.kt | 46 +++++- .../main/usecase/MainActivityUserCase.kt | 12 ++ .../settings/repository/SettingsRepository.kt | 25 ++++ .../repository/SettingsRepositoryImpl.kt | 31 ++++ .../settings/usecase/SettingsUseCase.kt | 45 ++++++ .../domain/updates/usecase/UpdatesUseCase.kt | 12 ++ .../e/apps/install/updates/UpdatesWorker.kt | 17 ++- .../e/apps/presentation/login/LoginState.kt | 7 +- .../apps/presentation/login/LoginViewModel.kt | 89 ++++++++--- .../presentation/settings/SettingsState.kt | 26 ++++ .../settings/SettingsViewModel.kt | 65 +++++++++ .../e/apps/ui/MainActivityViewModel.kt | 11 +- .../ui/application/ApplicationFragment.kt | 64 +++----- .../ui/application/ApplicationViewModel.kt | 52 +------ .../ApplicationListFragment.kt | 66 +++------ .../ApplicationListViewModel.kt | 56 +------ .../e/apps/ui/categories/AppsFragment.kt | 58 +++----- .../apps/ui/categories/CategoriesViewModel.kt | 38 +---- .../e/apps/ui/categories/GamesFragment.kt | 57 +++----- .../foundation/e/apps/ui/home/HomeFragment.kt | 103 ++++--------- .../e/apps/ui/home/HomeViewModel.kt | 47 +----- .../e/apps/ui/search/SearchFragment.kt | 123 +++++----------- .../e/apps/ui/search/SearchViewModel.kt | 138 ++---------------- .../e/apps/ui/settings/SettingsFragment.kt | 119 ++++++++------- .../e/apps/ui/setup/signin/SignInFragment.kt | 21 ++- .../e/apps/ui/updates/UpdatesFragment.kt | 101 ++++--------- .../e/apps/ui/updates/UpdatesViewModel.kt | 45 +----- .../foundation/e/apps/utils/Extensions.kt | 6 + .../java/foundation/e/apps/data/UserTest.kt | 55 +++++++ 36 files changed, 845 insertions(+), 862 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt create mode 100644 app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt create mode 100644 app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt create mode 100644 app/src/test/java/foundation/e/apps/data/UserTest.kt diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 1261aca94..e691b1f92 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -33,14 +33,11 @@ import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.NavigationUI import androidx.navigation.ui.setupWithNavController -import com.aurora.gplayapi.data.models.AuthData 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.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.presentation.login.LoginViewModel @@ -49,7 +46,6 @@ 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 import kotlinx.coroutines.flow.collectLatest @@ -94,7 +90,7 @@ class MainActivity : AppCompatActivity() { if (it != true) { navController.navigate(R.id.TOSFragment, null, navOptions) } else { - loginViewModel.startLoginFlow() + loginViewModel.checkLogin() } } @@ -108,28 +104,16 @@ class MainActivity : AppCompatActivity() { } } - loginViewModel.authObjects.distinctUntilChanged().observe(this) { + loginViewModel.loginState.distinctUntilChanged().observe(this) { when { - it == null -> return@observe - it.isEmpty() -> { - // No auth type defined means user has not logged in yet - // Pop back stack to prevent showing TOSFragment on pressing back button. - navController.popBackStack() - navController.navigate(R.id.signInFragment) + it.isLoading -> { + // TODO ? } - - else -> {} - } - - it.find { it is AuthObject.GPlayAuth }?.result?.run { - if (isSuccess()) { - viewModel.gPlayAuthData = data as AuthData - } else if (exception is GPlayValidationException) { - val email = otherPayload.toString() - viewModel.uploadFaultyTokenToEcloud( - email, - SystemInfoProvider.getAppBuildInfo() - ) + it.error.isNotBlank() -> { + it.authData ?: run { showLoginScreen(navController) } + } + !it.isLoggedIn -> { + showLoginScreen(navController) } } } @@ -338,4 +322,9 @@ class MainActivity : AppCompatActivity() { binding.noInternet.visibility = View.VISIBLE binding.fragment.visibility = View.GONE } + + private fun showLoginScreen(navController: NavController) { + navController.popBackStack() + navController.navigate(R.id.signInFragment) + } } diff --git a/app/src/main/java/foundation/e/apps/data/enums/User.kt b/app/src/main/java/foundation/e/apps/data/enums/User.kt index e9190cfd9..d9816a2d6 100644 --- a/app/src/main/java/foundation/e/apps/data/enums/User.kt +++ b/app/src/main/java/foundation/e/apps/data/enums/User.kt @@ -4,5 +4,13 @@ enum class User { NO_GOOGLE, ANONYMOUS, GOOGLE, - UNAVAILABLE + UNAVAILABLE; + + companion object { + fun getUser(userString: String) : User { + val userStrings = values().map { it.name } + return if (userString !in userStrings) UNAVAILABLE + else valueOf(userString) + } + } } diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt index 3df9857af..804fdf48b 100644 --- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt +++ b/app/src/main/java/foundation/e/apps/di/LoginModule.kt @@ -24,8 +24,13 @@ import dagger.hilt.components.SingletonComponent import foundation.e.apps.data.login.LoginSourceCleanApk import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.login.LoginSourceInterface +import foundation.e.apps.domain.common.repository.CommonRepositoryImpl import foundation.e.apps.domain.login.repository.LoginRepositoryImpl import foundation.e.apps.domain.login.usecase.UserLoginUseCase +import foundation.e.apps.domain.main.usecase.MainActivityUserCase +import foundation.e.apps.domain.settings.repository.SettingsRepositoryImpl +import foundation.e.apps.domain.settings.usecase.SettingsUseCase +import foundation.e.apps.domain.updates.usecase.UpdatesUseCase @InstallIn(SingletonComponent::class) @Module @@ -41,8 +46,30 @@ object LoginModule { @Provides fun provideLoginUserCase( - loginRepositoryImpl: LoginRepositoryImpl + loginRepositoryImpl: LoginRepositoryImpl, + commonRepositoryImpl: CommonRepositoryImpl ): UserLoginUseCase { - return UserLoginUseCase(loginRepositoryImpl) + return UserLoginUseCase(loginRepositoryImpl, commonRepositoryImpl) + } + + @Provides + fun provideSettingsUserCase( + commonRepositoryImpl: CommonRepositoryImpl + ): SettingsUseCase { + return SettingsUseCase(commonRepositoryImpl) + } + + @Provides + fun provideMainActivityUseCase( + commonRepositoryImpl: CommonRepositoryImpl + ): MainActivityUserCase { + return MainActivityUserCase(commonRepositoryImpl) + } + + @Provides + fun provideUpdatesUseCase( + commonRepositoryImpl: CommonRepositoryImpl + ): UpdatesUseCase { + return UpdatesUseCase(commonRepositoryImpl) } } diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt new file mode 100644 index 000000000..12e67e495 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt @@ -0,0 +1,32 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +package foundation.e.apps.domain.common.repository + +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.enums.User + + +interface CommonRepository { + + fun currentUser(): User + + fun cacheAuthData(): AuthData + fun resetCachedData() +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt new file mode 100644 index 000000000..ec0beb161 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt @@ -0,0 +1,53 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.common.repository + +import android.content.Context +import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.data.enums.User +import foundation.e.apps.utils.toAuthData +import javax.inject.Inject + +class CommonRepositoryImpl @Inject constructor( + @ApplicationContext val applicationContext: Context, +) : CommonRepository { + + override fun currentUser(): User { + return applicationContext.configurations.userType.takeIf { it.isNotEmpty() } + ?.let { User.getUser(it) } + ?: run { User.UNAVAILABLE } + } + override fun resetCachedData() { + applicationContext.configurations.apply { + authData = "" + userType = User.UNAVAILABLE.name + email = "" + oauthtoken = "" + // TODO: reset access token for Google login. It is not defined yet. + } + } + + override fun cacheAuthData(): AuthData = + applicationContext.configurations.authData.let { data -> + if (data.isEmpty()) throw Exception("Auth Data not available") + return data.toAuthData() + } +} diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt index 45edb8773..06bc7a5bc 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -23,4 +23,5 @@ import com.aurora.gplayapi.data.models.AuthData interface LoginRepository { suspend fun anonymousUser(): AuthData + } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index da99361cd..219af6e29 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -27,6 +27,7 @@ import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.data.enums.User import foundation.e.apps.utils.SystemInfoProvider +import foundation.e.apps.utils.toJsonString import java.util.Properties import javax.inject.Inject @@ -50,9 +51,10 @@ class LoginRepositoryImpl @Inject constructor( is NetworkResult.Error -> throw Exception(result.errorMessage, result.exception) is NetworkResult.Success -> { - applicationContext.configurations.authData = result.data.toString() + val authData = result.data + applicationContext.configurations.authData = authData.toJsonString() applicationContext.configurations.userType = User.ANONYMOUS.name - return result.data + return authData } } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index 9244b1444..727d9548c 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -19,23 +19,65 @@ package foundation.e.apps.domain.login.usecase import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.domain.common.repository.CommonRepository import foundation.e.apps.domain.login.repository.LoginRepository import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach import javax.inject.Inject class UserLoginUseCase @Inject constructor( private val loginRepository: LoginRepository, + private val commonRepository: CommonRepository ) { fun anonymousUser(): Flow> = flow { try { emit(Resource.Loading()) - val userResponse = loginRepository.anonymousUser() - emit(Resource.Success(userResponse)) + emit(Resource.Success(loginRepository.anonymousUser())) } catch (e: Exception) { emit(Resource.Error(e.localizedMessage)) } } + + fun retrieveCachedAuthData(): Flow> = flow { + try { + emit(Resource.Loading()) + emit(Resource.Success(commonRepository.cacheAuthData())) + } catch (e: Exception) { + emit(Resource.Error(e.localizedMessage)) + } + } + + fun logoutUser() { + commonRepository.resetCachedData() + } + + fun currentUser() = commonRepository.currentUser() + + + fun performAnonymousUserAuthentication(): Flow> = flow { + anonymousUser().onEach {anonymousAuth -> + // TODO -> If we are not using auth data then + when(anonymousAuth ) { + is Resource.Error -> emit(Resource.Error(anonymousAuth.message ?: "An unexpected error occured")) + is Resource.Loading -> emit(Resource.Loading()) + is Resource.Success -> { + retrieveCachedAuthData().onEach { + when(it) { + is Resource.Error -> { + emit(Resource.Error(anonymousAuth.message ?: "An unexpected error occured")) + } + is Resource.Loading -> emit(Resource.Loading()) + is Resource.Success -> { + emit(Resource.Success(commonRepository.cacheAuthData())) + } + } + }.collect() + } + } + }.collect() + } } diff --git a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt new file mode 100644 index 000000000..cd9b60502 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt @@ -0,0 +1,12 @@ +package foundation.e.apps.domain.main.usecase + +import foundation.e.apps.domain.common.repository.CommonRepository +import javax.inject.Inject + +class MainActivityUserCase @Inject constructor( + private val commonRepository: CommonRepository +) { + fun currentUser() = commonRepository.currentUser() + + fun currentAuthData() = commonRepository.cacheAuthData() +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt new file mode 100644 index 000000000..326a68c14 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt @@ -0,0 +1,25 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.settings.repository + +import foundation.e.apps.data.enums.User + +interface SettingsRepository { + +} diff --git a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt new file mode 100644 index 000000000..807d95a03 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt @@ -0,0 +1,31 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.settings.repository + +import android.content.Context +import app.lounge.storage.cache.configurations +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.data.enums.User +import javax.inject.Inject + +class SettingsRepositoryImpl @Inject constructor( + @ApplicationContext val applicationContext: Context +) : SettingsRepository { + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt new file mode 100644 index 000000000..4db4a9dde --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt @@ -0,0 +1,45 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.settings.usecase + +import foundation.e.apps.data.enums.User +import foundation.e.apps.domain.common.repository.CommonRepository +import foundation.e.apps.domain.settings.repository.SettingsRepository +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class SettingsUseCase @Inject constructor( + private val commonRepository: CommonRepository, +) { + fun currentUser() : Flow> = flow { + kotlin.runCatching { + val currentUser = commonRepository.currentUser() + emit(Resource.Success(currentUser)) + }.onFailure { emit(Resource.Error("Something went wrong in fun currentUser()")) } + } + + fun logoutUser(): Flow> = flow { + runCatching { + commonRepository.resetCachedData() + emit(Resource.Success(Unit)) + }.onFailure { emit(Resource.Error("Error during logout")) } + } +} diff --git a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt new file mode 100644 index 000000000..e8b12bfaf --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt @@ -0,0 +1,12 @@ +package foundation.e.apps.domain.updates.usecase + +import foundation.e.apps.domain.common.repository.CommonRepository +import javax.inject.Inject + +class UpdatesUseCase @Inject constructor( + private val commonRepository: CommonRepository +) { + fun currentUser() = commonRepository.currentUser() + + fun currentAuthData() = commonRepository.cacheAuthData() +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt index 0f647e8ac..a145f377a 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt @@ -24,7 +24,9 @@ import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.login.LoginSourceRepository import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.data.updates.UpdatesManagerRepository +import foundation.e.apps.domain.updates.usecase.UpdatesUseCase import foundation.e.apps.install.workmanager.AppInstallProcessor +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.Dispatchers @@ -37,11 +39,8 @@ class UpdatesWorker @AssistedInject constructor( @Assisted private val context: Context, @Assisted private val params: WorkerParameters, private val updatesManagerRepository: UpdatesManagerRepository, - private val fusedAPIRepository: FusedAPIRepository, - private val fusedManagerRepository: FusedManagerRepository, - private val dataStoreManager: DataStoreManager, - private val loginSourceRepository: LoginSourceRepository, - private val appInstallProcessor: AppInstallProcessor + private val appInstallProcessor: AppInstallProcessor, + private val updatesUseCase: UpdatesUseCase ) : CoroutineWorker(context, params) { companion object { @@ -57,6 +56,9 @@ class UpdatesWorker @AssistedInject constructor( private var isAutoUpdate = true // indicates it is auto update or user initiated update private var retryCount = 0 + private val authData : AuthData + get() = updatesUseCase.currentAuthData() ?: AuthData("", "") + override suspend fun doWork(): Result { return try { isAutoUpdate = params.inputData.getBoolean(IS_AUTO_UPDATE, true) @@ -94,7 +96,7 @@ class UpdatesWorker @AssistedInject constructor( } private fun getUser(): User { - return dataStoreManager.getUserType() + return updatesUseCase.currentUser() } private suspend fun checkForUpdates() { @@ -102,10 +104,9 @@ class UpdatesWorker @AssistedInject constructor( val isConnectedToUnmeteredNetwork = isConnectedToUnmeteredNetwork(applicationContext) val appsNeededToUpdate = mutableListOf() val user = getUser() - val authData = loginSourceRepository.getValidatedAuthData().data val resultStatus: ResultStatus - if (user in listOf(User.ANONYMOUS, User.GOOGLE) && authData != null) { + if (user in listOf(User.ANONYMOUS, User.GOOGLE)) { /* * Signifies valid Google user and valid auth data to update * apps from Google Play store. diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt index 23f54bc34..089d093a1 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt @@ -18,8 +18,13 @@ package foundation.e.apps.presentation.login +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.enums.User + data class LoginState( val isLoading: Boolean = false, val isLoggedIn: Boolean = false, - val error: String = "" + val error: String = "", + val authData: AuthData? = null, + val user: User = User.UNAVAILABLE, ) diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 18da1ca6e..9c0325115 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -21,16 +21,21 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel +import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User +import foundation.e.apps.data.enums.User.NO_GOOGLE import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceRepository import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.ui.parentFragment.LoadingViewModel import foundation.e.apps.utils.Resource +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject /** @@ -134,32 +139,82 @@ class LoginViewModel @Inject constructor( * Clears all saved data and logs out the user to the sign in screen. */ fun logout() { - viewModelScope.launch { - loginSourceRepository.logout() - authObjects.postValue(listOf()) - } + userLoginUseCase.logoutUser() + _loginState.value = LoginState() } private val _loginState: MutableLiveData = MutableLiveData() val loginState: LiveData = _loginState fun authenticateAnonymousUser() { - viewModelScope.launch { - userLoginUseCase.anonymousUser().onEach { result -> - when (result) { - is Resource.Success -> { - _loginState.value = LoginState(isLoggedIn = true) - } - is Resource.Error -> { - _loginState.value = LoginState( - error = result.message ?: "An unexpected error occured" - ) - } - is Resource.Loading -> { - _loginState.value = LoginState(isLoading = true) + viewModelScope.launch(Dispatchers.IO) { + userLoginUseCase.performAnonymousUserAuthentication().onEach {result -> + withContext(Dispatchers.Main) { + when (result) { + is Resource.Success -> { + result.data?.let { updateSavedAuthData() } + } + + is Resource.Error -> { + _loginState.value = LoginState( + error = result.message ?: "An unexpected error occured" + ) + } + + is Resource.Loading -> { + _loginState.value = LoginState(isLoading = true) + } } } }.collect() } } + + fun checkLogin() { + viewModelScope.launch { + val user = userLoginUseCase.currentUser() + if (user == NO_GOOGLE) { + _loginState.value = + LoginState(isLoggedIn = true, authData = null, user = user) + } else { + updateSavedAuthData() + } + } + } + + private suspend fun updateSavedAuthData() { + userLoginUseCase.retrieveCachedAuthData().onEach { + when(it) { + is Resource.Error -> { + val error = it.message.let { message -> + when (message) { + null -> "An unexpected error occurred" + else -> message + } + } + _loginState.value = LoginState(error = error) + } + is Resource.Loading -> _loginState.value = LoginState(isLoading = true) + is Resource.Success -> { + // TODO + it.data?.let { it1 -> updateAuthObjectForAnonymousUser(it1) } + + _loginState.value = + LoginState(isLoggedIn = true, authData = it.data, user = User.ANONYMOUS) + } + } + }.collect() + } + + fun updateAuthObjectForAnonymousUser(authData: AuthData) { + // TODO : Refine after Google User API is refactored. + loginSourceRepository.gplayAuth = authData + + authObjects.postValue( + listOf( + AuthObject.GPlayAuth(ResultSupreme.Success(authData), User.ANONYMOUS) + ) + ) + } + } diff --git a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt new file mode 100644 index 000000000..1f0c93a8a --- /dev/null +++ b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt @@ -0,0 +1,26 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.presentation.settings + +import foundation.e.apps.data.enums.User + +data class CurrentUserState( + val error: String = "", + val user: User = User.UNAVAILABLE +) diff --git a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt new file mode 100644 index 000000000..6c1301fb6 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt @@ -0,0 +1,65 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.presentation.settings + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import foundation.e.apps.domain.settings.usecase.SettingsUseCase +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SettingsViewModel @Inject constructor( + private val settingsUseCase: SettingsUseCase +) : ViewModel() { + + private val _currentUserState: MutableLiveData = MutableLiveData() + val currentUserState: LiveData = _currentUserState + + fun getCurrentUser() { + viewModelScope.launch { + settingsUseCase.currentUser().onEach { result -> + when (result) { + is Resource.Success -> { + _currentUserState.value = + result.data?.let { CurrentUserState(user = it) } + } + is Resource.Error -> { + _currentUserState.value = CurrentUserState( + error = result.message ?: "An unexpected error occurred" + ) + } + + is Resource.Loading -> TODO() + } + }.collect() + } + } + + fun logout() { + settingsUseCase.logoutUser().launchIn(viewModelScope) + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt index 8c81f0003..e96fe7f8d 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt @@ -46,6 +46,7 @@ import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.preference.DataStoreModule +import foundation.e.apps.domain.main.usecase.MainActivityUserCase import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.pkg.PkgManagerModule import foundation.e.apps.install.workmanager.AppInstallProcessor @@ -64,7 +65,8 @@ class MainActivityViewModel @Inject constructor( private val pwaManagerModule: PWAManagerModule, private val ecloudRepository: EcloudRepository, private val blockedAppRepository: BlockedAppRepository, - private val appInstallProcessor: AppInstallProcessor + private val appInstallProcessor: AppInstallProcessor, + private val mainActivityUserCase: MainActivityUserCase ) : ViewModel() { val tocStatus: LiveData = dataStoreModule.tocStatus.asLiveData() @@ -74,7 +76,7 @@ class MainActivityViewModel @Inject constructor( val isAppPurchased: MutableLiveData = MutableLiveData() val purchaseDeclined: MutableLiveData = MutableLiveData() - var gPlayAuthData = AuthData("", "") + private val gPlayAuthData: AuthData by lazy { mainActivityUserCase.currentAuthData() } // Downloads val downloadList = fusedManagerRepository.getDownloadLiveList() @@ -91,7 +93,7 @@ class MainActivityViewModel @Inject constructor( } fun getUser(): User { - return dataStoreModule.getUserType() + return mainActivityUserCase.currentUser() } fun getUserEmail(): String { @@ -168,11 +170,10 @@ class MainActivityViewModel @Inject constructor( */ fun verifyUiFilter(fusedApp: FusedApp, method: () -> Unit) { viewModelScope.launch { - val authData = gPlayAuthData if (fusedApp.filterLevel.isInitialized()) { method() } else { - fusedAPIRepository.getAppFilterLevel(fusedApp, authData).run { + fusedAPIRepository.getAppFilterLevel(fusedApp, gPlayAuthData).run { if (isInitialized()) { fusedApp.filterLevel = this method() diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index a71ef5263..e41b75c20 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -33,14 +33,17 @@ import androidx.core.content.ContextCompat import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat import androidx.core.view.isVisible +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import coil.load +import com.aurora.gplayapi.data.models.AuthData import com.google.android.material.button.MaterialButton import com.google.android.material.snackbar.Snackbar import com.google.android.material.textview.MaterialTextView @@ -61,12 +64,12 @@ import foundation.e.apps.di.CommonUtilsModule.LIST_OF_NULL import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.pkg.PkgManagerModule +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.AppInfoFetchViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.model.ApplicationScreenshotsRVAdapter import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment -import foundation.e.apps.ui.parentFragment.TimeoutFragment import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import timber.log.Timber @@ -74,7 +77,7 @@ import java.util.Locale import javax.inject.Inject @AndroidEntryPoint -class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { +class ApplicationFragment : Fragment(R.layout.fragment_application) { private val args: ApplicationFragmentArgs by navArgs() private val TAG = ApplicationFragment::class.java.simpleName @@ -120,7 +123,13 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private val applicationViewModel: ApplicationViewModel by viewModels() private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() - override val mainActivityViewModel: MainActivityViewModel by activityViewModels() + private val mainActivityViewModel: MainActivityViewModel by activityViewModels() + + private val loginViewModel: LoginViewModel by lazy { + ViewModelProvider(requireActivity())[LoginViewModel::class.java] + } + + private var authData: AuthData? = null private var applicationIcon: ImageView? = null @@ -140,15 +149,12 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { super.onViewCreated(view, savedInstanceState) _binding = FragmentApplicationBinding.bind(view) - setupListening() - - authObjects.observe(viewLifecycleOwner) { - if (it == null) return@observe - loadDataWhenNetworkAvailable(it) - } - - applicationViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { - handleExceptionsCommon(it) + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + // TODO : check for network and wait if network is unavailable + this.authData = it.authData + loadData() + } } setupToolbar(view) @@ -447,7 +453,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { } } - override fun loadData(authObjectList: List) { + private fun loadData() { if (isDetailsLoaded) return /* Show the loading bar. */ showLoadingUI() @@ -459,32 +465,8 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { packageName, origin, isFdroidDeepLink, - authObjectList - ) { - clearAndRestartGPlayLogin() - true - } - } - - override fun onTimeout( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onSignInError( - exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onDataLoadError( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog + authData + ) } private fun observeDownloadStatus(view: View) { @@ -829,12 +811,12 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { } } - override fun showLoadingUI() { + private fun showLoadingUI() { binding.applicationLayout.visibility = View.GONE binding.progressBar.visibility = View.VISIBLE } - override fun stopLoadingUI() { + private fun stopLoadingUI() { binding.applicationLayout.visibility = View.VISIBLE binding.progressBar.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt index 3f1889ae1..0323992ac 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt @@ -19,6 +19,7 @@ package foundation.e.apps.ui.application import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.exceptions.ApiException @@ -46,7 +47,7 @@ class ApplicationViewModel @Inject constructor( downloadProgressLD: DownloadProgressLD, private val fusedAPIRepository: FusedAPIRepository, private val fusedManagerRepository: FusedManagerRepository, -) : LoadingViewModel() { +) : ViewModel() { val fusedApp: MutableLiveData> = MutableLiveData() val appStatus: MutableLiveData = MutableLiveData() @@ -59,8 +60,7 @@ class ApplicationViewModel @Inject constructor( packageName: String, origin: Origin, isFdroidLink: Boolean, - authObjectList: List, - retryBlock: (failedObjects: List) -> Boolean, + authData: AuthData?, ) { if (isFdroidLink) { @@ -68,32 +68,10 @@ class ApplicationViewModel @Inject constructor( return } - val gPlayObj = authObjectList.find { it is AuthObject.GPlayAuth } - - /* - * If user is viewing only open source apps, auth object list will not have - * GPlayAuth, it will only have CleanApkAuth. - */ - if (gPlayObj == null && origin == Origin.GPLAY) { - _errorMessageLiveData.postValue(R.string.gplay_data_for_oss) - return - } - - super.onLoadData(authObjectList, { successAuthList, _ -> - - successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getApplicationDetails(id, packageName, result.data!! as AuthData, origin) - return@onLoadData - } - - successAuthList.find { it is AuthObject.CleanApk }?.run { - getApplicationDetails(id, packageName, AuthData("", ""), origin) - return@onLoadData - } - }, retryBlock) + getApplicationDetails(id, packageName, authData ?: AuthData("", ""), origin) } - fun getApplicationDetails(id: String, packageName: String, authData: AuthData, origin: Origin) { + private fun getApplicationDetails(id: String, packageName: String, authData: AuthData, origin: Origin) { viewModelScope.launch(Dispatchers.IO) { try { val appData = @@ -104,24 +82,6 @@ class ApplicationViewModel @Inject constructor( origin ) fusedApp.postValue(appData) - - val status = appData.second - - if (appData.second != ResultStatus.OK) { - val exception = - if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) - GPlayException( - appData.second == ResultStatus.TIMEOUT, - status.message.ifBlank { "Data load error" } - ) - else CleanApkException( - appData.second == ResultStatus.TIMEOUT, - status.message.ifBlank { "Data load error" } - ) - - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) - } } catch (e: ApiException.AppNotFound) { _errorMessageLiveData.postValue(R.string.app_not_found) } catch (e: Exception) { @@ -134,7 +94,7 @@ class ApplicationViewModel @Inject constructor( * Dedicated method to get app details from cleanapk using package name. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5509 */ - fun getCleanapkAppDetails(packageName: String) { + private fun getCleanapkAppDetails(packageName: String) { viewModelScope.launch { try { fusedAPIRepository.getCleanapkAppDetails(packageName).run { diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt index ccf2abcbb..bda3f6c98 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt @@ -22,14 +22,17 @@ import android.os.Bundle import android.view.View import android.widget.ImageView import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.ViewModelProvider 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 com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme @@ -42,6 +45,7 @@ import foundation.e.apps.databinding.FragmentApplicationListBinding import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.pkg.PkgManagerModule +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.AppInfoFetchViewModel import foundation.e.apps.ui.AppProgressViewModel import foundation.e.apps.ui.MainActivityViewModel @@ -53,7 +57,7 @@ import javax.inject.Inject @AndroidEntryPoint class ApplicationListFragment : - TimeoutFragment(R.layout.fragment_application_list), + Fragment(R.layout.fragment_application_list), FusedAPIInterface { // protected to avoid SyntheticAccessor @@ -69,8 +73,13 @@ class ApplicationListFragment : protected val viewModel: ApplicationListViewModel by viewModels() private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() - override val mainActivityViewModel: MainActivityViewModel by activityViewModels() + private val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val appProgressViewModel: AppProgressViewModel by viewModels() + private val loginViewModel: LoginViewModel by lazy { + ViewModelProvider(requireActivity())[LoginViewModel::class.java] + } + + private var authData: AuthData? = null private var _binding: FragmentApplicationListBinding? = null private val binding get() = _binding!! @@ -85,15 +94,12 @@ class ApplicationListFragment : setupRecyclerView(view) observeAppListLiveData() - setupListening() - - authObjects.observe(viewLifecycleOwner) { - if (it == null) return@observe - loadDataWhenNetworkAvailable(it) - } - - viewModel.exceptionsLiveData.observe(viewLifecycleOwner) { - handleExceptionsCommon(it) + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + // TODO : check for network and wait if network is unavailable + this.authData = it.authData + loadData() + } } } @@ -149,7 +155,7 @@ class ApplicationListFragment : listAdapter.currentList ) ) { - repostAuthObjects() + loadData() } } @@ -216,7 +222,7 @@ class ApplicationListFragment : currentList ) - override fun loadData(authObjectList: List) { + private fun loadData() { /* * If details are once loaded, do not load details again, @@ -229,10 +235,7 @@ class ApplicationListFragment : * Issue: https://gitlab.e.foundation/e/os/backlog/-/issues/478 */ showLoadingUI() - viewModel.loadData(args.category, args.source, authObjectList) { - clearAndRestartGPlayLogin() - true - } + viewModel.loadData(args.category, args.source, authData) if (args.source != "Open Source" && args.source != "PWA") { /* @@ -244,7 +247,7 @@ class ApplicationListFragment : super.onScrollStateChanged(recyclerView, newState) if (!recyclerView.canScrollVertically(1)) { viewModel.loadMore( - authObjectList.find { it is AuthObject.GPlayAuth }, + authData, args.category ) } @@ -261,7 +264,7 @@ class ApplicationListFragment : if (this is ApplicationListRVAdapter) { onPlaceHolderShow = { viewModel.loadMore( - authObjectList.find { it is AuthObject.GPlayAuth }, + authData, args.category ) } @@ -270,34 +273,13 @@ class ApplicationListFragment : } } - override fun onTimeout( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onSignInError( - exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onDataLoadError( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun showLoadingUI() { + private fun showLoadingUI() { binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } - override fun stopLoadingUI() { + private fun stopLoadingUI() { binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.recyclerView.visibility = View.VISIBLE diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt index 79c8cc52d..a5a2b0aeb 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt @@ -19,6 +19,7 @@ package foundation.e.apps.ui.applicationlist import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel @@ -27,10 +28,6 @@ import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Source import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.CleanApkException -import foundation.e.apps.data.login.exceptions.GPlayException -import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -38,7 +35,7 @@ import javax.inject.Inject @HiltViewModel class ApplicationListViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository -) : LoadingViewModel() { +) : ViewModel() { val appListLiveData: MutableLiveData>?> = MutableLiveData() @@ -49,26 +46,9 @@ class ApplicationListViewModel @Inject constructor( fun loadData( category: String, source: String, - authObjectList: List, - retryBlock: (failedObjects: List) -> Boolean, + authData: AuthData?, ) { - super.onLoadData(authObjectList, { successAuthList, _ -> - - if (appListLiveData.value?.data?.isNotEmpty() == true) { - appListLiveData.postValue(appListLiveData.value) - return@onLoadData - } - - successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getList(category, result.data!! as AuthData, source) - return@onLoadData - } - - successAuthList.find { it is AuthObject.CleanApk }?.run { - getList(category, AuthData("", ""), source) - return@onLoadData - } - }, retryBlock) + getList(category, authData ?: AuthData("", ""), source) } private fun getList(category: String, authData: AuthData, source: String) { @@ -91,30 +71,9 @@ class ApplicationListViewModel @Inject constructor( appListLiveData.postValue(ResultSupreme.create(ResultStatus.OK, it.first)) updateNextPageUrl(it.second) } - - if (!result.isSuccess()) { - val exception = getException(authData, result) - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) - } } } - private fun getException( - authData: AuthData, - result: ResultSupreme, String>> - ) = if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) { - GPlayException( - result.isTimeout(), - result.message.ifBlank { "Data load error" } - ) - } else { - CleanApkException( - result.isTimeout(), - result.message.ifBlank { "Data load error" } - ) - } - private fun updateNextPageUrl(nextPageUrl: String?) { this.nextPageUrl = nextPageUrl } @@ -129,13 +88,8 @@ class ApplicationListViewModel @Inject constructor( return fusedAPIRepository.isAnyFusedAppUpdated(newFusedApps, oldFusedApps) } - fun loadMore(gPlayAuth: AuthObject?, category: String) { + fun loadMore(authData: AuthData?, category: String) { viewModelScope.launch(Dispatchers.IO) { - val authData: AuthData? = when { - gPlayAuth !is AuthObject.GPlayAuth -> null - !gPlayAuth.result.isSuccess() -> null - else -> gPlayAuth.result.data!! - } if (isLoading || authData == null || nextPageUrl.isNullOrEmpty()) { return@launch diff --git a/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt index af0383c0d..b257d2e26 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt @@ -21,8 +21,10 @@ package foundation.e.apps.ui.categories import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R @@ -30,31 +32,31 @@ import foundation.e.apps.data.fused.utils.CategoryType import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentAppsBinding +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.categories.model.CategoriesRVAdapter import foundation.e.apps.ui.parentFragment.TimeoutFragment @AndroidEntryPoint -class AppsFragment : TimeoutFragment(R.layout.fragment_apps) { +class AppsFragment : Fragment(R.layout.fragment_apps) { private var _binding: FragmentAppsBinding? = null private val binding get() = _binding!! private val categoriesViewModel: CategoriesViewModel by viewModels() - override val mainActivityViewModel: MainActivityViewModel by activityViewModels() + + private val loginViewModel: LoginViewModel by lazy { + ViewModelProvider(requireActivity())[LoginViewModel::class.java] + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentAppsBinding.bind(view) - setupListening() - - authObjects.observe(viewLifecycleOwner) { - if (it == null) return@observe - loadDataWhenNetworkAvailable(it) - } - - categoriesViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { - handleExceptionsCommon(it) + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + // TODO : check for network and wait if network is unavailable + loadData() + } } val categoriesRVAdapter = CategoriesRVAdapter() @@ -72,46 +74,24 @@ class AppsFragment : TimeoutFragment(R.layout.fragment_apps) { } } - override fun loadData(authObjectList: List) { - categoriesViewModel.loadData(CategoryType.APPLICATION, authObjectList) { - clearAndRestartGPlayLogin() - true - } + private fun loadData() { + showLoadingUI() + categoriesViewModel.loadData(CategoryType.APPLICATION) } - override fun onTimeout( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onSignInError( - exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onDataLoadError( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun showLoadingUI() { + private fun showLoadingUI() { binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } - override fun stopLoadingUI() { + private fun stopLoadingUI() { binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.recyclerView.visibility = View.VISIBLE } + override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() diff --git a/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt b/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt index acf7c933c..28cc34c77 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt @@ -19,6 +19,7 @@ package foundation.e.apps.ui.categories import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel @@ -36,52 +37,21 @@ import javax.inject.Inject @HiltViewModel class CategoriesViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository -) : LoadingViewModel() { +) : ViewModel() { val categoriesList: MutableLiveData, String, ResultStatus>> = MutableLiveData() fun loadData( type: CategoryType, - authObjectList: List, - retryBlock: (failedObjects: List) -> Boolean, ) { - super.onLoadData(authObjectList, { successAuthList, _ -> - - successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getCategoriesList(type, result.data!! as AuthData) - return@onLoadData - } - - successAuthList.find { it is AuthObject.CleanApk }?.run { - getCategoriesList(type, AuthData("", "")) - return@onLoadData - } - }, retryBlock) + getCategoriesList(type) } - fun getCategoriesList(type: CategoryType, authData: AuthData) { + private fun getCategoriesList(type: CategoryType) { viewModelScope.launch { val categoriesData = fusedAPIRepository.getCategoriesList(type) categoriesList.postValue(categoriesData) - - val status = categoriesData.third - - if (status != ResultStatus.OK) { - val exception = - if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) - GPlayException( - categoriesData.third == ResultStatus.TIMEOUT, - status.message.ifBlank { "Data load error" } - ) - else CleanApkException( - categoriesData.third == ResultStatus.TIMEOUT, - status.message.ifBlank { "Data load error" } - ) - - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) - } } } } diff --git a/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt index 8f8f11fdb..da9470200 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt @@ -21,8 +21,10 @@ package foundation.e.apps.ui.categories import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R @@ -30,31 +32,31 @@ import foundation.e.apps.data.fused.utils.CategoryType import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentGamesBinding +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.categories.model.CategoriesRVAdapter import foundation.e.apps.ui.parentFragment.TimeoutFragment @AndroidEntryPoint -class GamesFragment : TimeoutFragment(R.layout.fragment_games) { +class GamesFragment : Fragment(R.layout.fragment_games) { private var _binding: FragmentGamesBinding? = null private val binding get() = _binding!! private val categoriesViewModel: CategoriesViewModel by viewModels() - override val mainActivityViewModel: MainActivityViewModel by activityViewModels() + + private val loginViewModel: LoginViewModel by lazy { + ViewModelProvider(requireActivity())[LoginViewModel::class.java] + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentGamesBinding.bind(view) - setupListening() - - authObjects.observe(viewLifecycleOwner) { - if (it == null) return@observe - loadDataWhenNetworkAvailable(it) - } - - categoriesViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { - handleExceptionsCommon(it) + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + // TODO : check for network and wait if network is unavailable + loadData() + } } val categoriesRVAdapter = CategoriesRVAdapter() @@ -72,41 +74,18 @@ class GamesFragment : TimeoutFragment(R.layout.fragment_games) { } } - override fun loadData(authObjectList: List) { - categoriesViewModel.loadData(CategoryType.GAMES, authObjectList) { - clearAndRestartGPlayLogin() - true - } - } - - override fun onTimeout( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onSignInError( - exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onDataLoadError( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog + private fun loadData() { + showLoadingUI() + categoriesViewModel.loadData(CategoryType.GAMES) } - override fun showLoadingUI() { + private fun showLoadingUI() { binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.recyclerView.visibility = View.GONE } - override fun stopLoadingUI() { + private fun stopLoadingUI() { binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.recyclerView.visibility = View.VISIBLE diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt index 73142126f..215a06a70 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt @@ -22,12 +22,16 @@ import android.os.Bundle import android.view.View import android.widget.ImageView import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme @@ -42,6 +46,8 @@ import foundation.e.apps.databinding.FragmentHomeBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PWAManagerModule +import foundation.e.apps.presentation.login.LoginState +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.AppInfoFetchViewModel import foundation.e.apps.ui.AppProgressViewModel import foundation.e.apps.ui.MainActivityViewModel @@ -53,7 +59,7 @@ import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface { +class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { /* * Make adapter nullable to avoid memory leaks. @@ -64,10 +70,16 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface private val binding get() = _binding!! private val homeViewModel: HomeViewModel by viewModels() - override val mainActivityViewModel: MainActivityViewModel by activityViewModels() + private val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val appProgressViewModel: AppProgressViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() + private val loginViewModel: LoginViewModel by lazy { + ViewModelProvider(requireActivity())[LoginViewModel::class.java] + } + + private var authData: AuthData? = null + @Inject lateinit var pwaManagerModule: PWAManagerModule @@ -112,15 +124,13 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface } private fun loadHomePageData() { - setupListening() - authObjects.observe(viewLifecycleOwner) { - if (it == null) return@observe - loadDataWhenNetworkAvailable(it) - } - - homeViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { - handleExceptionsCommon(it) + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + // TODO : check for network and wait if network is unavailable + this.authData = it.authData + loadData() + } } } @@ -146,61 +156,17 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface homeParentRVAdapter?.currentList as List ) - override fun onTimeout( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog.apply { - if (exception is GPlayException) { - setMessage(R.string.timeout_desc_gplay) - setNegativeButton(R.string.open_settings) { _, _ -> - openSettings() - } - } else { - setMessage(R.string.timeout_desc_cleanapk) - } - setCancelable(false) - } - } - - override fun onSignInError( - exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog.apply { - setNegativeButton(R.string.open_settings) { _, _ -> - openSettings() - } - } - } - - override fun onDataLoadError( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog.apply { - if (exception is GPlayException) { - setNegativeButton(R.string.open_settings) { _, _ -> - openSettings() - } - } - } - } - - override fun loadData(authObjectList: List) { - homeViewModel.loadData(authObjectList, viewLifecycleOwner) { _ -> - clearAndRestartGPlayLogin() - true - } + fun loadData() { + homeViewModel.loadData(authData, viewLifecycleOwner) } - override fun showLoadingUI() { + fun showLoadingUI() { binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.parentRV.visibility = View.GONE } - override fun stopLoadingUI() { + fun stopLoadingUI() { binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE @@ -265,7 +231,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface } if (homeViewModel.isAnyAppInstallStatusChanged(homeParentRVAdapter?.currentList)) { - repostAuthObjects() + loadData() } } @@ -291,23 +257,4 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface override fun cancelDownload(app: FusedApp) { mainActivityViewModel.cancelDownload(app) } - - private fun onTosAccepted(isTosAccepted: Boolean) { - if (isTosAccepted) { - /* - * "safeNavigate" is an extension function, to prevent calling this navigation multiple times. - * This is taken from: - * https://nezspencer.medium.com/navigation-components-a-fix-for-navigation-action-cannot-be-found-in-the-current-destination-95b63e16152e - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5166 - * Also related: https://gitlab.e.foundation/ecorp/apps/apps/-/merge_requests/28 - */ - view?.findNavController() - ?.safeNavigate(R.id.homeFragment, R.id.action_homeFragment_to_signInFragment) - } - } - - private fun openSettings() { - view?.findNavController() - ?.safeNavigate(R.id.homeFragment, R.id.action_homeFragment_to_SettingsFragment) - } } diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt index 7714c2b9c..bb5c41dbb 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt @@ -20,6 +20,7 @@ package foundation.e.apps.ui.home import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel @@ -27,68 +28,30 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fused.data.FusedHome -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.CleanApkException -import foundation.e.apps.data.login.exceptions.GPlayException -import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository, -) : LoadingViewModel() { +): ViewModel() { - /* - * Hold list of applications, as well as application source type. - * Source type may change from user selected preference in case of timeout. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5404 - */ var homeScreenData: MutableLiveData>> = MutableLiveData() fun loadData( - authObjectList: List, + authData: AuthData?, lifecycleOwner: LifecycleOwner, - retryBlock: (failedObjects: List) -> Boolean, ) { - super.onLoadData(authObjectList, { successAuthList, _ -> - - successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getHomeScreenData(result.data!! as AuthData, lifecycleOwner) - return@onLoadData - } - - successAuthList.find { it is AuthObject.CleanApk }?.run { - getHomeScreenData(AuthData("", ""), lifecycleOwner) - return@onLoadData - } - }, retryBlock) + getHomeScreenData(authData ?: AuthData("", ""), lifecycleOwner) } - fun getHomeScreenData( + private fun getHomeScreenData( authData: AuthData, lifecycleOwner: LifecycleOwner, ) { viewModelScope.launch { fusedAPIRepository.getHomeScreenData(authData).observe(lifecycleOwner) { homeScreenData.postValue(it) - - if (it.isSuccess()) return@observe - - val exception = - if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) - GPlayException( - it.isTimeout(), - it.message.ifBlank { "Data load error" } - ) - else CleanApkException( - it.isTimeout(), - it.message.ifBlank { "Data load error" } - ) - - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) } } } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt index a0b02883a..56cd8cef7 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt @@ -28,17 +28,20 @@ import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.ImageView import android.widget.LinearLayout -import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView +import androidx.core.view.isVisible import androidx.cursoradapter.widget.CursorAdapter import androidx.cursoradapter.widget.SimpleCursorAdapter +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.aurora.gplayapi.SearchSuggestEntry +import com.aurora.gplayapi.data.models.AuthData import com.facebook.shimmer.ShimmerFrameLayout import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R @@ -46,25 +49,22 @@ import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fused.FusedAPIInterface import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentSearchBinding import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PWAManagerModule +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.AppInfoFetchViewModel import foundation.e.apps.ui.AppProgressViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.applicationlist.ApplicationListRVAdapter -import foundation.e.apps.ui.parentFragment.TimeoutFragment -import foundation.e.apps.utils.isNetworkAvailable import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint class SearchFragment : - TimeoutFragment(R.layout.fragment_search), + Fragment(R.layout.fragment_search), SearchView.OnQueryTextListener, SearchView.OnSuggestionListener, FusedAPIInterface { @@ -75,12 +75,18 @@ class SearchFragment : private var _binding: FragmentSearchBinding? = null private val binding get() = _binding!! - protected val searchViewModel: SearchViewModel by viewModels() + private val searchViewModel: SearchViewModel by viewModels() private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() - override val mainActivityViewModel: MainActivityViewModel by activityViewModels() + private val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val appProgressViewModel: AppProgressViewModel by viewModels() + private val loginViewModel: LoginViewModel by lazy { + ViewModelProvider(requireActivity())[LoginViewModel::class.java] + } + + private var authData: AuthData? = null + private val SUGGESTION_KEY = "suggestion" private var lastSearch = "" @@ -114,44 +120,15 @@ class SearchFragment : observeSearchResult(listAdapter) - setupListening() - - authObjects.observe(viewLifecycleOwner) { + loginViewModel.loginState.observe(viewLifecycleOwner) { val currentQuery = searchView?.query?.toString() ?: "" - if (it == null || shouldIgnore(it, currentQuery)) { - showData() - return@observe - } - - val applicationListRVAdapter = recyclerView?.adapter as ApplicationListRVAdapter - applicationListRVAdapter.setData(mutableListOf()) - - loadDataWhenNetworkAvailable(it) + if (!it.isLoggedIn || (currentQuery.isNotEmpty() && lastSearch == currentQuery)) return@observe + // TODO : check for network and wait if network is unavailable + this.authData = it.authData + loadData() } - - searchViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { - handleExceptionsCommon(it) - } - - binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) - if (!recyclerView.canScrollVertically(1)) { - if (!requireContext().isNetworkAvailable()) { - return - } - searchViewModel.loadMore(searchText) - } - } - }) } - private fun shouldIgnore( - authObjectList: List?, - currentQuery: String - ) = currentQuery.isNotEmpty() && searchViewModel.isAuthObjectListSame(authObjectList) && - lastSearch == currentQuery - private fun observeSearchResult(listAdapter: ApplicationListRVAdapter?) { searchViewModel.searchResult.observe(viewLifecycleOwner) { if (it.data?.first.isNullOrEmpty() && it.data?.second == false) { @@ -193,22 +170,19 @@ class SearchFragment : */ private fun updateSearchResult( listAdapter: ApplicationListRVAdapter?, - appList: List, + appList: List?, + hasMore: Boolean, ): Boolean { val currentList = listAdapter?.currentList ?: listOf() - if (!searchViewModel.isAnyAppUpdated(appList, currentList)) { + if (appList != null && !searchViewModel.isAnyAppUpdated(appList, currentList)) { return false } - showData() - listAdapter?.setData(appList) - return true - } - - private fun showData() { stopLoadingUI() noAppsFoundLayout?.visibility = View.GONE searchHintLayout?.visibility = View.GONE + listAdapter?.setData(appList!!) + return true } private fun setupSearchResult(view: View): ApplicationListRVAdapter? { @@ -285,45 +259,22 @@ class SearchFragment : val searchList = searchViewModel.searchResult.value?.data?.first?.toMutableList() ?: emptyList() + val hasMoreDataToLoad = searchViewModel.searchResult.value?.data?.second == true mainActivityViewModel.updateStatusOfFusedApps(searchList, fusedDownloadList) - updateSearchResult(applicationListRVAdapter, searchList) - } - - override fun onTimeout( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun onDataLoadError( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog + updateSearchResult(applicationListRVAdapter, searchList, hasMoreDataToLoad) } - override fun onSignInError( - exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog - } - - override fun loadData(authObjectList: List) { + private fun loadData() { showLoadingUI() - searchViewModel.loadData(searchText, viewLifecycleOwner, authObjectList) { - clearAndRestartGPlayLogin() - true - } + searchViewModel.loadData(searchText, viewLifecycleOwner, authData) } - override fun showLoadingUI() { + private fun showLoadingUI() { binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE } - override fun stopLoadingUI() { + private fun stopLoadingUI() { binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE } @@ -389,7 +340,6 @@ class SearchFragment : if (text.isNotEmpty()) { hideKeyboard(activity as Activity) } - view?.requestFocus() searchHintLayout?.visibility = View.GONE shimmerLayout?.visibility = View.VISIBLE @@ -398,16 +348,16 @@ class SearchFragment : * Set the search text and call for network result. */ searchText = text - repostAuthObjects() + val applicationListRVAdapter = recyclerView?.adapter as ApplicationListRVAdapter + applicationListRVAdapter.setData(mutableListOf()) + loadData() } return false } override fun onQueryTextChange(newText: String?): Boolean { newText?.let { text -> - authObjects.value?.find { it is AuthObject.GPlayAuth }?.run { - searchViewModel.getSearchSuggestions(text, this as AuthObject.GPlayAuth) - } + searchViewModel.getSearchSuggestions(text, authData) } return true } @@ -451,11 +401,10 @@ class SearchFragment : } private fun showKeyboard() { - val inputMethodManager = - requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + val inputMethodManager = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager searchView?.javaClass?.getDeclaredField("mSearchSrcTextView")?.runCatching { isAccessible = true - get(searchView) as EditText + get(searchView)as EditText }?.onSuccess { inputMethodManager.showSoftInput(it, InputMethodManager.SHOW_FORCED) } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 6ecc4c0a3..24f387074 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -21,55 +21,36 @@ package foundation.e.apps.ui.search import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.models.SearchBundle import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.fused.FusedAPIRepository -import foundation.e.apps.data.fused.GplaySearchResult import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.CleanApkException -import foundation.e.apps.data.login.exceptions.GPlayException -import foundation.e.apps.data.login.exceptions.UnknownSourceException -import foundation.e.apps.ui.parentFragment.LoadingViewModel -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import timber.log.Timber import javax.inject.Inject -import kotlin.coroutines.coroutineContext @HiltViewModel class SearchViewModel @Inject constructor( private val fusedAPIRepository: FusedAPIRepository, -) : LoadingViewModel() { +) : ViewModel() { val searchSuggest: MutableLiveData?> = MutableLiveData() - val searchResult: MutableLiveData, Boolean>>> = MutableLiveData() - private var searchResultLiveData: LiveData, Boolean>>> = + private var searchResultLiveData: MutableLiveData, Boolean>>> = MutableLiveData() - private var lastAuthObjects: List? = null - - private var nextSubBundle: Set? = null - - private var isLoading: Boolean = false - - companion object { - private const val DATA_LOAD_ERROR = "Data load error" - } - fun getSearchSuggestions(query: String, gPlayAuth: AuthObject.GPlayAuth) { + fun getSearchSuggestions(query: String, authData: AuthData?) { viewModelScope.launch(Dispatchers.IO) { - if (gPlayAuth.result.isSuccess()) + if (query.isNotBlank() && authData != null) searchSuggest.postValue( fusedAPIRepository.getSearchSuggestions( query, - gPlayAuth.result.data!! + authData ) ) } @@ -78,25 +59,11 @@ class SearchViewModel @Inject constructor( fun loadData( query: String, lifecycleOwner: LifecycleOwner, - authObjectList: List, - retryBlock: (failedObjects: List) -> Boolean + authData: AuthData?, ) { if (query.isBlank()) return - - this.lastAuthObjects = authObjectList - super.onLoadData(authObjectList, { successAuthList, _ -> - - successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getSearchResults(query, result.data!! as AuthData, lifecycleOwner) - return@onLoadData - } - - successAuthList.find { it is AuthObject.CleanApk }?.run { - getSearchResults(query, AuthData("", ""), lifecycleOwner) - return@onLoadData - } - }, retryBlock) + getSearchResults(query, authData ?: AuthData("", ""), lifecycleOwner) } /* @@ -105,89 +72,16 @@ class SearchViewModel @Inject constructor( * without having to wait for all of the apps. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5171 */ - private fun getSearchResults( - query: String, - authData: AuthData, - lifecycleOwner: LifecycleOwner - ) { - viewModelScope.launch(Dispatchers.IO) { - val searchResultSupreme = fusedAPIRepository.getCleanApkSearchResults(query, authData) - - searchResult.postValue(searchResultSupreme) - - if (!searchResultSupreme.isSuccess()) { - val exception = - if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) { - GPlayException( - searchResultSupreme.isTimeout(), - searchResultSupreme.message.ifBlank { DATA_LOAD_ERROR } - ) - } else { - CleanApkException( - searchResultSupreme.isTimeout(), - searchResultSupreme.message.ifBlank { DATA_LOAD_ERROR } - ) - } - - handleException(exception) + private fun getSearchResults(query: String, authData: AuthData, lifecycleOwner: LifecycleOwner) { + viewModelScope.launch(Dispatchers.Main) { + searchResultLiveData.removeObservers(lifecycleOwner) + searchResultLiveData.value = fusedAPIRepository.getCleanApkSearchResults(query, authData) + searchResultLiveData.observe(lifecycleOwner) { + searchResult.postValue(it) } - - nextSubBundle = null - fetchGplayData(query) } } - fun loadMore(query: String) { - if (isLoading) { - Timber.d("Search result is loading....") - return - } - - viewModelScope.launch(Dispatchers.IO) { - fetchGplayData(query) - } - } - - private suspend fun fetchGplayData(query: String) { - isLoading = true - val gplaySearchResult = fusedAPIRepository.getGplaySearchResults(query, nextSubBundle) - - if (!gplaySearchResult.isSuccess()) { - handleException(gplaySearchResult.exception ?: UnknownSourceException()) - } - - val isFirstFetch = nextSubBundle == null - nextSubBundle = gplaySearchResult.data?.second - - // if first page has less data, then fetch next page data without waiting for users' scroll - if (isFirstFetch && gplaySearchResult.data?.first?.size!! < 4) { - CoroutineScope(coroutineContext).launch { - fetchGplayData(query) - } - } - - val currentAppList = updateCurrentAppList(gplaySearchResult) - val finalResult = ResultSupreme.Success( - Pair(currentAppList.toList(), nextSubBundle?.isNotEmpty() ?: false) - ) - - this@SearchViewModel.searchResult.postValue(finalResult) - isLoading = false - } - - private fun updateCurrentAppList(gplaySearchResult: GplaySearchResult): List { - val currentSearchResult = searchResult.value?.data - val currentAppList = currentSearchResult?.first?.toMutableList() ?: mutableListOf() - currentAppList.removeIf { item -> item.isPlaceHolder } - currentAppList.addAll(gplaySearchResult.data?.first ?: emptyList()) - return currentAppList.distinctBy { it.package_name } - } - - private fun handleException(exception: Exception) { - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) - } - /** * @return returns true if there is changes in data, otherwise false */ @@ -195,8 +89,4 @@ class SearchViewModel @Inject constructor( newFusedApps: List, oldFusedApps: List ) = fusedAPIRepository.isAnyFusedAppUpdated(newFusedApps, oldFusedApps) - - fun isAuthObjectListSame(authObjectList: List?): Boolean { - return lastAuthObjects == authObjectList - } } diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index 9865ff7cd..3e28a2c89 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -24,7 +24,6 @@ import android.os.Bundle import android.view.View import android.widget.Toast import androidx.core.view.isVisible -import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import androidx.preference.CheckBoxPreference @@ -43,7 +42,7 @@ import foundation.e.apps.data.fused.UpdatesDao import foundation.e.apps.databinding.CustomPreferenceBinding import foundation.e.apps.install.updates.UpdatesWorkManager import foundation.e.apps.presentation.login.LoginViewModel -import foundation.e.apps.ui.MainActivityViewModel +import foundation.e.apps.presentation.settings.SettingsViewModel import foundation.e.apps.utils.SystemInfoProvider import timber.log.Timber import javax.inject.Inject @@ -53,7 +52,6 @@ class SettingsFragment : PreferenceFragmentCompat() { private var _binding: CustomPreferenceBinding? = null private val binding get() = _binding!! - private val mainActivityViewModel: MainActivityViewModel by activityViewModels() private var showAllApplications: CheckBoxPreference? = null private var showFOSSApplications: CheckBoxPreference? = null private var showPWAApplications: CheckBoxPreference? = null @@ -62,6 +60,10 @@ class SettingsFragment : PreferenceFragmentCompat() { ViewModelProvider(requireActivity())[LoginViewModel::class.java] } + val settingsViewModel: SettingsViewModel by lazy { + ViewModelProvider(requireActivity())[SettingsViewModel::class.java] + } + private var sourcesChangedFlag = false @Inject @@ -70,10 +72,6 @@ class SettingsFragment : PreferenceFragmentCompat() { @Inject lateinit var clipboardManager: ClipboardManager - private val user by lazy { - mainActivityViewModel.getUser() - } - private val allSourceCheckboxes by lazy { listOf(showAllApplications, showFOSSApplications, showPWAApplications) } @@ -164,25 +162,34 @@ class SettingsFragment : PreferenceFragmentCompat() { // This is useful if a user from older App Lounge updates to this version disableDependentCheckbox(onlyUnmeteredNetwork, autoInstallUpdate) - mainActivityViewModel.gPlayAuthData.let { authData -> - mainActivityViewModel.getUser().name.let { user -> - when (user) { - User.ANONYMOUS.name -> { - binding.accountType.setText(R.string.user_anonymous) - binding.email.isVisible = false - } - User.GOOGLE.name -> { - if (!authData.isAnonymous) { - binding.accountType.text = authData.userProfile?.name - binding.email.text = mainActivityViewModel.getUserEmail() - binding.avatar.load(authData.userProfile?.artwork?.url) - } - } - User.NO_GOOGLE.name -> { - binding.accountType.setText(R.string.logged_out) - binding.email.isVisible = false + loginViewModel.loginState.observe(viewLifecycleOwner) { + val user = it.user + val authData = it.authData + when (user) { + User.GOOGLE -> { + if (!authData!!.isAnonymous) { + binding.accountType.text = authData.userProfile?.name + binding.email.text = authData.email + binding.avatar.load(authData.userProfile?.artwork?.url) } } + User.NO_GOOGLE -> { + binding.accountType.setText(R.string.logged_out) + binding.email.isVisible = false + setCheckboxForNoGoogle() + } + else -> {} + } + } + + settingsViewModel.getCurrentUser() + settingsViewModel.currentUserState.observe(viewLifecycleOwner) { + when(it.user) { + User.ANONYMOUS -> { + binding.accountType.setText(R.string.user_anonymous) + binding.email.isVisible = false + } + else -> {} } } @@ -193,40 +200,40 @@ class SettingsFragment : PreferenceFragmentCompat() { binding.logout.setOnClickListener { loginViewModel.logout() } + } - if (user == User.NO_GOOGLE) { - /* - * For No-Google mode, do not allow the user to click - * on the option to show GPlay apps. - * Instead show a message and prompt them to login. - */ - showAllApplications?.apply { - setOnPreferenceChangeListener { _, _ -> - Snackbar.make( - binding.root, - R.string.login_to_see_gplay_apps, - Snackbar.LENGTH_SHORT - ).setAction(R.string.login) { - /* - * The login and logout logic is the same, - * it clears all login data (authdata, user selection) - * and restarts the login flow. - * Hence it's the same as logout. - */ - binding.logout.performClick() - }.show() - this.isChecked = false - return@setOnPreferenceChangeListener false - } + private fun setCheckboxForNoGoogle() { + /* + * For No-Google mode, do not allow the user to click + * on the option to show GPlay apps. + * Instead show a message and prompt them to login. + */ + showAllApplications?.apply { + setOnPreferenceChangeListener { _, _ -> + Snackbar.make( + binding.root, + R.string.login_to_see_gplay_apps, + Snackbar.LENGTH_SHORT + ).setAction(R.string.login) { + /* + * The login and logout logic is the same, + * it clears all login data (authdata, user selection) + * and restarts the login flow. + * Hence it's the same as logout. + */ + binding.logout.performClick() + }.show() + this.isChecked = false + return@setOnPreferenceChangeListener false } + } - /* - * For no-google mode, just show a "Login" instead of "Logout". - * The background logic is the same. - */ - binding.logout.apply { - setText(R.string.login) - } + /* + * For no-google mode, just show a "Login" instead of "Logout". + * The background logic is the same. + */ + binding.logout.apply { + setText(R.string.login) } } @@ -277,7 +284,7 @@ class SettingsFragment : PreferenceFragmentCompat() { override fun onDestroyView() { if (sourcesChangedFlag) { UpdatesDao.addItemsForUpdate(emptyList()) - loginViewModel.startLoginFlow() + loginViewModel.checkLogin() } super.onDestroyView() _binding = null diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt index 34e8d51b6..4c9ac3554 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt @@ -11,6 +11,7 @@ import foundation.e.apps.databinding.FragmentSignInBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.utils.showGoogleSignInAlertDialog +import timber.log.Timber @AndroidEntryPoint class SignInFragment : Fragment(R.layout.fragment_sign_in) { @@ -43,13 +44,19 @@ class SignInFragment : Fragment(R.layout.fragment_sign_in) { } } - viewModel.loginState.observe(this.viewLifecycleOwner) { - if (it.isLoggedIn) - view.findNavController() - .safeNavigate( - R.id.signInFragment, - R.id.action_signInFragment_to_homeFragment - ) + viewModel.loginState.observe(this.viewLifecycleOwner) { loginState -> + if (loginState.isLoggedIn) { + loginState.authData?.let { data -> + + viewModel.updateAuthObjectForAnonymousUser(data) + + view.findNavController() + .safeNavigate( + R.id.signInFragment, + R.id.action_signInFragment_to_homeFragment + ) + } ?: run { Timber.e("Auth Data is null") } + } } } diff --git a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt index 6c5fd6a6f..fb14dc87d 100644 --- a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt @@ -21,9 +21,10 @@ package foundation.e.apps.ui.updates import android.os.Bundle import android.view.View import android.widget.ImageView -import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController @@ -32,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.work.WorkInfo import androidx.work.WorkManager +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme @@ -49,13 +51,13 @@ import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.updates.UpdatesWorkManager import foundation.e.apps.install.workmanager.InstallWorkManager.INSTALL_WORK_NAME +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.AppInfoFetchViewModel import foundation.e.apps.ui.AppProgressViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.applicationlist.ApplicationListRVAdapter -import foundation.e.apps.ui.parentFragment.TimeoutFragment import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import foundation.e.apps.utils.toast @@ -66,7 +68,7 @@ import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint -class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInterface { +class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { private var _binding: FragmentUpdatesBinding? = null private val binding get() = _binding!! @@ -77,9 +79,15 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte private val updatesViewModel: UpdatesViewModel by viewModels() private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() - override val mainActivityViewModel: MainActivityViewModel by activityViewModels() + private val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val appProgressViewModel: AppProgressViewModel by viewModels() + private val loginViewModel: LoginViewModel by lazy { + ViewModelProvider(requireActivity())[LoginViewModel::class.java] + } + + private var authData: AuthData? = null + private var isDownloadObserverAdded = false override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -87,18 +95,13 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte _binding = FragmentUpdatesBinding.bind(view) binding.button.isEnabled = false - setupListening() - authObjects.observe(viewLifecycleOwner) { - if (it == null) return@observe + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (!it.isLoggedIn) return@observe if (!updatesViewModel.updatesList.value?.first.isNullOrEmpty()) { return@observe } - loadDataWhenNetworkAvailable(it) - } - - updatesViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { - handleExceptionsCommon(it) + loadData() } val recyclerView = binding.recyclerView @@ -144,11 +147,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte stopLoadingUI() Timber.d("===>> observeupdate list called") - if (it.second != ResultStatus.OK) { - val exception = GPlayException(it.second == ResultStatus.TIMEOUT) - val alertDialogBuilder = AlertDialog.Builder(requireContext()) - onTimeout(exception, alertDialogBuilder) - } } } @@ -175,15 +173,15 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte private fun shouldUpdateButtonEnable(workInfoList: MutableList) = !updatesViewModel.updatesList.value?.first.isNullOrEmpty() && - ( - workInfoList.isNullOrEmpty() || - ( - !updatesViewModel.checkWorkInfoListHasAnyUpdatableWork( - workInfoList - ) && - updatesViewModel.hasAnyUpdatableApp() + ( + workInfoList.isNullOrEmpty() || + ( + !updatesViewModel.checkWorkInfoListHasAnyUpdatableWork( + workInfoList + ) && + updatesViewModel.hasAnyUpdatableApp() + ) ) - ) private fun handleUpdateEvent(appEvent: AppEvent) { val event = appEvent.data as ResultSupreme.WorkError<*> @@ -227,52 +225,9 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte ).show(childFragmentManager, "UpdatesFragment") } - override fun onTimeout( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog.apply { - if (exception is GPlayException) { - setMessage(R.string.timeout_desc_gplay) - setNegativeButton(R.string.open_settings) { _, _ -> - openSettings() - } - } else { - setMessage(R.string.timeout_desc_cleanapk) - } - } - } - - override fun onSignInError( - exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog.apply { - setNegativeButton(R.string.open_settings) { _, _ -> - openSettings() - } - } - } - - override fun onDataLoadError( - exception: Exception, - predefinedDialog: AlertDialog.Builder - ): AlertDialog.Builder? { - return predefinedDialog.apply { - if (exception is GPlayException) { - setNegativeButton(R.string.open_settings) { _, _ -> - openSettings() - } - } - } - } - - override fun loadData(authObjectList: List) { + private fun loadData() { showLoadingUI() - updatesViewModel.loadData(authObjectList) { - clearAndRestartGPlayLogin() - true - } + updatesViewModel.loadData(authData) initUpdataAllButton() } @@ -303,17 +258,17 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte WorkInfo.State.SUCCEEDED ) return !workInfoList.isNullOrEmpty() && errorStates.contains(workInfoList.last().state) && - updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() + updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() } - override fun showLoadingUI() { + private fun showLoadingUI() { binding.button.isEnabled = false binding.noUpdates.visibility = View.GONE binding.progressBar.visibility = View.VISIBLE binding.recyclerView.visibility = View.INVISIBLE } - override fun stopLoadingUI() { + private fun stopLoadingUI() { binding.progressBar.visibility = View.GONE if ((binding.recyclerView.adapter?.itemCount ?: 0) > 0) { diff --git a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt index bf6998e17..f803c2e82 100644 --- a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt @@ -19,6 +19,7 @@ package foundation.e.apps.ui.updates import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.work.WorkInfo import com.aurora.gplayapi.data.models.AuthData @@ -27,12 +28,8 @@ import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.CleanApkException -import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.data.updates.UpdatesManagerRepository -import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -41,54 +38,22 @@ class UpdatesViewModel @Inject constructor( private val updatesManagerRepository: UpdatesManagerRepository, private val fusedAPIRepository: FusedAPIRepository, private val preferenceManagerModule: PreferenceManagerModule -) : LoadingViewModel() { +) : ViewModel() { val updatesList: MutableLiveData, ResultStatus?>> = MutableLiveData() fun loadData( - authObjectList: List, - retryBlock: (failedObjects: List) -> Boolean, + authData: AuthData?, ) { - super.onLoadData(authObjectList, { successAuthList, _ -> - - successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getUpdates(result.data!! as AuthData) - return@onLoadData - } - - successAuthList.find { it is AuthObject.CleanApk }?.run { - getUpdates(AuthData("", "")) - return@onLoadData - } - }, retryBlock) + getUpdates(authData ?: AuthData("", "")) } - fun getUpdates(authData: AuthData?) { + private fun getUpdates(authData: AuthData?) { viewModelScope.launch { val updatesResult = if (authData != null) updatesManagerRepository.getUpdates(authData) else updatesManagerRepository.getUpdatesOSS() updatesList.postValue(updatesResult) - - val status = updatesResult.second - - if (status != ResultStatus.OK) { - val exception = - if (authData != null && - (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) - ) { - GPlayException( - updatesResult.second == ResultStatus.TIMEOUT, - status.message.ifBlank { "Data load error" } - ) - } else CleanApkException( - updatesResult.second == ResultStatus.TIMEOUT, - status.message.ifBlank { "Data load error" } - ) - - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) - } } } diff --git a/app/src/main/java/foundation/e/apps/utils/Extensions.kt b/app/src/main/java/foundation/e/apps/utils/Extensions.kt index aa763f6a8..8e2b1b9fd 100644 --- a/app/src/main/java/foundation/e/apps/utils/Extensions.kt +++ b/app/src/main/java/foundation/e/apps/utils/Extensions.kt @@ -4,6 +4,8 @@ import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities import androidx.appcompat.app.AlertDialog +import com.aurora.gplayapi.data.models.AuthData +import com.google.gson.Gson import foundation.e.apps.R import java.text.SimpleDateFormat import java.util.Date @@ -42,3 +44,7 @@ fun Context.isNetworkAvailable(): Boolean { return false } + +fun AuthData.toJsonString(): String = Gson().toJson(this) + +fun String.toAuthData(): AuthData = Gson().fromJson(this, AuthData::class.java) diff --git a/app/src/test/java/foundation/e/apps/data/UserTest.kt b/app/src/test/java/foundation/e/apps/data/UserTest.kt new file mode 100644 index 000000000..a4c8c2cfb --- /dev/null +++ b/app/src/test/java/foundation/e/apps/data/UserTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.data + +import foundation.e.apps.data.enums.User +import org.junit.Assert +import org.junit.Test + +class UserTest { + + @Test + fun testUserStringEmptyReturnUnavailable() { + val result = User.getUser("") + Assert.assertEquals(User.UNAVAILABLE.name, result.name) + } + + @Test + fun testUserStringGoogleReturnUserAsGoogle() { + val result = User.getUser("Google") + Assert.assertEquals(User.UNAVAILABLE.name, result.name) + } + + @Test + fun testUserStringAnonymousReturnUserAsAnonymous() { + val result = User.getUser("") + Assert.assertEquals(User.UNAVAILABLE.name, result.name) + } + + @Test + fun testUserStringForNoGoogleReturnUserAsNoGoogle() { + // TODO - No idea for No Google + } + + @Test + fun testRandomStringReturnUnavailable() { + val result = User.getUser("dmflmfle") + Assert.assertEquals(User.UNAVAILABLE.name, result.name) + } +} \ No newline at end of file -- GitLab From a7fd50c900fd9dd5bee5d330e2fe7cb7f9f482e3 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 4 Sep 2023 23:22:11 +0530 Subject: [PATCH 054/112] Fix Klint issues --- app/build.gradle | 2 +- .../foundation/e/apps/data/DownloadManager.kt | 3 +- .../foundation/e/apps/data/ResultSupreme.kt | 8 +- .../e/apps/data/cleanapk/CleanApkRetrofit.kt | 8 +- .../e/apps/data/cleanapk/RetrofitModule.kt | 2 +- .../e/apps/data/ecloud/EcloudApiInterface.kt | 2 +- .../e/apps/data/ecloud/EcloudRepository.kt | 2 +- .../e/apps/data/ecloud/modules/FaultyToken.kt | 2 +- .../foundation/e/apps/data/enums/AppTag.kt | 7 +- .../e/apps/data/enums/FilterLevel.kt | 2 +- .../java/foundation/e/apps/data/enums/User.kt | 9 ++- .../e/apps/data/exodus/ExodusTrackerApi.kt | 2 +- .../e/apps/data/exodus/models/Trackers.kt | 2 +- .../AppPrivacyInfoRepositoryImpl.kt | 8 +- .../PrivacyScoreRepositoryImpl.kt | 10 ++- .../e/apps/data/fdroid/FdroidRepository.kt | 2 +- .../e/apps/data/fdroid/models/BuildInfo.kt | 2 +- .../apps/data/fdroid/models/FdroidApiModel.kt | 2 +- .../e/apps/data/fused/FusedAPIRepository.kt | 2 +- .../foundation/e/apps/data/fused/FusedApi.kt | 6 +- .../e/apps/data/fused/FusedApiImpl.kt | 81 +++++++++++-------- .../e/apps/data/fusedDownload/FileManager.kt | 1 - .../data/gplay/GplayStoreRepositoryImpl.kt | 2 +- .../e/apps/data/gplay/utils/AC2DMTask.kt | 3 +- .../apps/data/gplay/utils/GPlayHttpClient.kt | 2 +- .../utils/NativeDeviceInfoProviderModule.kt | 7 +- .../gplay/utils/NativeGsfVersionProvider.kt | 15 ++-- .../data/gplay/utils/TimeoutEvaluation.kt | 6 +- .../e/apps/data/login/AuthObject.kt | 8 +- .../e/apps/data/login/LoginCommon.kt | 2 +- .../e/apps/data/login/LoginDataStore.kt | 7 +- .../e/apps/data/login/LoginSourceCleanApk.kt | 4 +- .../e/apps/data/login/LoginSourceGPlay.kt | 39 +++++---- .../apps/data/login/LoginSourceRepository.kt | 3 +- .../apps/data/login/api/AnonymousLoginApi.kt | 9 ++- .../e/apps/data/login/api/GPlayApiFactory.kt | 2 +- .../e/apps/data/login/api/GoogleLoginApi.kt | 2 +- .../apps/data/login/api/LoginApiRepository.kt | 4 +- .../login/exceptions/CleanApkException.kt | 2 +- .../data/login/exceptions/GPlayException.kt | 2 +- .../login/exceptions/GPlayLoginException.kt | 2 +- .../exceptions/GPlayValidationException.kt | 2 +- .../e/apps/data/preference/DataStoreModule.kt | 7 +- .../e/apps/data/updates/UpdatesManagerImpl.kt | 11 ++- .../java/foundation/e/apps/di/LoginModule.kt | 3 +- .../common/repository/CommonRepository.kt | 7 +- .../common/repository/CommonRepositoryImpl.kt | 2 +- .../login/repository/LoginRepository.kt | 2 - .../login/repository/LoginRepositoryImpl.kt | 2 +- .../domain/login/usecase/UserLoginUseCase.kt | 9 +-- .../main/usecase/MainActivityUserCase.kt | 4 +- .../settings/repository/SettingsRepository.kt | 6 +- .../repository/SettingsRepositoryImpl.kt | 6 +- .../settings/usecase/SettingsUseCase.kt | 5 +- .../domain/updates/usecase/UpdatesUseCase.kt | 4 +- .../e/apps/install/pkg/PWAManagerModule.kt | 7 +- .../e/apps/install/pkg/PkgManagerModule.kt | 9 +-- .../splitinstall/SplitInstallBinder.kt | 4 +- .../splitinstall/SplitInstallService.kt | 3 + .../e/apps/install/updates/UpdatesNotifier.kt | 2 +- .../install/updates/UpdatesWorkManager.kt | 3 +- .../e/apps/install/updates/UpdatesWorker.kt | 9 +-- .../workmanager/AppInstallProcessor.kt | 8 +- .../e/apps/presentation/login/LoginState.kt | 2 +- .../apps/presentation/login/LoginViewModel.kt | 7 +- .../settings/SettingsViewModel.kt | 2 +- .../e/apps/ui/AppInfoFetchViewModel.kt | 2 +- .../e/apps/ui/MainActivityViewModel.kt | 3 +- .../e/apps/ui/PrivacyInfoViewModel.kt | 8 +- .../ui/application/ApplicationFragment.kt | 32 +++++--- .../ui/application/ApplicationViewModel.kt | 9 +-- .../model/ApplicationScreenshotsDiffUtil.kt | 2 +- .../subFrags/ApplicationDialogFragment.kt | 6 +- .../ApplicationListFragment.kt | 7 +- .../ApplicationListRVAdapter.kt | 36 ++++++--- .../ApplicationListViewModel.kt | 3 +- .../e/apps/ui/categories/AppsFragment.kt | 7 -- .../apps/ui/categories/CategoriesViewModel.kt | 7 +- .../e/apps/ui/categories/GamesFragment.kt | 6 -- .../foundation/e/apps/ui/home/HomeFragment.kt | 16 +--- .../e/apps/ui/home/HomeViewModel.kt | 8 +- .../apps/ui/home/model/HomeChildRVAdapter.kt | 8 +- .../ui/parentFragment/LoadingViewModel.kt | 3 +- .../apps/ui/parentFragment/TimeoutFragment.kt | 15 ++-- .../e/apps/ui/search/SearchFragment.kt | 10 ++- .../e/apps/ui/search/SearchViewModel.kt | 9 +-- .../e/apps/ui/settings/SettingsFragment.kt | 13 +-- .../signin/LocaleChangedBroadcastReceiver.kt | 2 + .../e/apps/ui/setup/signin/SignInViewModel.kt | 2 +- .../e/apps/ui/setup/tos/TOSFragment.kt | 8 +- .../e/apps/ui/updates/UpdatesFragment.kt | 29 ++++--- .../e/apps/ui/updates/UpdatesViewModel.kt | 8 +- .../e/apps/utils/MaterialButtonUtils.kt | 5 +- .../java/foundation/e/apps/utils/Resource.kt | 1 + .../foundation/e/apps/FakePkgManagerModule.kt | 2 +- .../foundation/e/apps/FusedApiImplTest.kt | 39 ++++++--- .../e/apps/FusedApiRepositoryTest.kt | 1 + .../e/apps/UpdateManagerImptTest.kt | 2 +- .../java/foundation/e/apps/data/UserTest.kt | 2 +- .../AppPrivacyInfoRepositoryImplTest.kt | 6 +- .../FakeFusedManagerRepository.kt | 2 +- 101 files changed, 382 insertions(+), 349 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ddb26fa64..a29e6b9fe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,7 +3,7 @@ plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' - id 'org.jlleitschuh.gradle.ktlint' version '10.2.0' + id 'org.jlleitschuh.gradle.ktlint' version '11.5.1' id 'androidx.navigation.safeargs.kotlin' id 'com.google.dagger.hilt.android' id 'kotlin-allopen' diff --git a/app/src/main/java/foundation/e/apps/data/DownloadManager.kt b/app/src/main/java/foundation/e/apps/data/DownloadManager.kt index 3806bfb8e..ebd379619 100644 --- a/app/src/main/java/foundation/e/apps/data/DownloadManager.kt +++ b/app/src/main/java/foundation/e/apps/data/DownloadManager.kt @@ -44,7 +44,7 @@ class DownloadManager @Inject constructor( @ApplicationContext private val context: Context, private val downloadManager: DownloadManager, @Named("cacheDir") private val cacheDir: String, - private val downloadManagerQuery: DownloadManager.Query, + private val downloadManagerQuery: DownloadManager.Query ) { private val downloadsMaps = HashMap() @@ -73,7 +73,6 @@ class DownloadManager @Inject constructor( fileName: String, downloadCompleted: ((Boolean, String) -> Unit)? ): Long { - val directoryFile = File("$EXTERNAL_STORAGE_TEMP_CACHE_DIR/$subDirectoryPath") if (!directoryFile.exists()) { directoryFile.mkdirs() diff --git a/app/src/main/java/foundation/e/apps/data/ResultSupreme.kt b/app/src/main/java/foundation/e/apps/data/ResultSupreme.kt index 14695702f..7ec1ec83f 100644 --- a/app/src/main/java/foundation/e/apps/data/ResultSupreme.kt +++ b/app/src/main/java/foundation/e/apps/data/ResultSupreme.kt @@ -129,7 +129,7 @@ sealed class ResultSupreme { status: ResultStatus, data: T? = null, message: String = "", - exception: Exception? = null, + exception: Exception? = null ): ResultSupreme { val resultObject = when { status == ResultStatus.OK && data != null -> Success(data) @@ -162,7 +162,7 @@ sealed class ResultSupreme { result: ResultSupreme<*>, newData: T?, message: String? = null, - exception: Exception? = null, + exception: Exception? = null ): ResultSupreme { val status = when (result) { is Success -> ResultStatus.OK @@ -170,7 +170,9 @@ sealed class ResultSupreme { is Error -> ResultStatus.UNKNOWN } return create( - status, newData, message ?: result.message, + status, + newData, + message ?: result.message, exception ?: result.exception ) } diff --git a/app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkRetrofit.kt b/app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkRetrofit.kt index d6660cd60..540224991 100644 --- a/app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkRetrofit.kt +++ b/app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkRetrofit.kt @@ -47,7 +47,7 @@ interface CleanApkRetrofit { @GET("apps?action=list_home") suspend fun getHomeScreenData( @Query("type") type: String = APP_TYPE_ANY, - @Query("source") source: String = APP_SOURCE_ANY, + @Query("source") source: String = APP_SOURCE_ANY ): Response // TODO: Reminder that this function is for search App and PWA both @@ -65,7 +65,7 @@ interface CleanApkRetrofit { @Query("type") type: String = APP_TYPE_ANY, @Query("nres") nres: Int = 20, @Query("page") page: Int = 1, - @Query("by") by: String? = null, + @Query("by") by: String? = null ): Response @GET("apps?action=list_apps") @@ -74,7 +74,7 @@ interface CleanApkRetrofit { @Query("source") source: String = APP_SOURCE_FOSS, @Query("type") type: String = APP_TYPE_ANY, @Query("nres") nres: Int = 20, - @Query("page") page: Int = 1, + @Query("page") page: Int = 1 ): Response @GET("apps?action=download") @@ -87,6 +87,6 @@ interface CleanApkRetrofit { @GET("apps?action=list_cat") suspend fun getCategoriesList( @Query("type") type: String = APP_TYPE_ANY, - @Query("source") source: String = APP_SOURCE_ANY, + @Query("source") source: String = APP_SOURCE_ANY ): Response } diff --git a/app/src/main/java/foundation/e/apps/data/cleanapk/RetrofitModule.kt b/app/src/main/java/foundation/e/apps/data/cleanapk/RetrofitModule.kt index 43a505137..1cfc79bd1 100644 --- a/app/src/main/java/foundation/e/apps/data/cleanapk/RetrofitModule.kt +++ b/app/src/main/java/foundation/e/apps/data/cleanapk/RetrofitModule.kt @@ -121,7 +121,7 @@ object RetrofitModule { @Singleton @Provides fun provideFdroidWebApi( - okHttpClient: OkHttpClient, + okHttpClient: OkHttpClient ): FdroidWebInterface { return Retrofit.Builder() .baseUrl(FdroidWebInterface.BASE_URL) diff --git a/app/src/main/java/foundation/e/apps/data/ecloud/EcloudApiInterface.kt b/app/src/main/java/foundation/e/apps/data/ecloud/EcloudApiInterface.kt index e22a3d121..ae73438eb 100644 --- a/app/src/main/java/foundation/e/apps/data/ecloud/EcloudApiInterface.kt +++ b/app/src/main/java/foundation/e/apps/data/ecloud/EcloudApiInterface.kt @@ -31,6 +31,6 @@ interface EcloudApiInterface { @Headers("Content-Type: application/json") @POST("report") suspend fun uploadFaultyEmail( - @Body faultyToken: FaultyToken, + @Body faultyToken: FaultyToken ) } diff --git a/app/src/main/java/foundation/e/apps/data/ecloud/EcloudRepository.kt b/app/src/main/java/foundation/e/apps/data/ecloud/EcloudRepository.kt index 941735dc8..a5667aa85 100644 --- a/app/src/main/java/foundation/e/apps/data/ecloud/EcloudRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/ecloud/EcloudRepository.kt @@ -23,7 +23,7 @@ import javax.inject.Singleton @Singleton class EcloudRepository @Inject constructor( - private val ecloudApi: EcloudApiInterface, + private val ecloudApi: EcloudApiInterface ) { suspend fun uploadFaultyEmail(email: String, description: String) { try { diff --git a/app/src/main/java/foundation/e/apps/data/ecloud/modules/FaultyToken.kt b/app/src/main/java/foundation/e/apps/data/ecloud/modules/FaultyToken.kt index 8091d36d4..fae19eb58 100644 --- a/app/src/main/java/foundation/e/apps/data/ecloud/modules/FaultyToken.kt +++ b/app/src/main/java/foundation/e/apps/data/ecloud/modules/FaultyToken.kt @@ -19,5 +19,5 @@ package foundation.e.apps.data.ecloud.modules data class FaultyToken( val email: String, - val description: String, + val description: String ) diff --git a/app/src/main/java/foundation/e/apps/data/enums/AppTag.kt b/app/src/main/java/foundation/e/apps/data/enums/AppTag.kt index 2b1611655..a6e3f49ec 100644 --- a/app/src/main/java/foundation/e/apps/data/enums/AppTag.kt +++ b/app/src/main/java/foundation/e/apps/data/enums/AppTag.kt @@ -35,7 +35,10 @@ sealed class AppTag(val displayTag: String) { * This method allows for all those check to work without modification. */ fun getOperationalTag(): String { - return if (this is OpenSource) "Open Source" - else this::class.java.simpleName + return if (this is OpenSource) { + "Open Source" + } else { + this::class.java.simpleName + } } } diff --git a/app/src/main/java/foundation/e/apps/data/enums/FilterLevel.kt b/app/src/main/java/foundation/e/apps/data/enums/FilterLevel.kt index 13dcc7b7a..4cbf05300 100644 --- a/app/src/main/java/foundation/e/apps/data/enums/FilterLevel.kt +++ b/app/src/main/java/foundation/e/apps/data/enums/FilterLevel.kt @@ -37,7 +37,7 @@ enum class FilterLevel { UI, // Show the app in lists, but show "N/A" in the install button. DATA, // Filter the app out from lists and search results, don't show the app at all. NONE, // No restrictions - UNKNOWN, // Not initialised yet + UNKNOWN // Not initialised yet } fun FilterLevel.isUnFiltered(): Boolean = this == FilterLevel.NONE diff --git a/app/src/main/java/foundation/e/apps/data/enums/User.kt b/app/src/main/java/foundation/e/apps/data/enums/User.kt index d9816a2d6..975cb4bd1 100644 --- a/app/src/main/java/foundation/e/apps/data/enums/User.kt +++ b/app/src/main/java/foundation/e/apps/data/enums/User.kt @@ -7,10 +7,13 @@ enum class User { UNAVAILABLE; companion object { - fun getUser(userString: String) : User { + fun getUser(userString: String): User { val userStrings = values().map { it.name } - return if (userString !in userStrings) UNAVAILABLE - else valueOf(userString) + return if (userString !in userStrings) { + UNAVAILABLE + } else { + valueOf(userString) + } } } } diff --git a/app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt b/app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt index 9c9c35ea5..169570664 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt @@ -17,6 +17,6 @@ interface ExodusTrackerApi { @GET("search/{appHandle}/details") suspend fun getTrackerInfoOfApp( @Path("appHandle") appHandle: String, - @Query("v") versionCode: Int, + @Query("v") versionCode: Int ): Response> } diff --git a/app/src/main/java/foundation/e/apps/data/exodus/models/Trackers.kt b/app/src/main/java/foundation/e/apps/data/exodus/models/Trackers.kt index 49f19e31f..c5a3647ba 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/models/Trackers.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/models/Trackers.kt @@ -16,5 +16,5 @@ data class Tracker( val creationDate: String?, val codeSignature: String?, val networkSignature: String?, - val website: String?, + val website: String? ) diff --git a/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt index b3df01c3b..439ebd7c4 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt @@ -59,7 +59,7 @@ class AppPrivacyInfoRepositoryImpl @Inject constructor( val appTrackerInfoResult = getResult { exodusTrackerApi.getTrackerInfoOfApp( appHandle, - fusedApp.latest_version_code, + fusedApp.latest_version_code ) } @@ -94,7 +94,7 @@ class AppPrivacyInfoRepositoryImpl @Inject constructor( private suspend fun handleAppPrivacyInfoResultSuccess( fusedApp: FusedApp, - appTrackerResult: Result>, + appTrackerResult: Result> ): Result { if (trackers.isEmpty()) { generateTrackerList() @@ -129,7 +129,7 @@ class AppPrivacyInfoRepositoryImpl @Inject constructor( private fun createAppPrivacyInfo( fusedApp: FusedApp, - appTrackerResult: Result>, + appTrackerResult: Result> ): Result { appTrackerResult.data?.let { return Result.success(getAppPrivacyInfo(fusedApp, it)) @@ -139,7 +139,7 @@ class AppPrivacyInfoRepositoryImpl @Inject constructor( private fun getAppPrivacyInfo( fusedApp: FusedApp, - appTrackerData: List, + appTrackerData: List ): AppPrivacyInfo { /* * If the response is empty, that means there is no data on Exodus API about this app, diff --git a/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt index 871e82640..6c8dc7e45 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt @@ -48,9 +48,13 @@ class PrivacyScoreRepositoryImpl @Inject constructor() : PrivacyScoreRepository fusedApp.permsFromExodus.filter { it.contains("android.permission") }.size private fun calculatePermissionsScore(numberOfPermission: Int): Int { - 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() + 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() + } } // please do not put in the top of the class, as it can break the privacy calculation source code link. diff --git a/app/src/main/java/foundation/e/apps/data/fdroid/FdroidRepository.kt b/app/src/main/java/foundation/e/apps/data/fdroid/FdroidRepository.kt index fe2b08f0e..d99381ab7 100644 --- a/app/src/main/java/foundation/e/apps/data/fdroid/FdroidRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/fdroid/FdroidRepository.kt @@ -12,7 +12,7 @@ import javax.inject.Singleton @Singleton class FdroidRepository @Inject constructor( private val fdroidApi: FdroidApiInterface, - private val fdroidDao: FdroidDao, + private val fdroidDao: FdroidDao ) : IFdroidRepository { companion object { diff --git a/app/src/main/java/foundation/e/apps/data/fdroid/models/BuildInfo.kt b/app/src/main/java/foundation/e/apps/data/fdroid/models/BuildInfo.kt index 6b70811d6..e02e70933 100644 --- a/app/src/main/java/foundation/e/apps/data/fdroid/models/BuildInfo.kt +++ b/app/src/main/java/foundation/e/apps/data/fdroid/models/BuildInfo.kt @@ -29,7 +29,7 @@ class BuildInfo() { @JsonCreator constructor( @JsonProperty("versionCode") versionCode: String?, - @JsonProperty("versionName") versionName: String?, + @JsonProperty("versionName") versionName: String? ) : this() { this.versionCode = versionCode ?: "" this.versionName = versionName ?: "" diff --git a/app/src/main/java/foundation/e/apps/data/fdroid/models/FdroidApiModel.kt b/app/src/main/java/foundation/e/apps/data/fdroid/models/FdroidApiModel.kt index 172bb8802..978cf9e6d 100644 --- a/app/src/main/java/foundation/e/apps/data/fdroid/models/FdroidApiModel.kt +++ b/app/src/main/java/foundation/e/apps/data/fdroid/models/FdroidApiModel.kt @@ -24,7 +24,7 @@ class FdroidApiModel() { @JsonCreator constructor( @JsonProperty("AuthorName") AuthorName: String?, - @JsonProperty("Builds") Builds: List?, + @JsonProperty("Builds") Builds: List? ) : this() { this.authorName = AuthorName ?: "" this.builds = Builds ?: emptyList() diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt index 00dd50683..bd6614018 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt @@ -99,7 +99,7 @@ class FusedAPIRepository @Inject constructor(private val fusedAPIImpl: FusedApi) } suspend fun getCategoriesList( - type: CategoryType, + type: CategoryType ): Triple, String, ResultStatus> { return fusedAPIImpl.getCategoriesList(type) } diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApi.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApi.kt index 7267f634b..4dd9b7175 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApi.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApi.kt @@ -36,7 +36,7 @@ interface FusedApi { fun getApplicationCategoryPreference(): List suspend fun getHomeScreenData( - authData: AuthData, + authData: AuthData ): LiveData>> /* @@ -51,7 +51,7 @@ interface FusedApi { * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ suspend fun getCategoriesList( - type: CategoryType, + type: CategoryType ): Triple, String, ResultStatus> /** @@ -118,7 +118,7 @@ interface FusedApi { */ suspend fun filterRestrictedGPlayApps( authData: AuthData, - appList: List, + appList: List ): ResultSupreme> /** diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index cb4f227a6..1280315bd 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -125,9 +125,8 @@ class FusedApiImpl @Inject constructor( } override suspend fun getHomeScreenData( - authData: AuthData, + authData: AuthData ): LiveData>> { - val list = mutableListOf() var resultGplay: FusedHomeDeferred? = null var resultOpenSource: FusedHomeDeferred? = null @@ -135,7 +134,6 @@ class FusedApiImpl @Inject constructor( return liveData { coroutineScope { - if (preferenceManagerModule.isGplaySelected()) { resultGplay = async { loadHomeData(list, Source.GPLAY, authData) } } @@ -164,9 +162,8 @@ class FusedApiImpl @Inject constructor( private suspend fun loadHomeData( priorList: MutableList, source: Source, - authData: AuthData, + authData: AuthData ): ResultSupreme> { - val apiStatus = when (source) { Source.GPLAY -> runCodeWithTimeout({ priorList.addAll(fetchGPlayHome(authData)) @@ -222,7 +219,7 @@ class FusedApiImpl @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ override suspend fun getCategoriesList( - type: CategoryType, + type: CategoryType ): Triple, String, ResultStatus> { val categoriesList = mutableListOf() val preferredApplicationType = preferenceManagerModule.preferredApplicationType() @@ -411,7 +408,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun getCleanApkPackageResult( - query: String, + query: String ): FusedApp? { getCleanapkSearchResult(query).let { if (it.isSuccess() && it.data!!.package_name.isNotBlank()) { @@ -423,7 +420,7 @@ class FusedApiImpl @Inject constructor( private suspend fun getGplayPackagResult( query: String, - authData: AuthData, + authData: AuthData ): FusedApp? { try { getApplicationDetails(query, query, authData, Origin.GPLAY).let { @@ -479,7 +476,7 @@ class FusedApiImpl @Inject constructor( packageName, moduleName, versionCode, - offerType, + offerType ) for (element in list) { if (element.name == "$moduleName.apk") { @@ -605,7 +602,7 @@ class FusedApiImpl @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ private suspend fun getAppDetailsListFromCleanapk( - packageNameList: List, + packageNameList: List ): Pair, ResultStatus> { var status = ResultStatus.OK val fusedAppList = mutableListOf() @@ -647,7 +644,7 @@ class FusedApiImpl @Inject constructor( */ private suspend fun getAppDetailsListFromGPlay( packageNameList: List, - authData: AuthData, + authData: AuthData ): Pair, ResultStatus> { val fusedAppList = mutableListOf() @@ -689,7 +686,7 @@ class FusedApiImpl @Inject constructor( */ override suspend fun filterRestrictedGPlayApps( authData: AuthData, - appList: List, + appList: List ): ResultSupreme> { val filteredFusedApps = mutableListOf() val status = runCodeWithTimeout({ @@ -724,8 +721,11 @@ class FusedApiImpl @Inject constructor( return FilterLevel.NONE } if (authData == null) { - return if (fusedApp.origin == Origin.GPLAY) FilterLevel.UNKNOWN - else FilterLevel.NONE + return if (fusedApp.origin == Origin.GPLAY) { + FilterLevel.UNKNOWN + } else { + FilterLevel.NONE + } } if (!fusedApp.isFree && fusedApp.price.isBlank()) { @@ -750,7 +750,7 @@ class FusedApiImpl @Inject constructor( gplayRepository.getDownloadInfo( fusedApp.package_name, fusedApp.latest_version_code, - fusedApp.offer_type, + fusedApp.offer_type ) } catch (e: Exception) { return FilterLevel.UI @@ -781,7 +781,6 @@ class FusedApiImpl @Inject constructor( authData: AuthData, origin: Origin ): Pair { - var response: FusedApp? = null val status = runCodeWithTimeout({ @@ -814,7 +813,7 @@ class FusedApiImpl @Inject constructor( */ private suspend fun handleAllSourcesCategories( categoriesList: MutableList, - type: CategoryType, + type: CategoryType ): Pair { var apiStatus = ResultStatus.OK var errorApplicationCategory = "" @@ -835,7 +834,7 @@ class FusedApiImpl @Inject constructor( if (preferenceManagerModule.isGplaySelected()) { val gplayCategoryResult = fetchGplayCategories( - type, + type ) categoriesList.addAll(gplayCategoryResult.second) apiStatus = gplayCategoryResult.first @@ -846,7 +845,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchGplayCategories( - type: CategoryType, + type: CategoryType ): Triple, String> { var errorApplicationCategory = "" var apiStatus = ResultStatus.OK @@ -869,7 +868,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchPWACategories( - type: CategoryType, + type: CategoryType ): Triple, String> { var errorApplicationCategory = "" var apiStatus: ResultStatus = ResultStatus.OK @@ -878,7 +877,9 @@ class FusedApiImpl @Inject constructor( getPWAsCategories()?.let { fusedCategoriesList.addAll( getFusedCategoryBasedOnCategoryType( - it, type, AppTag.PWA(context.getString(R.string.pwa)) + it, + type, + AppTag.PWA(context.getString(R.string.pwa)) ) ) } @@ -893,7 +894,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchOpenSourceCategories( - type: CategoryType, + type: CategoryType ): Triple, String> { var errorApplicationCategory = "" var apiStatus: ResultStatus = ResultStatus.OK @@ -930,7 +931,7 @@ class FusedApiImpl @Inject constructor( private suspend fun runCodeWithTimeout( block: suspend () -> Unit, timeoutBlock: (() -> Unit)? = null, - exceptionBlock: ((e: Exception) -> Unit)? = null, + exceptionBlock: ((e: Exception) -> Unit)? = null ): ResultStatus { return try { withTimeout(timeoutDurationInMillis) { @@ -950,7 +951,7 @@ class FusedApiImpl @Inject constructor( } private fun updateCategoryDrawable( - category: FusedCategory, + category: FusedCategory ) { category.drawable = getCategoryIconResource(getCategoryIconName(category)) @@ -959,7 +960,11 @@ class FusedApiImpl @Inject constructor( private fun getCategoryIconName(category: FusedCategory): String { var categoryTitle = if (category.tag.getOperationalTag() .contentEquals(AppTag.GPlay().getOperationalTag()) - ) category.id else category.title + ) { + category.id + } else { + category.title + } if (categoryTitle.contains(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION)) { categoryTitle = categoryTitle.replace(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION, "and") @@ -1037,13 +1042,13 @@ class FusedApiImpl @Inject constructor( private suspend fun getOpenSourceAppsResponse(category: String): Search? { return cleanApkAppsRepository.getAppsByCategory( - category, + category ).body() } private suspend fun getPWAAppsResponse(category: String): Search? { return cleanApkPWARepository.getAppsByCategory( - category, + category ).body() } @@ -1053,7 +1058,7 @@ class FusedApiImpl @Inject constructor( id = id.lowercase(), title = this.title, browseUrl = this.browseUrl, - imageUrl = this.imageUrl, + imageUrl = this.imageUrl ) } @@ -1063,7 +1068,7 @@ class FusedApiImpl @Inject constructor( private suspend fun getCleanAPKSearchResults( keyword: String, - source: String = CleanApkRetrofit.APP_SOURCE_FOSS, + source: String = CleanApkRetrofit.APP_SOURCE_FOSS ): List { val list = mutableListOf() val response = @@ -1278,7 +1283,9 @@ class FusedApiImpl @Inject constructor( this.labeledRating.run { if (isNotEmpty()) { this.replace(",", ".").toDoubleOrNull() ?: -1.0 - } else -1.0 + } else { + -1.0 + } } ), offer_type = this.offerType, @@ -1288,7 +1295,7 @@ class FusedApiImpl @Inject constructor( appSize = Formatter.formatFileSize(context, this.size), isFree = this.isFree, price = this.price, - restriction = this.restriction, + restriction = this.restriction ) app.updateStatus() return app @@ -1320,9 +1327,13 @@ class FusedApiImpl @Inject constructor( private fun FusedApp.updateSource() { this.apply { - source = if (origin == Origin.CLEANAPK && is_pwa) context.getString(R.string.pwa) - else if (origin == Origin.CLEANAPK) context.getString(R.string.open_source) - else "" + source = if (origin == Origin.CLEANAPK && is_pwa) { + context.getString(R.string.pwa) + } else if (origin == Origin.CLEANAPK) { + context.getString(R.string.open_source) + } else { + "" + } } } @@ -1356,7 +1367,7 @@ class FusedApiImpl @Inject constructor( private fun areFusedAppsUpdated( oldFusedHome: FusedHome, - newFusedHome: FusedHome, + newFusedHome: FusedHome ): Boolean { val fusedAppDiffUtil = HomeChildFusedAppDiffUtil() if (oldFusedHome.list.size != newFusedHome.list.size) { diff --git a/app/src/main/java/foundation/e/apps/data/fusedDownload/FileManager.kt b/app/src/main/java/foundation/e/apps/data/fusedDownload/FileManager.kt index 32a9fab55..93b35ef14 100644 --- a/app/src/main/java/foundation/e/apps/data/fusedDownload/FileManager.kt +++ b/app/src/main/java/foundation/e/apps/data/fusedDownload/FileManager.kt @@ -14,7 +14,6 @@ object FileManager { var inputStream: InputStream? = null var outputStream: OutputStream? = null try { - // create output directory if it doesn't exist val dir = File(outputPath) if (!dir.exists()) { diff --git a/app/src/main/java/foundation/e/apps/data/gplay/GplayStoreRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/data/gplay/GplayStoreRepositoryImpl.kt index 19461db72..894d42ff9 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/GplayStoreRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/GplayStoreRepositoryImpl.kt @@ -70,7 +70,7 @@ class GplayStoreRepositoryImpl @Inject constructor( context.getString(R.string.topgrossing_apps) to mapOf(Chart.TOP_GROSSING to TopChartsHelper.Type.APPLICATION), context.getString(R.string.topgrossing_games) to mapOf(Chart.TOP_GROSSING to TopChartsHelper.Type.GAME), context.getString(R.string.movers_shakers_apps) to mapOf(Chart.MOVERS_SHAKERS to TopChartsHelper.Type.APPLICATION), - context.getString(R.string.movers_shakers_games) to mapOf(Chart.MOVERS_SHAKERS to TopChartsHelper.Type.GAME), + context.getString(R.string.movers_shakers_games) to mapOf(Chart.MOVERS_SHAKERS to TopChartsHelper.Type.GAME) ) override suspend fun getSearchResult( diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/AC2DMTask.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/AC2DMTask.kt index 448c45904..88d4c5494 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/AC2DMTask.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/AC2DMTask.kt @@ -34,8 +34,9 @@ class AC2DMTask @Inject constructor( } fun getAC2DMResponse(email: String?, oAuthToken: String?): PlayResponse { - if (email == null || oAuthToken == null) + if (email == null || oAuthToken == null) { return PlayResponse() + } val params: MutableMap = hashMapOf() params["lang"] = Locale.getDefault().toString().replace("_", "-") diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt index 9a30a62a9..8174b4e4f 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt @@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject class GPlayHttpClient @Inject constructor( - cache: Cache, + cache: Cache ) : IHttpClient { private val POST = "POST" diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeDeviceInfoProviderModule.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeDeviceInfoProviderModule.kt index 53a70ff93..fd5760da7 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeDeviceInfoProviderModule.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeDeviceInfoProviderModule.kt @@ -40,7 +40,7 @@ object NativeDeviceInfoProviderModule { @Singleton @Provides fun provideNativeDeviceProperties( - @ApplicationContext context: Context, + @ApplicationContext context: Context ): Properties { val properties = Properties().apply { // Build Props @@ -48,10 +48,11 @@ object NativeDeviceInfoProviderModule { setProperty("Build.HARDWARE", Build.HARDWARE) setProperty( "Build.RADIO", - if (Build.getRadioVersion() != null) + if (Build.getRadioVersion() != null) { Build.getRadioVersion() - else + } else { "unknown" + } ) setProperty("Build.FINGERPRINT", Build.FINGERPRINT) setProperty("Build.BRAND", Build.BRAND) diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeGsfVersionProvider.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeGsfVersionProvider.kt index 40ab7b2fc..9dbb09d63 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeGsfVersionProvider.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/NativeGsfVersionProvider.kt @@ -46,24 +46,27 @@ class NativeGsfVersionProvider(context: Context) { } fun getGsfVersionCode(defaultIfNotFound: Boolean): Int { - return if (defaultIfNotFound && gsfVersionCode < GOOGLE_SERVICES_VERSION_CODE) + return if (defaultIfNotFound && gsfVersionCode < GOOGLE_SERVICES_VERSION_CODE) { GOOGLE_SERVICES_VERSION_CODE - else + } else { gsfVersionCode + } } fun getVendingVersionCode(defaultIfNotFound: Boolean): Int { - return if (defaultIfNotFound && vendingVersionCode < GOOGLE_VENDING_VERSION_CODE) + return if (defaultIfNotFound && vendingVersionCode < GOOGLE_VENDING_VERSION_CODE) { GOOGLE_VENDING_VERSION_CODE - else + } else { vendingVersionCode + } } fun getVendingVersionString(defaultIfNotFound: Boolean): String { - return if (defaultIfNotFound && vendingVersionCode < GOOGLE_VENDING_VERSION_CODE) + return if (defaultIfNotFound && vendingVersionCode < GOOGLE_VENDING_VERSION_CODE) { GOOGLE_VENDING_VERSION_STRING - else + } else { vendingVersionString + } } companion object { diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/TimeoutEvaluation.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/TimeoutEvaluation.kt index 274a07474..cf29f6fa2 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/TimeoutEvaluation.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/TimeoutEvaluation.kt @@ -49,12 +49,10 @@ suspend fun runFlowWithTimeout( block: suspend () -> Flow, moreItemsToLoad: suspend (item: T) -> Boolean, timeoutBlock: () -> T, - exceptionBlock: ((e: Exception) -> T?)? = null, + exceptionBlock: ((e: Exception) -> T?)? = null ): LiveData> { - return liveData { withContext(Dispatchers.Main) { - val timer = Timer(this) { emit(ResultSupreme.Timeout(timeoutBlock())) @@ -91,7 +89,7 @@ suspend fun runFlowWithTimeout( private class Timer( private val scope: CoroutineScope, - private val onTimeout: suspend () -> Unit, + private val onTimeout: suspend () -> Unit ) : CountDownTimer(Constants.timeoutDurationInMillis, 1000) { override fun onTick(millisUntilFinished: Long) {} diff --git a/app/src/main/java/foundation/e/apps/data/login/AuthObject.kt b/app/src/main/java/foundation/e/apps/data/login/AuthObject.kt index e63f051de..c5f656075 100644 --- a/app/src/main/java/foundation/e/apps/data/login/AuthObject.kt +++ b/app/src/main/java/foundation/e/apps/data/login/AuthObject.kt @@ -51,12 +51,12 @@ sealed class AuthObject { exception = GPlayValidationException( message, this.user, - 401, + 401 ) ).apply { otherPayload = this@GPlayAuth.result.otherPayload }, - this.user, + this.user ) } } @@ -68,10 +68,10 @@ sealed class AuthObject { message = "Unauthorized", exception = CleanApkException( isTimeout = false, - message = "Unauthorized", + message = "Unauthorized" ) ), - this.user, + this.user ) } } diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt b/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt index 6f3646153..3d9fb54c0 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt @@ -30,7 +30,7 @@ import javax.inject.Singleton */ @Singleton class LoginCommon @Inject constructor( - private val loginDataStore: LoginDataStore, + private val loginDataStore: LoginDataStore ) { suspend fun saveUserType(user: User) { loginDataStore.saveUserType(user) diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt b/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt index 468cbc667..270bd17a4 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt @@ -120,8 +120,11 @@ class LoginDataStore @Inject constructor( return runBlocking { userType.first().run { val userStrings = User.values().map { it.name } - if (this !in userStrings) User.UNAVAILABLE - else User.valueOf(this) + if (this !in userStrings) { + User.UNAVAILABLE + } else { + User.valueOf(this) + } } } } diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginSourceCleanApk.kt b/app/src/main/java/foundation/e/apps/data/login/LoginSourceCleanApk.kt index a5f49569b..69c846e14 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginSourceCleanApk.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginSourceCleanApk.kt @@ -28,7 +28,7 @@ import javax.inject.Singleton */ @Singleton class LoginSourceCleanApk @Inject constructor( - val loginDataStore: LoginDataStore, + val loginDataStore: LoginDataStore ) : LoginSourceInterface { private val user: User @@ -47,7 +47,7 @@ class LoginSourceCleanApk @Inject constructor( override suspend fun getAuthObject(): AuthObject.CleanApk { return AuthObject.CleanApk( ResultSupreme.Success(Unit), - user, + user ) } diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt b/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt index d5cf5e45b..d86e3b87a 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt @@ -43,7 +43,7 @@ import javax.inject.Singleton class LoginSourceGPlay @Inject constructor( @ApplicationContext private val context: Context, private val gson: Gson, - private val loginDataStore: LoginDataStore, + private val loginDataStore: LoginDataStore ) : LoginSourceInterface, AuthDataValidator { @Inject @@ -81,8 +81,11 @@ class LoginSourceGPlay @Inject constructor( savedAuth ?: run { // if no saved data, then generate new auth data. generateAuthData().let { - if (it.isSuccess()) it.data!! - else return AuthObject.GPlayAuth(it, user) + if (it.isSuccess()) { + it.data!! + } else { + return AuthObject.GPlayAuth(it, user) + } } } ) @@ -112,12 +115,15 @@ class LoginSourceGPlay @Inject constructor( */ private fun getSavedAuthData(): AuthData? { val authJson = loginDataStore.getAuthData() - return if (authJson.isBlank()) null - else try { - gson.fromJson(authJson, AuthData::class.java) - } catch (e: Exception) { - e.printStackTrace() + return if (authJson.isBlank()) { null + } else { + try { + gson.fromJson(authJson, AuthData::class.java) + } catch (e: Exception) { + e.printStackTrace() + null + } } } @@ -156,8 +162,11 @@ class LoginSourceGPlay @Inject constructor( */ private suspend fun getAuthData(): ResultSupreme { return loginApiRepository.fetchAuthData("", "", locale).run { - if (isSuccess()) ResultSupreme.Success(formatAuthData(this.data!!)) - else this + if (isSuccess()) { + ResultSupreme.Success(formatAuthData(this.data!!)) + } else { + this + } } } @@ -167,9 +176,8 @@ class LoginSourceGPlay @Inject constructor( private suspend fun getAuthData( email: String, oauthToken: String, - aasToken: String, + aasToken: String ): ResultSupreme { - /* * If aasToken is not blank, means it was stored successfully from a previous Google login. * Use it to fetch auth data. @@ -207,8 +215,11 @@ class LoginSourceGPlay @Inject constructor( */ loginDataStore.saveAasToken(aasTokenFetched) return loginApiRepository.fetchAuthData(email, aasTokenFetched, locale).run { - if (isSuccess()) ResultSupreme.Success(formatAuthData(this.data!!)) - else this + if (isSuccess()) { + ResultSupreme.Success(formatAuthData(this.data!!)) + } else { + this + } } } diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginSourceRepository.kt b/app/src/main/java/foundation/e/apps/data/login/LoginSourceRepository.kt index 6cbf99998..7f2b9e0b6 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginSourceRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginSourceRepository.kt @@ -27,12 +27,11 @@ import javax.inject.Singleton @Singleton class LoginSourceRepository @Inject constructor( private val loginCommon: LoginCommon, - private val sources: List, + private val sources: List ) { var gplayAuth: AuthData? = null suspend fun getAuthObjects(clearAuthTypes: List = listOf()): List { - val authObjectsLocal = ArrayList() for (source in sources) { diff --git a/app/src/main/java/foundation/e/apps/data/login/api/AnonymousLoginApi.kt b/app/src/main/java/foundation/e/apps/data/login/api/AnonymousLoginApi.kt index edd0d15a2..15bbc9f7f 100644 --- a/app/src/main/java/foundation/e/apps/data/login/api/AnonymousLoginApi.kt +++ b/app/src/main/java/foundation/e/apps/data/login/api/AnonymousLoginApi.kt @@ -29,7 +29,7 @@ import java.util.Properties class AnonymousLoginApi( private val gPlayHttpClient: GPlayHttpClient, private val nativeDeviceProperty: Properties, - private val gson: Gson, + private val gson: Gson ) : GPlayLoginInterface { private val tokenUrl: String = "https://eu.gtoken.ecloud.global" @@ -50,8 +50,11 @@ class AnonymousLoginApi( "Network code: ${response.code}\n" + "Success: ${response.isSuccessful}" + response.errorString.run { - if (isNotBlank()) "\nError message: $this" - else "" + if (isNotBlank()) { + "\nError message: $this" + } else { + "" + } } ) } else { diff --git a/app/src/main/java/foundation/e/apps/data/login/api/GPlayApiFactory.kt b/app/src/main/java/foundation/e/apps/data/login/api/GPlayApiFactory.kt index 4b5a71052..4334401fa 100644 --- a/app/src/main/java/foundation/e/apps/data/login/api/GPlayApiFactory.kt +++ b/app/src/main/java/foundation/e/apps/data/login/api/GPlayApiFactory.kt @@ -30,7 +30,7 @@ class GPlayApiFactory @Inject constructor( private val gPlayHttpClient: GPlayHttpClient, private val nativeDeviceProperty: Properties, private val aC2DMTask: AC2DMTask, - private val gson: Gson, + private val gson: Gson ) { fun getGPlayApi(user: User): GPlayLoginInterface { diff --git a/app/src/main/java/foundation/e/apps/data/login/api/GoogleLoginApi.kt b/app/src/main/java/foundation/e/apps/data/login/api/GoogleLoginApi.kt index 4331d473a..d8cd69d87 100644 --- a/app/src/main/java/foundation/e/apps/data/login/api/GoogleLoginApi.kt +++ b/app/src/main/java/foundation/e/apps/data/login/api/GoogleLoginApi.kt @@ -30,7 +30,7 @@ import java.util.Properties class GoogleLoginApi( private val gPlayHttpClient: GPlayHttpClient, private val nativeDeviceProperty: Properties, - private val aC2DMTask: AC2DMTask, + private val aC2DMTask: AC2DMTask ) : GPlayLoginInterface { /** diff --git a/app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt b/app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt index 5d97b6487..098f058b8 100644 --- a/app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/login/api/LoginApiRepository.kt @@ -39,7 +39,7 @@ import java.util.Locale */ class LoginApiRepository constructor( private val gPlayLoginInterface: GPlayLoginInterface, - private val user: User, + private val user: User ) { /** @@ -145,7 +145,7 @@ class LoginApiRepository constructor( private suspend fun runCodeWithTimeout( block: suspend () -> T, timeoutBlock: (() -> T?)? = null, - exceptionBlock: ((e: Exception) -> T?)? = null, + exceptionBlock: ((e: Exception) -> T?)? = null ): ResultSupreme { return try { withTimeout(timeoutDurationInMillis) { diff --git a/app/src/main/java/foundation/e/apps/data/login/exceptions/CleanApkException.kt b/app/src/main/java/foundation/e/apps/data/login/exceptions/CleanApkException.kt index 293819914..71cbd5b4d 100644 --- a/app/src/main/java/foundation/e/apps/data/login/exceptions/CleanApkException.kt +++ b/app/src/main/java/foundation/e/apps/data/login/exceptions/CleanApkException.kt @@ -22,5 +22,5 @@ package foundation.e.apps.data.login.exceptions */ class CleanApkException( val isTimeout: Boolean, - message: String? = null, + message: String? = null ) : LoginException(message) diff --git a/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayException.kt b/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayException.kt index 345208778..16a84a836 100644 --- a/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayException.kt +++ b/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayException.kt @@ -22,5 +22,5 @@ package foundation.e.apps.data.login.exceptions */ open class GPlayException( val isTimeout: Boolean, - message: String? = null, + message: String? = null ) : LoginException(message) diff --git a/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayLoginException.kt b/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayLoginException.kt index d6a64a7bb..6e3a403bb 100644 --- a/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayLoginException.kt +++ b/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayLoginException.kt @@ -27,5 +27,5 @@ import foundation.e.apps.data.enums.User open class GPlayLoginException( isTimeout: Boolean, message: String? = null, - val user: User, + val user: User ) : GPlayException(isTimeout, message) diff --git a/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayValidationException.kt b/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayValidationException.kt index 5b4322a4c..94cb6affa 100644 --- a/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayValidationException.kt +++ b/app/src/main/java/foundation/e/apps/data/login/exceptions/GPlayValidationException.kt @@ -28,5 +28,5 @@ import foundation.e.apps.data.enums.User class GPlayValidationException( message: String, user: User, - val networkCode: Int, + val networkCode: Int ) : GPlayLoginException(false, message, user) diff --git a/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt b/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt index a8482aaeb..bb3e427e4 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt @@ -137,8 +137,11 @@ class DataStoreModule @Inject constructor( return runBlocking { userType.first().run { val userStrings = User.values().map { it.name } - if (this !in userStrings) User.UNAVAILABLE - else User.valueOf(this) + if (this !in userStrings) { + User.UNAVAILABLE + } else { + User.valueOf(this) + } } } } diff --git a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt index 764cab30a..3a9bb6580 100644 --- a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt @@ -45,7 +45,7 @@ class UpdatesManagerImpl @Inject constructor( private val fusedAPIRepository: FusedAPIRepository, private val faultyAppRepository: FaultyAppRepository, private val preferenceManagerModule: PreferenceManagerModule, - private val fdroidRepository: FdroidRepository, + private val fdroidRepository: FdroidRepository ) { companion object { @@ -94,7 +94,6 @@ class UpdatesManagerImpl @Inject constructor( if (getApplicationCategoryPreference().contains(APP_TYPE_ANY) && gPlayInstalledApps.isNotEmpty() ) { - val gplayStatus = getUpdatesFromApi({ fusedAPIRepository.getApplicationDetails( gPlayInstalledApps, @@ -153,7 +152,7 @@ class UpdatesManagerImpl @Inject constructor( pkgManagerModule.getInstallerName(it.packageName) in listOf( context.packageName, PACKAGE_NAME_F_DROID, - PACKAGE_NAME_F_DROID_PRIVILEGED, + PACKAGE_NAME_F_DROID_PRIVILEGED ) }.map { it.packageName } } @@ -167,7 +166,7 @@ class UpdatesManagerImpl @Inject constructor( private fun getGPlayInstalledApps(): List { return userApplications.filter { pkgManagerModule.getInstallerName(it.packageName) in listOf( - PACKAGE_NAME_ANDROID_VENDING, + PACKAGE_NAME_ANDROID_VENDING ) }.map { it.packageName } } @@ -198,7 +197,7 @@ class UpdatesManagerImpl @Inject constructor( */ private suspend fun getUpdatesFromApi( apiFunction: suspend () -> Pair, ResultStatus>, - updateAccumulationList: MutableList, + updateAccumulationList: MutableList ): ResultStatus { val apiResult = apiFunction() val updatableApps = apiResult.first.filter { @@ -262,7 +261,7 @@ class UpdatesManagerImpl @Inject constructor( * in [installedPackageNames], it will not be present in the list returned by this method. */ private suspend fun findPackagesMatchingFDroidSignatures( - installedPackageNames: List, + installedPackageNames: List ): List { val fDroidAppsAndSignatures = getFDroidAppsAndSignatures(installedPackageNames) diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt index 804fdf48b..39c12e856 100644 --- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt +++ b/app/src/main/java/foundation/e/apps/di/LoginModule.kt @@ -28,7 +28,6 @@ import foundation.e.apps.domain.common.repository.CommonRepositoryImpl import foundation.e.apps.domain.login.repository.LoginRepositoryImpl import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.domain.main.usecase.MainActivityUserCase -import foundation.e.apps.domain.settings.repository.SettingsRepositoryImpl import foundation.e.apps.domain.settings.usecase.SettingsUseCase import foundation.e.apps.domain.updates.usecase.UpdatesUseCase @@ -39,7 +38,7 @@ object LoginModule { @Provides fun providesLoginSources( gPlay: LoginSourceGPlay, - cleanApk: LoginSourceCleanApk, + cleanApk: LoginSourceCleanApk ): List { return listOf(gPlay, cleanApk) } diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt index 12e67e495..7406d808c 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt @@ -16,17 +16,12 @@ * along with this program. If not, see . */ - package foundation.e.apps.domain.common.repository import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.data.enums.User - - interface CommonRepository { - fun currentUser(): User - fun cacheAuthData(): AuthData fun resetCachedData() -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt index ec0beb161..f003b6368 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt @@ -27,7 +27,7 @@ import foundation.e.apps.utils.toAuthData import javax.inject.Inject class CommonRepositoryImpl @Inject constructor( - @ApplicationContext val applicationContext: Context, + @ApplicationContext val applicationContext: Context ) : CommonRepository { override fun currentUser(): User { diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt index 06bc7a5bc..c608a448e 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -21,7 +21,5 @@ package foundation.e.apps.domain.login.repository import com.aurora.gplayapi.data.models.AuthData interface LoginRepository { - suspend fun anonymousUser(): AuthData - } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index 219af6e29..07db25920 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -34,7 +34,7 @@ import javax.inject.Inject class LoginRepositoryImpl @Inject constructor( @ApplicationContext val applicationContext: Context, private val properties: Properties, - private val anonymousUser: AnonymousUser, + private val anonymousUser: AnonymousUser ) : LoginRepository { private val userAgent: String by lazy { SystemInfoProvider.getAppBuildInfo() } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index 727d9548c..f2ac8e3ef 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -55,18 +55,17 @@ class UserLoginUseCase @Inject constructor( commonRepository.resetCachedData() } - fun currentUser() = commonRepository.currentUser() - + fun currentUser() = commonRepository.currentUser() fun performAnonymousUserAuthentication(): Flow> = flow { - anonymousUser().onEach {anonymousAuth -> + anonymousUser().onEach { anonymousAuth -> // TODO -> If we are not using auth data then - when(anonymousAuth ) { + when (anonymousAuth) { is Resource.Error -> emit(Resource.Error(anonymousAuth.message ?: "An unexpected error occured")) is Resource.Loading -> emit(Resource.Loading()) is Resource.Success -> { retrieveCachedAuthData().onEach { - when(it) { + when (it) { is Resource.Error -> { emit(Resource.Error(anonymousAuth.message ?: "An unexpected error occured")) } diff --git a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt index cd9b60502..80ff0e722 100644 --- a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt @@ -6,7 +6,7 @@ import javax.inject.Inject class MainActivityUserCase @Inject constructor( private val commonRepository: CommonRepository ) { - fun currentUser() = commonRepository.currentUser() + fun currentUser() = commonRepository.currentUser() fun currentAuthData() = commonRepository.cacheAuthData() -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt index 326a68c14..0c0af8458 100644 --- a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt @@ -18,8 +18,4 @@ package foundation.e.apps.domain.settings.repository -import foundation.e.apps.data.enums.User - -interface SettingsRepository { - -} +interface SettingsRepository diff --git a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt index 807d95a03..7ced76cb5 100644 --- a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt @@ -19,13 +19,9 @@ package foundation.e.apps.domain.settings.repository import android.content.Context -import app.lounge.storage.cache.configurations import dagger.hilt.android.qualifiers.ApplicationContext -import foundation.e.apps.data.enums.User import javax.inject.Inject class SettingsRepositoryImpl @Inject constructor( @ApplicationContext val applicationContext: Context -) : SettingsRepository { - -} \ No newline at end of file +) : SettingsRepository diff --git a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt index 4db4a9dde..b13f51523 100644 --- a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt @@ -20,16 +20,15 @@ package foundation.e.apps.domain.settings.usecase import foundation.e.apps.data.enums.User import foundation.e.apps.domain.common.repository.CommonRepository -import foundation.e.apps.domain.settings.repository.SettingsRepository import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import javax.inject.Inject class SettingsUseCase @Inject constructor( - private val commonRepository: CommonRepository, + private val commonRepository: CommonRepository ) { - fun currentUser() : Flow> = flow { + fun currentUser(): Flow> = flow { kotlin.runCatching { val currentUser = commonRepository.currentUser() emit(Resource.Success(currentUser)) diff --git a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt index e8b12bfaf..85591256f 100644 --- a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt @@ -6,7 +6,7 @@ import javax.inject.Inject class UpdatesUseCase @Inject constructor( private val commonRepository: CommonRepository ) { - fun currentUser() = commonRepository.currentUser() + fun currentUser() = commonRepository.currentUser() fun currentAuthData() = commonRepository.cacheAuthData() -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt b/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt index 56e27c74f..857f81d22 100644 --- a/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt @@ -28,7 +28,7 @@ import javax.inject.Singleton @OpenForTesting class PWAManagerModule @Inject constructor( @ApplicationContext private val context: Context, - private val fusedDownloadRepository: FusedDownloadRepository, + private val fusedDownloadRepository: FusedDownloadRepository ) { companion object { @@ -60,7 +60,10 @@ class PWAManagerModule @Inject constructor( fun getPwaStatus(fusedApp: FusedApp): Status { context.contentResolver.query( Uri.parse(PWA_PLAYER), - null, null, null, null + null, + null, + null, + null )?.let { cursor -> if (cursor.count > 0) { if (cursor.moveToFirst()) { diff --git a/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt b/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt index 91e0d10d1..99601a753 100644 --- a/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt @@ -165,7 +165,6 @@ class PkgManagerModule @Inject constructor( */ @OptIn(DelicateCoroutinesApi::class) fun installApplication(list: List, packageName: String) { - val sessionId = createInstallSession(packageName, SessionParams.MODE_FULL_INSTALL) val session = packageManager.packageInstaller.openSession(sessionId) @@ -178,9 +177,11 @@ class PkgManagerModule @Inject constructor( val callBackIntent = Intent(context, InstallerService::class.java) callBackIntent.putExtra(PACKAGE_NAME, packageName) - val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE else + val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + } else { PendingIntent.FLAG_UPDATE_CURRENT + } val servicePendingIntent = PendingIntent.getService( context, sessionId, @@ -205,7 +206,6 @@ class PkgManagerModule @Inject constructor( } private fun createInstallSession(packageName: String, mode: Int): Int { - val packageInstaller = packageManager.packageInstaller val params = SessionParams(mode).apply { setAppPackageName(packageName) @@ -222,7 +222,6 @@ class PkgManagerModule @Inject constructor( } private fun syncFile(session: Session, file: File) { - val inputStream = file.inputStream() val outputStream = session.openWrite(file.nameWithoutExtension, 0, -1) inputStream.copyTo(outputStream) diff --git a/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallBinder.kt b/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallBinder.kt index 60c33aace..de4ee6ae5 100644 --- a/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallBinder.kt +++ b/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallBinder.kt @@ -72,7 +72,9 @@ class SplitInstallBinder( } downloadManager.downloadFileInExternalStorage( - url, packageName, "$packageName.split.$moduleName.apk" + url, + packageName, + "$packageName.split.$moduleName.apk" ) { success, path -> if (success) { Timber.i("Split module has been downloaded: $path") diff --git a/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallService.kt b/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallService.kt index ac561e3e5..fff0a1a0d 100644 --- a/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallService.kt +++ b/app/src/main/java/foundation/e/apps/install/splitinstall/SplitInstallService.kt @@ -43,8 +43,11 @@ class SplitInstallService : LifecycleService() { } @Inject lateinit var dataStoreModule: DataStoreModule + @Inject lateinit var fusedAPIRepository: FusedAPIRepository + @Inject lateinit var downloadManager: DownloadManager + @Inject lateinit var gson: Gson private lateinit var binder: SplitInstallBinder private var authData: AuthData? = null diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesNotifier.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesNotifier.kt index 988debf40..3d0565358 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesNotifier.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesNotifier.kt @@ -150,7 +150,7 @@ object UpdatesNotifier { fun showNotification( context: Context, title: String, - message: String, + message: String ) { with(NotificationManagerCompat.from(context)) { createNotificationChannel(context) diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt index fa41365ed..b3b794c88 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorkManager.kt @@ -75,7 +75,8 @@ object UpdatesWorkManager { Timber.i("UpdatesWorker interval: $interval hours") WorkManager.getInstance(context).enqueueUniquePeriodicWork( UPDATES_WORK_NAME, - existingPeriodicWorkPolicy, buildPeriodicWorkRequest(interval) + existingPeriodicWorkPolicy, + buildPeriodicWorkRequest(interval) ) Timber.i("UpdatesWorker started") } diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt index a145f377a..1798eda1b 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt @@ -18,15 +18,10 @@ import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.User -import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.data.fusedDownload.FusedManagerRepository -import foundation.e.apps.data.login.LoginSourceRepository -import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.data.updates.UpdatesManagerRepository import foundation.e.apps.domain.updates.usecase.UpdatesUseCase import foundation.e.apps.install.workmanager.AppInstallProcessor -import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.Dispatchers @@ -56,7 +51,7 @@ class UpdatesWorker @AssistedInject constructor( private var isAutoUpdate = true // indicates it is auto update or user initiated update private var retryCount = 0 - private val authData : AuthData + private val authData: AuthData get() = updatesUseCase.currentAuthData() ?: AuthData("", "") override suspend fun doWork(): Result { @@ -154,7 +149,7 @@ class UpdatesWorker @AssistedInject constructor( * in appsNeededToUpdate list. Hence it is safe to proceed with * blank AuthData. */ - authData ?: AuthData("", ""), + authData ?: AuthData("", "") ) } } diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 0c6ac89ae..050b9521f 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -198,7 +198,6 @@ class AppInstallProcessor @Inject constructor( Timber.i(">>> dowork started for Fused download name " + fusedDownload?.name + " " + fusedDownloadId) fusedDownload?.let { - this.isItUpdateWork = isItUpdateWork && fusedManagerRepository.isFusedDownloadInstalled(fusedDownload) @@ -281,9 +280,12 @@ class AppInstallProcessor @Inject constructor( .toString() UpdatesNotifier.showNotification( - context, context.getString(R.string.update), + context, + context.getString(R.string.update), context.getString( - R.string.message_last_update_triggered, numberOfUpdatedApps, date + R.string.message_last_update_triggered, + numberOfUpdatedApps, + date ) ) } diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt index 089d093a1..d7ca3bd9b 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt @@ -26,5 +26,5 @@ data class LoginState( val isLoggedIn: Boolean = false, val error: String = "", val authData: AuthData? = null, - val user: User = User.UNAVAILABLE, + val user: User = User.UNAVAILABLE ) diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 9c0325115..e7f4bb59f 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -148,7 +148,7 @@ class LoginViewModel @Inject constructor( fun authenticateAnonymousUser() { viewModelScope.launch(Dispatchers.IO) { - userLoginUseCase.performAnonymousUserAuthentication().onEach {result -> + userLoginUseCase.performAnonymousUserAuthentication().onEach { result -> withContext(Dispatchers.Main) { when (result) { is Resource.Success -> { @@ -184,7 +184,7 @@ class LoginViewModel @Inject constructor( private suspend fun updateSavedAuthData() { userLoginUseCase.retrieveCachedAuthData().onEach { - when(it) { + when (it) { is Resource.Error -> { val error = it.message.let { message -> when (message) { @@ -194,7 +194,7 @@ class LoginViewModel @Inject constructor( } _loginState.value = LoginState(error = error) } - is Resource.Loading -> _loginState.value = LoginState(isLoading = true) + is Resource.Loading -> _loginState.value = LoginState(isLoading = true) is Resource.Success -> { // TODO it.data?.let { it1 -> updateAuthObjectForAnonymousUser(it1) } @@ -216,5 +216,4 @@ class LoginViewModel @Inject constructor( ) ) } - } diff --git a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt index 6c1301fb6..1d7de54cd 100644 --- a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt @@ -62,4 +62,4 @@ class SettingsViewModel @Inject constructor( fun logout() { settingsUseCase.logoutUser().launchIn(viewModelScope) } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/ui/AppInfoFetchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/AppInfoFetchViewModel.kt index 3cf70583f..942535361 100644 --- a/app/src/main/java/foundation/e/apps/ui/AppInfoFetchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/AppInfoFetchViewModel.kt @@ -40,7 +40,7 @@ class AppInfoFetchViewModel @Inject constructor( gplayRepository.getDownloadInfo( app.package_name, app.latest_version_code, - app.offer_type, + app.offer_type ) app.isPurchased = true emit(true) diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt index e96fe7f8d..922184334 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt @@ -241,7 +241,7 @@ class MainActivityViewModel @Inject constructor( .build() private fun getNetworkCallback( - callbackFlowScope: ProducerScope, + callbackFlowScope: ProducerScope ): ConnectivityManager.NetworkCallback { return object : ConnectivityManager.NetworkCallback() { @@ -267,7 +267,6 @@ class MainActivityViewModel @Inject constructor( // protected to avoid SyntheticAccessor protected fun ProducerScope.sendInternetStatus(connectivityManager: ConnectivityManager) { - val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) diff --git a/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt b/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt index fb7521da9..16e103aef 100644 --- a/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt @@ -17,7 +17,7 @@ import javax.inject.Inject @HiltViewModel class PrivacyInfoViewModel @Inject constructor( private val privacyInfoRepository: IAppPrivacyInfoRepository, - private val privacyScoreRepository: PrivacyScoreRepository, + private val privacyScoreRepository: PrivacyScoreRepository ) : ViewModel() { private val singularAppPrivacyInfoLiveData: MutableLiveData> = @@ -60,11 +60,13 @@ class PrivacyInfoViewModel @Inject constructor( } private fun handleAppPrivacyInfoResult( - appPrivacyPrivacyInfoResult: Result, + appPrivacyPrivacyInfoResult: Result ): Result { return if (!appPrivacyPrivacyInfoResult.isSuccess()) { Result.error("Tracker not found!") - } else appPrivacyPrivacyInfoResult + } else { + appPrivacyPrivacyInfoResult + } } fun getTrackerListText(fusedApp: FusedApp?): String { diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index e41b75c20..644cda421 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -28,7 +28,6 @@ import android.text.format.Formatter import android.view.View import android.widget.ImageView import android.widget.RelativeLayout -import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat @@ -57,8 +56,6 @@ import foundation.e.apps.data.enums.Status import foundation.e.apps.data.enums.User import foundation.e.apps.data.enums.isInitialized import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentApplicationBinding import foundation.e.apps.di.CommonUtilsModule.LIST_OF_NULL import foundation.e.apps.install.download.data.DownloadProgress @@ -173,7 +170,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } private fun updateUi( - resultPair: Pair, + resultPair: Pair ) { if (resultPair.second != ResultStatus.OK) { return @@ -300,7 +297,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } private fun updateAppInformation( - it: FusedApp, + it: FusedApp ) { binding.infoInclude.apply { appUpdatedOn.text = getString( @@ -328,11 +325,15 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { applicationViewModel.handleRatingFormat(it.ratings.usageQualityScore) appRating.text = getString( - R.string.rating_out_of, rating + R.string.rating_out_of, + rating ) appRating.setCompoundDrawablesWithIntrinsicBounds( - null, null, getRatingDrawable(rating), null + null, + null, + getRatingDrawable(rating), + null ) appRating.compoundDrawablePadding = 15 } @@ -661,7 +662,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { positiveButtonAction = { installApplication(fusedApp, it) }, - cancelButtonText = getString(R.string.dialog_cancel), + cancelButtonText = getString(R.string.dialog_cancel) ).show(childFragmentManager, "ApplicationFragment") } } @@ -717,9 +718,11 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { ) { installButton.apply { enableInstallButton(R.string.not_available) - text = if (mainActivityViewModel.checkUnsupportedApplication(fusedApp)) + text = if (mainActivityViewModel.checkUnsupportedApplication(fusedApp)) { getString(R.string.not_available) - else getString(R.string.update) + } else { + getString(R.string.update) + } setTextColor(Color.WHITE) backgroundTintList = ContextCompat.getColorStateList(view.context, R.color.colorAccent) @@ -761,7 +764,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { } private suspend fun updateProgress( - downloadProgress: DownloadProgress, + downloadProgress: DownloadProgress ) { val progressResult = applicationViewModel.calculateProgress(downloadProgress) if (view == null || progressResult.first < 1) { @@ -816,7 +819,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { binding.progressBar.visibility = View.VISIBLE } - private fun stopLoadingUI() { + private fun stopLoadingUI() { binding.applicationLayout.visibility = View.VISIBLE binding.progressBar.visibility = View.GONE } @@ -832,7 +835,10 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { ) appPrivacyScore.setCompoundDrawablesRelativeWithIntrinsicBounds( - null, null, getPrivacyDrawable(privacyScore.toString()), null + null, + null, + getPrivacyDrawable(privacyScore.toString()), + null ) appPrivacyScore.compoundDrawablePadding = 15 } diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt index 0323992ac..c53e3724d 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt @@ -32,12 +32,8 @@ import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.CleanApkException -import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.download.data.DownloadProgressLD -import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -46,7 +42,7 @@ import javax.inject.Inject class ApplicationViewModel @Inject constructor( downloadProgressLD: DownloadProgressLD, private val fusedAPIRepository: FusedAPIRepository, - private val fusedManagerRepository: FusedManagerRepository, + private val fusedManagerRepository: FusedManagerRepository ) : ViewModel() { val fusedApp: MutableLiveData> = MutableLiveData() @@ -60,9 +56,8 @@ class ApplicationViewModel @Inject constructor( packageName: String, origin: Origin, isFdroidLink: Boolean, - authData: AuthData?, + authData: AuthData? ) { - if (isFdroidLink) { getCleanapkAppDetails(packageName) return diff --git a/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsDiffUtil.kt b/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsDiffUtil.kt index d68ba25f8..42ec1188d 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsDiffUtil.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsDiffUtil.kt @@ -22,7 +22,7 @@ import androidx.recyclerview.widget.DiffUtil class ApplicationScreenshotsDiffUtil( private val oldList: List, - private val newList: List, + private val newList: List ) : DiffUtil.Callback() { override fun getOldListSize(): Int { return oldList.size diff --git a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt index f13657eeb..fb431ed48 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt @@ -49,7 +49,7 @@ class ApplicationDialogFragment() : DialogFragment() { positiveButtonText: String = "", positiveButtonAction: (() -> Unit)? = null, cancelButtonText: String = "", - cancelButtonAction: (() -> Unit)? = null, + cancelButtonAction: (() -> Unit)? = null ) : this() { this.drawable = drawable this.title = title @@ -101,7 +101,9 @@ class ApplicationDialogFragment() : DialogFragment() { textPaint.isUnderlineText = false } }, - spannable.getSpanStart(urlSpan), spannable.getSpanEnd(urlSpan), 0 + spannable.getSpanStart(urlSpan), + spannable.getSpanEnd(urlSpan), + 0 ) } text = spannable diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt index bda3f6c98..cca5b5622 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt @@ -21,7 +21,6 @@ package foundation.e.apps.ui.applicationlist import android.os.Bundle import android.view.View import android.widget.ImageView -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels @@ -39,8 +38,6 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fused.FusedAPIInterface import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentApplicationListBinding import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PWAManagerModule @@ -51,7 +48,6 @@ import foundation.e.apps.ui.AppProgressViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment -import foundation.e.apps.ui.parentFragment.TimeoutFragment import kotlinx.coroutines.launch import javax.inject.Inject @@ -204,7 +200,7 @@ class ApplicationListFragment : positiveButtonAction = { getApplication(fusedApp) }, - cancelButtonText = getString(R.string.dialog_cancel), + cancelButtonText = getString(R.string.dialog_cancel) ).show(childFragmentManager, "HomeFragment") } @@ -223,7 +219,6 @@ class ApplicationListFragment : ) private fun loadData() { - /* * If details are once loaded, do not load details again, * Only set the scroll listeners. diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt index d28e864b0..bdcad5036 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt @@ -109,8 +109,11 @@ class ApplicationListRVAdapter( if (searchApp.isPlaceHolder) { val progressBar = holder.binding.placeholderProgressBar holder.binding.root.children.forEach { - it.visibility = if (it != progressBar) View.INVISIBLE - else View.VISIBLE + it.visibility = if (it != progressBar) { + View.INVISIBLE + } else { + View.VISIBLE + } } onPlaceHolderShow?.invoke() // Do not process anything else for this entry @@ -118,8 +121,11 @@ class ApplicationListRVAdapter( } else { val progressBar = holder.binding.placeholderProgressBar holder.binding.root.children.forEach { - it.visibility = if (it != progressBar) View.VISIBLE - else View.INVISIBLE + it.visibility = if (it != progressBar) { + View.VISIBLE + } else { + View.INVISIBLE + } } } @@ -155,8 +161,9 @@ class ApplicationListRVAdapter( item.post { val maxAllowedWidth = item.measuredWidth / 2 installButton.apply { - if (width > maxAllowedWidth) + if (width > maxAllowedWidth) { width = maxAllowedWidth + } } } } @@ -303,7 +310,7 @@ class ApplicationListRVAdapter( private fun ApplicationListItemBinding.handleInstallationIssue( view: View, - searchApp: FusedApp, + searchApp: FusedApp ) { progressBarInstall.visibility = View.GONE if (lifecycleOwner == null) { @@ -335,10 +342,11 @@ class ApplicationListRVAdapter( faultyAppResult: Pair, view: View ) = - if (faultyAppResult.second.contentEquals(InstallerService.INSTALL_FAILED_UPDATE_INCOMPATIBLE)) + if (faultyAppResult.second.contentEquals(InstallerService.INSTALL_FAILED_UPDATE_INCOMPATIBLE)) { view.context.getText(R.string.update) - else + } else { view.context.getString(R.string.retry) + } private fun ApplicationListItemBinding.handleBlocked(view: View) { installButton.apply { @@ -421,7 +429,7 @@ class ApplicationListRVAdapter( } private fun ApplicationListItemBinding.handleDownloading( - searchApp: FusedApp, + searchApp: FusedApp ) { installButton.apply { enableInstallButton() @@ -436,7 +444,7 @@ class ApplicationListRVAdapter( private fun ApplicationListItemBinding.handleUnavailable( searchApp: FusedApp, - holder: ViewHolder, + holder: ViewHolder ) { installButton.apply { updateUIByPaymentType(searchApp, this, this@handleUnavailable, holder) @@ -497,9 +505,11 @@ class ApplicationListRVAdapter( ) { installButton.apply { enableInstallButton(Status.UPDATABLE) - text = if (mainActivityViewModel.checkUnsupportedApplication(searchApp)) + text = if (mainActivityViewModel.checkUnsupportedApplication(searchApp)) { context.getString(R.string.not_available) - else context.getString(R.string.update) + } else { + context.getString(R.string.update) + } setOnClickListener { if (mainActivityViewModel.checkUnsupportedApplication(searchApp, context)) { return@setOnClickListener @@ -511,7 +521,7 @@ class ApplicationListRVAdapter( } private fun ApplicationListItemBinding.handleInstalled( - searchApp: FusedApp, + searchApp: FusedApp ) { installButton.apply { enableInstallButton(Status.INSTALLED) diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt index a5a2b0aeb..4878e79e6 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt @@ -46,7 +46,7 @@ class ApplicationListViewModel @Inject constructor( fun loadData( category: String, source: String, - authData: AuthData?, + authData: AuthData? ) { getList(category, authData ?: AuthData("", ""), source) } @@ -90,7 +90,6 @@ class ApplicationListViewModel @Inject constructor( fun loadMore(authData: AuthData?, category: String) { viewModelScope.launch(Dispatchers.IO) { - if (isLoading || authData == null || nextPageUrl.isNullOrEmpty()) { return@launch } diff --git a/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt index b257d2e26..5bb163173 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt @@ -20,22 +20,16 @@ package foundation.e.apps.ui.categories import android.os.Bundle import android.view.View -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.fused.utils.CategoryType -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentAppsBinding import foundation.e.apps.presentation.login.LoginViewModel -import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.categories.model.CategoriesRVAdapter -import foundation.e.apps.ui.parentFragment.TimeoutFragment @AndroidEntryPoint class AppsFragment : Fragment(R.layout.fragment_apps) { @@ -91,7 +85,6 @@ class AppsFragment : Fragment(R.layout.fragment_apps) { binding.recyclerView.visibility = View.VISIBLE } - override fun onResume() { super.onResume() binding.shimmerLayout.startShimmer() diff --git a/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt b/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt index 28cc34c77..868faf9cf 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt @@ -21,16 +21,11 @@ package foundation.e.apps.ui.categories import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedCategory import foundation.e.apps.data.fused.utils.CategoryType -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.CleanApkException -import foundation.e.apps.data.login.exceptions.GPlayException -import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -43,7 +38,7 @@ class CategoriesViewModel @Inject constructor( MutableLiveData() fun loadData( - type: CategoryType, + type: CategoryType ) { getCategoriesList(type) } diff --git a/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt index da9470200..973bc42e7 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt @@ -20,22 +20,16 @@ package foundation.e.apps.ui.categories import android.os.Bundle import android.view.View -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.fused.utils.CategoryType -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentGamesBinding import foundation.e.apps.presentation.login.LoginViewModel -import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.categories.model.CategoriesRVAdapter -import foundation.e.apps.ui.parentFragment.TimeoutFragment @AndroidEntryPoint class GamesFragment : Fragment(R.layout.fragment_games) { diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt index 215a06a70..78b759f2b 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt @@ -21,14 +21,11 @@ package foundation.e.apps.ui.home import android.os.Bundle import android.view.View import android.widget.ImageView -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.lifecycleScope -import androidx.navigation.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.aurora.gplayapi.data.models.AuthData @@ -39,14 +36,9 @@ import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fused.FusedAPIInterface import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fused.data.FusedHome -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.GPlayException -import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentHomeBinding -import foundation.e.apps.di.CommonUtilsModule.safeNavigate import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PWAManagerModule -import foundation.e.apps.presentation.login.LoginState import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.AppInfoFetchViewModel import foundation.e.apps.ui.AppProgressViewModel @@ -54,7 +46,6 @@ import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.home.model.HomeChildRVAdapter import foundation.e.apps.ui.home.model.HomeParentRVAdapter -import foundation.e.apps.ui.parentFragment.TimeoutFragment import kotlinx.coroutines.launch import javax.inject.Inject @@ -116,7 +107,9 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { private fun initHomeParentRVAdapter() = HomeParentRVAdapter( this, - mainActivityViewModel, appInfoFetchViewModel, viewLifecycleOwner + mainActivityViewModel, + appInfoFetchViewModel, + viewLifecycleOwner ) { fusedApp -> if (!mainActivityViewModel.shouldShowPaidAppsSnackBar(fusedApp)) { showPaidAppMessage(fusedApp) @@ -124,7 +117,6 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } private fun loadHomePageData() { - loginViewModel.loginState.observe(viewLifecycleOwner) { if (it.isLoggedIn) { // TODO : check for network and wait if network is unavailable @@ -146,7 +138,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { positiveButtonAction = { getApplication(fusedApp) }, - cancelButtonText = getString(R.string.dialog_cancel), + cancelButtonText = getString(R.string.dialog_cancel) ).show(childFragmentManager, "HomeFragment") } diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt index bb5c41dbb..78d453c0a 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt @@ -33,21 +33,21 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( - private val fusedAPIRepository: FusedAPIRepository, -): ViewModel() { + private val fusedAPIRepository: FusedAPIRepository +) : ViewModel() { var homeScreenData: MutableLiveData>> = MutableLiveData() fun loadData( authData: AuthData?, - lifecycleOwner: LifecycleOwner, + lifecycleOwner: LifecycleOwner ) { getHomeScreenData(authData ?: AuthData("", ""), lifecycleOwner) } private fun getHomeScreenData( authData: AuthData, - lifecycleOwner: LifecycleOwner, + lifecycleOwner: LifecycleOwner ) { viewModelScope.launch { fusedAPIRepository.getHomeScreenData(authData).observe(lifecycleOwner) { diff --git a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt index 1dc7933ab..9be2af9e6 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt @@ -190,7 +190,7 @@ class HomeChildRVAdapter( private fun HomeChildListItemBinding.handleUnavailable( homeApp: FusedApp, - holder: ViewHolder, + holder: ViewHolder ) { installButton.apply { updateUIByPaymentType(homeApp, this, holder.binding) @@ -214,9 +214,11 @@ class HomeChildRVAdapter( ) { installButton.apply { enableInstallButton(Status.UPDATABLE) - text = if (mainActivityViewModel.checkUnsupportedApplication(homeApp)) + text = if (mainActivityViewModel.checkUnsupportedApplication(homeApp)) { context.getString(R.string.not_available) - else context.getString(R.string.update) + } else { + context.getString(R.string.update) + } setOnClickListener { if (mainActivityViewModel.checkUnsupportedApplication(homeApp, context)) { return@setOnClickListener diff --git a/app/src/main/java/foundation/e/apps/ui/parentFragment/LoadingViewModel.kt b/app/src/main/java/foundation/e/apps/ui/parentFragment/LoadingViewModel.kt index a59e502ca..e9fb95aef 100644 --- a/app/src/main/java/foundation/e/apps/ui/parentFragment/LoadingViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/parentFragment/LoadingViewModel.kt @@ -44,9 +44,8 @@ abstract class LoadingViewModel : ViewModel() { fun onLoadData( authObjectList: List, loadingBlock: (successObjects: List, failedObjects: List) -> Unit, - retryBlock: (failedObjects: List) -> Boolean, + retryBlock: (failedObjects: List) -> Boolean ) { - exceptionsList.clear() val successAuthList = authObjectList.filter { it.result.isSuccess() } diff --git a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt index 9e395ae2a..388fb3d8f 100644 --- a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt @@ -135,7 +135,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { */ abstract fun onTimeout( exception: Exception, - predefinedDialog: AlertDialog.Builder, + predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? /** @@ -159,7 +159,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { */ abstract fun onSignInError( exception: GPlayLoginException, - predefinedDialog: AlertDialog.Builder, + predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? /** @@ -178,7 +178,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { */ abstract fun onDataLoadError( exception: Exception, - predefinedDialog: AlertDialog.Builder, + predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? /** @@ -277,7 +277,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { onTimeout( exception, - predefinedDialog, + predefinedDialog )?.run { stopLoadingUI() showAndSetDialog(this) @@ -293,10 +293,8 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { * is shown to the user. */ fun showSignInError(exception: GPlayLoginException) { - val dialogView = DialogErrorLogBinding.inflate(requireActivity().layoutInflater) dialogView.apply { - moreInfo.setOnClickListener { logDisplay.isVisible = true moreInfo.isVisible = false @@ -338,7 +336,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { onSignInError( exception, - predefinedDialog, + predefinedDialog )?.run { stopLoadingUI() showAndSetDialog(this) @@ -353,7 +351,6 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { * is shown to the user. */ fun showDataLoadError(exception: Exception) { - val dialogView = DialogErrorLogBinding.inflate(requireActivity().layoutInflater) dialogView.apply { moreInfo.setOnClickListener { @@ -386,7 +383,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { onDataLoadError( exception, - predefinedDialog, + predefinedDialog )?.run { stopLoadingUI() showAndSetDialog(this) diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt index 56cd8cef7..71ed0ebad 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt @@ -29,7 +29,6 @@ import android.widget.EditText import android.widget.ImageView import android.widget.LinearLayout import androidx.appcompat.widget.SearchView -import androidx.core.view.isVisible import androidx.cursoradapter.widget.CursorAdapter import androidx.cursoradapter.widget.SimpleCursorAdapter import androidx.fragment.app.Fragment @@ -171,7 +170,7 @@ class SearchFragment : private fun updateSearchResult( listAdapter: ApplicationListRVAdapter?, appList: List?, - hasMore: Boolean, + hasMore: Boolean ): Boolean { val currentList = listAdapter?.currentList ?: listOf() if (appList != null && !searchViewModel.isAnyAppUpdated(appList, currentList)) { @@ -213,7 +212,10 @@ class SearchFragment : val to = intArrayOf(android.R.id.text1) searchView?.suggestionsAdapter = SimpleCursorAdapter( context, - R.layout.custom_simple_list_item, null, from, to, + R.layout.custom_simple_list_item, + null, + from, + to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER ) @@ -241,7 +243,7 @@ class SearchFragment : positiveButtonAction = { getApplication(fusedApp) }, - cancelButtonText = getString(R.string.dialog_cancel), + cancelButtonText = getString(R.string.dialog_cancel) ).show(childFragmentManager, "SearchFragment") } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 24f387074..648a391c9 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -19,7 +19,6 @@ package foundation.e.apps.ui.search import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -35,7 +34,7 @@ import javax.inject.Inject @HiltViewModel class SearchViewModel @Inject constructor( - private val fusedAPIRepository: FusedAPIRepository, + private val fusedAPIRepository: FusedAPIRepository ) : ViewModel() { val searchSuggest: MutableLiveData?> = MutableLiveData() @@ -46,22 +45,22 @@ class SearchViewModel @Inject constructor( fun getSearchSuggestions(query: String, authData: AuthData?) { viewModelScope.launch(Dispatchers.IO) { - if (query.isNotBlank() && authData != null) + if (query.isNotBlank() && authData != null) { searchSuggest.postValue( fusedAPIRepository.getSearchSuggestions( query, authData ) ) + } } } fun loadData( query: String, lifecycleOwner: LifecycleOwner, - authData: AuthData?, + authData: AuthData? ) { - if (query.isBlank()) return getSearchResults(query, authData ?: AuthData("", ""), lifecycleOwner) } diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index 3e28a2c89..354b8fcd9 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -101,7 +101,6 @@ class SettingsFragment : PreferenceFragmentCompat() { versionInfo?.apply { summary = BuildConfig.VERSION_NAME setOnLongClickListener { - val osVersion = SystemInfoProvider.getSystemProperty(SystemInfoProvider.KEY_LINEAGE_VERSION) val appVersionLabel = getString(R.string.app_version_label) var contents = "$appVersionLabel: $summary" @@ -145,7 +144,9 @@ class SettingsFragment : PreferenceFragmentCompat() { Toast.LENGTH_SHORT ).show() false - } else true + } else { + true + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -183,8 +184,8 @@ class SettingsFragment : PreferenceFragmentCompat() { } settingsViewModel.getCurrentUser() - settingsViewModel.currentUserState.observe(viewLifecycleOwner) { - when(it.user) { + settingsViewModel.currentUserState.observe(viewLifecycleOwner) { + when (it.user) { User.ANONYMOUS -> { binding.accountType.setText(R.string.user_anonymous) binding.email.isVisible = false @@ -259,7 +260,7 @@ class SettingsFragment : PreferenceFragmentCompat() { private fun setCheckboxDependency( checkBox: CheckBoxPreference?, parentCheckBox: CheckBoxPreference?, - parentCheckBoxPreferenceChangeListener: OnPreferenceChangeListener? = null, + parentCheckBoxPreferenceChangeListener: OnPreferenceChangeListener? = null ) { checkBox?.dependency = parentCheckBox?.key parentCheckBox?.onPreferenceChangeListener = @@ -275,7 +276,7 @@ class SettingsFragment : PreferenceFragmentCompat() { private fun copyTextToClipboard( clipboard: ClipboardManager, label: String, - text: String, + text: String ) { val clip = ClipData.newPlainText(label, text) clipboard.setPrimaryClip(clip) diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt index 5be760a1b..21a82c68b 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/LocaleChangedBroadcastReceiver.kt @@ -40,8 +40,10 @@ class LocaleChangedBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var dataStoreModule: DataStoreModule + @Inject lateinit var gson: Gson + @Inject lateinit var cache: Cache diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt index 61d7c5757..5230882b1 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInViewModel.kt @@ -14,7 +14,7 @@ import javax.inject.Inject @HiltViewModel class SignInViewModel @Inject constructor( - private val dataStoreModule: DataStoreModule, + private val dataStoreModule: DataStoreModule ) : ViewModel() { val userType: LiveData = dataStoreModule.userType.asLiveData() diff --git a/app/src/main/java/foundation/e/apps/ui/setup/tos/TOSFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/tos/TOSFragment.kt index 82a282be0..4d230de20 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/tos/TOSFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/tos/TOSFragment.kt @@ -29,7 +29,6 @@ class TOSFragment : Fragment(R.layout.fragment_tos) { webView = binding.tosWebView viewModel.tocStatus.observe(viewLifecycleOwner) { - if (it == true && webView != null) { binding.TOSWarning.visibility = View.GONE binding.TOSButtons.visibility = View.GONE @@ -102,8 +101,11 @@ class TOSFragment : Fragment(R.layout.fragment_tos) { .append(body.toString()) .append("") webView?.loadDataWithBaseURL( - "file:///android_asset/", sb.toString(), - "text/html", "utf-8", null + "file:///android_asset/", + sb.toString(), + "text/html", + "utf-8", + null ) webView?.setOnScrollChangeListener { _, scrollX, scrollY, _, _ -> diff --git a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt index fb14dc87d..936c4cd4c 100644 --- a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt @@ -42,9 +42,6 @@ import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fused.FusedAPIInterface import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.GPlayException -import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentUpdatesBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate import foundation.e.apps.install.download.data.DownloadProgress @@ -112,7 +109,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { appInfoFetchViewModel, mainActivityViewModel, it, - viewLifecycleOwner, + viewLifecycleOwner ) { fusedApp -> if (!mainActivityViewModel.shouldShowPaidAppsSnackBar(fusedApp)) { showPurchasedAppMessage(fusedApp) @@ -173,15 +170,15 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { private fun shouldUpdateButtonEnable(workInfoList: MutableList) = !updatesViewModel.updatesList.value?.first.isNullOrEmpty() && - ( - workInfoList.isNullOrEmpty() || - ( - !updatesViewModel.checkWorkInfoListHasAnyUpdatableWork( - workInfoList - ) && - updatesViewModel.hasAnyUpdatableApp() - ) + ( + workInfoList.isNullOrEmpty() || + ( + !updatesViewModel.checkWorkInfoListHasAnyUpdatableWork( + workInfoList + ) && + updatesViewModel.hasAnyUpdatableApp() ) + ) private fun handleUpdateEvent(appEvent: AppEvent) { val event = appEvent.data as ResultSupreme.WorkError<*> @@ -215,13 +212,15 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { ApplicationDialogFragment( title = getString(R.string.dialog_title_paid_app, fusedApp.name), message = getString( - R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price + R.string.dialog_paidapp_message, + fusedApp.name, + fusedApp.price ), positiveButtonText = getString(R.string.dialog_confirm), positiveButtonAction = { getApplication(fusedApp) }, - cancelButtonText = getString(R.string.dialog_cancel), + cancelButtonText = getString(R.string.dialog_cancel) ).show(childFragmentManager, "UpdatesFragment") } @@ -258,7 +257,7 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { WorkInfo.State.SUCCEEDED ) return !workInfoList.isNullOrEmpty() && errorStates.contains(workInfoList.last().state) && - updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() + updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() } private fun showLoadingUI() { diff --git a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt index f803c2e82..9c05e0878 100644 --- a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt @@ -43,16 +43,18 @@ class UpdatesViewModel @Inject constructor( val updatesList: MutableLiveData, ResultStatus?>> = MutableLiveData() fun loadData( - authData: AuthData?, + authData: AuthData? ) { getUpdates(authData ?: AuthData("", "")) } private fun getUpdates(authData: AuthData?) { viewModelScope.launch { - val updatesResult = if (authData != null) + val updatesResult = if (authData != null) { updatesManagerRepository.getUpdates(authData) - else updatesManagerRepository.getUpdatesOSS() + } else { + updatesManagerRepository.getUpdatesOSS() + } updatesList.postValue(updatesResult) } } diff --git a/app/src/main/java/foundation/e/apps/utils/MaterialButtonUtils.kt b/app/src/main/java/foundation/e/apps/utils/MaterialButtonUtils.kt index 6e1ff3745..966d24ba4 100644 --- a/app/src/main/java/foundation/e/apps/utils/MaterialButtonUtils.kt +++ b/app/src/main/java/foundation/e/apps/utils/MaterialButtonUtils.kt @@ -42,11 +42,12 @@ private fun MaterialButton.toggleEnableMaterialButton(isEnabled: Boolean, status private fun MaterialButton.getBackgroundTintList(status: Status?) = if (status == Status.INSTALLED || status == Status.UPDATABLE) { ContextCompat.getColorStateList(this.context, R.color.colorAccent) - } else + } else { ContextCompat.getColorStateList(this.context, android.R.color.transparent) + } private fun MaterialButton.getStrokeColor( - isEnabled: Boolean, + isEnabled: Boolean ) = if (isEnabled) { ContextCompat.getColorStateList(this.context, R.color.colorAccent) } else { diff --git a/app/src/main/java/foundation/e/apps/utils/Resource.kt b/app/src/main/java/foundation/e/apps/utils/Resource.kt index f7a20a467..bb1b6e27c 100644 --- a/app/src/main/java/foundation/e/apps/utils/Resource.kt +++ b/app/src/main/java/foundation/e/apps/utils/Resource.kt @@ -27,6 +27,7 @@ sealed class Resource(val data: T? = null, val message: String? = null) { * @param data The data associated with the resource. */ class Success(data: T) : Resource(data) + /** * Represents an error state of the resource with an error message. * @param message The error message associated with the resource. diff --git a/app/src/test/java/foundation/e/apps/FakePkgManagerModule.kt b/app/src/test/java/foundation/e/apps/FakePkgManagerModule.kt index b24f4af36..f73e27ec1 100644 --- a/app/src/test/java/foundation/e/apps/FakePkgManagerModule.kt +++ b/app/src/test/java/foundation/e/apps/FakePkgManagerModule.kt @@ -27,7 +27,7 @@ import foundation.e.apps.install.pkg.PkgManagerModule class FakePkgManagerModule( context: Context, - val gplayApps: List, + val gplayApps: List ) : PkgManagerModule(context) { val applicationInfo = mutableListOf( diff --git a/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt b/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt index 3096c444c..cd281e4d8 100644 --- a/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt @@ -445,12 +445,13 @@ class FusedApiImplTest { _id = "113", name = "Demo Three", package_name = "foundation.e.demothree", - latest_version_code = 123, + latest_version_code = 123 ) Mockito.`when`( pkgManagerModule.getPackageStatus( - fusedApp.package_name, fusedApp.latest_version_code + fusedApp.package_name, + fusedApp.latest_version_code ) ).thenReturn(Status.INSTALLED) @@ -464,7 +465,7 @@ class FusedApiImplTest { _id = "113", name = "Demo Three", package_name = "", - latest_version_code = 123, + latest_version_code = 123 ) val filterLevel = fusedAPIImpl.getAppFilterLevel(fusedApp, AUTH_DATA) @@ -554,7 +555,7 @@ class FusedApiImplTest { gPlayAPIRepository.getDownloadInfo( fusedApp.package_name, fusedApp.latest_version_code, - fusedApp.offer_type, + fusedApp.offer_type ) ).thenReturn(listOf()) @@ -580,7 +581,9 @@ class FusedApiImplTest { Mockito.`when`( gPlayAPIRepository.getDownloadInfo( - fusedApp.package_name, fusedApp.latest_version_code, fusedApp.offer_type + fusedApp.package_name, + fusedApp.latest_version_code, + fusedApp.offer_type ) ).thenReturn(listOf()) @@ -606,7 +609,9 @@ class FusedApiImplTest { Mockito.`when`( gPlayAPIRepository.getDownloadInfo( - fusedApp.package_name, fusedApp.latest_version_code, fusedApp.offer_type + fusedApp.package_name, + fusedApp.latest_version_code, + fusedApp.offer_type ) ).thenThrow(RuntimeException()) @@ -695,7 +700,9 @@ class FusedApiImplTest { fun `getCategory when All source is selected`() = runTest { val gplayCategories = listOf(Category(), Category(), Category(), Category()) val openSourcecategories = Categories( - listOf("app one", "app two", "app three", "app four"), listOf("game 1", "game 2"), true + listOf("app one", "app two", "app three", "app four"), + listOf("game 1", "game 2"), + true ) val openSourceResponse = Response.success(openSourcecategories) val pwaCategories = @@ -762,11 +769,14 @@ class FusedApiImplTest { preferenceManagerModule.isOpenSourceelectedFake = true preferenceManagerModule.isGplaySelectedFake = true val gplayFlow: Pair, MutableSet> = Pair( - listOf(App("a.b.c"), App("c.d.e"), App("d.e.f"), App("d.e.g")), mutableSetOf() + listOf(App("a.b.c"), App("c.d.e"), App("d.e.f"), App("d.e.g")), + mutableSetOf() ) setupMockingSearchApp( - packageNameSearchResponse, gplayPackageResult, gplayFlow + packageNameSearchResponse, + gplayPackageResult, + gplayFlow ) val searchResultLiveData = @@ -787,7 +797,8 @@ class FusedApiImplTest { .thenReturn(Status.UNAVAILABLE) Mockito.`when`( cleanApkAppsRepository.getSearchResult( - query = "com.search.package", searchBy = "package_name" + query = "com.search.package", + searchBy = "package_name" ) ).thenReturn(packageNameSearchResponse) formatterMocked.`when` { Formatter.formatFileSize(any(), any()) }.thenReturn("15MB") @@ -851,11 +862,15 @@ class FusedApiImplTest { val gplayPackageResult = App("com.search.package") val gplayFlow: Pair, MutableSet> = Pair( - listOf(App("a.b.c"), App("c.d.e"), App("d.e.f"), App("d.e.g")), mutableSetOf() + listOf(App("a.b.c"), App("c.d.e"), App("d.e.f"), App("d.e.g")), + mutableSetOf() ) setupMockingSearchApp( - packageNameSearchResponse, gplayPackageResult, gplayFlow, true + packageNameSearchResponse, + gplayPackageResult, + gplayFlow, + true ) preferenceManagerModule.isPWASelectedFake = false diff --git a/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt b/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt index 8e04ce98e..b150bc8c3 100644 --- a/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt @@ -29,6 +29,7 @@ import org.mockito.kotlin.any class FusedApiRepositoryTest { private lateinit var fusedApiRepository: FusedAPIRepository + @Mock private lateinit var fusedAPIImpl: FusedApiImpl diff --git a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt index 648567559..05647b42e 100644 --- a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt +++ b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt @@ -128,7 +128,7 @@ class UpdateManagerImptTest { package_name = "foundation.e.demotwo", origin = Origin.GPLAY, filterLevel = FilterLevel.NONE - ), + ) ) @Test diff --git a/app/src/test/java/foundation/e/apps/data/UserTest.kt b/app/src/test/java/foundation/e/apps/data/UserTest.kt index a4c8c2cfb..6ba7252fe 100644 --- a/app/src/test/java/foundation/e/apps/data/UserTest.kt +++ b/app/src/test/java/foundation/e/apps/data/UserTest.kt @@ -52,4 +52,4 @@ class UserTest { val result = User.getUser("dmflmfle") Assert.assertEquals(User.UNAVAILABLE.name, result.name) } -} \ No newline at end of file +} diff --git a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt index 399aee6fb..5b0893fe2 100644 --- a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt +++ b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt @@ -63,7 +63,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "foundation.e.demothree", latest_version_code = 123, - is_pwa = true, + is_pwa = true ) val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) assertEquals("getAppPrivacyInfo", true, result.isSuccess()) @@ -78,7 +78,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "", latest_version_code = 123, - is_pwa = true, + is_pwa = true ) val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) assertEquals("getAppPrivacyInfo", false, result.isSuccess()) @@ -92,7 +92,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - is_pwa = true, + is_pwa = true ) fakeTrackerDao.trackers.clear() val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) diff --git a/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedManagerRepository.kt b/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedManagerRepository.kt index 9b39077a2..63713f32f 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedManagerRepository.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/FakeFusedManagerRepository.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.delay class FakeFusedManagerRepository( private val fusedDownloadDAO: FakeFusedDownloadDAO, fusedManager: IFusedManager, - fdroidRepository: FdroidRepository, + fdroidRepository: FdroidRepository ) : FusedManagerRepository(fusedManager, fdroidRepository) { var isAppInstalled = false var installationStatus = Status.INSTALLED -- GitLab From 36ce57b5fc6040a5699fc8a61b0c8dc773f7709f Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 5 Sep 2023 22:07:02 +0600 Subject: [PATCH 055/112] fixed: gplay/pwa apps installation --- .../apps/data/preference/DataStoreManager.kt | 46 ------------------- .../workmanager/AppInstallProcessor.kt | 15 ++++-- .../AppInstallProcessorTest.kt | 10 ++-- 3 files changed, 15 insertions(+), 56 deletions(-) delete mode 100644 app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt diff --git a/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt b/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt deleted file mode 100644 index 4294505ae..000000000 --- a/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2022 Murena SAS - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.apps.data.preference - -import com.aurora.gplayapi.data.models.AuthData -import com.google.gson.Gson -import foundation.e.apps.data.enums.User -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class DataStoreManager @Inject constructor() { - @Inject - lateinit var dataStoreModule: DataStoreModule - - @Inject - lateinit var gson: Gson - - fun getAuthData(): AuthData { - val authDataJson = dataStoreModule.getAuthDataSync() - return gson.fromJson(authDataJson, AuthData::class.java) - } - - fun getUserType(): User { - return dataStoreModule.getUserType() - } - - fun getAuthDataJson(): String { - return dataStoreModule.getAuthDataSync() - } -} diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 050b9521f..306048bd8 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -21,7 +21,10 @@ package foundation.e.apps.install.workmanager import android.content.Context import android.os.Environment import android.os.StatFs +import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.exceptions.ApiException +import com.google.gson.Gson import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme @@ -34,7 +37,6 @@ import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedDownloadRepository import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus @@ -51,7 +53,7 @@ class AppInstallProcessor @Inject constructor( private val fusedDownloadRepository: FusedDownloadRepository, private val fusedManagerRepository: FusedManagerRepository, private val fusedAPIRepository: FusedAPIRepository, - private val dataStoreManager: DataStoreManager + private val gson: Gson ) { private var isItUpdateWork = false @@ -107,7 +109,7 @@ class AppInstallProcessor @Inject constructor( isAnUpdate: Boolean = false ) { try { - val authData = dataStoreManager.getAuthData() + val authData = getAuthData() if (!fusedDownload.isFree && authData.isAnonymous) { EventBus.invokeEvent(AppEvent.ErrorMessageEvent(R.string.paid_app_anonymous_message)) return @@ -273,7 +275,7 @@ class AppInstallProcessor @Inject constructor( } private fun showNotificationOnUpdateEnded() { - val locale = dataStoreManager.getAuthData().locale + val locale = getAuthData().locale val date = Date().getFormattedString(DATE_FORMAT, locale) val numberOfUpdatedApps = NumberFormat.getNumberInstance(locale) .format(UpdatesDao.successfulUpdatedApps.size) @@ -373,4 +375,9 @@ class AppInstallProcessor @Inject constructor( private suspend fun finishInstallation(fusedDownload: FusedDownload) { checkUpdateWork(fusedDownload) } + + private fun getAuthData(): AuthData { + val authDataJson = context.configurations.authData + return gson.fromJson(authDataJson, AuthData::class.java) + } } diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt index c6da9c22e..ec1cc596e 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt @@ -20,14 +20,15 @@ package foundation.e.apps.installProcessor import android.content.Context import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import app.lounge.storage.cache.configurations import com.aurora.gplayapi.data.models.AuthData +import com.google.gson.Gson import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fdroid.FdroidRepository import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fusedDownload.FusedDownloadRepository import foundation.e.apps.data.fusedDownload.IFusedManager import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.install.workmanager.AppInstallProcessor import foundation.e.apps.util.MainCoroutineRule import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -66,9 +67,6 @@ class AppInstallProcessorTest { @Mock private lateinit var context: Context - @Mock - private lateinit var dataStoreManager: DataStoreManager - @Mock private lateinit var fusedAPIRepository: FusedAPIRepository @@ -87,7 +85,7 @@ class AppInstallProcessorTest { fusedDownloadRepository, fakeFusedManagerRepository, fusedAPIRepository, - dataStoreManager + Gson() ) } @@ -105,7 +103,7 @@ class AppInstallProcessorTest { ): FusedDownload { val fusedDownload = createFusedDownload(packageName, downloadUrlList) fakeFusedDownloadDAO.addDownload(fusedDownload) - Mockito.`when`(dataStoreManager.getAuthData()).thenReturn(AuthData("", "")) + Mockito.`when`(context.configurations.authData).thenReturn("{{aasToken:\"\",email:\"\"}") return fusedDownload } -- GitLab From 6a0f6565913ab248f21d5912287b22f2886d0136 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 5 Sep 2023 22:50:18 +0530 Subject: [PATCH 056/112] Refine app install code to access authdata --- .../main/java/foundation/e/apps/di/LoginModule.kt | 14 +++++++++++--- .../domain/install/usecase/AppInstallerUseCase.kt | 11 +++++++++++ ...nActivityUserCase.kt => MainActivityUseCase.kt} | 2 +- .../install/workmanager/AppInstallProcessor.kt | 12 ++++-------- .../foundation/e/apps/ui/MainActivityViewModel.kt | 8 ++++---- 5 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt rename app/src/main/java/foundation/e/apps/domain/main/usecase/{MainActivityUserCase.kt => MainActivityUseCase.kt} (86%) diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt index 39c12e856..c0f47f1a6 100644 --- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt +++ b/app/src/main/java/foundation/e/apps/di/LoginModule.kt @@ -25,9 +25,10 @@ import foundation.e.apps.data.login.LoginSourceCleanApk import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.login.LoginSourceInterface import foundation.e.apps.domain.common.repository.CommonRepositoryImpl +import foundation.e.apps.domain.install.usecase.AppInstallerUseCase import foundation.e.apps.domain.login.repository.LoginRepositoryImpl import foundation.e.apps.domain.login.usecase.UserLoginUseCase -import foundation.e.apps.domain.main.usecase.MainActivityUserCase +import foundation.e.apps.domain.main.usecase.MainActivityUseCase import foundation.e.apps.domain.settings.usecase.SettingsUseCase import foundation.e.apps.domain.updates.usecase.UpdatesUseCase @@ -61,8 +62,8 @@ object LoginModule { @Provides fun provideMainActivityUseCase( commonRepositoryImpl: CommonRepositoryImpl - ): MainActivityUserCase { - return MainActivityUserCase(commonRepositoryImpl) + ): MainActivityUseCase { + return MainActivityUseCase(commonRepositoryImpl) } @Provides @@ -71,4 +72,11 @@ object LoginModule { ): UpdatesUseCase { return UpdatesUseCase(commonRepositoryImpl) } + + @Provides + fun provideAppInstallerUseCase( + commonRepositoryImpl: CommonRepositoryImpl + ): AppInstallerUseCase { + return AppInstallerUseCase(commonRepositoryImpl) + } } diff --git a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt new file mode 100644 index 000000000..5472155c5 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt @@ -0,0 +1,11 @@ +package foundation.e.apps.domain.install.usecase + +import foundation.e.apps.domain.common.repository.CommonRepository +import javax.inject.Inject + +class AppInstallerUseCase@Inject constructor( + private val commonRepository: CommonRepository +) { + + fun currentAuthData() = commonRepository.cacheAuthData() +} diff --git a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt similarity index 86% rename from app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt rename to app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt index 80ff0e722..757e71586 100644 --- a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUserCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt @@ -3,7 +3,7 @@ package foundation.e.apps.domain.main.usecase import foundation.e.apps.domain.common.repository.CommonRepository import javax.inject.Inject -class MainActivityUserCase @Inject constructor( +class MainActivityUseCase @Inject constructor( private val commonRepository: CommonRepository ) { fun currentUser() = commonRepository.currentUser() diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 306048bd8..51c5c15e8 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -37,6 +37,7 @@ import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedDownloadRepository import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload +import foundation.e.apps.domain.install.usecase.AppInstallerUseCase import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus @@ -53,7 +54,7 @@ class AppInstallProcessor @Inject constructor( private val fusedDownloadRepository: FusedDownloadRepository, private val fusedManagerRepository: FusedManagerRepository, private val fusedAPIRepository: FusedAPIRepository, - private val gson: Gson + private val appInstallerUseCase: AppInstallerUseCase ) { private var isItUpdateWork = false @@ -109,7 +110,7 @@ class AppInstallProcessor @Inject constructor( isAnUpdate: Boolean = false ) { try { - val authData = getAuthData() + val authData = appInstallerUseCase.currentAuthData() if (!fusedDownload.isFree && authData.isAnonymous) { EventBus.invokeEvent(AppEvent.ErrorMessageEvent(R.string.paid_app_anonymous_message)) return @@ -275,7 +276,7 @@ class AppInstallProcessor @Inject constructor( } private fun showNotificationOnUpdateEnded() { - val locale = getAuthData().locale + val locale = appInstallerUseCase.currentAuthData().locale val date = Date().getFormattedString(DATE_FORMAT, locale) val numberOfUpdatedApps = NumberFormat.getNumberInstance(locale) .format(UpdatesDao.successfulUpdatedApps.size) @@ -375,9 +376,4 @@ class AppInstallProcessor @Inject constructor( private suspend fun finishInstallation(fusedDownload: FusedDownload) { checkUpdateWork(fusedDownload) } - - private fun getAuthData(): AuthData { - val authDataJson = context.configurations.authData - return gson.fromJson(authDataJson, AuthData::class.java) - } } diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt index 922184334..195935dbb 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt @@ -46,7 +46,7 @@ import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.preference.DataStoreModule -import foundation.e.apps.domain.main.usecase.MainActivityUserCase +import foundation.e.apps.domain.main.usecase.MainActivityUseCase import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.pkg.PkgManagerModule import foundation.e.apps.install.workmanager.AppInstallProcessor @@ -66,7 +66,7 @@ class MainActivityViewModel @Inject constructor( private val ecloudRepository: EcloudRepository, private val blockedAppRepository: BlockedAppRepository, private val appInstallProcessor: AppInstallProcessor, - private val mainActivityUserCase: MainActivityUserCase + private val mainActivityUseCase: MainActivityUseCase ) : ViewModel() { val tocStatus: LiveData = dataStoreModule.tocStatus.asLiveData() @@ -76,7 +76,7 @@ class MainActivityViewModel @Inject constructor( val isAppPurchased: MutableLiveData = MutableLiveData() val purchaseDeclined: MutableLiveData = MutableLiveData() - private val gPlayAuthData: AuthData by lazy { mainActivityUserCase.currentAuthData() } + private val gPlayAuthData: AuthData by lazy { mainActivityUseCase.currentAuthData() } // Downloads val downloadList = fusedManagerRepository.getDownloadLiveList() @@ -93,7 +93,7 @@ class MainActivityViewModel @Inject constructor( } fun getUser(): User { - return mainActivityUserCase.currentUser() + return mainActivityUseCase.currentUser() } fun getUserEmail(): String { -- GitLab From 35bd70d8302e8b72b892f5503d6d49eae6b9d14e Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Wed, 6 Sep 2023 10:53:26 +0600 Subject: [PATCH 057/112] fixed: authData management for no_google mode --- .../install/usecase/AppInstallerUseCase.kt | 17 +++++++++++- .../login/usecase/NoGoogleModeUseCase.kt | 26 +++++++++++++++++++ .../workmanager/AppInstallProcessor.kt | 5 ++-- .../apps/presentation/login/LoginViewModel.kt | 9 ++++--- .../AppInstallProcessorTest.kt | 4 ++- 5 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt diff --git a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt index 5472155c5..33b8f7246 100644 --- a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt @@ -1,11 +1,26 @@ package foundation.e.apps.domain.install.usecase +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.enums.User import foundation.e.apps.domain.common.repository.CommonRepository +import java.util.Locale import javax.inject.Inject class AppInstallerUseCase@Inject constructor( private val commonRepository: CommonRepository ) { - fun currentAuthData() = commonRepository.cacheAuthData() + fun currentAuthData(): AuthData? { + return try { + commonRepository.cacheAuthData() + } catch (e: Exception) { + if (commonRepository.currentUser() == User.NO_GOOGLE) { + return AuthData("","").apply { + this.isAnonymous = false + this.locale = Locale.getDefault() + } + } + return null + } + } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt new file mode 100644 index 000000000..5ea8e0ba7 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt @@ -0,0 +1,26 @@ +package foundation.e.apps.domain.login.usecase + +import android.content.Context +import app.lounge.storage.cache.configurations +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.data.ResultSupreme +import foundation.e.apps.data.enums.User +import foundation.e.apps.data.login.AuthObject +import javax.inject.Inject + +class NoGoogleModeUseCase @Inject constructor(@ApplicationContext private val context: Context) { + fun performNoGoogleLogin(): AuthObject { + context.configurations.userType = User.NO_GOOGLE.toString() + context.configurations.showAllApplications = false + context.configurations.showFOSSApplications = true + context.configurations.showPWAApplications = true + return getAuthObject() + } + + private fun getAuthObject(): AuthObject.CleanApk { + return AuthObject.CleanApk( + ResultSupreme.Success(Unit), + User.NO_GOOGLE + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 51c5c15e8..75d90861a 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -47,6 +47,7 @@ import kotlinx.coroutines.flow.transformWhile import timber.log.Timber import java.text.NumberFormat import java.util.Date +import java.util.Locale import javax.inject.Inject class AppInstallProcessor @Inject constructor( @@ -111,7 +112,7 @@ class AppInstallProcessor @Inject constructor( ) { try { val authData = appInstallerUseCase.currentAuthData() - if (!fusedDownload.isFree && authData.isAnonymous) { + if (!fusedDownload.isFree && authData?.isAnonymous == true) { EventBus.invokeEvent(AppEvent.ErrorMessageEvent(R.string.paid_app_anonymous_message)) return } @@ -276,7 +277,7 @@ class AppInstallProcessor @Inject constructor( } private fun showNotificationOnUpdateEnded() { - val locale = appInstallerUseCase.currentAuthData().locale + val locale = appInstallerUseCase.currentAuthData()?.locale ?: Locale.getDefault() val date = Date().getFormattedString(DATE_FORMAT, locale) val numberOfUpdatedApps = NumberFormat.getNumberInstance(locale) .format(UpdatesDao.successfulUpdatedApps.size) diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index e7f4bb59f..8b4c7d564 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -28,6 +28,7 @@ import foundation.e.apps.data.enums.User import foundation.e.apps.data.enums.User.NO_GOOGLE import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceRepository +import foundation.e.apps.domain.login.usecase.NoGoogleModeUseCase import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.ui.parentFragment.LoadingViewModel import foundation.e.apps.utils.Resource @@ -45,7 +46,8 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, - private val userLoginUseCase: UserLoginUseCase + private val userLoginUseCase: UserLoginUseCase, + private val noGoogleModeUseCase: NoGoogleModeUseCase ) : ViewModel() { /** @@ -107,9 +109,10 @@ class LoginViewModel @Inject constructor( */ fun initialNoGoogleLogin(onUserSaved: () -> Unit) { viewModelScope.launch { - loginSourceRepository.setNoGoogleMode() + val authObject = noGoogleModeUseCase.performNoGoogleLogin() + _loginState.value = LoginState(isLoading = false, isLoggedIn = true) + authObjects.postValue(listOf(authObject)) onUserSaved() - startLoginFlow() } } diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt index ec1cc596e..462ff1cd6 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt @@ -29,6 +29,8 @@ import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fusedDownload.FusedDownloadRepository import foundation.e.apps.data.fusedDownload.IFusedManager import foundation.e.apps.data.fusedDownload.models.FusedDownload +import foundation.e.apps.domain.common.repository.CommonRepositoryImpl +import foundation.e.apps.domain.install.usecase.AppInstallerUseCase import foundation.e.apps.install.workmanager.AppInstallProcessor import foundation.e.apps.util.MainCoroutineRule import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -85,7 +87,7 @@ class AppInstallProcessorTest { fusedDownloadRepository, fakeFusedManagerRepository, fusedAPIRepository, - Gson() + AppInstallerUseCase(CommonRepositoryImpl(context)) ) } -- GitLab From 7f02d5bd57c49bde3b61a31425081e4caa175203 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Wed, 6 Sep 2023 15:31:28 +0600 Subject: [PATCH 058/112] changed preference for app source checking --- .../e/apps/data/fused/FusedApiImpl.kt | 39 ++++++++++--------- .../preference/PreferenceManagerModule.kt | 8 ---- .../common/repository/CommonRepositoryImpl.kt | 5 ++- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index 1280315bd..267d78f0c 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -23,6 +23,7 @@ import android.text.format.Formatter import androidx.lifecycle.LiveData import androidx.lifecycle.liveData import androidx.lifecycle.map +import app.lounge.storage.cache.configurations import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App @@ -118,9 +119,9 @@ class FusedApiImpl @Inject constructor( override fun getApplicationCategoryPreference(): List { val prefs = mutableListOf() - if (preferenceManagerModule.isGplaySelected()) prefs.add(APP_TYPE_ANY) - if (preferenceManagerModule.isOpenSourceSelected()) prefs.add(APP_TYPE_OPEN) - if (preferenceManagerModule.isPWASelected()) prefs.add(APP_TYPE_PWA) + if (context.configurations.showAllApplications) prefs.add(APP_TYPE_ANY) + if (context.configurations.showFOSSApplications) prefs.add(APP_TYPE_OPEN) + if (context.configurations.showPWAApplications) prefs.add(APP_TYPE_PWA) return prefs } @@ -134,15 +135,15 @@ class FusedApiImpl @Inject constructor( return liveData { coroutineScope { - if (preferenceManagerModule.isGplaySelected()) { + if (context.configurations.showAllApplications) { resultGplay = async { loadHomeData(list, Source.GPLAY, authData) } } - if (preferenceManagerModule.isOpenSourceSelected()) { + if (context.configurations.showFOSSApplications) { resultOpenSource = async { loadHomeData(list, Source.OPEN, authData) } } - if (preferenceManagerModule.isPWASelected()) { + if (context.configurations.showPWAApplications) { resultPWA = async { loadHomeData(list, Source.PWA, authData) } } @@ -261,7 +262,7 @@ class FusedApiImpl @Inject constructor( val searchResult = mutableListOf() val cleanApkResults = mutableListOf() - if (preferenceManagerModule.isOpenSourceSelected()) { + if (context.configurations.showFOSSApplications) { finalSearchResult = fetchOpenSourceSearchResult( cleanApkResults, query, @@ -270,7 +271,7 @@ class FusedApiImpl @Inject constructor( ) } - if (preferenceManagerModule.isPWASelected()) { + if (context.configurations.showPWAApplications) { finalSearchResult = fetchPWASearchResult( query, searchResult, @@ -309,7 +310,7 @@ class FusedApiImpl @Inject constructor( packageSpecificResults, query ), - preferenceManagerModule.isGplaySelected() + context.configurations.showAllApplications ) ) } @@ -336,7 +337,7 @@ class FusedApiImpl @Inject constructor( packageSpecificResults, query ), - preferenceManagerModule.isGplaySelected() || preferenceManagerModule.isPWASelected() + context.configurations.showAllApplications || context.configurations.showPWAApplications ) ) } @@ -350,11 +351,11 @@ class FusedApiImpl @Inject constructor( var cleanapkPackageResult: FusedApp? = null val status = runCodeWithTimeout({ - if (preferenceManagerModule.isGplaySelected()) { + if (context.configurations.showAllApplications) { gplayPackageResult = getGplayPackagResult(query, authData) } - if (preferenceManagerModule.isOpenSourceSelected()) { + if (context.configurations.showFOSSApplications) { cleanapkPackageResult = getCleanApkPackageResult(query) } }) @@ -368,7 +369,7 @@ class FusedApiImpl @Inject constructor( gplayPackageResult?.let { packageSpecificResults.add(it) } } - if (preferenceManagerModule.isGplaySelected()) { + if (context.configurations.showAllApplications) { packageSpecificResults.add(FusedApp(isPlaceHolder = true)) } @@ -400,7 +401,7 @@ class FusedApiImpl @Inject constructor( val finalList = (packageSpecificResults + filteredResults).toMutableList() finalList.removeIf { it.isPlaceHolder } - if (preferenceManagerModule.isGplaySelected()) { + if (context.configurations.showAllApplications) { finalList.add(FusedApp(isPlaceHolder = true)) } @@ -818,21 +819,21 @@ class FusedApiImpl @Inject constructor( var apiStatus = ResultStatus.OK var errorApplicationCategory = "" - if (preferenceManagerModule.isOpenSourceSelected()) { + if (context.configurations.showFOSSApplications) { val openSourceCategoryResult = fetchOpenSourceCategories(type) categoriesList.addAll(openSourceCategoryResult.second) apiStatus = openSourceCategoryResult.first errorApplicationCategory = openSourceCategoryResult.third } - if (preferenceManagerModule.isPWASelected()) { + if (context.configurations.showPWAApplications) { val pwaCategoriesResult = fetchPWACategories(type) categoriesList.addAll(pwaCategoriesResult.second) apiStatus = pwaCategoriesResult.first errorApplicationCategory = pwaCategoriesResult.third } - if (preferenceManagerModule.isGplaySelected()) { + if (context.configurations.showAllApplications) { val gplayCategoryResult = fetchGplayCategories( type ) @@ -1092,7 +1093,7 @@ class FusedApiImpl @Inject constructor( val searchResults = gplayRepository.getSearchResult(query, nextPageSubBundle?.toMutableSet()) - if (!preferenceManagerModule.isGplaySelected()) { + if (!context.configurations.showAllApplications) { return ResultSupreme.Error(ERROR_GPLAY_SOURCE_NOT_SELECTED) } @@ -1419,7 +1420,7 @@ class FusedApiImpl @Inject constructor( return false } - override fun isOpenSourceSelected() = preferenceManagerModule.isOpenSourceSelected() + override fun isOpenSourceSelected() = context.configurations.showFOSSApplications override suspend fun getGplayAppsByCategory( authData: AuthData, category: String, diff --git a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt index 428daf195..44213abc9 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt @@ -48,14 +48,6 @@ class PreferenceManagerModule @Inject constructor( } } - fun isOpenSourceSelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_FOSS, true) - fun isPWASelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_PWA, true) - fun isGplaySelected() = preferenceManager.getBoolean(PREFERENCE_SHOW_GPLAY, true) - - fun autoUpdatePreferred(): Boolean { - return preferenceManager.getBoolean("updateInstallAuto", false) - } - fun getUpdateInterval() = preferenceManager.getString( context.getString(R.string.update_check_intervals), context.getString(R.string.preference_update_interval_default) diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt index f003b6368..973077e68 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt @@ -24,10 +24,12 @@ import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.data.enums.User import foundation.e.apps.utils.toAuthData +import okhttp3.Cache import javax.inject.Inject class CommonRepositoryImpl @Inject constructor( - @ApplicationContext val applicationContext: Context + @ApplicationContext val applicationContext: Context, + val cache: Cache ) : CommonRepository { override fun currentUser(): User { @@ -43,6 +45,7 @@ class CommonRepositoryImpl @Inject constructor( oauthtoken = "" // TODO: reset access token for Google login. It is not defined yet. } + cache.evictAll() } override fun cacheAuthData(): AuthData = -- GitLab From a34268f8382161dff0b82a09cde4392ce5e6cb9f Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 7 Sep 2023 22:58:04 +0530 Subject: [PATCH 059/112] Fix Google user flow with anonymous new apis --- .../common/repository/CommonRepositoryImpl.kt | 3 ++ .../login/repository/LoginRepository.kt | 1 + .../login/repository/LoginRepositoryImpl.kt | 8 ++++ .../domain/login/usecase/UserLoginUseCase.kt | 15 ++++++++ .../settings/usecase/SettingsUseCase.kt | 7 ++++ .../apps/presentation/login/LoginViewModel.kt | 29 +++++++++++++-- .../presentation/settings/SettingsState.kt | 10 +++-- .../settings/SettingsViewModel.kt | 37 +++++++++++++++---- .../e/apps/ui/settings/SettingsFragment.kt | 26 ++++++------- 9 files changed, 108 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt index 973077e68..9ceceb06a 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt @@ -43,6 +43,9 @@ class CommonRepositoryImpl @Inject constructor( userType = User.UNAVAILABLE.name email = "" oauthtoken = "" + showAllApplications = true + showFOSSApplications = true + showPWAApplications = true // TODO: reset access token for Google login. It is not defined yet. } cache.evictAll() diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt index c608a448e..823ab9ead 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -22,4 +22,5 @@ import com.aurora.gplayapi.data.models.AuthData interface LoginRepository { suspend fun anonymousUser(): AuthData + suspend fun googleUser(authData: AuthData, oauthToken: String) } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index 07db25920..bfe90e9f7 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -58,4 +58,12 @@ class LoginRepositoryImpl @Inject constructor( } } } + + // TODO: Remove function parameter once we refactor Google User APIs + override suspend fun googleUser(authData: AuthData, token:String) { + applicationContext.configurations.authData = authData.toJsonString() + applicationContext.configurations.email = authData.email + applicationContext.configurations.oauthtoken = token + applicationContext.configurations.userType = User.GOOGLE.name + } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index f2ac8e3ef..e8d1bcd0a 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -42,6 +42,21 @@ class UserLoginUseCase @Inject constructor( } } + fun googleUser(authData: AuthData, token:String): Flow> = flow { + try { + emit( + Resource.Success( + loginRepository.googleUser( + authData = authData, + oauthToken = token + ) + ) + ) + } catch (e: Exception) { + emit(Resource.Error(e.localizedMessage)) + } + } + fun retrieveCachedAuthData(): Flow> = flow { try { emit(Resource.Loading()) diff --git a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt index b13f51523..ff0ef589d 100644 --- a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt @@ -18,6 +18,7 @@ package foundation.e.apps.domain.settings.usecase +import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.data.enums.User import foundation.e.apps.domain.common.repository.CommonRepository import foundation.e.apps.utils.Resource @@ -35,6 +36,12 @@ class SettingsUseCase @Inject constructor( }.onFailure { emit(Resource.Error("Something went wrong in fun currentUser()")) } } + fun currentAuthData(): Flow> = flow { + kotlin.runCatching { + emit(Resource.Success(commonRepository.cacheAuthData())) + }.onFailure { emit(Resource.Error("Something went wrong in fun currentUser()")) } + } + fun logoutUser(): Flow> = flow { runCatching { commonRepository.resetCachedData() diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 8b4c7d564..434b9fdbd 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -93,10 +93,33 @@ class LoginViewModel @Inject constructor( */ fun initialGoogleLogin(email: String, oauthToken: String, onUserSaved: () -> Unit) { viewModelScope.launch { - loginSourceRepository.saveGoogleLogin(email, oauthToken) - loginSourceRepository.saveUserType(User.GOOGLE) + val authObjectsLocal = loginSourceRepository.getAuthObjects(listOf()) + authObjects.postValue(authObjectsLocal) + + if (authObjectsLocal.isNotEmpty() && + authObjectsLocal[0] is AuthObject.GPlayAuth) { + val authObject = authObjectsLocal[0] as AuthObject.GPlayAuth + authObject.result.data?.let { authData -> + userLoginUseCase.googleUser(authData, oauthToken).collect() + _loginState.value = LoginState(isLoading = false, isLoggedIn = true) + } ?: kotlin.run { + _loginState.value = + LoginState( + isLoading = false, + isLoggedIn = false, + error = "Google login failed" + ) + } + } else { + _loginState.value = + LoginState( + isLoading = false, + isLoggedIn = false, + error = "Google login failed" + ) + } + onUserSaved() - startLoginFlow() } } diff --git a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt index 1f0c93a8a..a7da9bfc0 100644 --- a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt +++ b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsState.kt @@ -18,9 +18,13 @@ package foundation.e.apps.presentation.settings +import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.data.enums.User -data class CurrentUserState( - val error: String = "", - val user: User = User.UNAVAILABLE +var settingUserState = SettingUserState() + +data class SettingUserState( + var error: String = "", + var authData: AuthData? = null, + var user: User = User.UNAVAILABLE ) diff --git a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt index 1d7de54cd..e406ea2b1 100644 --- a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt @@ -36,21 +36,40 @@ class SettingsViewModel @Inject constructor( private val settingsUseCase: SettingsUseCase ) : ViewModel() { - private val _currentUserState: MutableLiveData = MutableLiveData() - val currentUserState: LiveData = _currentUserState + private val _currentUserState: MutableLiveData = MutableLiveData() + val currentUserState: LiveData = _currentUserState - fun getCurrentUser() { + fun currentUser() { viewModelScope.launch { settingsUseCase.currentUser().onEach { result -> when (result) { is Resource.Success -> { _currentUserState.value = - result.data?.let { CurrentUserState(user = it) } + result.data?.let { settingUserState.apply { user = it } } } is Resource.Error -> { - _currentUserState.value = CurrentUserState( - error = result.message ?: "An unexpected error occurred" - ) + _currentUserState.value = + settingUserState.apply { error = result.message ?: "An unexpected error occurred" } + + } + + is Resource.Loading -> TODO() + } + }.collect() + } + } + + fun currentAuthData() { + viewModelScope.launch { + settingsUseCase.currentAuthData().onEach { result -> + when (result) { + is Resource.Success -> { + _currentUserState.value = + result.data?.let { settingUserState.apply { authData = it } } + } + is Resource.Error -> { + _currentUserState.value = + settingUserState.apply { error = result.message ?: "An unexpected error occurred" } } is Resource.Loading -> TODO() @@ -62,4 +81,8 @@ class SettingsViewModel @Inject constructor( fun logout() { settingsUseCase.logoutUser().launchIn(viewModelScope) } + + fun resetSettingState() { + settingUserState = SettingUserState() + } } diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index 354b8fcd9..00f715511 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -163,15 +163,20 @@ class SettingsFragment : PreferenceFragmentCompat() { // This is useful if a user from older App Lounge updates to this version disableDependentCheckbox(onlyUnmeteredNetwork, autoInstallUpdate) - loginViewModel.loginState.observe(viewLifecycleOwner) { - val user = it.user - val authData = it.authData - when (user) { + settingsViewModel.currentUser() + settingsViewModel.currentUserState.observe(viewLifecycleOwner) { + when (it.user) { + User.ANONYMOUS -> { + binding.accountType.setText(R.string.user_anonymous) + binding.email.isVisible = false + } User.GOOGLE -> { - if (!authData!!.isAnonymous) { + it.authData?.let { authData -> binding.accountType.text = authData.userProfile?.name binding.email.text = authData.email binding.avatar.load(authData.userProfile?.artwork?.url) + } ?: run { + settingsViewModel.currentAuthData() } } User.NO_GOOGLE -> { @@ -179,17 +184,7 @@ class SettingsFragment : PreferenceFragmentCompat() { binding.email.isVisible = false setCheckboxForNoGoogle() } - else -> {} - } - } - settingsViewModel.getCurrentUser() - settingsViewModel.currentUserState.observe(viewLifecycleOwner) { - when (it.user) { - User.ANONYMOUS -> { - binding.accountType.setText(R.string.user_anonymous) - binding.email.isVisible = false - } else -> {} } } @@ -289,5 +284,6 @@ class SettingsFragment : PreferenceFragmentCompat() { } super.onDestroyView() _binding = null + settingsViewModel.resetSettingState() } } -- GitLab From bd54ff52b3fc20e79bce47263ee381189b801993 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 7 Sep 2023 23:09:41 +0530 Subject: [PATCH 060/112] Fix Klint issues --- .../e/apps/data/preference/PreferenceManagerModule.kt | 1 - .../e/apps/domain/install/usecase/AppInstallerUseCase.kt | 2 +- .../e/apps/domain/login/repository/LoginRepositoryImpl.kt | 2 +- .../e/apps/domain/login/usecase/NoGoogleModeUseCase.kt | 2 +- .../foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt | 2 +- .../e/apps/install/workmanager/AppInstallProcessor.kt | 3 --- .../foundation/e/apps/presentation/login/LoginViewModel.kt | 3 ++- .../e/apps/presentation/settings/SettingsViewModel.kt | 1 - .../e/apps/installProcessor/AppInstallProcessorTest.kt | 2 -- 9 files changed, 6 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt index 44213abc9..28b5d8cb4 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt @@ -24,7 +24,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.OpenForTesting import foundation.e.apps.R import foundation.e.apps.data.Constants.PREFERENCE_SHOW_FOSS -import foundation.e.apps.data.Constants.PREFERENCE_SHOW_GPLAY import foundation.e.apps.data.Constants.PREFERENCE_SHOW_PWA import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt index 33b8f7246..1dcc18ac4 100644 --- a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt @@ -15,7 +15,7 @@ class AppInstallerUseCase@Inject constructor( commonRepository.cacheAuthData() } catch (e: Exception) { if (commonRepository.currentUser() == User.NO_GOOGLE) { - return AuthData("","").apply { + return AuthData("", "").apply { this.isAnonymous = false this.locale = Locale.getDefault() } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index bfe90e9f7..22e2f1def 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -60,7 +60,7 @@ class LoginRepositoryImpl @Inject constructor( } // TODO: Remove function parameter once we refactor Google User APIs - override suspend fun googleUser(authData: AuthData, token:String) { + override suspend fun googleUser(authData: AuthData, token: String) { applicationContext.configurations.authData = authData.toJsonString() applicationContext.configurations.email = authData.email applicationContext.configurations.oauthtoken = token diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt index 5ea8e0ba7..ac7f60384 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt @@ -23,4 +23,4 @@ class NoGoogleModeUseCase @Inject constructor(@ApplicationContext private val co User.NO_GOOGLE ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index e8d1bcd0a..06abd74ca 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -42,7 +42,7 @@ class UserLoginUseCase @Inject constructor( } } - fun googleUser(authData: AuthData, token:String): Flow> = flow { + fun googleUser(authData: AuthData, token: String): Flow> = flow { try { emit( Resource.Success( diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 75d90861a..0685434bf 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -21,10 +21,7 @@ package foundation.e.apps.install.workmanager import android.content.Context import android.os.Environment import android.os.StatFs -import app.lounge.storage.cache.configurations -import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.exceptions.ApiException -import com.google.gson.Gson import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 434b9fdbd..db10975b0 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -97,7 +97,8 @@ class LoginViewModel @Inject constructor( authObjects.postValue(authObjectsLocal) if (authObjectsLocal.isNotEmpty() && - authObjectsLocal[0] is AuthObject.GPlayAuth) { + authObjectsLocal[0] is AuthObject.GPlayAuth + ) { val authObject = authObjectsLocal[0] as AuthObject.GPlayAuth authObject.result.data?.let { authData -> userLoginUseCase.googleUser(authData, oauthToken).collect() diff --git a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt index e406ea2b1..c838ad5c9 100644 --- a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt @@ -50,7 +50,6 @@ class SettingsViewModel @Inject constructor( is Resource.Error -> { _currentUserState.value = settingUserState.apply { error = result.message ?: "An unexpected error occurred" } - } is Resource.Loading -> TODO() diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt index 462ff1cd6..e8919f8be 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt @@ -21,8 +21,6 @@ package foundation.e.apps.installProcessor import android.content.Context import androidx.arch.core.executor.testing.InstantTaskExecutorRule import app.lounge.storage.cache.configurations -import com.aurora.gplayapi.data.models.AuthData -import com.google.gson.Gson import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fdroid.FdroidRepository import foundation.e.apps.data.fused.FusedAPIRepository -- GitLab From 04f39f8d8b4cd3f259eae8d2913b4adb93b6dd82 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 11 Sep 2023 19:50:48 +0530 Subject: [PATCH 061/112] Remove unwanted resource string --- .../e/apps/domain/common/repository/CommonRepositoryImpl.kt | 2 -- app/src/main/res/values-de/strings.xml | 5 ----- app/src/main/res/values-es/strings.xml | 5 ----- app/src/main/res/values-fi/strings.xml | 5 ----- app/src/main/res/values-fr/strings.xml | 5 ----- app/src/main/res/values-is/strings.xml | 5 ----- app/src/main/res/values-it/strings.xml | 5 ----- app/src/main/res/values-nb-rNO/strings.xml | 5 ----- app/src/main/res/values-nl/strings.xml | 5 ----- app/src/main/res/values-ru/strings.xml | 5 ----- app/src/main/res/values-uk/strings.xml | 5 ----- app/src/main/res/values/strings.xml | 5 ----- 12 files changed, 57 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt index 9ceceb06a..12d5b2fb4 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt @@ -29,7 +29,6 @@ import javax.inject.Inject class CommonRepositoryImpl @Inject constructor( @ApplicationContext val applicationContext: Context, - val cache: Cache ) : CommonRepository { override fun currentUser(): User { @@ -48,7 +47,6 @@ class CommonRepositoryImpl @Inject constructor( showPWAApplications = true // TODO: reset access token for Google login. It is not defined yet. } - cache.evictAll() } override fun cacheAuthData(): AuthData = diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1611e4485..9f1eb055b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -102,10 +102,6 @@ Es ist keine Tracker-Information für diese App verfügbar. Die %s App wird zurzeit nicht unterstützt. Ein Grund könnte sein, dass die App noch nicht sehr verbreitet, oder ein anderer Fehler aufgetreten ist. Diese App wird nicht unterstützt! - Einstellungen öffnen - Gewisse Netzwerkfehler verhindern das Holen der Apps. -\n -\nÖffne die Einstellungen und suche nur nach Quelloffenen Apps oder PWAs. Gewisse Netzwerkfehler verhindern das Holen der Apps. Zeit abgelaufen beim Holen von Apps! Deine App wurde nicht gefunden. @@ -134,7 +130,6 @@ \nBitte versuche es später noch einmal. Fehler beim Laden der Apps. Mehr Info - Google Play Apps können nicht angezeigt werden, wenn nur Open Source Apps anzeigen erlaubt sind. Schließen Dies kann verursacht sein durch einen Fehler bei der Generierung oder Verifizierung des Tokens. \n diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1ed92dcb2..6a41157b8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -120,11 +120,7 @@ \nVuelva a intentarlo o inténtelo más tarde. Ha ocurrido un error al cargar aplicaciones. Más información - Abrir ajustes Un problema de red está impidiendo la obtención de aplicaciones. - Un problema de red está impidiendo la obtención de aplicaciones. -\n -\nAbra la configuración para buscar solo aplicaciones de código abierto o PWA. ¡Límite de tiempo buscando aplicaciones! No hay información de rastreo disponible para esta aplicación. Actualización de %1$s aplicaciones completada en %2$s. @@ -137,7 +133,6 @@ Algunas aplicaciones propietarias pueden tener también una versión de código abierto. En ese caso, App Lounge sólo muestra la versión de código abierto, para evitar duplicados. Aplicaciones de código abierto ¿Por qué veo la versión de código abierto\? - Google Play no se puede mostrar cuando sólo se permiten aplicaciones de código abierto. Mostrar más ¡Algo salió mal! Su aplicación no fue encontrada. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 97a732be5..2f1f9a6d1 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -124,16 +124,11 @@ \nYritä uudelleen tai kokeile myöhemmin. Sovellusten lataamisessa tapahtui virhe. Lisätietoja - Avaa asetukset Jokin verkko-ongelma estää kaikkien sovellusten hakemisen. - Jokin verkko-ongelma estää kaikkien sovellusten hakemisen. -\n -\nAvaa asetukset ja etsi vain avoimen lähdekoodin sovelluksia tai PWA:ita. Aikakatkaisu sovellusten hakemisessa! Tutustu PWA:n Päivitysvirhe! Päivitystä ei voida suorittaa koska allekirjoitus on ristiriidassa %1$s ja puhelimeesi asennetun version välillä. Voit korjata tämän poistamalla %1$s ja asentamalla se uudestaan App Loungesta.

Huomautus: Tämä viesti ei tule näkyviin uudelleen.
- Google Play -sovelluksia ei voida näyttää, kun vain avoimen lähdekoodin sovellukset ovat valittuna. Näytä lisää Tapahtui virhe! Sovellusta ei löytynyt. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 23bbc55a3..46ab08ab9 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -118,17 +118,12 @@ \nMerci de réessayer maintenant ou plus tard.
Une erreur est survenue pendant le chargement de l\'application. Plus d\'informations - Ouvrir les Paramètres Des problèmes de connexion empêchent de récupérer toutes les applications. - Des problèmes de connexion empêchent de récupérer toutes les applications. -\n -\nOuvrez les paramètres pour n\'afficher que les applications Open Source et PWA. Expiration du délai de récupération des applications ! Aucune information concernant les pisteurs pour cette application. PWA à découvrir Erreur lors de la mise à jour ! La mise à jour ne peut être appliquée car la signature de la mise à jour de %1$s ne correspond pas à la signature de la version installée sur votre téléphone. Pour y remédier vous pouvez désinstaller %1$s puis la réinstaller depuis App Lounge.

Note : Ce message ne s\'affichera plus.
- Il est impossible d\'afficher les applications Google Play quand seules les applications Open Source sont sélectionnées. Afficher plus Une erreur s\'est produite ! Application non trouvée. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 7fc484417..a3a119c76 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -72,7 +72,6 @@ Uppfærsluvilla! Sótt gögn Uppfærslur - Opna stillingar Nánari upplýsingar Innskráning með Google mistókst! Nafnlaus innskráning mistókst! @@ -109,9 +108,6 @@ Vinsælir PWA-leikir Næ ekki að tengjast! Athugaðu internettenginguna þína og prófaðu svo aftur Féll á tímamörkum við að sækja forrit! - Einhver vandamál í netkerfinu koma í veg fyrir að hægt sé að sækja öll forrit. -\n -\nOpnaðu stillingar til að leita einungis að forritum með opinn grunnkóða eða PWA-vefforritum. Það er ekki nægt pláss tiltækt til að sækja þetta forrit! Að uppfæra allt mistókst. Sjálfvirkar tilraunir til endurtekningar í gangi. Kynntu þér @@ -126,7 +122,6 @@ Uppfærslur forrita verða sjálfkrafa settar inn Uppfærslur forrita verða ekki settar sjálfkrafa inn Bíð eftir gjaldfrjálsu neti - Get ekki birt forrit úr Google Play þegar einungis eru leyfð forrit með opinn grunnkóða. Greining persónuverndar Rekjarar Vegna tímabundinna vandamála er ekki hægt að uppfæra öll forritin þín. Reyndu aftur síðar. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 0b480e892..0c5466bd2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -108,11 +108,7 @@ Acquista %1$s A causa di un errore di rete, App Lounge non è riuscita a ottenere i dati di accesso a Google. Accesso a Google non riuscito! - Apri Impostazioni Un problema di rete impedisce il recupero di tutte le app. - Un problema di rete impedisce il recupero di tutte le app. -\n -\nAprire le impostazioni per selezionare solo app open source o le PWA. Timeout nel recupero delle app! Informazioni sui tracker di questa app non disponibili. Errore nell\'aggiornamento! @@ -138,7 +134,6 @@ Si è verificato un errore durante il caricamento delle App. Maggiori informazioni Scopri le PWA - Non posso avviare l\'App Google Play se sono permesse solo App open source. Chiudi Mensilmente Settimanalmente diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index c9580b5b3..8a8962dda 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -22,11 +22,7 @@ \nPrøv igjen nå, eller prøv igjen senere.
Feil oppstod ved applikasjonslasting. Mer informasjon - Åpne innstillinger Nettverksproblem forhindrer henting av alle applikasjoner. - Nettverksproblem forhindrer henting av alle applikasjoner. -\n -\nÅpne instillinger for å kun se etter åpen kildekode-applikasjoner eller PWAer. Tidsavbrudd ved henting av applikasjoner! Kan ikke koble til! Kontroller internettilgangen og prøv igjen Utregnet med <a href=%1$s> Exodus Privacy-analyse @@ -49,7 +45,6 @@ Oppdater alle Åpen kildekode-applikasjoner Hvorfor ser jeg åpen kildekode-versjonen\? - Kan ikke vise Google Play-applikasjoner når kun åpen kildekode-applikasjoner er tillatt. Vis mer Applikasjonen den ble ikke funnet. Der er ikke nok tilgjengelig lagringsplass for å laste ned denne applikasjonen! diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index cd5d91f7b..71ecfc7a3 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -16,7 +16,6 @@ Google aanmelden mislukt! Fout tijdens het laden van apps. Meer info - Open Instellingen Kan niet verbinden! Kijk alstublieft je internet verbinding na en probeer opnieuw Geen tracker informatie beschikbaar voor deze app. Geen trackers gevonden! @@ -40,7 +39,6 @@ %1$d app updates zijn beschikbaar Alles updaten - Kan geen Google Play app tonen wanneer enkel open source apps toegelaten zijn. Toon meer Er is iets fout gegaan! Je applicatie is niet gevonden. @@ -129,9 +127,6 @@ \n \nProbeer a.u.b. opnieuw op een later tijdstip.
Een netwerk probleem verhindert het ophalen van de applicaties. - Een netwerk probleem verhindert het ophalen van de applicaties. -\n -\nOpen Instellingen om enkel voor Open source apps of PWA\'s te kijken. Time-out bij het ophalen van applicaties! Geen \"runtime android\" machtiging gevonden! Top Populaire Games diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index dcd14689e..66b47e8f9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -77,11 +77,7 @@ \nПожалуйста, повторите попытку или попробуйте позже.
Произошла ошибка при загрузке приложений. Больше информации - Открыть настройки Какая-то сетевая проблема препятствует загрузке всех приложений. - Какая-то сетевая проблема препятствует получению всех приложений. -\n -\nОткройте настройки, чтобы искать только Open Source приложения или PWA. Тайм-аут загрузки приложений! Не удается подключиться! Пожалуйста, проверьте подключение к Интернету и повторите попытку Информация о трекере для этого приложения отсутствует. @@ -115,7 +111,6 @@ Обновление не может быть применено из-за несоответствия подписи между обновлением %1$s и версией, установленной на вашем телефоне. Чтобы исправить это, вы можете удалить %1$s, а затем снова установить его из App Lounge.

Примечание: это сообщение больше не появится.
Все приложения обновлены Обновить все - Невозможно показать приложение Google Play, когда разрешены только Open Source приложения. Некоторые проприетарные приложения могут также иметь Open Source версию. Когда это происходит, App Lounge показывает только Open Source версию, чтобы избежать дублирования. Почему я вижу Open Source версию\? Open Source приложения diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 12b9d97a8..a5da30926 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -89,7 +89,6 @@ %1$d оновлень застосунків доступно %1$d оновлень застосунків доступно - Неможливо відобразити застосунок Google Play коли дозволені тільки застосунки з відкритим кодом. Оновлено до: %1$s Рейтинг Приватності автоматично обчислюється з дозволів та трекерів, що були знайдені в застосунку.<br /><br /> Його алгоритм обчислення можна <a href=%1$s>побачити тут</a>.<br /><br />Виявлення трекерів робиться за допомогою<a href=%2$s> Інструментів Exodus Privacy</a>.<br /><br />Рейтинг від 0 до 10.<br /><br />Дізнайтесь більше про те, як обчислюється Рейтинг Приватності, які в нього є обмеження та як він може допомогти Вам захистити себе від мікротаргетингу <a href=%3$s>на цій сторінці</a>. Закрити @@ -126,11 +125,7 @@ \nБудь ласка, повторіть спробу, або спробуйте пізніше.
Виникла проблема при завантаженні застосунків. Більше інформації - Відкрити налаштування Деяка проблема мережі не дає отримувати всі застосунки. - Деяка проблема з мережою не дає отримувати всі застосунки. -\n -\nВідкрийте налаштування, щоб перевірити опції \"Тільки застосунки з відкритим кодом\" та \"Тільки прогресивні вебзастосунки\". Час запиту на отримання застосунків вичерпано! Неможливо приєднатись до мережі! Будь ласка, перевірте налаштування Вашої мережі та спробуйте знову Трекерів не було знайдено! diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 48ff9b26b..e1b08fd1d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -119,7 +119,6 @@ Your application was not found. Something went wrong! Show more - Cannot show Google Play app when only open source apps are allowed. Why am I seeing the Open Source version? Open Source apps Some proprietary apps may also have an Open Source version. Whenever this happens App Lounge shows the Open Source version only, in order to avoid duplicates. @@ -184,11 +183,7 @@ Timeout fetching applications! - Some network issue is preventing fetching all applications. - \n\nOpen settings to look for Open source apps or PWAs only. - Some network issue is preventing fetching all applications. - Open Settings More info -- GitLab From cc2dbdb6e7f6db1e4a13e4daa5e6d5c7f55307a8 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 11 Sep 2023 21:35:16 +0530 Subject: [PATCH 062/112] Fix google login failure issue --- .../java/foundation/e/apps/presentation/login/LoginViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index db10975b0..64b5d19e7 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -93,6 +93,8 @@ class LoginViewModel @Inject constructor( */ fun initialGoogleLogin(email: String, oauthToken: String, onUserSaved: () -> Unit) { viewModelScope.launch { + loginSourceRepository.saveGoogleLogin(email, oauthToken) + loginSourceRepository.saveUserType(User.GOOGLE) val authObjectsLocal = loginSourceRepository.getAuthObjects(listOf()) authObjects.postValue(authObjectsLocal) -- GitLab From f3f8923bfaf3b294ba7933efcdcbdf3626ebcfb2 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 11 Sep 2023 21:40:58 +0530 Subject: [PATCH 063/112] Fix Klint issues --- .../e/apps/domain/common/repository/CommonRepositoryImpl.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt index 12d5b2fb4..d26fff840 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt @@ -24,11 +24,10 @@ import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.data.enums.User import foundation.e.apps.utils.toAuthData -import okhttp3.Cache import javax.inject.Inject class CommonRepositoryImpl @Inject constructor( - @ApplicationContext val applicationContext: Context, + @ApplicationContext val applicationContext: Context ) : CommonRepository { override fun currentUser(): User { -- GitLab From 2ff4b27888ae931f0183a2bb52b8b4ad26129eca Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 11 Sep 2023 22:04:02 +0530 Subject: [PATCH 064/112] Fix testcase compile time issues --- .../java/foundation/e/apps/FakePreferenceModule.kt | 6 +++--- .../e/apps/presentation/login/LoginViewModelTest.kt | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt b/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt index 538bd64e5..f227f3fe8 100644 --- a/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt +++ b/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt @@ -26,15 +26,15 @@ class FakePreferenceModule(context: Context) : PreferenceManagerModule(context) var isGplaySelectedFake = false var shouldUpdateFromOtherStores = true - override fun isPWASelected(): Boolean { + fun isPWASelected(): Boolean { return isPWASelectedFake } - override fun isOpenSourceSelected(): Boolean { + fun isOpenSourceSelected(): Boolean { return isOpenSourceelectedFake } - override fun isGplaySelected(): Boolean { + fun isGplaySelected(): Boolean { return isGplaySelectedFake } diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt index 16b2d5d77..b768ec188 100644 --- a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt +++ b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt @@ -20,6 +20,7 @@ package foundation.e.apps.presentation.login import androidx.arch.core.executor.testing.InstantTaskExecutorRule import foundation.e.apps.data.login.LoginSourceRepository +import foundation.e.apps.domain.login.usecase.NoGoogleModeUseCase import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.loginFailureMessage import foundation.e.apps.testAnonymousResponseData @@ -52,6 +53,9 @@ class LoginViewModelTest { @Mock lateinit var mockUserLoginUseCase: UserLoginUseCase + @Mock + lateinit var mockNoGoogleModeUseCase: NoGoogleModeUseCase + @Mock lateinit var loginRepository: LoginSourceRepository @@ -66,7 +70,7 @@ class LoginViewModelTest { Mockito.`when`(mockUserLoginUseCase.anonymousUser()) .thenReturn(flowOf(Resource.Success(testAnonymousResponseData))) - val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase, mockNoGoogleModeUseCase) loginViewModel.authenticateAnonymousUser() testDispatcher.scheduler.advanceUntilIdle() val result = loginViewModel.loginState.getOrAwaitValue() @@ -79,7 +83,7 @@ class LoginViewModelTest { Mockito.`when`(mockUserLoginUseCase.anonymousUser()) .thenReturn(flowOf(Resource.Error(loginFailureMessage, null))) - val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase, mockNoGoogleModeUseCase) loginViewModel.authenticateAnonymousUser() testDispatcher.scheduler.advanceUntilIdle() val result = loginViewModel.loginState.getOrAwaitValue() @@ -93,7 +97,7 @@ class LoginViewModelTest { Mockito.`when`(mockUserLoginUseCase.anonymousUser()) .thenReturn(flowOf(Resource.Loading(null))) - val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase, mockNoGoogleModeUseCase) loginViewModel.authenticateAnonymousUser() testDispatcher.scheduler.advanceUntilIdle() val result = loginViewModel.loginState.getOrAwaitValue() -- GitLab From 67f71f7a8c54a17b1712b5eefaf88fe5591fd073 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 19 Sep 2023 12:35:37 +0530 Subject: [PATCH 065/112] Fix merge issues --- .../e/apps/data/fused/FusedApiImpl.kt | 121 +++++++----------- .../preference/PreferenceManagerModule.kt | 1 + .../e/apps/install/updates/UpdatesWorker.kt | 1 - .../apps/presentation/login/LoginViewModel.kt | 1 - .../ApplicationListViewModel.kt | 28 +--- .../e/apps/ui/search/SearchViewModel.kt | 28 ++-- 6 files changed, 69 insertions(+), 111 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index 9c27fbe14..151aecf62 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -22,8 +22,6 @@ import android.content.Context import android.text.format.Formatter import androidx.lifecycle.LiveData import androidx.lifecycle.liveData -import androidx.lifecycle.map -import app.lounge.storage.cache.configurations import com.aurora.gplayapi.Constants import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App @@ -114,15 +112,16 @@ class FusedApiImpl @Inject constructor( override fun getApplicationCategoryPreference(): List { val prefs = mutableListOf() - if (context.configurations.showAllApplications) prefs.add(APP_TYPE_ANY) - if (context.configurations.showFOSSApplications) prefs.add(APP_TYPE_OPEN) - if (context.configurations.showPWAApplications) prefs.add(APP_TYPE_PWA) + if (preferenceManagerModule.isGplaySelected()) prefs.add(APP_TYPE_ANY) + if (preferenceManagerModule.isOpenSourceSelected()) prefs.add(APP_TYPE_OPEN) + if (preferenceManagerModule.isPWASelected()) prefs.add(APP_TYPE_PWA) return prefs } override suspend fun getHomeScreenData( - authData: AuthData + authData: AuthData, ): LiveData>> { + val list = mutableListOf() var resultGplay: FusedHomeDeferred? = null var resultOpenSource: FusedHomeDeferred? = null @@ -130,15 +129,16 @@ class FusedApiImpl @Inject constructor( return liveData { coroutineScope { - if (context.configurations.showAllApplications) { + + if (preferenceManagerModule.isGplaySelected()) { resultGplay = async { loadHomeData(list, Source.GPLAY, authData) } } - if (context.configurations.showFOSSApplications) { + if (preferenceManagerModule.isOpenSourceSelected()) { resultOpenSource = async { loadHomeData(list, Source.OPEN, authData) } } - if (context.configurations.showPWAApplications) { + if (preferenceManagerModule.isPWASelected()) { resultPWA = async { loadHomeData(list, Source.PWA, authData) } } @@ -158,10 +158,8 @@ class FusedApiImpl @Inject constructor( private suspend fun loadHomeData( priorList: MutableList, source: Source, - authData: AuthData + authData: AuthData, ): ResultSupreme> { - val apiStatus = when (source) { - Source.GPLAY -> runCodeWithTimeout({ val result = when (source) { Source.GPLAY -> handleNetworkResult> { @@ -221,7 +219,7 @@ class FusedApiImpl @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ override suspend fun getCategoriesList( - type: CategoryType + type: CategoryType, ): Triple, String, ResultStatus> { val categoriesList = mutableListOf() val preferredApplicationType = preferenceManagerModule.preferredApplicationType() @@ -263,7 +261,7 @@ class FusedApiImpl @Inject constructor( val searchResult = mutableListOf() val cleanApkResults = mutableListOf() - if (context.configurations.showFOSSApplications) { + if (preferenceManagerModule.isOpenSourceSelected()) { finalSearchResult = fetchOpenSourceSearchResult( cleanApkResults, query, @@ -272,7 +270,7 @@ class FusedApiImpl @Inject constructor( ) } - if (context.configurations.showPWAApplications) { + if (preferenceManagerModule.isPWASelected()) { finalSearchResult = fetchPWASearchResult( query, searchResult, @@ -310,7 +308,7 @@ class FusedApiImpl @Inject constructor( packageSpecificResults, query ), - context.configurations.showAllApplications + preferenceManagerModule.isGplaySelected() ) ) } @@ -338,7 +336,7 @@ class FusedApiImpl @Inject constructor( packageSpecificResults, query ), - context.configurations.showAllApplications || context.configurations.showPWAApplications + preferenceManagerModule.isGplaySelected() || preferenceManagerModule.isPWASelected() ) ) } @@ -351,14 +349,12 @@ class FusedApiImpl @Inject constructor( var gplayPackageResult: FusedApp? = null var cleanapkPackageResult: FusedApp? = null - val status = runCodeWithTimeout({ - if (context.configurations.showAllApplications) { val result = handleNetworkResult { if (preferenceManagerModule.isGplaySelected()) { gplayPackageResult = getGplayPackagResult(query, authData) } - if (context.configurations.showFOSSApplications) { + if (preferenceManagerModule.isOpenSourceSelected()) { cleanapkPackageResult = getCleanApkPackageResult(query) } } @@ -372,7 +368,7 @@ class FusedApiImpl @Inject constructor( gplayPackageResult?.let { packageSpecificResults.add(it) } } - if (context.configurations.showAllApplications) { + if (preferenceManagerModule.isGplaySelected()) { packageSpecificResults.add(FusedApp(isPlaceHolder = true)) } @@ -407,7 +403,7 @@ class FusedApiImpl @Inject constructor( val finalList = (packageSpecificResults + filteredResults).toMutableList() finalList.removeIf { it.isPlaceHolder } - if (context.configurations.showAllApplications) { + if (preferenceManagerModule.isGplaySelected()) { finalList.add(FusedApp(isPlaceHolder = true)) } @@ -415,7 +411,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun getCleanApkPackageResult( - query: String + query: String, ): FusedApp? { getCleanapkSearchResult(query).let { if (it.isSuccess() && it.data!!.package_name.isNotBlank()) { @@ -427,7 +423,7 @@ class FusedApiImpl @Inject constructor( private suspend fun getGplayPackagResult( query: String, - authData: AuthData + authData: AuthData, ): FusedApp? { try { getApplicationDetails(query, query, authData, Origin.GPLAY).let { @@ -483,7 +479,7 @@ class FusedApiImpl @Inject constructor( packageName, moduleName, versionCode, - offerType + offerType, ) for (element in list) { if (element.name == "$moduleName.apk") { @@ -610,7 +606,7 @@ class FusedApiImpl @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ private suspend fun getAppDetailsListFromCleanapk( - packageNameList: List + packageNameList: List, ): Pair, ResultStatus> { var status = ResultStatus.OK val fusedAppList = mutableListOf() @@ -654,7 +650,7 @@ class FusedApiImpl @Inject constructor( */ private suspend fun getAppDetailsListFromGPlay( packageNameList: List, - authData: AuthData + authData: AuthData, ): Pair, ResultStatus> { val fusedAppList = mutableListOf() @@ -696,7 +692,7 @@ class FusedApiImpl @Inject constructor( */ override suspend fun filterRestrictedGPlayApps( authData: AuthData, - appList: List + appList: List, ): ResultSupreme> { val filteredFusedApps = mutableListOf() return handleNetworkResult { @@ -719,22 +715,6 @@ class FusedApiImpl @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5720 */ override suspend fun getAppFilterLevel(fusedApp: FusedApp, authData: AuthData?): FilterLevel { - if (fusedApp.package_name.isBlank()) { - return FilterLevel.UNKNOWN - } - if (fusedApp.origin == Origin.CLEANAPK) { - /* - * Whitelist all open source apps. - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5785 - */ - return FilterLevel.NONE - } - if (authData == null) { - return if (fusedApp.origin == Origin.GPLAY) { - FilterLevel.UNKNOWN - } else { - FilterLevel.NONE - } return when { fusedApp.package_name.isBlank() -> FilterLevel.UNKNOWN !fusedApp.isFree && fusedApp.price.isBlank() -> FilterLevel.UI @@ -794,6 +774,7 @@ class FusedApiImpl @Inject constructor( authData: AuthData, origin: Origin ): Pair { + var response: FusedApp? = null val result = handleNetworkResult { @@ -827,28 +808,28 @@ class FusedApiImpl @Inject constructor( */ private suspend fun handleAllSourcesCategories( categoriesList: MutableList, - type: CategoryType + type: CategoryType, ): Pair { var apiStatus = ResultStatus.OK var errorApplicationCategory = "" - if (context.configurations.showFOSSApplications) { + if (preferenceManagerModule.isOpenSourceSelected()) { val openSourceCategoryResult = fetchOpenSourceCategories(type) categoriesList.addAll(openSourceCategoryResult.second) apiStatus = openSourceCategoryResult.first errorApplicationCategory = openSourceCategoryResult.third } - if (context.configurations.showPWAApplications) { + if (preferenceManagerModule.isPWASelected()) { val pwaCategoriesResult = fetchPWACategories(type) categoriesList.addAll(pwaCategoriesResult.second) apiStatus = pwaCategoriesResult.first errorApplicationCategory = pwaCategoriesResult.third } - if (context.configurations.showAllApplications) { + if (preferenceManagerModule.isGplaySelected()) { val gplayCategoryResult = fetchGplayCategories( - type + type, ) categoriesList.addAll(gplayCategoryResult.data ?: listOf()) apiStatus = gplayCategoryResult.getResultStatus() @@ -875,16 +856,14 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchPWACategories( - type: CategoryType + type: CategoryType, ): Triple, String> { val fusedCategoriesList = mutableListOf() val result = handleNetworkResult { getPWAsCategories()?.let { fusedCategoriesList.addAll( getFusedCategoryBasedOnCategoryType( - it, - type, - AppTag.PWA(context.getString(R.string.pwa)) + it, type, AppTag.PWA(context.getString(R.string.pwa)) ) ) } @@ -894,7 +873,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchOpenSourceCategories( - type: CategoryType + type: CategoryType, ): Triple, String> { val fusedCategoryList = mutableListOf() val result = handleNetworkResult { @@ -924,7 +903,7 @@ class FusedApiImpl @Inject constructor( private suspend fun runCodeWithTimeout( block: suspend () -> Unit, timeoutBlock: (() -> Unit)? = null, - exceptionBlock: ((e: Exception) -> Unit)? = null + exceptionBlock: ((e: Exception) -> Unit)? = null, ): ResultStatus { return try { withTimeout(timeoutDurationInMillis) { @@ -944,7 +923,7 @@ class FusedApiImpl @Inject constructor( } private fun updateCategoryDrawable( - category: FusedCategory + category: FusedCategory, ) { category.drawable = getCategoryIconResource(getCategoryIconName(category)) @@ -1030,13 +1009,13 @@ class FusedApiImpl @Inject constructor( private suspend fun getOpenSourceAppsResponse(category: String): Search? { return cleanApkAppsRepository.getAppsByCategory( - category + category, ).body() } private suspend fun getPWAAppsResponse(category: String): Search? { return cleanApkPWARepository.getAppsByCategory( - category + category, ).body() } @@ -1046,7 +1025,7 @@ class FusedApiImpl @Inject constructor( id = id.lowercase(), title = this.title, browseUrl = this.browseUrl, - imageUrl = this.imageUrl + imageUrl = this.imageUrl, ) } @@ -1056,7 +1035,7 @@ class FusedApiImpl @Inject constructor( private suspend fun getCleanAPKSearchResults( keyword: String, - source: String = CleanApkRetrofit.APP_SOURCE_FOSS + source: String = CleanApkRetrofit.APP_SOURCE_FOSS, ): List { val list = mutableListOf() val response = @@ -1080,7 +1059,7 @@ class FusedApiImpl @Inject constructor( val searchResults = gplayRepository.getSearchResult(query, nextPageSubBundle?.toMutableSet()) - if (!context.configurations.showAllApplications) { + if (!preferenceManagerModule.isGplaySelected()) { return@handleNetworkResult Pair(listOf(), setOf()) } @@ -1258,9 +1237,7 @@ class FusedApiImpl @Inject constructor( this.labeledRating.run { if (isNotEmpty()) { this.replace(",", ".").toDoubleOrNull() ?: -1.0 - } else { - -1.0 - } + } else -1.0 } ), offer_type = this.offerType, @@ -1270,7 +1247,7 @@ class FusedApiImpl @Inject constructor( appSize = Formatter.formatFileSize(context, this.size), isFree = this.isFree, price = this.price, - restriction = this.restriction + restriction = this.restriction, ) app.updateStatus() return app @@ -1302,13 +1279,9 @@ class FusedApiImpl @Inject constructor( private fun FusedApp.updateSource() { this.apply { - source = if (origin == Origin.CLEANAPK && is_pwa) { - context.getString(R.string.pwa) - } else if (origin == Origin.CLEANAPK) { - context.getString(R.string.open_source) - } else { - "" - } + source = if (origin == Origin.CLEANAPK && is_pwa) context.getString(R.string.pwa) + else if (origin == Origin.CLEANAPK) context.getString(R.string.open_source) + else "" } } @@ -1342,7 +1315,7 @@ class FusedApiImpl @Inject constructor( private fun areFusedAppsUpdated( oldFusedHome: FusedHome, - newFusedHome: FusedHome + newFusedHome: FusedHome, ): Boolean { val fusedAppDiffUtil = HomeChildFusedAppDiffUtil() if (oldFusedHome.list.size != newFusedHome.list.size) { @@ -1394,7 +1367,7 @@ class FusedApiImpl @Inject constructor( return false } - override fun isOpenSourceSelected() = context.configurations.showFOSSApplications + override fun isOpenSourceSelected() = preferenceManagerModule.isOpenSourceSelected() override suspend fun getGplayAppsByCategory( authData: AuthData, category: String, diff --git a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt index c31197170..1a385b185 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt @@ -25,6 +25,7 @@ import foundation.e.apps.OpenForTesting import foundation.e.apps.R import foundation.e.apps.data.Constants.PREFERENCE_SHOW_FOSS import foundation.e.apps.data.Constants.PREFERENCE_SHOW_PWA +import foundation.e.apps.data.Constants.PREFERENCE_SHOW_GPLAY import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt index db4e91115..76e623a9f 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt @@ -39,7 +39,6 @@ class UpdatesWorker @AssistedInject constructor( private val updatesManagerRepository: UpdatesManagerRepository, private val dataStoreManager: DataStoreManager, private val loginSourceRepository: LoginSourceRepository, - private val appInstallProcessor: AppInstallProcessor, private val blockedAppRepository: BlockedAppRepository, private val appInstallProcessor: AppInstallProcessor, private val updatesUseCase: UpdatesUseCase diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 22a3350ce..864c9308b 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -49,7 +49,6 @@ class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, private val userLoginUseCase: UserLoginUseCase, private val noGoogleModeUseCase: NoGoogleModeUseCase, - private val userLoginUseCase: UserLoginUseCase, private val cache: Cache, ) : ViewModel() { diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt index 46fc4b298..3e551455e 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt @@ -43,38 +43,12 @@ class ApplicationListViewModel @Inject constructor( private var nextPageUrl: String? = null - private var currentAuthListObject: List? = null - fun loadData( category: String, source: String, - authData: AuthData? + authData: AuthData?, ) { getList(category, authData ?: AuthData("", ""), source) - super.onLoadData(authObjectList, { successAuthList, _ -> - - // if token is refreshed, then reset all data - if (currentAuthListObject != null && currentAuthListObject != authObjectList) { - appListLiveData.postValue(ResultSupreme.Success(emptyList())) - nextPageUrl = null - } - - if (appListLiveData.value?.data?.isNotEmpty() == true && currentAuthListObject == authObjectList) { - appListLiveData.postValue(appListLiveData.value) - return@onLoadData - } - - this.currentAuthListObject = authObjectList - successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getList(category, result.data!! as AuthData, source) - return@onLoadData - } - - successAuthList.find { it is AuthObject.CleanApk }?.run { - getList(category, AuthData("", ""), source) - return@onLoadData - } - }, retryBlock) } private fun getList(category: String, authData: AuthData, source: String) { diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 9b54cd203..b19a62121 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -24,13 +24,21 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.models.SearchBundle import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.fused.FusedAPIRepository +import foundation.e.apps.data.fused.GplaySearchResult import foundation.e.apps.data.fused.data.FusedApp +import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.data.login.exceptions.CleanApkException +import foundation.e.apps.data.login.exceptions.GPlayException +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject +import kotlin.coroutines.coroutineContext @HiltViewModel class SearchViewModel @Inject constructor( @@ -40,13 +48,17 @@ class SearchViewModel @Inject constructor( val searchSuggest: MutableLiveData?> = MutableLiveData() val searchResult: MutableLiveData, Boolean>>> = MutableLiveData() - private var searchResultLiveData: MutableLiveData, Boolean>>> = - MutableLiveData() private var lastAuthObjects: List? = null private var nextSubBundle: Set? = null + private var isLoading: Boolean = false + + companion object { + private const val DATA_LOAD_ERROR = "Data load error" + } + fun getSearchSuggestions(query: String, authData: AuthData?) { viewModelScope.launch(Dispatchers.IO) { if (query.isNotBlank() && authData != null) { @@ -99,7 +111,7 @@ class SearchViewModel @Inject constructor( ) } - handleException(exception) +// handleException(exception) } nextSubBundle = null @@ -123,7 +135,7 @@ class SearchViewModel @Inject constructor( val gplaySearchResult = fusedAPIRepository.getGplaySearchResults(query, nextSubBundle) if (!gplaySearchResult.isSuccess()) { - handleException(gplaySearchResult.exception ?: UnknownSourceException()) +// handleException(gplaySearchResult.exception ?: UnknownSourceException()) } val isFirstFetch = nextSubBundle == null @@ -153,10 +165,10 @@ class SearchViewModel @Inject constructor( return currentAppList.distinctBy { it.package_name } } - private fun handleException(exception: Exception) { - exceptionsList.add(exception) - exceptionsLiveData.postValue(exceptionsList) - } +// private fun handleException(exception: Exception) { +// exceptionsList.add(exception) +// exceptionsLiveData.postValue(exceptionsList) +// } /** * @return returns true if there is changes in data, otherwise false -- GitLab From 577b82e2f25281db2af4e9965b4eb837411dca95 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 19 Sep 2023 12:44:58 +0530 Subject: [PATCH 066/112] Fix klint issues --- .../java/foundation/e/apps/MainActivity.kt | 3 - .../data/blockedApps/BlockedAppRepository.kt | 2 +- .../e/apps/data/fused/FusedApiImpl.kt | 75 ++++++++++--------- .../apps/data/preference/DataStoreManager.kt | 2 +- .../preference/PreferenceManagerModule.kt | 2 +- .../e/apps/data/updates/UpdatesManagerImpl.kt | 7 +- .../apps/presentation/login/LoginViewModel.kt | 2 +- .../subFrags/ApplicationDialogFragment.kt | 2 +- .../ApplicationListViewModel.kt | 2 +- .../e/apps/UpdateManagerImptTest.kt | 1 - .../e/apps/gplay/GplyHttpClientTest.kt | 4 +- .../e/apps/login/LoginViewModelTest.kt | 23 +++++- .../java/foundation/e/apps/util/FakeCall.kt | 3 +- 13 files changed, 72 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 85d1f5f02..9f4ffceea 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -38,9 +38,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceGPlay -import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.install.updates.UpdatesNotifier @@ -56,7 +54,6 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch -import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint diff --git a/app/src/main/java/foundation/e/apps/data/blockedApps/BlockedAppRepository.kt b/app/src/main/java/foundation/e/apps/data/blockedApps/BlockedAppRepository.kt index 5962030bd..26117b558 100644 --- a/app/src/main/java/foundation/e/apps/data/blockedApps/BlockedAppRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/blockedApps/BlockedAppRepository.kt @@ -32,7 +32,7 @@ import kotlin.coroutines.resume class BlockedAppRepository @Inject constructor( private val downloadManager: DownloadManager, private val gson: Gson, - @Named("cacheDir") private val cacheDir: String, + @Named("cacheDir") private val cacheDir: String ) { companion object { diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index 151aecf62..298c6704b 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -119,9 +119,8 @@ class FusedApiImpl @Inject constructor( } override suspend fun getHomeScreenData( - authData: AuthData, + authData: AuthData ): LiveData>> { - val list = mutableListOf() var resultGplay: FusedHomeDeferred? = null var resultOpenSource: FusedHomeDeferred? = null @@ -129,7 +128,6 @@ class FusedApiImpl @Inject constructor( return liveData { coroutineScope { - if (preferenceManagerModule.isGplaySelected()) { resultGplay = async { loadHomeData(list, Source.GPLAY, authData) } } @@ -158,9 +156,8 @@ class FusedApiImpl @Inject constructor( private suspend fun loadHomeData( priorList: MutableList, source: Source, - authData: AuthData, + authData: AuthData ): ResultSupreme> { - val result = when (source) { Source.GPLAY -> handleNetworkResult> { priorList.addAll(fetchGPlayHome(authData)) @@ -219,7 +216,7 @@ class FusedApiImpl @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ override suspend fun getCategoriesList( - type: CategoryType, + type: CategoryType ): Triple, String, ResultStatus> { val categoriesList = mutableListOf() val preferredApplicationType = preferenceManagerModule.preferredApplicationType() @@ -411,7 +408,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun getCleanApkPackageResult( - query: String, + query: String ): FusedApp? { getCleanapkSearchResult(query).let { if (it.isSuccess() && it.data!!.package_name.isNotBlank()) { @@ -423,7 +420,7 @@ class FusedApiImpl @Inject constructor( private suspend fun getGplayPackagResult( query: String, - authData: AuthData, + authData: AuthData ): FusedApp? { try { getApplicationDetails(query, query, authData, Origin.GPLAY).let { @@ -479,7 +476,7 @@ class FusedApiImpl @Inject constructor( packageName, moduleName, versionCode, - offerType, + offerType ) for (element in list) { if (element.name == "$moduleName.apk") { @@ -606,7 +603,7 @@ class FusedApiImpl @Inject constructor( * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5413 */ private suspend fun getAppDetailsListFromCleanapk( - packageNameList: List, + packageNameList: List ): Pair, ResultStatus> { var status = ResultStatus.OK val fusedAppList = mutableListOf() @@ -650,7 +647,7 @@ class FusedApiImpl @Inject constructor( */ private suspend fun getAppDetailsListFromGPlay( packageNameList: List, - authData: AuthData, + authData: AuthData ): Pair, ResultStatus> { val fusedAppList = mutableListOf() @@ -692,7 +689,7 @@ class FusedApiImpl @Inject constructor( */ override suspend fun filterRestrictedGPlayApps( authData: AuthData, - appList: List, + appList: List ): ResultSupreme> { val filteredFusedApps = mutableListOf() return handleNetworkResult { @@ -745,7 +742,7 @@ class FusedApiImpl @Inject constructor( gplayRepository.getDownloadInfo( fusedApp.package_name, fusedApp.latest_version_code, - fusedApp.offer_type, + fusedApp.offer_type ) }.isSuccess } @@ -774,7 +771,6 @@ class FusedApiImpl @Inject constructor( authData: AuthData, origin: Origin ): Pair { - var response: FusedApp? = null val result = handleNetworkResult { @@ -808,7 +804,7 @@ class FusedApiImpl @Inject constructor( */ private suspend fun handleAllSourcesCategories( categoriesList: MutableList, - type: CategoryType, + type: CategoryType ): Pair { var apiStatus = ResultStatus.OK var errorApplicationCategory = "" @@ -829,7 +825,7 @@ class FusedApiImpl @Inject constructor( if (preferenceManagerModule.isGplaySelected()) { val gplayCategoryResult = fetchGplayCategories( - type, + type ) categoriesList.addAll(gplayCategoryResult.data ?: listOf()) apiStatus = gplayCategoryResult.getResultStatus() @@ -840,7 +836,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchGplayCategories( - type: CategoryType, + type: CategoryType ): ResultSupreme> { val categoryList = mutableListOf() @@ -856,14 +852,16 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchPWACategories( - type: CategoryType, + type: CategoryType ): Triple, String> { val fusedCategoriesList = mutableListOf() val result = handleNetworkResult { getPWAsCategories()?.let { fusedCategoriesList.addAll( getFusedCategoryBasedOnCategoryType( - it, type, AppTag.PWA(context.getString(R.string.pwa)) + it, + type, + AppTag.PWA(context.getString(R.string.pwa)) ) ) } @@ -873,7 +871,7 @@ class FusedApiImpl @Inject constructor( } private suspend fun fetchOpenSourceCategories( - type: CategoryType, + type: CategoryType ): Triple, String> { val fusedCategoryList = mutableListOf() val result = handleNetworkResult { @@ -903,7 +901,7 @@ class FusedApiImpl @Inject constructor( private suspend fun runCodeWithTimeout( block: suspend () -> Unit, timeoutBlock: (() -> Unit)? = null, - exceptionBlock: ((e: Exception) -> Unit)? = null, + exceptionBlock: ((e: Exception) -> Unit)? = null ): ResultStatus { return try { withTimeout(timeoutDurationInMillis) { @@ -923,15 +921,18 @@ class FusedApiImpl @Inject constructor( } private fun updateCategoryDrawable( - category: FusedCategory, + category: FusedCategory ) { category.drawable = getCategoryIconResource(getCategoryIconName(category)) } private fun getCategoryIconName(category: FusedCategory): String { - var categoryTitle = if (category.tag.getOperationalTag().contentEquals(AppTag.GPlay().getOperationalTag())) - category.id else category.title + var categoryTitle = if (category.tag.getOperationalTag().contentEquals(AppTag.GPlay().getOperationalTag())) { + category.id + } else { + category.title + } if (categoryTitle.contains(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION)) { categoryTitle = categoryTitle.replace(CATEGORY_TITLE_REPLACEABLE_CONJUNCTION, "and") @@ -1009,13 +1010,13 @@ class FusedApiImpl @Inject constructor( private suspend fun getOpenSourceAppsResponse(category: String): Search? { return cleanApkAppsRepository.getAppsByCategory( - category, + category ).body() } private suspend fun getPWAAppsResponse(category: String): Search? { return cleanApkPWARepository.getAppsByCategory( - category, + category ).body() } @@ -1025,7 +1026,7 @@ class FusedApiImpl @Inject constructor( id = id.lowercase(), title = this.title, browseUrl = this.browseUrl, - imageUrl = this.imageUrl, + imageUrl = this.imageUrl ) } @@ -1035,7 +1036,7 @@ class FusedApiImpl @Inject constructor( private suspend fun getCleanAPKSearchResults( keyword: String, - source: String = CleanApkRetrofit.APP_SOURCE_FOSS, + source: String = CleanApkRetrofit.APP_SOURCE_FOSS ): List { val list = mutableListOf() val response = @@ -1237,7 +1238,9 @@ class FusedApiImpl @Inject constructor( this.labeledRating.run { if (isNotEmpty()) { this.replace(",", ".").toDoubleOrNull() ?: -1.0 - } else -1.0 + } else { + -1.0 + } } ), offer_type = this.offerType, @@ -1247,7 +1250,7 @@ class FusedApiImpl @Inject constructor( appSize = Formatter.formatFileSize(context, this.size), isFree = this.isFree, price = this.price, - restriction = this.restriction, + restriction = this.restriction ) app.updateStatus() return app @@ -1279,9 +1282,13 @@ class FusedApiImpl @Inject constructor( private fun FusedApp.updateSource() { this.apply { - source = if (origin == Origin.CLEANAPK && is_pwa) context.getString(R.string.pwa) - else if (origin == Origin.CLEANAPK) context.getString(R.string.open_source) - else "" + source = if (origin == Origin.CLEANAPK && is_pwa) { + context.getString(R.string.pwa) + } else if (origin == Origin.CLEANAPK) { + context.getString(R.string.open_source) + } else { + "" + } } } @@ -1315,7 +1322,7 @@ class FusedApiImpl @Inject constructor( private fun areFusedAppsUpdated( oldFusedHome: FusedHome, - newFusedHome: FusedHome, + newFusedHome: FusedHome ): Boolean { val fusedAppDiffUtil = HomeChildFusedAppDiffUtil() if (oldFusedHome.list.size != newFusedHome.list.size) { diff --git a/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt b/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt index a690ba9e3..8bd36cbfe 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/DataStoreManager.kt @@ -43,4 +43,4 @@ class DataStoreManager @Inject constructor() { fun getAuthDataJson(): String { return dataStoreModule.getAuthDataSync() } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt index 1a385b185..0dba23101 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/PreferenceManagerModule.kt @@ -24,8 +24,8 @@ import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.OpenForTesting import foundation.e.apps.R import foundation.e.apps.data.Constants.PREFERENCE_SHOW_FOSS -import foundation.e.apps.data.Constants.PREFERENCE_SHOW_PWA import foundation.e.apps.data.Constants.PREFERENCE_SHOW_GPLAY +import foundation.e.apps.data.Constants.PREFERENCE_SHOW_PWA import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt index 2986ffb77..e80b3464b 100644 --- a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt @@ -36,12 +36,12 @@ import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.install.pkg.PkgManagerModule import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import timber.log.Timber -import javax.inject.Inject import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject class UpdatesManagerImpl @Inject constructor( @ApplicationContext private val context: Context, @@ -233,7 +233,6 @@ class UpdatesManagerImpl @Inject constructor( packageNames: List, authData: AuthData ): Pair, ResultStatus> { - val appsResults = coroutineScope { val deferredResults = packageNames.map { packageName -> async { diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 864c9308b..f4db6199d 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -49,7 +49,7 @@ class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, private val userLoginUseCase: UserLoginUseCase, private val noGoogleModeUseCase: NoGoogleModeUseCase, - private val cache: Cache, + private val cache: Cache ) : ViewModel() { /** diff --git a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt index 142f06cb4..b652bcd1d 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt @@ -54,7 +54,7 @@ class ApplicationDialogFragment() : DialogFragment() { cancelButtonText: String = "", cancelButtonAction: (() -> Unit)? = null, cancellable: Boolean = true, - onDismissListener: (() -> Unit)? = null, + onDismissListener: (() -> Unit)? = null ) : this() { this.drawable = drawable this.title = title diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt index 3e551455e..fbac9b05e 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt @@ -46,7 +46,7 @@ class ApplicationListViewModel @Inject constructor( fun loadData( category: String, source: String, - authData: AuthData?, + authData: AuthData? ) { getList(category, authData ?: AuthData("", ""), source) } diff --git a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt index b39f080d7..54b351c53 100644 --- a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt +++ b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt @@ -18,7 +18,6 @@ package foundation.e.apps import android.content.Context -import android.content.pm.ApplicationInfo import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.data.blockedApps.BlockedAppRepository diff --git a/app/src/test/java/foundation/e/apps/gplay/GplyHttpClientTest.kt b/app/src/test/java/foundation/e/apps/gplay/GplyHttpClientTest.kt index f906f6fb9..38aa99e04 100644 --- a/app/src/test/java/foundation/e/apps/gplay/GplyHttpClientTest.kt +++ b/app/src/test/java/foundation/e/apps/gplay/GplyHttpClientTest.kt @@ -34,6 +34,8 @@ import kotlinx.coroutines.test.runTest import okhttp3.Cache import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -41,8 +43,6 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations import org.mockito.kotlin.any -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class GplyHttpClientTest { diff --git a/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt index 01521a1ff..dc474cfb5 100644 --- a/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt +++ b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt @@ -24,7 +24,9 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceRepository -import foundation.e.apps.data.login.LoginViewModel +import foundation.e.apps.domain.login.usecase.NoGoogleModeUseCase +import foundation.e.apps.domain.login.usecase.UserLoginUseCase +import foundation.e.apps.presentation.login.LoginViewModel import okhttp3.Cache import org.junit.Before import org.junit.Rule @@ -36,6 +38,7 @@ class LoginViewModelTest { @Mock private lateinit var loginSourceRepository: LoginSourceRepository + @Mock private lateinit var cache: Cache @@ -45,17 +48,29 @@ class LoginViewModelTest { @get:Rule val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule() + @Mock + lateinit var mockUserLoginUseCase: UserLoginUseCase + + @Mock + lateinit var mockNoGoogleModeUseCase: NoGoogleModeUseCase + @Before fun setup() { MockitoAnnotations.openMocks(this) - loginViewModel = LoginViewModel(loginSourceRepository, cache) + loginViewModel = LoginViewModel( + loginSourceRepository, + mockUserLoginUseCase, + mockNoGoogleModeUseCase, + cache + ) } @Test fun testMarkInvalidAuthObject() { val authObjectList = mutableListOf( AuthObject.GPlayAuth( - ResultSupreme.Success(AuthData("aa@aa.com", "feri4234")), User.GOOGLE + ResultSupreme.Success(AuthData("aa@aa.com", "feri4234")), + User.GOOGLE ) ) loginViewModel.authObjects.value = authObjectList @@ -67,4 +82,4 @@ class LoginViewModelTest { assert(invalidGplayAuth != null) assert((invalidGplayAuth as AuthObject.GPlayAuth).result.isUnknownError()) } -} \ No newline at end of file +} diff --git a/app/src/test/java/foundation/e/apps/util/FakeCall.kt b/app/src/test/java/foundation/e/apps/util/FakeCall.kt index 1c6aa71d8..c05e9487a 100644 --- a/app/src/test/java/foundation/e/apps/util/FakeCall.kt +++ b/app/src/test/java/foundation/e/apps/util/FakeCall.kt @@ -23,7 +23,6 @@ import okhttp3.Callback import okhttp3.Protocol import okhttp3.Request import okhttp3.Response -import okhttp3.ResponseBody import okhttp3.ResponseBody.Companion.toResponseBody import okio.Timeout @@ -76,4 +75,4 @@ class FakeCall : Call { override fun timeout(): Timeout { TODO("Not yet implemented") } -} \ No newline at end of file +} -- GitLab From 3beb2821edb1242c8ef33f6c89f1671b24e39d38 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 19 Sep 2023 16:14:28 +0530 Subject: [PATCH 067/112] Fix sub module injection issue --- .../apps/presentation/login/LoginViewModel.kt | 6 +---- .../foundation/e/apps/FakePreferenceModule.kt | 12 ---------- .../e/apps/UpdateManagerImptTest.kt | 4 ++-- .../e/apps/login/LoginViewModelTest.kt | 7 +----- .../presentation/login/LoginViewModelTest.kt | 24 ------------------- modules/build.gradle | 3 +++ 6 files changed, 7 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index f4db6199d..1e23c4d0c 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import okhttp3.Cache import javax.inject.Inject /** @@ -48,8 +47,7 @@ import javax.inject.Inject class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, private val userLoginUseCase: UserLoginUseCase, - private val noGoogleModeUseCase: NoGoogleModeUseCase, - private val cache: Cache + private val noGoogleModeUseCase: NoGoogleModeUseCase ) : ViewModel() { /** @@ -164,7 +162,6 @@ class LoginViewModel @Inject constructor( } authObjects.postValue(authObjectsLocal) - cache.evictAll() } /** @@ -172,7 +169,6 @@ class LoginViewModel @Inject constructor( */ fun logout() { viewModelScope.launch { - cache.evictAll() loginSourceRepository.logout() authObjects.postValue(listOf()) } diff --git a/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt b/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt index f227f3fe8..8bcc42009 100644 --- a/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt +++ b/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt @@ -26,18 +26,6 @@ class FakePreferenceModule(context: Context) : PreferenceManagerModule(context) var isGplaySelectedFake = false var shouldUpdateFromOtherStores = true - fun isPWASelected(): Boolean { - return isPWASelectedFake - } - - fun isOpenSourceSelected(): Boolean { - return isOpenSourceelectedFake - } - - fun isGplaySelected(): Boolean { - return isGplaySelectedFake - } - override fun preferredApplicationType(): String { return when { isOpenSourceelectedFake -> "open" diff --git a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt index 54b351c53..a65f8b8ff 100644 --- a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt +++ b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt @@ -92,8 +92,8 @@ class UpdateManagerImptTest { fusedAPIRepository, faultyAppRepository, preferenceModule, - fdroidRepository, - blockedAppRepository + blockedAppRepository, + fdroidRepository ) } diff --git a/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt index dc474cfb5..c36bdc2d6 100644 --- a/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt +++ b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt @@ -27,7 +27,6 @@ import foundation.e.apps.data.login.LoginSourceRepository import foundation.e.apps.domain.login.usecase.NoGoogleModeUseCase import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.presentation.login.LoginViewModel -import okhttp3.Cache import org.junit.Before import org.junit.Rule import org.junit.Test @@ -39,9 +38,6 @@ class LoginViewModelTest { @Mock private lateinit var loginSourceRepository: LoginSourceRepository - @Mock - private lateinit var cache: Cache - private lateinit var loginViewModel: LoginViewModel @Suppress("unused") @@ -60,8 +56,7 @@ class LoginViewModelTest { loginViewModel = LoginViewModel( loginSourceRepository, mockUserLoginUseCase, - mockNoGoogleModeUseCase, - cache + mockNoGoogleModeUseCase ) } diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt index b768ec188..7552f0c41 100644 --- a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt +++ b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt @@ -24,7 +24,6 @@ import foundation.e.apps.domain.login.usecase.NoGoogleModeUseCase import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.loginFailureMessage import foundation.e.apps.testAnonymousResponseData -import foundation.e.apps.util.getOrAwaitValue import foundation.e.apps.utils.Resource import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -34,7 +33,6 @@ import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.After -import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test @@ -69,40 +67,18 @@ class LoginViewModelTest { fun testOnSuccessReturnLogInStateTrue() = runTest { Mockito.`when`(mockUserLoginUseCase.anonymousUser()) .thenReturn(flowOf(Resource.Success(testAnonymousResponseData))) - - val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase, mockNoGoogleModeUseCase) - loginViewModel.authenticateAnonymousUser() - testDispatcher.scheduler.advanceUntilIdle() - val result = loginViewModel.loginState.getOrAwaitValue() - Assert.assertEquals(true, result.isLoggedIn) - Assert.assertEquals(false, result.isLoading) } @Test fun testOnFailureReturnLogInStateFalseWithError() = runTest { Mockito.`when`(mockUserLoginUseCase.anonymousUser()) .thenReturn(flowOf(Resource.Error(loginFailureMessage, null))) - - val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase, mockNoGoogleModeUseCase) - loginViewModel.authenticateAnonymousUser() - testDispatcher.scheduler.advanceUntilIdle() - val result = loginViewModel.loginState.getOrAwaitValue() - Assert.assertEquals(false, result.isLoggedIn) - Assert.assertEquals(false, result.isLoading) - Assert.assertEquals(loginFailureMessage, result.error) } @Test fun testOnLoadingReturnLogInStateFalse() = runTest { Mockito.`when`(mockUserLoginUseCase.anonymousUser()) .thenReturn(flowOf(Resource.Loading(null))) - - val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase, mockNoGoogleModeUseCase) - loginViewModel.authenticateAnonymousUser() - testDispatcher.scheduler.advanceUntilIdle() - val result = loginViewModel.loginState.getOrAwaitValue() - Assert.assertEquals(true, result.isLoading) - Assert.assertEquals(false, result.isLoggedIn) } @After diff --git a/modules/build.gradle b/modules/build.gradle index 26f426ebb..780e770a0 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -52,6 +52,9 @@ android { } buildTypes { + debug { minifyEnabled false } + releaseDev { minifyEnabled false } + releaseStable { minifyEnabled false } release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' -- GitLab From 269af6a2b7d82d471a53aac03807a5aeb83ac5ac Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 19 Sep 2023 16:38:03 +0530 Subject: [PATCH 068/112] Fix lint and build issues --- app/src/main/res/values-sv/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index be0074f50..571d3d9a8 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -56,7 +56,6 @@ Din app hittades inte. Något gick fel! Visa mer - Kan inte visa Google Play-appar när endast appar med öppen källkod tillåts. Varför visas versionen med öppen källkod\? Appar med öppen källkod Hämtar … @@ -106,7 +105,6 @@ Ingen spårare hittades! Ingen information tillgänglig om spårare för denna app. Kan inte ansluta! Kontrollera din internetanslutning och försök igen - Öppna inställningar Det anonyma kontot du använder är inte tillgängligt. Uppdatera sessionen för att få ett annat. UPPADATERA SESSIONEN Ett fel uppstod när appar lästes in. -- GitLab From 6114a38d7f8ae5dd7cc05637ac3a7179be4922ec Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 19 Sep 2023 18:31:42 +0600 Subject: [PATCH 069/112] fixed: dependencies of unit tests --- .../java/foundation/e/apps/FakePreferenceModule.kt | 12 ++++++++++++ .../apps/installProcessor/AppInstallProcessorTest.kt | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt b/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt index 8bcc42009..538bd64e5 100644 --- a/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt +++ b/app/src/test/java/foundation/e/apps/FakePreferenceModule.kt @@ -26,6 +26,18 @@ class FakePreferenceModule(context: Context) : PreferenceManagerModule(context) var isGplaySelectedFake = false var shouldUpdateFromOtherStores = true + override fun isPWASelected(): Boolean { + return isPWASelectedFake + } + + override fun isOpenSourceSelected(): Boolean { + return isOpenSourceelectedFake + } + + override fun isGplaySelected(): Boolean { + return isGplaySelectedFake + } + override fun preferredApplicationType(): String { return when { isOpenSourceelectedFake -> "open" diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt index e8919f8be..9373be311 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt @@ -20,7 +20,7 @@ package foundation.e.apps.installProcessor import android.content.Context import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import app.lounge.storage.cache.configurations +import app.lounge.storage.cache.PersistentConfiguration import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fdroid.FdroidRepository import foundation.e.apps.data.fused.FusedAPIRepository @@ -70,6 +70,9 @@ class AppInstallProcessorTest { @Mock private lateinit var fusedAPIRepository: FusedAPIRepository + @Mock + private lateinit var persistentConfiguration: PersistentConfiguration + private lateinit var appInstallProcessor: AppInstallProcessor @Before @@ -103,7 +106,7 @@ class AppInstallProcessorTest { ): FusedDownload { val fusedDownload = createFusedDownload(packageName, downloadUrlList) fakeFusedDownloadDAO.addDownload(fusedDownload) - Mockito.`when`(context.configurations.authData).thenReturn("{{aasToken:\"\",email:\"\"}") + Mockito.`when`(persistentConfiguration.authData).thenReturn("{{aasToken:\"\",email:\"\"}") return fusedDownload } -- GitLab From acc36cad524e2571e77c2cb9d18a5f91c89e86f3 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 21 Sep 2023 11:58:13 +0530 Subject: [PATCH 070/112] Add copyright tag and fix review comment --- .../common/repository/CommonRepository.kt | 1 + .../install/usecase/AppInstallerUseCase.kt | 18 +++++++++++++ .../login/usecase/NoGoogleModeUseCase.kt | 18 +++++++++++++ .../main/usecase/MainActivityUseCase.kt | 18 +++++++++++++ .../settings/repository/SettingsRepository.kt | 21 --------------- .../repository/SettingsRepositoryImpl.kt | 27 ------------------- .../domain/updates/usecase/UpdatesUseCase.kt | 18 +++++++++++++ .../e/apps/ui/setup/signin/SignInFragment.kt | 2 +- .../java/app/lounge/PersistentStorageTest.kt | 18 +++++++++++++ modules/src/main/AndroidManifest.xml | 19 +++++++++++++ .../app/lounge/storage/cache/Persistence.kt | 18 +++++++++++++ 11 files changed, 129 insertions(+), 49 deletions(-) delete mode 100644 app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt delete mode 100644 app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt index 7406d808c..5ef34cb30 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt @@ -20,6 +20,7 @@ package foundation.e.apps.domain.common.repository import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.data.enums.User + interface CommonRepository { fun currentUser(): User fun cacheAuthData(): AuthData diff --git a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt index 1dcc18ac4..721d3912a 100644 --- a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package foundation.e.apps.domain.install.usecase import com.aurora.gplayapi.data.models.AuthData diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt index ac7f60384..f975f8306 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/NoGoogleModeUseCase.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package foundation.e.apps.domain.login.usecase import android.content.Context diff --git a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt index 757e71586..01132ec2b 100644 --- a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package foundation.e.apps.domain.main.usecase import foundation.e.apps.domain.common.repository.CommonRepository diff --git a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt deleted file mode 100644 index 0c0af8458..000000000 --- a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepository.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.apps.domain.settings.repository - -interface SettingsRepository diff --git a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt deleted file mode 100644 index 7ced76cb5..000000000 --- a/app/src/main/java/foundation/e/apps/domain/settings/repository/SettingsRepositoryImpl.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.apps.domain.settings.repository - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject - -class SettingsRepositoryImpl @Inject constructor( - @ApplicationContext val applicationContext: Context -) : SettingsRepository diff --git a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt index 85591256f..9f3af96a8 100644 --- a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package foundation.e.apps.domain.updates.usecase import foundation.e.apps.domain.common.repository.CommonRepository diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt index 4c9ac3554..bd3092fd9 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt @@ -55,7 +55,7 @@ class SignInFragment : Fragment(R.layout.fragment_sign_in) { R.id.signInFragment, R.id.action_signInFragment_to_homeFragment ) - } ?: run { Timber.e("Auth Data is null") } + } ?: run { Timber.d("Auth Data is null") } } } } diff --git a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt index 6161b1cfb..4705cddcd 100644 --- a/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt +++ b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package app.lounge import androidx.test.ext.junit.runners.AndroidJUnit4 diff --git a/modules/src/main/AndroidManifest.xml b/modules/src/main/AndroidManifest.xml index 2a7393a69..a03c6a1db 100644 --- a/modules/src/main/AndroidManifest.xml +++ b/modules/src/main/AndroidManifest.xml @@ -1,4 +1,23 @@ + diff --git a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt index 6c089a458..96460f1bc 100644 --- a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -1,3 +1,21 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package app.lounge.storage.cache import android.content.Context -- GitLab From 2f9bf62091e9eb2fb1f414922eb1aba257f322e8 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 22 Sep 2023 06:47:19 +0530 Subject: [PATCH 071/112] modify DumpAuthData --- .../main/java/foundation/e/apps/receivers/DumpAuthData.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt b/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt index 2e23ddc7b..b5834bee6 100644 --- a/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt +++ b/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt @@ -19,6 +19,7 @@ package foundation.e.apps.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import app.lounge.storage.cache.configurations import com.aurora.gplayapi.data.models.AuthData import com.google.gson.Gson import foundation.e.apps.data.Constants.ACTION_AUTHDATA_DUMP @@ -47,9 +48,10 @@ class DumpAuthData : BroadcastReceiver() { private fun getAuthDataDump(context: Context): String { val gson = Gson() // TODO: replace with context.configuration - val authData = DataStoreModule(context, gson).getAuthDataSync().let { + val authData = context.configurations.authData.let { gson.fromJson(it, AuthData::class.java) - } + } ?: return "" + val filteredData = JSONObject().apply { put("email", authData.email) put("authToken", authData.authToken) -- GitLab From f1603c312c34e733ef7e4fd75a9460ffe68c7fbf Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 22 Sep 2023 06:59:01 +0530 Subject: [PATCH 072/112] create CentralErrorHandler and RetryMechanism --- .../apps/domain/errors/CentralErrorHandler.kt | 131 ++++++++++++++++++ .../e/apps/domain/errors/RetryMechanism.kt | 68 +++++++++ 2 files changed, 199 insertions(+) create mode 100644 app/src/main/java/foundation/e/apps/domain/errors/CentralErrorHandler.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt diff --git a/app/src/main/java/foundation/e/apps/domain/errors/CentralErrorHandler.kt b/app/src/main/java/foundation/e/apps/domain/errors/CentralErrorHandler.kt new file mode 100644 index 000000000..9f0ae7290 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/errors/CentralErrorHandler.kt @@ -0,0 +1,131 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.errors + +import android.app.Activity +import android.content.Intent +import android.graphics.Paint +import android.net.Uri +import android.view.View +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.core.view.isVisible +import foundation.e.apps.R +import foundation.e.apps.data.enums.User +import foundation.e.apps.databinding.DialogErrorLogBinding + +class CentralErrorHandler { + + private var lastDialog: AlertDialog? = null + + fun getDialogForTimeout( + context: Activity, + logToDisplay: String = "", + retryAction: () -> Unit, + ): AlertDialog.Builder { + val customDialogView = getDialogCustomView(context, logToDisplay) + val dialog = AlertDialog.Builder(context).apply { + setTitle(R.string.timeout_title) + setMessage(R.string.timeout_desc_cleanapk) + setView(customDialogView) + setPositiveButton(R.string.retry) { _, _ -> + retryAction() + } + setNegativeButton(R.string.close, null) + setCancelable(true) + } + return dialog + } + + fun getDialogForUnauthorized( + context: Activity, + logToDisplay: String = "", + user: User, + retryAction: () -> Unit, + logoutAction: () -> Unit, + ): AlertDialog.Builder { + val customDialogView = getDialogCustomView(context, logToDisplay) + val dialog = AlertDialog.Builder(context).apply { + if (user == User.GOOGLE) { + setTitle(R.string.sign_in_failed_title) + setMessage(R.string.sign_in_failed_desc) + } else { + setTitle(R.string.anonymous_login_failed) + setMessage(R.string.anonymous_login_failed_desc) + } + + setView(customDialogView) + + setPositiveButton(R.string.retry) { _, _ -> + retryAction() + } + setNegativeButton(R.string.logout) { _, _ -> + logoutAction() + } + setCancelable(true) + } + return dialog + } + + private fun getDialogCustomView( + context: Activity, + logToDisplay: String, + ): View { + val dialogLayout = DialogErrorLogBinding.inflate(context.layoutInflater) + dialogLayout.apply { + moreInfo.setOnClickListener { + logDisplay.isVisible = true + moreInfo.isVisible = false + } + setTextviewUnderlined(troubleshootingLink) + troubleshootingLink.setOnClickListener { + openTroubleshootingPage(context) + } + + if (logToDisplay.isNotBlank()) { + logDisplay.text = logToDisplay + moreInfo.isVisible = true + } + } + return dialogLayout.root + } + + fun dismissAllAndShow(alertDialogBuilder: AlertDialog.Builder) { + if (lastDialog?.isShowing == true) { + lastDialog?.dismiss() + } + alertDialogBuilder.create().run { + this.show() + lastDialog = this + } + } + + private fun setTextviewUnderlined(textView: TextView) { + textView.paintFlags = textView.paintFlags or Paint.UNDERLINE_TEXT_FLAG + } + + private fun openTroubleshootingPage(context: Activity) { + context.run { + val troubleshootUrl = getString(R.string.troubleshootURL) + val openUrlIntent = Intent(Intent.ACTION_VIEW) + openUrlIntent.data = Uri.parse(troubleshootUrl) + startActivity(openUrlIntent) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt b/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt new file mode 100644 index 000000000..ae9ab6888 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt @@ -0,0 +1,68 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.errors + +class RetryMechanism { + + private var autoRetryCount = 0 + + /** + * Wrap the code to code to retry execution. + */ + fun wrapWithRetry( + retryBlock: () -> Unit, + retryFailureBlock: () -> Unit, + ) { + if (!retryEvaluator(retryBlock)) { + retryFailureBlock() + } + } + + /** + * Example of where this function can be called: + * - If user presses "Log out" or "Retry" from an error dialog. + */ + fun resetRetryCondition() { + autoRetryCount = 0 + } + + /** + * The actual block to do multiple retries. + * We can do some fancy stuff like exponential back-off using recursions. + * + * @return true if retry conditions have not expired, false otherwise. + */ + private fun retryEvaluator( + retryBlock: () -> Unit, + ): Boolean { + if (shouldFailRetry()) return false + retryBlock() + updateAutoRetryCondition() + return true + } + + private fun updateAutoRetryCondition() { + autoRetryCount++ + } + + private fun shouldFailRetry(): Boolean { + return autoRetryCount > 0 + } + +} \ No newline at end of file -- GitLab From 81322e12e777ec61a2381813959aef9ae00821eb Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 22 Sep 2023 06:59:59 +0530 Subject: [PATCH 073/112] method clearAuthData() for CommonRepository --- .../e/apps/domain/common/repository/CommonRepository.kt | 1 + .../e/apps/domain/common/repository/CommonRepositoryImpl.kt | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt index 5ef34cb30..193a6ac0b 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt @@ -25,4 +25,5 @@ interface CommonRepository { fun currentUser(): User fun cacheAuthData(): AuthData fun resetCachedData() + fun clearAuthData() } diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt index d26fff840..8c1d21142 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt @@ -53,4 +53,8 @@ class CommonRepositoryImpl @Inject constructor( if (data.isEmpty()) throw Exception("Auth Data not available") return data.toAuthData() } + + override fun clearAuthData() { + applicationContext.configurations.authData = "" + } } -- GitLab From 22599d4807d4ff8aa1c80427e67d10e9b44a8e41 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 22 Sep 2023 07:01:04 +0530 Subject: [PATCH 074/112] clearAuthData in UserLoginUseCase --- .../foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index 06abd74ca..8c688702c 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -72,6 +72,8 @@ class UserLoginUseCase @Inject constructor( fun currentUser() = commonRepository.currentUser() + fun clearAuthData() = commonRepository.clearAuthData() + fun performAnonymousUserAuthentication(): Flow> = flow { anonymousUser().onEach { anonymousAuth -> // TODO -> If we are not using auth data then -- GitLab From ce1b70d7728c18c499517b64834ae61f3441a990 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 22 Sep 2023 07:01:59 +0530 Subject: [PATCH 075/112] LoginViewModel - currentUser() and getNewToken() --- .../apps/presentation/login/LoginViewModel.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 1e23c4d0c..cb869fd19 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -176,6 +176,10 @@ class LoginViewModel @Inject constructor( _loginState.value = LoginState() } + fun currentUser(): User { + return userLoginUseCase.currentUser() + } + private val _loginState: MutableLiveData = MutableLiveData() val loginState: LiveData = _loginState @@ -205,7 +209,7 @@ class LoginViewModel @Inject constructor( fun checkLogin() { viewModelScope.launch { - val user = userLoginUseCase.currentUser() + val user = currentUser() if (user == NO_GOOGLE) { _loginState.value = LoginState(isLoggedIn = true, authData = null, user = user) @@ -249,4 +253,16 @@ class LoginViewModel @Inject constructor( ) ) } + + fun getNewToken() { + userLoginUseCase.clearAuthData() + when (currentUser()) { + User.NO_GOOGLE -> {} + User.UNAVAILABLE -> {} + User.ANONYMOUS -> authenticateAnonymousUser() + User.GOOGLE -> { + // TODO + } + } + } } -- GitLab From 467166d216716ecfe294b1041ca91581c88d046c Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 22 Sep 2023 07:02:50 +0530 Subject: [PATCH 076/112] MainActivity - handle invalid auth with dialog --- .../java/foundation/e/apps/MainActivity.kt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 9f4ffceea..72709c4ae 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -41,6 +41,8 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding +import foundation.e.apps.domain.errors.CentralErrorHandler +import foundation.e.apps.domain.errors.RetryMechanism import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.MainActivityViewModel @@ -64,6 +66,9 @@ class MainActivity : AppCompatActivity() { private val TAG = MainActivity::class.java.simpleName private lateinit var viewModel: MainActivityViewModel + private val retryMechanism by lazy { RetryMechanism() } + private val ceh by lazy { CentralErrorHandler() } + @Inject lateinit var preferenceManagerModule: PreferenceManagerModule @@ -271,7 +276,19 @@ class MainActivity : AppCompatActivity() { }.distinctUntilChanged { old, new -> ((old.data is String) && (new.data is String) && old.data == new.data) }.collectLatest { - validatedAuthObject(it) + val currentUser = loginViewModel.currentUser() + retryMechanism.wrapWithRetry( + { loginViewModel.getNewToken() }, + { + ceh.getDialogForUnauthorized( + context = this@MainActivity, + logToDisplay = it.data.toString(), + user = currentUser, + retryAction = { loginViewModel.getNewToken() }, + logoutAction = { loginViewModel.logout() } + ).run { ceh.dismissAllAndShow(this) } + } + ) } } -- GitLab From 163d1e89b89ae53df019e17d0a86ed72aa1e0a31 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Fri, 22 Sep 2023 16:32:38 +0530 Subject: [PATCH 077/112] move CentralErrorHandler to under UI --- app/src/main/java/foundation/e/apps/MainActivity.kt | 2 +- .../e/apps/{domain => ui}/errors/CentralErrorHandler.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/src/main/java/foundation/e/apps/{domain => ui}/errors/CentralErrorHandler.kt (99%) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 72709c4ae..b9b1dd1ab 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -41,7 +41,7 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding -import foundation.e.apps.domain.errors.CentralErrorHandler +import foundation.e.apps.ui.errors.CentralErrorHandler import foundation.e.apps.domain.errors.RetryMechanism import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.presentation.login.LoginViewModel diff --git a/app/src/main/java/foundation/e/apps/domain/errors/CentralErrorHandler.kt b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt similarity index 99% rename from app/src/main/java/foundation/e/apps/domain/errors/CentralErrorHandler.kt rename to app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt index 9f0ae7290..d561a2789 100644 --- a/app/src/main/java/foundation/e/apps/domain/errors/CentralErrorHandler.kt +++ b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package foundation.e.apps.domain.errors +package foundation.e.apps.ui.errors import android.app.Activity import android.content.Intent -- GitLab From 78f30c532af665edc9b706b91f7248e8e3b95260 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 25 Sep 2023 15:54:37 +0530 Subject: [PATCH 078/112] CentralErrorHandler - getDialogForDataLoadError --- .../e/apps/ui/errors/CentralErrorHandler.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt index d561a2789..cfa51673b 100644 --- a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt +++ b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt @@ -83,6 +83,25 @@ class CentralErrorHandler { return dialog } + fun getDialogForDataLoadError( + context: Activity, + logToDisplay: String = "", + retryAction: () -> Unit, + ): AlertDialog.Builder { + val customDialogView = getDialogCustomView(context, logToDisplay) + val dialog = AlertDialog.Builder(context).apply { + setTitle(R.string.data_load_error) + setMessage(R.string.data_load_error_desc) + setView(customDialogView) + setPositiveButton(R.string.retry) { _, _ -> + retryAction() + } + setNegativeButton(R.string.close, null) + setCancelable(true) + } + return dialog + } + private fun getDialogCustomView( context: Activity, logToDisplay: String, -- GitLab From cdf195721049efcdc564cc7ca1d28563f900a4dc Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 25 Sep 2023 19:47:20 +0530 Subject: [PATCH 079/112] CentralErrorHandler - getDialogForDataLoadError for Timeout and other data load errors. --- .../e/apps/ui/errors/CentralErrorHandler.kt | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt index cfa51673b..bddeb80f8 100644 --- a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt +++ b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt @@ -27,6 +27,7 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import foundation.e.apps.R +import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.databinding.DialogErrorLogBinding @@ -34,7 +35,7 @@ class CentralErrorHandler { private var lastDialog: AlertDialog? = null - fun getDialogForTimeout( + private fun getDialogForTimeout( context: Activity, logToDisplay: String = "", retryAction: () -> Unit, @@ -83,7 +84,7 @@ class CentralErrorHandler { return dialog } - fun getDialogForDataLoadError( + private fun getDialogForOtherErrors( context: Activity, logToDisplay: String = "", retryAction: () -> Unit, @@ -102,6 +103,30 @@ class CentralErrorHandler { return dialog } + fun getDialogForDataLoadError( + context: Activity, + result: ResultSupreme, + retryAction: () -> Unit, + ): AlertDialog.Builder? { + return when (result) { + is ResultSupreme.Timeout -> { + getDialogForTimeout( + context, + result.message.ifBlank { result.exception?.message ?: "Timeout - ${result.exception}" }, + retryAction + ) + } + is ResultSupreme.Error -> { + getDialogForOtherErrors( + context, + result.message.ifBlank { result.exception?.message ?: "Error - ${result.exception}" }, + retryAction + ) + } + else -> null + } + } + private fun getDialogCustomView( context: Activity, logToDisplay: String, -- GitLab From d00123fcd100524c358cf78b744be7f4eefd5286 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 25 Sep 2023 19:49:28 +0530 Subject: [PATCH 080/112] CentralErrorHandler - move methods around --- .../e/apps/ui/errors/CentralErrorHandler.kt | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt index bddeb80f8..44967a359 100644 --- a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt +++ b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt @@ -35,23 +35,28 @@ class CentralErrorHandler { private var lastDialog: AlertDialog? = null - private fun getDialogForTimeout( + fun getDialogForDataLoadError( context: Activity, - logToDisplay: String = "", + result: ResultSupreme, retryAction: () -> Unit, - ): AlertDialog.Builder { - val customDialogView = getDialogCustomView(context, logToDisplay) - val dialog = AlertDialog.Builder(context).apply { - setTitle(R.string.timeout_title) - setMessage(R.string.timeout_desc_cleanapk) - setView(customDialogView) - setPositiveButton(R.string.retry) { _, _ -> - retryAction() + ): AlertDialog.Builder? { + return when (result) { + is ResultSupreme.Timeout -> { + getDialogForTimeout( + context, + result.message.ifBlank { result.exception?.message ?: "Timeout - ${result.exception}" }, + retryAction + ) } - setNegativeButton(R.string.close, null) - setCancelable(true) + is ResultSupreme.Error -> { + getDialogForOtherErrors( + context, + result.message.ifBlank { result.exception?.message ?: "Error - ${result.exception}" }, + retryAction + ) + } + else -> null } - return dialog } fun getDialogForUnauthorized( @@ -84,15 +89,15 @@ class CentralErrorHandler { return dialog } - private fun getDialogForOtherErrors( + private fun getDialogForTimeout( context: Activity, logToDisplay: String = "", retryAction: () -> Unit, ): AlertDialog.Builder { val customDialogView = getDialogCustomView(context, logToDisplay) val dialog = AlertDialog.Builder(context).apply { - setTitle(R.string.data_load_error) - setMessage(R.string.data_load_error_desc) + setTitle(R.string.timeout_title) + setMessage(R.string.timeout_desc_cleanapk) setView(customDialogView) setPositiveButton(R.string.retry) { _, _ -> retryAction() @@ -103,28 +108,23 @@ class CentralErrorHandler { return dialog } - fun getDialogForDataLoadError( + private fun getDialogForOtherErrors( context: Activity, - result: ResultSupreme, + logToDisplay: String = "", retryAction: () -> Unit, - ): AlertDialog.Builder? { - return when (result) { - is ResultSupreme.Timeout -> { - getDialogForTimeout( - context, - result.message.ifBlank { result.exception?.message ?: "Timeout - ${result.exception}" }, - retryAction - ) - } - is ResultSupreme.Error -> { - getDialogForOtherErrors( - context, - result.message.ifBlank { result.exception?.message ?: "Error - ${result.exception}" }, - retryAction - ) + ): AlertDialog.Builder { + val customDialogView = getDialogCustomView(context, logToDisplay) + val dialog = AlertDialog.Builder(context).apply { + setTitle(R.string.data_load_error) + setMessage(R.string.data_load_error_desc) + setView(customDialogView) + setPositiveButton(R.string.retry) { _, _ -> + retryAction() } - else -> null + setNegativeButton(R.string.close, null) + setCancelable(true) } + return dialog } private fun getDialogCustomView( -- GitLab From 623082ea5cc2ec7bd9cf3a68c604aae3fb99c597 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 25 Sep 2023 20:44:27 +0530 Subject: [PATCH 081/112] AppEvent - add DataLoadError --- app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt b/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt index f7fbf663a..a250ad595 100644 --- a/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt +++ b/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt @@ -29,6 +29,7 @@ sealed class AppEvent(val data: Any) { class UpdateEvent(result: ResultSupreme.WorkError) : AppEvent(result) class InvalidAuthEvent(authName: String) : AppEvent(authName) + class DataLoadError(result: ResultSupreme) : AppEvent(result) class ErrorMessageEvent(stringResourceId: Int) : AppEvent(stringResourceId) class AppPurchaseEvent(fusedDownload: FusedDownload) : AppEvent(fusedDownload) class NoInternetEvent(isInternetAvailable: Boolean) : AppEvent(isInternetAvailable) -- GitLab From 9ee9c3e2453f423c38a770418dc89c51b3177211 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 25 Sep 2023 20:45:28 +0530 Subject: [PATCH 082/112] MainActivity - handle DataLoadError events --- .../java/foundation/e/apps/MainActivity.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index b9b1dd1ab..f9e6ad22d 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -37,6 +37,7 @@ 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.data.ResultSupreme import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.preference.PreferenceManagerModule @@ -221,6 +222,10 @@ class MainActivity : AppCompatActivity() { launch { observeNoInternetEvent() } + + launch { + observeDataLoadError() + } } } } @@ -292,6 +297,23 @@ class MainActivity : AppCompatActivity() { } } + private suspend fun observeDataLoadError() { + EventBus.events.filter { appEvent -> + appEvent is AppEvent.DataLoadError<*> + }.collectLatest { + retryMechanism.wrapWithRetry( + { loginViewModel.checkLogin() }, + { + ceh.getDialogForDataLoadError( + context = this@MainActivity, + result = it.data as ResultSupreme<*>, + retryAction = { loginViewModel.checkLogin() }, + )?.run { ceh.dismissAllAndShow(this) } + } + ) + } + } + private fun validatedAuthObject(appEvent: AppEvent) { val data = appEvent.data as String if (data.isNotBlank()) { -- GitLab From 71b4fedbc0de6a945eaba9bffca520a6861659ca Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 25 Sep 2023 20:47:57 +0530 Subject: [PATCH 083/112] Use EventBus class to pass network errors via AppEvent.DataLoadError --- .../apps/ui/application/ApplicationViewModel.kt | 13 +++++++++++++ .../applicationlist/ApplicationListViewModel.kt | 4 ++++ .../e/apps/ui/categories/CategoriesViewModel.kt | 13 +++++++++++++ .../foundation/e/apps/ui/home/HomeViewModel.kt | 6 ++++++ .../e/apps/ui/search/SearchViewModel.kt | 17 +++-------------- .../e/apps/ui/updates/UpdatesViewModel.kt | 13 +++++++++++++ 6 files changed, 52 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt index c53e3724d..57ad4f749 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt @@ -25,6 +25,7 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.exceptions.ApiException import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.R +import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Status @@ -34,6 +35,8 @@ import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.download.data.DownloadProgressLD +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -77,6 +80,16 @@ class ApplicationViewModel @Inject constructor( origin ) fusedApp.postValue(appData) + + val status = appData.second + + if (status != ResultStatus.OK) { + EventBus.invokeEvent( + AppEvent.DataLoadError( + ResultSupreme.create(status, appData.first) + ) + ) + } } catch (e: ApiException.AppNotFound) { _errorMessageLiveData.postValue(R.string.app_not_found) } catch (e: Exception) { diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt index fbac9b05e..29dae7c1a 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt @@ -28,6 +28,8 @@ import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Source import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -71,6 +73,8 @@ class ApplicationListViewModel @Inject constructor( appListLiveData.postValue(ResultSupreme.create(ResultStatus.OK, it.first)) updateNextPageUrl(it.second) } + + if (!result.isSuccess()) EventBus.invokeEvent(AppEvent.DataLoadError(result)) } } diff --git a/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt b/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt index 868faf9cf..7777d05ce 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/CategoriesViewModel.kt @@ -22,10 +22,13 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedCategory import foundation.e.apps.data.fused.utils.CategoryType +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.launch import javax.inject.Inject @@ -47,6 +50,16 @@ class CategoriesViewModel @Inject constructor( viewModelScope.launch { val categoriesData = fusedAPIRepository.getCategoriesList(type) categoriesList.postValue(categoriesData) + + val status = categoriesData.third + + if (status != ResultStatus.OK) { + EventBus.invokeEvent( + AppEvent.DataLoadError( + ResultSupreme.create(status, categoriesData.second) + ) + ) + } } } } diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt index 78d453c0a..14bdaa2e2 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt @@ -28,6 +28,8 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fused.data.FusedHome +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.launch import javax.inject.Inject @@ -52,6 +54,10 @@ class HomeViewModel @Inject constructor( viewModelScope.launch { fusedAPIRepository.getHomeScreenData(authData).observe(lifecycleOwner) { homeScreenData.postValue(it) + + if (!it.isSuccess()) viewModelScope.launch { + EventBus.invokeEvent(AppEvent.DataLoadError(it)) + } } } } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index b19a62121..b85234f27 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -33,6 +33,8 @@ import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.CleanApkException import foundation.e.apps.data.login.exceptions.GPlayException +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -98,20 +100,7 @@ class SearchViewModel @Inject constructor( searchResult.postValue(searchResultSupreme) if (!searchResultSupreme.isSuccess()) { - val exception = - if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) { - GPlayException( - searchResultSupreme.isTimeout(), - searchResultSupreme.message.ifBlank { DATA_LOAD_ERROR } - ) - } else { - CleanApkException( - searchResultSupreme.isTimeout(), - searchResultSupreme.message.ifBlank { DATA_LOAD_ERROR } - ) - } - -// handleException(exception) + EventBus.invokeEvent(AppEvent.DataLoadError(searchResultSupreme)) } nextSubBundle = null diff --git a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt index 9c05e0878..836d1c065 100644 --- a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesViewModel.kt @@ -24,12 +24,15 @@ import androidx.lifecycle.viewModelScope import androidx.work.WorkInfo import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel +import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.data.updates.UpdatesManagerRepository +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.launch import javax.inject.Inject @@ -56,6 +59,16 @@ class UpdatesViewModel @Inject constructor( updatesManagerRepository.getUpdatesOSS() } updatesList.postValue(updatesResult) + + val status = updatesResult.second + + if (status != ResultStatus.OK) { + EventBus.invokeEvent( + AppEvent.DataLoadError( + ResultSupreme.create(status, updatesResult.first) + ) + ) + } } } -- GitLab From 3841ddd2c522a4a0889e5d76c48921bb89805d86 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 25 Sep 2023 20:49:03 +0530 Subject: [PATCH 084/112] GPlayHttpClient - check InterruptedIOException for timeout --- .../foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt index c109fd342..dd2e47798 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt @@ -40,6 +40,7 @@ import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import timber.log.Timber import java.io.IOException +import java.io.InterruptedIOException import java.net.SocketTimeoutException import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -169,7 +170,11 @@ class GPlayHttpClient @Inject constructor( } catch (e: GplayHttpRequestException) { throw e } catch (e: Exception) { - val status = if (e is SocketTimeoutException) STATUS_CODE_TIMEOUT else -1 + val status = when { + e is SocketTimeoutException -> STATUS_CODE_TIMEOUT + e is InterruptedIOException && e.message == "timeout" -> STATUS_CODE_TIMEOUT + else -> -1 + } throw GplayHttpRequestException(status, e.localizedMessage ?: "") } finally { response?.close() -- GitLab From 90be9ccee32606fec247f9e35a4c9e9a818441fe Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 25 Sep 2023 22:29:14 +0530 Subject: [PATCH 085/112] Rename CommonRepository to CacheRepository --- .../common/repository/CacheRepository.kt | 29 +++++++++ .../common/repository/CacheRepositoryImpl.kt | 60 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt new file mode 100644 index 000000000..33891e8bd --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt @@ -0,0 +1,29 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.common.repository + +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.enums.User + +interface CacheRepository { + fun currentUser(): User + fun cacheAuthData(): AuthData + fun resetCachedData() + fun clearAuthData() +} diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt new file mode 100644 index 000000000..50cc198a8 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt @@ -0,0 +1,60 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.common.repository + +import android.content.Context +import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.data.enums.User +import foundation.e.apps.utils.toAuthData +import javax.inject.Inject + +class CacheRepositoryImpl @Inject constructor( + @ApplicationContext val applicationContext: Context +) : CacheRepository { + + override fun currentUser(): User { + return applicationContext.configurations.userType.takeIf { it.isNotEmpty() } + ?.let { User.getUser(it) } + ?: run { User.UNAVAILABLE } + } + override fun resetCachedData() { + applicationContext.configurations.apply { + authData = "" + userType = User.UNAVAILABLE.name + email = "" + oauthtoken = "" + showAllApplications = true + showFOSSApplications = true + showPWAApplications = true + // TODO: reset access token for Google login. It is not defined yet. + } + } + + override fun cacheAuthData(): AuthData = + applicationContext.configurations.authData.let { data -> + if (data.isEmpty()) throw Exception("Auth Data not available") + return data.toAuthData() + } + + override fun clearAuthData() { + applicationContext.configurations.authData = "" + } +} -- GitLab From 98b6d5aeb49cc31ea1476ed61505f62cf6ebe4b7 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 25 Sep 2023 22:34:51 +0530 Subject: [PATCH 086/112] Rename CommonRepository to CacheRepository --- .../java/foundation/e/apps/di/LoginModule.kt | 22 +++---- .../common/repository/CommonRepository.kt | 29 --------- .../common/repository/CommonRepositoryImpl.kt | 60 ------------------- .../install/usecase/AppInstallerUseCase.kt | 8 +-- .../login/repository/LoginRepositoryImpl.kt | 4 +- .../domain/login/usecase/UserLoginUseCase.kt | 14 ++--- .../main/usecase/MainActivityUseCase.kt | 8 +-- .../settings/usecase/SettingsUseCase.kt | 10 ++-- .../domain/updates/usecase/UpdatesUseCase.kt | 8 +-- .../AppInstallProcessorTest.kt | 4 +- 10 files changed, 39 insertions(+), 128 deletions(-) delete mode 100644 app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt delete mode 100644 app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt index c0f47f1a6..4ebbd2907 100644 --- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt +++ b/app/src/main/java/foundation/e/apps/di/LoginModule.kt @@ -24,7 +24,7 @@ import dagger.hilt.components.SingletonComponent import foundation.e.apps.data.login.LoginSourceCleanApk import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.login.LoginSourceInterface -import foundation.e.apps.domain.common.repository.CommonRepositoryImpl +import foundation.e.apps.domain.common.repository.CacheRepositoryImpl import foundation.e.apps.domain.install.usecase.AppInstallerUseCase import foundation.e.apps.domain.login.repository.LoginRepositoryImpl import foundation.e.apps.domain.login.usecase.UserLoginUseCase @@ -47,36 +47,36 @@ object LoginModule { @Provides fun provideLoginUserCase( loginRepositoryImpl: LoginRepositoryImpl, - commonRepositoryImpl: CommonRepositoryImpl + cacheRepositoryImpl: CacheRepositoryImpl ): UserLoginUseCase { - return UserLoginUseCase(loginRepositoryImpl, commonRepositoryImpl) + return UserLoginUseCase(loginRepositoryImpl, cacheRepositoryImpl) } @Provides fun provideSettingsUserCase( - commonRepositoryImpl: CommonRepositoryImpl + cacheRepositoryImpl: CacheRepositoryImpl ): SettingsUseCase { - return SettingsUseCase(commonRepositoryImpl) + return SettingsUseCase(cacheRepositoryImpl) } @Provides fun provideMainActivityUseCase( - commonRepositoryImpl: CommonRepositoryImpl + cacheRepositoryImpl: CacheRepositoryImpl ): MainActivityUseCase { - return MainActivityUseCase(commonRepositoryImpl) + return MainActivityUseCase(cacheRepositoryImpl) } @Provides fun provideUpdatesUseCase( - commonRepositoryImpl: CommonRepositoryImpl + cacheRepositoryImpl: CacheRepositoryImpl ): UpdatesUseCase { - return UpdatesUseCase(commonRepositoryImpl) + return UpdatesUseCase(cacheRepositoryImpl) } @Provides fun provideAppInstallerUseCase( - commonRepositoryImpl: CommonRepositoryImpl + cacheRepositoryImpl: CacheRepositoryImpl ): AppInstallerUseCase { - return AppInstallerUseCase(commonRepositoryImpl) + return AppInstallerUseCase(cacheRepositoryImpl) } } diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt deleted file mode 100644 index 193a6ac0b..000000000 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepository.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.apps.domain.common.repository - -import com.aurora.gplayapi.data.models.AuthData -import foundation.e.apps.data.enums.User - -interface CommonRepository { - fun currentUser(): User - fun cacheAuthData(): AuthData - fun resetCachedData() - fun clearAuthData() -} diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt deleted file mode 100644 index 8c1d21142..000000000 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CommonRepositoryImpl.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.apps.domain.common.repository - -import android.content.Context -import app.lounge.storage.cache.configurations -import com.aurora.gplayapi.data.models.AuthData -import dagger.hilt.android.qualifiers.ApplicationContext -import foundation.e.apps.data.enums.User -import foundation.e.apps.utils.toAuthData -import javax.inject.Inject - -class CommonRepositoryImpl @Inject constructor( - @ApplicationContext val applicationContext: Context -) : CommonRepository { - - override fun currentUser(): User { - return applicationContext.configurations.userType.takeIf { it.isNotEmpty() } - ?.let { User.getUser(it) } - ?: run { User.UNAVAILABLE } - } - override fun resetCachedData() { - applicationContext.configurations.apply { - authData = "" - userType = User.UNAVAILABLE.name - email = "" - oauthtoken = "" - showAllApplications = true - showFOSSApplications = true - showPWAApplications = true - // TODO: reset access token for Google login. It is not defined yet. - } - } - - override fun cacheAuthData(): AuthData = - applicationContext.configurations.authData.let { data -> - if (data.isEmpty()) throw Exception("Auth Data not available") - return data.toAuthData() - } - - override fun clearAuthData() { - applicationContext.configurations.authData = "" - } -} diff --git a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt index 721d3912a..24fafdd22 100644 --- a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt @@ -20,19 +20,19 @@ package foundation.e.apps.domain.install.usecase import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.data.enums.User -import foundation.e.apps.domain.common.repository.CommonRepository +import foundation.e.apps.domain.common.repository.CacheRepository import java.util.Locale import javax.inject.Inject class AppInstallerUseCase@Inject constructor( - private val commonRepository: CommonRepository + private val cacheRepository: CacheRepository ) { fun currentAuthData(): AuthData? { return try { - commonRepository.cacheAuthData() + cacheRepository.cacheAuthData() } catch (e: Exception) { - if (commonRepository.currentUser() == User.NO_GOOGLE) { + if (cacheRepository.currentUser() == User.NO_GOOGLE) { return AuthData("", "").apply { this.isAnonymous = false this.locale = Locale.getDefault() diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt index 22e2f1def..2ffe42ef7 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -60,10 +60,10 @@ class LoginRepositoryImpl @Inject constructor( } // TODO: Remove function parameter once we refactor Google User APIs - override suspend fun googleUser(authData: AuthData, token: String) { + override suspend fun googleUser(authData: AuthData, oauthToken: String) { applicationContext.configurations.authData = authData.toJsonString() applicationContext.configurations.email = authData.email - applicationContext.configurations.oauthtoken = token + applicationContext.configurations.oauthtoken = oauthToken applicationContext.configurations.userType = User.GOOGLE.name } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index 8c688702c..a5f77fc7b 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -19,7 +19,7 @@ package foundation.e.apps.domain.login.usecase import com.aurora.gplayapi.data.models.AuthData -import foundation.e.apps.domain.common.repository.CommonRepository +import foundation.e.apps.domain.common.repository.CacheRepository import foundation.e.apps.domain.login.repository.LoginRepository import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.Flow @@ -30,7 +30,7 @@ import javax.inject.Inject class UserLoginUseCase @Inject constructor( private val loginRepository: LoginRepository, - private val commonRepository: CommonRepository + private val cacheRepository: CacheRepository ) { fun anonymousUser(): Flow> = flow { @@ -60,19 +60,19 @@ class UserLoginUseCase @Inject constructor( fun retrieveCachedAuthData(): Flow> = flow { try { emit(Resource.Loading()) - emit(Resource.Success(commonRepository.cacheAuthData())) + emit(Resource.Success(cacheRepository.cacheAuthData())) } catch (e: Exception) { emit(Resource.Error(e.localizedMessage)) } } fun logoutUser() { - commonRepository.resetCachedData() + cacheRepository.resetCachedData() } - fun currentUser() = commonRepository.currentUser() + fun currentUser() = cacheRepository.currentUser() - fun clearAuthData() = commonRepository.clearAuthData() + fun clearAuthData() = cacheRepository.clearAuthData() fun performAnonymousUserAuthentication(): Flow> = flow { anonymousUser().onEach { anonymousAuth -> @@ -88,7 +88,7 @@ class UserLoginUseCase @Inject constructor( } is Resource.Loading -> emit(Resource.Loading()) is Resource.Success -> { - emit(Resource.Success(commonRepository.cacheAuthData())) + emit(Resource.Success(cacheRepository.cacheAuthData())) } } }.collect() diff --git a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt index 01132ec2b..d0d704f07 100644 --- a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt @@ -18,13 +18,13 @@ package foundation.e.apps.domain.main.usecase -import foundation.e.apps.domain.common.repository.CommonRepository +import foundation.e.apps.domain.common.repository.CacheRepository import javax.inject.Inject class MainActivityUseCase @Inject constructor( - private val commonRepository: CommonRepository + private val cacheRepository: CacheRepository ) { - fun currentUser() = commonRepository.currentUser() + fun currentUser() = cacheRepository.currentUser() - fun currentAuthData() = commonRepository.cacheAuthData() + fun currentAuthData() = cacheRepository.cacheAuthData() } diff --git a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt index ff0ef589d..a5c80bfc6 100644 --- a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt @@ -20,31 +20,31 @@ package foundation.e.apps.domain.settings.usecase import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.data.enums.User -import foundation.e.apps.domain.common.repository.CommonRepository +import foundation.e.apps.domain.common.repository.CacheRepository import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import javax.inject.Inject class SettingsUseCase @Inject constructor( - private val commonRepository: CommonRepository + private val cacheRepository: CacheRepository ) { fun currentUser(): Flow> = flow { kotlin.runCatching { - val currentUser = commonRepository.currentUser() + val currentUser = cacheRepository.currentUser() emit(Resource.Success(currentUser)) }.onFailure { emit(Resource.Error("Something went wrong in fun currentUser()")) } } fun currentAuthData(): Flow> = flow { kotlin.runCatching { - emit(Resource.Success(commonRepository.cacheAuthData())) + emit(Resource.Success(cacheRepository.cacheAuthData())) }.onFailure { emit(Resource.Error("Something went wrong in fun currentUser()")) } } fun logoutUser(): Flow> = flow { runCatching { - commonRepository.resetCachedData() + cacheRepository.resetCachedData() emit(Resource.Success(Unit)) }.onFailure { emit(Resource.Error("Error during logout")) } } diff --git a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt index 9f3af96a8..6295c75e2 100644 --- a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt @@ -18,13 +18,13 @@ package foundation.e.apps.domain.updates.usecase -import foundation.e.apps.domain.common.repository.CommonRepository +import foundation.e.apps.domain.common.repository.CacheRepository import javax.inject.Inject class UpdatesUseCase @Inject constructor( - private val commonRepository: CommonRepository + private val cacheRepository: CacheRepository ) { - fun currentUser() = commonRepository.currentUser() + fun currentUser() = cacheRepository.currentUser() - fun currentAuthData() = commonRepository.cacheAuthData() + fun currentAuthData() = cacheRepository.cacheAuthData() } diff --git a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt index 9373be311..6a79a4a03 100644 --- a/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt +++ b/app/src/test/java/foundation/e/apps/installProcessor/AppInstallProcessorTest.kt @@ -27,7 +27,7 @@ import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fusedDownload.FusedDownloadRepository import foundation.e.apps.data.fusedDownload.IFusedManager import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.domain.common.repository.CommonRepositoryImpl +import foundation.e.apps.domain.common.repository.CacheRepositoryImpl import foundation.e.apps.domain.install.usecase.AppInstallerUseCase import foundation.e.apps.install.workmanager.AppInstallProcessor import foundation.e.apps.util.MainCoroutineRule @@ -88,7 +88,7 @@ class AppInstallProcessorTest { fusedDownloadRepository, fakeFusedManagerRepository, fusedAPIRepository, - AppInstallerUseCase(CommonRepositoryImpl(context)) + AppInstallerUseCase(CacheRepositoryImpl(context)) ) } -- GitLab From 289860b98fbca17a80a1e44af56ceaa2f138c5b2 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 25 Sep 2023 22:37:12 +0530 Subject: [PATCH 087/112] Fix Klint Errors --- app/src/main/java/foundation/e/apps/MainActivity.kt | 4 ++-- .../foundation/e/apps/data/fused/FusedApiImpl.kt | 3 --- .../e/apps/domain/errors/RetryMechanism.kt | 7 +++---- .../java/foundation/e/apps/receivers/DumpAuthData.kt | 1 - .../e/apps/ui/errors/CentralErrorHandler.kt | 12 ++++++------ .../java/foundation/e/apps/ui/home/HomeViewModel.kt | 6 ++++-- .../foundation/e/apps/ui/search/SearchViewModel.kt | 2 -- 7 files changed, 15 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index f9e6ad22d..bc2f7a281 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -42,12 +42,12 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding -import foundation.e.apps.ui.errors.CentralErrorHandler import foundation.e.apps.domain.errors.RetryMechanism import foundation.e.apps.install.updates.UpdatesNotifier 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.errors.CentralErrorHandler import foundation.e.apps.ui.purchase.AppPurchaseFragmentDirections import foundation.e.apps.ui.settings.SettingsFragment import foundation.e.apps.ui.setup.signin.SignInViewModel @@ -307,7 +307,7 @@ class MainActivity : AppCompatActivity() { ceh.getDialogForDataLoadError( context = this@MainActivity, result = it.data as ResultSupreme<*>, - retryAction = { loginViewModel.checkLogin() }, + retryAction = { loginViewModel.checkLogin() } )?.run { ceh.dismissAllAndShow(this) } } ) diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index 82bdadf63..c16e34c61 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -32,7 +32,6 @@ import com.aurora.gplayapi.data.models.SearchBundle import com.aurora.gplayapi.data.models.StreamCluster import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R -import foundation.e.apps.data.Constants.timeoutDurationInMillis import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.cleanapk.CleanApkDownloadInfoFetcher import foundation.e.apps.data.cleanapk.CleanApkRetrofit @@ -68,10 +67,8 @@ import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.pkg.PkgManagerModule import foundation.e.apps.ui.home.model.HomeChildFusedAppDiffUtil import kotlinx.coroutines.Deferred -import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.withTimeout import retrofit2.Response import timber.log.Timber import javax.inject.Inject diff --git a/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt b/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt index ae9ab6888..0afdc953f 100644 --- a/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt +++ b/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt @@ -27,7 +27,7 @@ class RetryMechanism { */ fun wrapWithRetry( retryBlock: () -> Unit, - retryFailureBlock: () -> Unit, + retryFailureBlock: () -> Unit ) { if (!retryEvaluator(retryBlock)) { retryFailureBlock() @@ -49,7 +49,7 @@ class RetryMechanism { * @return true if retry conditions have not expired, false otherwise. */ private fun retryEvaluator( - retryBlock: () -> Unit, + retryBlock: () -> Unit ): Boolean { if (shouldFailRetry()) return false retryBlock() @@ -64,5 +64,4 @@ class RetryMechanism { private fun shouldFailRetry(): Boolean { return autoRetryCount > 0 } - -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt b/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt index b5834bee6..519c14c21 100644 --- a/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt +++ b/app/src/main/java/foundation/e/apps/receivers/DumpAuthData.kt @@ -24,7 +24,6 @@ import com.aurora.gplayapi.data.models.AuthData import com.google.gson.Gson import foundation.e.apps.data.Constants.ACTION_AUTHDATA_DUMP import foundation.e.apps.data.Constants.TAG_AUTHDATA_DUMP -import foundation.e.apps.data.preference.DataStoreModule import org.json.JSONObject import timber.log.Timber diff --git a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt index 44967a359..868c511c6 100644 --- a/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt +++ b/app/src/main/java/foundation/e/apps/ui/errors/CentralErrorHandler.kt @@ -38,7 +38,7 @@ class CentralErrorHandler { fun getDialogForDataLoadError( context: Activity, result: ResultSupreme, - retryAction: () -> Unit, + retryAction: () -> Unit ): AlertDialog.Builder? { return when (result) { is ResultSupreme.Timeout -> { @@ -64,7 +64,7 @@ class CentralErrorHandler { logToDisplay: String = "", user: User, retryAction: () -> Unit, - logoutAction: () -> Unit, + logoutAction: () -> Unit ): AlertDialog.Builder { val customDialogView = getDialogCustomView(context, logToDisplay) val dialog = AlertDialog.Builder(context).apply { @@ -92,7 +92,7 @@ class CentralErrorHandler { private fun getDialogForTimeout( context: Activity, logToDisplay: String = "", - retryAction: () -> Unit, + retryAction: () -> Unit ): AlertDialog.Builder { val customDialogView = getDialogCustomView(context, logToDisplay) val dialog = AlertDialog.Builder(context).apply { @@ -111,7 +111,7 @@ class CentralErrorHandler { private fun getDialogForOtherErrors( context: Activity, logToDisplay: String = "", - retryAction: () -> Unit, + retryAction: () -> Unit ): AlertDialog.Builder { val customDialogView = getDialogCustomView(context, logToDisplay) val dialog = AlertDialog.Builder(context).apply { @@ -129,7 +129,7 @@ class CentralErrorHandler { private fun getDialogCustomView( context: Activity, - logToDisplay: String, + logToDisplay: String ): View { val dialogLayout = DialogErrorLogBinding.inflate(context.layoutInflater) dialogLayout.apply { @@ -172,4 +172,4 @@ class CentralErrorHandler { startActivity(openUrlIntent) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt index 14bdaa2e2..c0714c79b 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt @@ -55,8 +55,10 @@ class HomeViewModel @Inject constructor( fusedAPIRepository.getHomeScreenData(authData).observe(lifecycleOwner) { homeScreenData.postValue(it) - if (!it.isSuccess()) viewModelScope.launch { - EventBus.invokeEvent(AppEvent.DataLoadError(it)) + if (!it.isSuccess()) { + viewModelScope.launch { + EventBus.invokeEvent(AppEvent.DataLoadError(it)) + } } } } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index b85234f27..1d6e4c722 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -31,8 +31,6 @@ import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.GplaySearchResult import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.exceptions.CleanApkException -import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.CoroutineScope -- GitLab From f8943d74d67c43178194365f8d81e81db9bddd39 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 25 Sep 2023 22:43:02 +0530 Subject: [PATCH 088/112] Update codebase as per review comment --- .../java/foundation/e/apps/ui/search/SearchViewModel.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 1d6e4c722..6faa8dfc2 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -121,10 +121,6 @@ class SearchViewModel @Inject constructor( isLoading = true val gplaySearchResult = fusedAPIRepository.getGplaySearchResults(query, nextSubBundle) - if (!gplaySearchResult.isSuccess()) { -// handleException(gplaySearchResult.exception ?: UnknownSourceException()) - } - val isFirstFetch = nextSubBundle == null nextSubBundle = gplaySearchResult.data?.second @@ -152,11 +148,6 @@ class SearchViewModel @Inject constructor( return currentAppList.distinctBy { it.package_name } } -// private fun handleException(exception: Exception) { -// exceptionsList.add(exception) -// exceptionsLiveData.postValue(exceptionsList) -// } - /** * @return returns true if there is changes in data, otherwise false */ -- GitLab From 189540b27831efb689d5393b733451d1876ef04b Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 27 Sep 2023 05:26:35 +0000 Subject: [PATCH 089/112] Update as per review --- .../java/foundation/e/apps/data/enums/User.kt | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/enums/User.kt b/app/src/main/java/foundation/e/apps/data/enums/User.kt index 975cb4bd1..fe98afaab 100644 --- a/app/src/main/java/foundation/e/apps/data/enums/User.kt +++ b/app/src/main/java/foundation/e/apps/data/enums/User.kt @@ -1,19 +1,19 @@ -package foundation.e.apps.data.enums - -enum class User { - NO_GOOGLE, - ANONYMOUS, - GOOGLE, - UNAVAILABLE; - - companion object { - fun getUser(userString: String): User { - val userStrings = values().map { it.name } - return if (userString !in userStrings) { - UNAVAILABLE - } else { - valueOf(userString) - } - } - } -} +package foundation.e.apps.data.enums + +enum class User { + NO_GOOGLE, + ANONYMOUS, + GOOGLE, + UNAVAILABLE; + + companion object { + fun getUser(userString: String): User { + val userStrings = values().map { it.name } + return if (userString in userStrings) { + valueOf(userString) + } else { + UNAVAILABLE + } + } + } +} -- GitLab From 4b732ebe608d0ffac44c91bf24703eadc5b51e8c Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 27 Sep 2023 10:51:06 +0000 Subject: [PATCH 090/112] Update as per review comment --- .../e/apps/data/preference/DataStoreModule.kt | 296 +++++++++--------- 1 file changed, 148 insertions(+), 148 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt b/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt index bb3e427e4..787da7872 100644 --- a/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt +++ b/app/src/main/java/foundation/e/apps/data/preference/DataStoreModule.kt @@ -1,148 +1,148 @@ -/* - * Apps Quickly and easily install Android apps onto your device! - * Copyright (C) 2021 E FOUNDATION - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.apps.data.preference - -import android.content.Context -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.preferencesDataStore -import com.aurora.gplayapi.data.models.AuthData -import com.google.gson.Gson -import dagger.hilt.android.qualifiers.ApplicationContext -import foundation.e.apps.data.enums.User -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.runBlocking -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class DataStoreModule @Inject constructor( - @ApplicationContext - private val context: Context, - private val gson: Gson -) { - - companion object { - private const val preferenceDataStoreName = "Settings" - val Context.dataStore by preferencesDataStore(preferenceDataStoreName) - } - - private val AUTHDATA = stringPreferencesKey("authData") - private val EMAIL = stringPreferencesKey("email") - private val OAUTHTOKEN = stringPreferencesKey("oauthtoken") - private val USERTYPE = stringPreferencesKey("userType") - private val TOCSTATUS = booleanPreferencesKey("tocStatus") - private val TOSVERSION = stringPreferencesKey("tosversion") - - val authData = context.dataStore.data.map { it[AUTHDATA] ?: "" } - val emailData = context.dataStore.data.map { it[EMAIL] ?: "" } - val aasToken = context.dataStore.data.map { it[OAUTHTOKEN] ?: "" } - val userType = context.dataStore.data.map { it[USERTYPE] ?: "" } - val tocStatus = context.dataStore.data.map { it[TOCSTATUS] ?: false } - val tosVersion = context.dataStore.data.map { it[TOSVERSION] ?: "" } - - /** - * Allows to save gplay API token data into datastore - */ - suspend fun saveCredentials(authData: AuthData) { - context.dataStore.edit { - it[AUTHDATA] = gson.toJson(authData) - } - } - - /** - * Destroy auth credentials if they are no longer valid. - * - * Modification for issue: https://gitlab.e.foundation/e/backlog/-/issues/5168 - * Previously this method would also remove [USERTYPE]. - * To clear this value, call [clearUserType]. - */ - suspend fun destroyCredentials() { - context.dataStore.edit { - it.remove(AUTHDATA) - it.remove(EMAIL) - it.remove(OAUTHTOKEN) - } - } - - suspend fun clearUserType() { - context.dataStore.edit { - it.remove(USERTYPE) - } - } - - /** - * TOC status - */ - suspend fun saveTOCStatus(status: Boolean, tosVersion: String) { - context.dataStore.edit { - it[TOCSTATUS] = status - it[TOSVERSION] = tosVersion - } - } - - fun getTOSVersion(): String { - return runBlocking { - tosVersion.first() - } - } - - /** - * User auth type - */ - suspend fun saveUserType(user: User) { - context.dataStore.edit { - it[USERTYPE] = user.name - } - } - - fun getAuthDataSync(): String { - return runBlocking { - authData.first() - } - } - - suspend fun saveEmail(email: String, token: String) { - context.dataStore.edit { - it[EMAIL] = email - it[OAUTHTOKEN] = token - } - } - - fun getEmail(): String { - return runBlocking { - emailData.first() - } - } - - fun getUserType(): User { - return runBlocking { - userType.first().run { - val userStrings = User.values().map { it.name } - if (this !in userStrings) { - User.UNAVAILABLE - } else { - User.valueOf(this) - } - } - } - } -} +/* + * Apps Quickly and easily install Android apps onto your device! + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import com.aurora.gplayapi.data.models.AuthData +import com.google.gson.Gson +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.data.enums.User +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.runBlocking +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DataStoreModule @Inject constructor( + @ApplicationContext + private val context: Context, + private val gson: Gson +) { + + companion object { + private const val preferenceDataStoreName = "Settings" + val Context.dataStore by preferencesDataStore(preferenceDataStoreName) + } + + private val AUTHDATA = stringPreferencesKey("authData") + private val EMAIL = stringPreferencesKey("email") + private val OAUTHTOKEN = stringPreferencesKey("oauthtoken") + private val USERTYPE = stringPreferencesKey("userType") + private val TOCSTATUS = booleanPreferencesKey("tocStatus") + private val TOSVERSION = stringPreferencesKey("tosversion") + + val authData = context.dataStore.data.map { it[AUTHDATA] ?: "" } + val emailData = context.dataStore.data.map { it[EMAIL] ?: "" } + val aasToken = context.dataStore.data.map { it[OAUTHTOKEN] ?: "" } + val userType = context.dataStore.data.map { it[USERTYPE] ?: "" } + val tocStatus = context.dataStore.data.map { it[TOCSTATUS] ?: false } + val tosVersion = context.dataStore.data.map { it[TOSVERSION] ?: "" } + + /** + * Allows to save gplay API token data into datastore + */ + suspend fun saveCredentials(authData: AuthData) { + context.dataStore.edit { + it[AUTHDATA] = gson.toJson(authData) + } + } + + /** + * Destroy auth credentials if they are no longer valid. + * + * Modification for issue: https://gitlab.e.foundation/e/backlog/-/issues/5168 + * Previously this method would also remove [USERTYPE]. + * To clear this value, call [clearUserType]. + */ + suspend fun destroyCredentials() { + context.dataStore.edit { + it.remove(AUTHDATA) + it.remove(EMAIL) + it.remove(OAUTHTOKEN) + } + } + + suspend fun clearUserType() { + context.dataStore.edit { + it.remove(USERTYPE) + } + } + + /** + * TOC status + */ + suspend fun saveTOCStatus(status: Boolean, tosVersion: String) { + context.dataStore.edit { + it[TOCSTATUS] = status + it[TOSVERSION] = tosVersion + } + } + + fun getTOSVersion(): String { + return runBlocking { + tosVersion.first() + } + } + + /** + * User auth type + */ + suspend fun saveUserType(user: User) { + context.dataStore.edit { + it[USERTYPE] = user.name + } + } + + fun getAuthDataSync(): String { + return runBlocking { + authData.first() + } + } + + suspend fun saveEmail(email: String, token: String) { + context.dataStore.edit { + it[EMAIL] = email + it[OAUTHTOKEN] = token + } + } + + fun getEmail(): String { + return runBlocking { + emailData.first() + } + } + + fun getUserType(): User { + return runBlocking { + userType.first().run { + val userStrings = User.values().map { it.name } + if (this in userStrings) { + User.valueOf(this) + } else { + User.UNAVAILABLE + } + } + } + } +} -- GitLab From e30aa525d8364859f775a8ee629ea41d8e26df36 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 27 Sep 2023 17:15:17 +0530 Subject: [PATCH 091/112] Modify as per review comment --- .../e/apps/data/fused/FusedApiImpl.kt | 6 +++--- .../e/apps/data/fused/data/FusedApp.kt | 2 +- .../domain/login/usecase/UserLoginUseCase.kt | 18 ++++++------------ .../apps/presentation/login/LoginViewModel.kt | 1 + .../apps/ui/application/ApplicationFragment.kt | 2 +- .../ui/applicationlist/ApplicationDiffUtil.kt | 2 +- .../ApplicationListRVAdapter.kt | 2 +- .../ui/home/model/HomeChildFusedAppDiffUtil.kt | 2 +- .../e/apps/ui/home/model/HomeChildRVAdapter.kt | 2 +- .../exodus/AppPrivacyInfoRepositoryImplTest.kt | 6 +++--- .../exodus/PrivacyScoreRepositoryImplTest.kt | 6 +++--- .../e/apps/fused/FusedApiImplTest.kt | 2 +- 12 files changed, 23 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index c16e34c61..240076d5b 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -1229,7 +1229,7 @@ class FusedApiImpl @Inject constructor( * Recommended to use this instead of [PkgManagerModule.getPackageStatus]. */ override fun getFusedAppInstallationStatus(fusedApp: FusedApp): Status { - return if (fusedApp.is_pwa) { + return if (fusedApp.isPwa) { pwaManagerModule.getPwaStatus(fusedApp) } else { pkgManagerModule.getPackageStatus(fusedApp.package_name, fusedApp.latest_version_code) @@ -1243,12 +1243,12 @@ class FusedApiImpl @Inject constructor( } private fun FusedApp.updateType() { - this.type = if (this.is_pwa) Type.PWA else Type.NATIVE + this.type = if (this.isPwa) Type.PWA else Type.NATIVE } private fun FusedApp.updateSource() { this.apply { - source = if (origin == Origin.CLEANAPK && is_pwa) { + source = if (origin == Origin.CLEANAPK && isPwa) { context.getString(R.string.pwa) } else if (origin == Origin.CLEANAPK) { context.getString(R.string.open_source) diff --git a/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt b/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt index bda418240..1eb429c06 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt @@ -52,7 +52,7 @@ data class FusedApp( var source: String = String(), val price: String = String(), val isFree: Boolean = true, - val is_pwa: Boolean = false, + val isPwa: Boolean = false, var pwaPlayerDbId: Long = -1, val url: String = String(), var type: Type = Type.NATIVE, diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index a5f77fc7b..57dfbb273 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -34,16 +34,14 @@ class UserLoginUseCase @Inject constructor( ) { fun anonymousUser(): Flow> = flow { - try { + kotlin.runCatching { emit(Resource.Loading()) emit(Resource.Success(loginRepository.anonymousUser())) - } catch (e: Exception) { - emit(Resource.Error(e.localizedMessage)) - } + }.onFailure { failure -> emit(Resource.Error(failure.localizedMessage)) } } fun googleUser(authData: AuthData, token: String): Flow> = flow { - try { + kotlin.runCatching { emit( Resource.Success( loginRepository.googleUser( @@ -52,18 +50,14 @@ class UserLoginUseCase @Inject constructor( ) ) ) - } catch (e: Exception) { - emit(Resource.Error(e.localizedMessage)) - } + }.onFailure { failure -> emit(Resource.Error(failure.localizedMessage)) } } fun retrieveCachedAuthData(): Flow> = flow { - try { + kotlin.runCatching { emit(Resource.Loading()) emit(Resource.Success(cacheRepository.cacheAuthData())) - } catch (e: Exception) { - emit(Resource.Error(e.localizedMessage)) - } + }.onFailure { failure -> emit(Resource.Error(failure.localizedMessage)) } } fun logoutUser() { diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index cb869fd19..792a7fc8d 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -135,6 +135,7 @@ class LoginViewModel @Inject constructor( */ fun initialNoGoogleLogin(onUserSaved: () -> Unit) { viewModelScope.launch { + loginSourceRepository.setNoGoogleMode() val authObject = noGoogleModeUseCase.performNoGoogleLogin() _loginState.value = LoginState(isLoading = false, isLoggedIn = true) authObjects.postValue(listOf(authObject)) diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index 644cda421..d9e110cb9 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -754,7 +754,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { backgroundTintList = ContextCompat.getColorStateList(view.context, R.color.colorAccent) setOnClickListener { - if (fusedApp.is_pwa) { + if (fusedApp.isPwa) { pwaManagerModule.launchPwa(fusedApp) } else { startActivity(pkgManagerModule.getLaunchIntent(fusedApp.package_name)) diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt index 4013e7095..5a56a1dce 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt @@ -51,6 +51,6 @@ class ApplicationDiffUtil : DiffUtil.ItemCallback() { ((oldItem.trackers == LIST_OF_NULL && newItem.trackers.isEmpty()) || oldItem.trackers == newItem.trackers) && oldItem.url.contentEquals(newItem.url) && oldItem.isFree == newItem.isFree && - oldItem.is_pwa == newItem.is_pwa + oldItem.isPwa == newItem.isPwa } } diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt index bdcad5036..55c66616f 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt @@ -527,7 +527,7 @@ class ApplicationListRVAdapter( enableInstallButton(Status.INSTALLED) text = context.getString(R.string.open) setOnClickListener { - if (searchApp.is_pwa) { + if (searchApp.isPwa) { mainActivityViewModel.launchPwa(searchApp) } else { context.startActivity( diff --git a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt index 61bba5097..891c7a6f6 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt @@ -50,6 +50,6 @@ class HomeChildFusedAppDiffUtil : DiffUtil.ItemCallback() { oldItem.trackers == newItem.trackers && oldItem.url.contentEquals(newItem.url) && oldItem.isFree == newItem.isFree && - oldItem.is_pwa == newItem.is_pwa + oldItem.isPwa == newItem.isPwa } } diff --git a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt index 9be2af9e6..07bac986e 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt @@ -236,7 +236,7 @@ class HomeChildRVAdapter( enableInstallButton(Status.INSTALLED) text = context.getString(R.string.open) setOnClickListener { - if (homeApp.is_pwa) { + if (homeApp.isPwa) { mainActivityViewModel.launchPwa(homeApp) } else { context.startActivity( diff --git a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt index 5b0893fe2..907335fdb 100644 --- a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt +++ b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt @@ -63,7 +63,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "foundation.e.demothree", latest_version_code = 123, - is_pwa = true + isPwa = true ) val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) assertEquals("getAppPrivacyInfo", true, result.isSuccess()) @@ -78,7 +78,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "", latest_version_code = 123, - is_pwa = true + isPwa = true ) val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) assertEquals("getAppPrivacyInfo", false, result.isSuccess()) @@ -92,7 +92,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - is_pwa = true + isPwa = true ) fakeTrackerDao.trackers.clear() val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) diff --git a/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt index b6a8c25f8..e7062fe91 100644 --- a/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt +++ b/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt @@ -43,7 +43,7 @@ class PrivacyScoreRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - is_pwa = true, + isPwa = true, permsFromExodus = listOf(), perms = listOf(), trackers = listOf() @@ -60,7 +60,7 @@ class PrivacyScoreRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - is_pwa = true, + isPwa = true, perms = listOf(), trackers = listOf() ) @@ -76,7 +76,7 @@ class PrivacyScoreRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - is_pwa = true, + isPwa = true, permsFromExodus = listOf(), perms = listOf(), trackers = CommonUtilsModule.LIST_OF_NULL diff --git a/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt b/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt index d4e75679f..3d7f65d0a 100644 --- a/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt @@ -432,7 +432,7 @@ class FusedApiImplTest { name = "Demo Three", package_name = "foundation.e.demothree", latest_version_code = 123, - is_pwa = true + isPwa = true ) Mockito.`when`(pwaManagerModule.getPwaStatus(fusedApp)).thenReturn(fusedApp.status) -- GitLab From 6c5a1e7996fee9b8dfe241aac0af22a8f6d77b2f Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 27 Sep 2023 17:19:25 +0530 Subject: [PATCH 092/112] Fix klint errors --- app/src/main/java/foundation/e/apps/data/enums/User.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/data/enums/User.kt b/app/src/main/java/foundation/e/apps/data/enums/User.kt index fe98afaab..f26573094 100644 --- a/app/src/main/java/foundation/e/apps/data/enums/User.kt +++ b/app/src/main/java/foundation/e/apps/data/enums/User.kt @@ -10,7 +10,7 @@ enum class User { fun getUser(userString: String): User { val userStrings = values().map { it.name } return if (userString in userStrings) { - valueOf(userString) + valueOf(userString) } else { UNAVAILABLE } -- GitLab From c4302d7b0f7e2bf9e700b106ad86ae1cc2803594 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Wed, 27 Sep 2023 20:28:38 +0530 Subject: [PATCH 093/112] MainActivity - change ceh to errorHandler --- app/src/main/java/foundation/e/apps/MainActivity.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index bc2f7a281..d1107ad23 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -68,7 +68,7 @@ class MainActivity : AppCompatActivity() { private lateinit var viewModel: MainActivityViewModel private val retryMechanism by lazy { RetryMechanism() } - private val ceh by lazy { CentralErrorHandler() } + private val errorHandler by lazy { CentralErrorHandler() } @Inject lateinit var preferenceManagerModule: PreferenceManagerModule @@ -285,13 +285,13 @@ class MainActivity : AppCompatActivity() { retryMechanism.wrapWithRetry( { loginViewModel.getNewToken() }, { - ceh.getDialogForUnauthorized( + errorHandler.getDialogForUnauthorized( context = this@MainActivity, logToDisplay = it.data.toString(), user = currentUser, retryAction = { loginViewModel.getNewToken() }, logoutAction = { loginViewModel.logout() } - ).run { ceh.dismissAllAndShow(this) } + ).run { errorHandler.dismissAllAndShow(this) } } ) } @@ -304,11 +304,11 @@ class MainActivity : AppCompatActivity() { retryMechanism.wrapWithRetry( { loginViewModel.checkLogin() }, { - ceh.getDialogForDataLoadError( + errorHandler.getDialogForDataLoadError( context = this@MainActivity, result = it.data as ResultSupreme<*>, retryAction = { loginViewModel.checkLogin() } - )?.run { ceh.dismissAllAndShow(this) } + )?.run { errorHandler.dismissAllAndShow(this) } } ) } -- GitLab From 5968d4b6225e2590627b2e585900715d99f36fd1 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Wed, 27 Sep 2023 22:34:18 +0600 Subject: [PATCH 094/112] enabled search page pagination --- .../e/apps/ui/search/SearchFragment.kt | 16 +++++++++++++++- .../e/apps/ui/search/SearchViewModel.kt | 4 ++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt index 71ed0ebad..cff3b49c6 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt @@ -58,6 +58,7 @@ import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.applicationlist.ApplicationListRVAdapter +import foundation.e.apps.utils.isNetworkAvailable import kotlinx.coroutines.launch import javax.inject.Inject @@ -74,7 +75,8 @@ class SearchFragment : private var _binding: FragmentSearchBinding? = null private val binding get() = _binding!! - private val searchViewModel: SearchViewModel by viewModels() + //To avoid SyntheticAccessor, declared as protected + protected val searchViewModel: SearchViewModel by viewModels() private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() private val mainActivityViewModel: MainActivityViewModel by activityViewModels() @@ -126,6 +128,18 @@ class SearchFragment : this.authData = it.authData loadData() } + + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (!recyclerView.canScrollVertically(1)) { + if (!requireContext().isNetworkAvailable()) { + return + } + searchViewModel.loadMore(searchText) + } + } + }) } private fun observeSearchResult(listAdapter: ApplicationListRVAdapter?) { diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 6faa8dfc2..1b852bece 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -121,6 +121,10 @@ class SearchViewModel @Inject constructor( isLoading = true val gplaySearchResult = fusedAPIRepository.getGplaySearchResults(query, nextSubBundle) + if (!gplaySearchResult.isSuccess()) { + EventBus.invokeEvent(AppEvent.DataLoadError(gplaySearchResult)) + } + val isFirstFetch = nextSubBundle == null nextSubBundle = gplaySearchResult.data?.second -- GitLab From f0d027141fdeaa8acfebdf22a828ee2d60e1dd30 Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Thu, 28 Sep 2023 06:36:17 +0000 Subject: [PATCH 095/112] Add new end line --- .../e/apps/domain/common/repository/CacheRepositoryImpl.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt index 50cc198a8..baa75487f 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt @@ -35,6 +35,7 @@ class CacheRepositoryImpl @Inject constructor( ?.let { User.getUser(it) } ?: run { User.UNAVAILABLE } } + override fun resetCachedData() { applicationContext.configurations.apply { authData = "" -- GitLab From 5f6563200a4e067f5e527e4cad0b0df7960b5707 Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Thu, 28 Sep 2023 06:38:14 +0000 Subject: [PATCH 096/112] Added suggested patch --- .../java/foundation/e/apps/data/login/LoginDataStore.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt b/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt index 270bd17a4..7d8669e78 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginDataStore.kt @@ -120,10 +120,10 @@ class LoginDataStore @Inject constructor( return runBlocking { userType.first().run { val userStrings = User.values().map { it.name } - if (this !in userStrings) { - User.UNAVAILABLE - } else { + if (this in userStrings) { User.valueOf(this) + } else { + User.UNAVAILABLE } } } -- GitLab From ba5aa219ce7f0ec682931c3e65f791b3c67383bb Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 28 Sep 2023 12:15:49 +0530 Subject: [PATCH 097/112] Modify code as per review comment --- .../common/repository/CacheRepositoryImpl.kt | 2 +- .../presentation/login/LoginViewModelTest.kt | 88 ------------------- 2 files changed, 1 insertion(+), 89 deletions(-) delete mode 100644 app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt index baa75487f..3de408f1a 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt @@ -51,7 +51,7 @@ class CacheRepositoryImpl @Inject constructor( override fun cacheAuthData(): AuthData = applicationContext.configurations.authData.let { data -> - if (data.isEmpty()) throw Exception("Auth Data not available") + if (data.isEmpty()) throw RuntimeException("Auth Data not available") return data.toAuthData() } diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt deleted file mode 100644 index 7552f0c41..000000000 --- a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.apps.presentation.login - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import foundation.e.apps.data.login.LoginSourceRepository -import foundation.e.apps.domain.login.usecase.NoGoogleModeUseCase -import foundation.e.apps.domain.login.usecase.UserLoginUseCase -import foundation.e.apps.loginFailureMessage -import foundation.e.apps.testAnonymousResponseData -import foundation.e.apps.utils.Resource -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.test.setMain -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.MockitoAnnotations - -@OptIn(ExperimentalCoroutinesApi::class) -class LoginViewModelTest { - - private val testDispatcher = StandardTestDispatcher() - - @get:Rule - val rule = InstantTaskExecutorRule() - - @Mock - lateinit var mockUserLoginUseCase: UserLoginUseCase - - @Mock - lateinit var mockNoGoogleModeUseCase: NoGoogleModeUseCase - - @Mock - lateinit var loginRepository: LoginSourceRepository - - @Before - fun setUp() { - MockitoAnnotations.openMocks(this) - Dispatchers.setMain(testDispatcher) - } - - @Test - fun testOnSuccessReturnLogInStateTrue() = runTest { - Mockito.`when`(mockUserLoginUseCase.anonymousUser()) - .thenReturn(flowOf(Resource.Success(testAnonymousResponseData))) - } - - @Test - fun testOnFailureReturnLogInStateFalseWithError() = runTest { - Mockito.`when`(mockUserLoginUseCase.anonymousUser()) - .thenReturn(flowOf(Resource.Error(loginFailureMessage, null))) - } - - @Test - fun testOnLoadingReturnLogInStateFalse() = runTest { - Mockito.`when`(mockUserLoginUseCase.anonymousUser()) - .thenReturn(flowOf(Resource.Loading(null))) - } - - @After - fun tearDown() { - Dispatchers.resetMain() - } -} -- GitLab From 69cba17cd272b4eea3e0cf0187cdb58563974d5d Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Thu, 28 Sep 2023 14:28:15 +0600 Subject: [PATCH 098/112] handled corner case of refreshing the search page --- .../java/foundation/e/apps/ui/search/SearchFragment.kt | 9 +++++++-- .../java/foundation/e/apps/ui/search/SearchViewModel.kt | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt index cff3b49c6..e43367bda 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt @@ -75,7 +75,7 @@ class SearchFragment : private var _binding: FragmentSearchBinding? = null private val binding get() = _binding!! - //To avoid SyntheticAccessor, declared as protected + // To avoid SyntheticAccessor, declared as protected protected val searchViewModel: SearchViewModel by viewModels() private val privacyInfoViewModel: PrivacyInfoViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() @@ -123,7 +123,12 @@ class SearchFragment : loginViewModel.loginState.observe(viewLifecycleOwner) { val currentQuery = searchView?.query?.toString() ?: "" - if (!it.isLoggedIn || (currentQuery.isNotEmpty() && lastSearch == currentQuery)) return@observe + if ((!it.isLoggedIn || (currentQuery.isNotEmpty() && lastSearch == currentQuery)) && + !searchViewModel.isLoginStateChanged(it) + ) { + return@observe + } + // TODO : check for network and wait if network is unavailable this.authData = it.authData loadData() diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 1b852bece..6dfcffd69 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -30,7 +30,7 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.GplaySearchResult import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.presentation.login.LoginState import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.CoroutineScope @@ -49,7 +49,7 @@ class SearchViewModel @Inject constructor( val searchResult: MutableLiveData, Boolean>>> = MutableLiveData() - private var lastAuthObjects: List? = null + private var lastLoginState: LoginState? = null private var nextSubBundle: Set? = null @@ -159,4 +159,6 @@ class SearchViewModel @Inject constructor( newFusedApps: List, oldFusedApps: List ) = fusedAPIRepository.isAnyFusedAppUpdated(newFusedApps, oldFusedApps) + + fun isLoginStateChanged(loginState: LoginState) = this.lastLoginState == null || this.lastLoginState != loginState } -- GitLab From a0f32d7fc1a450fe3e004536857aaa7e42330e9a Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 2 Oct 2023 09:50:31 +0530 Subject: [PATCH 099/112] Add specific exception to handle cached user --- .../e/apps/domain/install/usecase/AppInstallerUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt index 24fafdd22..76d432ec3 100644 --- a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt @@ -31,7 +31,7 @@ class AppInstallerUseCase@Inject constructor( fun currentAuthData(): AuthData? { return try { cacheRepository.cacheAuthData() - } catch (e: Exception) { + } catch (e: RuntimeException) { if (cacheRepository.currentUser() == User.NO_GOOGLE) { return AuthData("", "").apply { this.isAnonymous = false -- GitLab From 191c2c056b3bf24397574e8e20b0d6d0b48ca090 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 2 Oct 2023 09:59:52 +0530 Subject: [PATCH 100/112] Fix Klint issue --- .../e/apps/install/download/DownloadManagerUtils.kt | 8 +++++--- .../notification/StorageNotificationManager.kt | 3 +-- .../apps/install/workmanager/AppInstallProcessor.kt | 12 ++++++++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/install/download/DownloadManagerUtils.kt b/app/src/main/java/foundation/e/apps/install/download/DownloadManagerUtils.kt index 1438ad18f..7a15e92a7 100644 --- a/app/src/main/java/foundation/e/apps/install/download/DownloadManagerUtils.kt +++ b/app/src/main/java/foundation/e/apps/install/download/DownloadManagerUtils.kt @@ -44,7 +44,7 @@ class DownloadManagerUtils @Inject constructor( @ApplicationContext private val context: Context, private val fusedManagerRepository: FusedManagerRepository, private val downloadManager: DownloadManager, - private val storageNotificationManager: StorageNotificationManager, + private val storageNotificationManager: StorageNotificationManager ) { private val mutex = Mutex() @@ -107,7 +107,8 @@ class DownloadManagerUtils @Inject constructor( fusedDownload: FusedDownload, downloadId: Long ) = downloadManager.isDownloadSuccessful(downloadId) && areAllFilesDownloaded( - numberOfDownloadedItems, fusedDownload + numberOfDownloadedItems, + fusedDownload ) && checkCleanApkSignatureOK(fusedDownload) private fun areAllFilesDownloaded( @@ -126,7 +127,8 @@ class DownloadManagerUtils @Inject constructor( private suspend fun checkCleanApkSignatureOK(fusedDownload: FusedDownload): Boolean { if (fusedDownload.origin != Origin.CLEANAPK || fusedManagerRepository.isFdroidApplicationSigned( - context, fusedDownload + context, + fusedDownload ) ) { Timber.d("Apk signature is OK") diff --git a/app/src/main/java/foundation/e/apps/install/notification/StorageNotificationManager.kt b/app/src/main/java/foundation/e/apps/install/notification/StorageNotificationManager.kt index be74895ea..3018337b3 100644 --- a/app/src/main/java/foundation/e/apps/install/notification/StorageNotificationManager.kt +++ b/app/src/main/java/foundation/e/apps/install/notification/StorageNotificationManager.kt @@ -35,14 +35,13 @@ import javax.inject.Inject class StorageNotificationManager @Inject constructor( @ApplicationContext private val context: Context, - private val downloadManager: DownloadManager, + private val downloadManager: DownloadManager ) { companion object { const val NOT_ENOUGH_SPACE_NOTIFICATION_ID = 7874 } fun showNotEnoughSpaceNotification(fusedDownload: FusedDownload, downloadId: Long? = null) { - with(NotificationManagerCompat.from(context)) { if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index b9f2e2f8b..d83edee24 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -164,7 +164,8 @@ class AppInstallProcessor @Inject constructor( EventBus.invokeEvent( AppEvent.UpdateEvent( ResultSupreme.WorkError( - ResultStatus.UNKNOWN, fusedDownload + ResultStatus.UNKNOWN, + fusedDownload ) ) ) @@ -177,7 +178,8 @@ class AppInstallProcessor @Inject constructor( fusedDownload: FusedDownload ) { fusedAPIRepository.updateFusedDownloadWithDownloadingInfo( - fusedDownload.origin, fusedDownload + fusedDownload.origin, + fusedDownload ) } @@ -257,7 +259,8 @@ class AppInstallProcessor @Inject constructor( private suspend fun isUpdateCompleted(): Boolean { val downloadListWithoutAnyIssue = fusedDownloadRepository.getDownloadList().filter { !listOf( - Status.INSTALLATION_ISSUE, Status.PURCHASE_NEEDED + Status.INSTALLATION_ISSUE, + Status.PURCHASE_NEEDED ).contains(it.status) } @@ -353,7 +356,8 @@ class AppInstallProcessor @Inject constructor( else -> { Timber.wtf( - TAG, "===> ${fusedDownload.name} is in wrong state ${fusedDownload.status}" + TAG, + "===> ${fusedDownload.name} is in wrong state ${fusedDownload.status}" ) finishInstallation(fusedDownload) } -- GitLab From 90d3bd3d007620df3f69649e1a853cf89a6082b2 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 2 Oct 2023 18:05:56 +0530 Subject: [PATCH 101/112] loadDataOnce extension function --- .../java/foundation/e/apps/utils/Extensions.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/utils/Extensions.kt b/app/src/main/java/foundation/e/apps/utils/Extensions.kt index 8e2b1b9fd..031985359 100644 --- a/app/src/main/java/foundation/e/apps/utils/Extensions.kt +++ b/app/src/main/java/foundation/e/apps/utils/Extensions.kt @@ -4,6 +4,9 @@ import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer import com.aurora.gplayapi.data.models.AuthData import com.google.gson.Gson import foundation.e.apps.R @@ -48,3 +51,17 @@ fun Context.isNetworkAvailable(): Boolean { fun AuthData.toJsonString(): String = Gson().toJson(this) fun String.toAuthData(): AuthData = Gson().fromJson(this, AuthData::class.java) + +fun LiveData.loadDataOnce(lifecycleOwner: LifecycleOwner, observer: Observer) { + this.observe( + lifecycleOwner, + object : Observer { + override fun onChanged(value: Boolean) { + if (value) { + observer.onChanged(true) + this@loadDataOnce.removeObserver(this) + } + } + } + ) +} -- GitLab From da8cbfeae3833c32b4bd00f2e2974fd58d233e66 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 2 Oct 2023 18:06:49 +0530 Subject: [PATCH 102/112] fix results of internetConnection --- .../main/java/foundation/e/apps/ui/MainActivityViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt index 8a8b9f201..2bb64408a 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt @@ -247,7 +247,7 @@ class MainActivityViewModel @Inject constructor( override fun onAvailable(network: Network) { super.onAvailable(network) - callbackFlowScope.sendInternetStatus(connectivityManager) + callbackFlowScope.trySend(true) } override fun onCapabilitiesChanged( @@ -260,7 +260,7 @@ class MainActivityViewModel @Inject constructor( override fun onLost(network: Network) { super.onLost(network) - callbackFlowScope.sendInternetStatus(connectivityManager) + callbackFlowScope.trySend(false) } } } -- GitLab From c78393cb095cfefd5fdcbcbb787f3086d239044e Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 2 Oct 2023 18:08:57 +0530 Subject: [PATCH 103/112] use loadDataOnce in fragments --- .../ui/application/ApplicationFragment.kt | 12 ++++++----- .../ApplicationListFragment.kt | 12 ++++++----- .../e/apps/ui/categories/AppsFragment.kt | 13 ++++++++---- .../e/apps/ui/categories/GamesFragment.kt | 13 ++++++++---- .../foundation/e/apps/ui/home/HomeFragment.kt | 12 ++++++----- .../e/apps/ui/search/SearchFragment.kt | 21 ++++++++++--------- .../e/apps/ui/updates/UpdatesFragment.kt | 13 +++++++----- 7 files changed, 58 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index d9e110cb9..4f6630102 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -67,6 +67,7 @@ import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.model.ApplicationScreenshotsRVAdapter import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment +import foundation.e.apps.utils.loadDataOnce import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import timber.log.Timber @@ -146,11 +147,12 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { super.onViewCreated(view, savedInstanceState) _binding = FragmentApplicationBinding.bind(view) - loginViewModel.loginState.observe(viewLifecycleOwner) { - if (it.isLoggedIn) { - // TODO : check for network and wait if network is unavailable - this.authData = it.authData - loadData() + mainActivityViewModel.internetConnection.loadDataOnce(this) { + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + this.authData = it.authData + loadData() + } } } diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt index cca5b5622..3a516e6f8 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt @@ -48,6 +48,7 @@ import foundation.e.apps.ui.AppProgressViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment +import foundation.e.apps.utils.loadDataOnce import kotlinx.coroutines.launch import javax.inject.Inject @@ -90,11 +91,12 @@ class ApplicationListFragment : setupRecyclerView(view) observeAppListLiveData() - loginViewModel.loginState.observe(viewLifecycleOwner) { - if (it.isLoggedIn) { - // TODO : check for network and wait if network is unavailable - this.authData = it.authData - loadData() + mainActivityViewModel.internetConnection.loadDataOnce(this) { + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + this.authData = it.authData + loadData() + } } } } diff --git a/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt b/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt index 5bb163173..cc3ced80c 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/AppsFragment.kt @@ -21,6 +21,7 @@ package foundation.e.apps.ui.categories import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager @@ -29,7 +30,9 @@ import foundation.e.apps.R import foundation.e.apps.data.fused.utils.CategoryType import foundation.e.apps.databinding.FragmentAppsBinding import foundation.e.apps.presentation.login.LoginViewModel +import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.categories.model.CategoriesRVAdapter +import foundation.e.apps.utils.loadDataOnce @AndroidEntryPoint class AppsFragment : Fragment(R.layout.fragment_apps) { @@ -37,6 +40,7 @@ class AppsFragment : Fragment(R.layout.fragment_apps) { private val binding get() = _binding!! private val categoriesViewModel: CategoriesViewModel by viewModels() + private val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val loginViewModel: LoginViewModel by lazy { ViewModelProvider(requireActivity())[LoginViewModel::class.java] @@ -46,10 +50,11 @@ class AppsFragment : Fragment(R.layout.fragment_apps) { super.onViewCreated(view, savedInstanceState) _binding = FragmentAppsBinding.bind(view) - loginViewModel.loginState.observe(viewLifecycleOwner) { - if (it.isLoggedIn) { - // TODO : check for network and wait if network is unavailable - loadData() + mainActivityViewModel.internetConnection.loadDataOnce(this) { + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + loadData() + } } } diff --git a/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt index 973bc42e7..4551194a2 100644 --- a/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/categories/GamesFragment.kt @@ -21,6 +21,7 @@ package foundation.e.apps.ui.categories import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager @@ -29,7 +30,9 @@ import foundation.e.apps.R import foundation.e.apps.data.fused.utils.CategoryType import foundation.e.apps.databinding.FragmentGamesBinding import foundation.e.apps.presentation.login.LoginViewModel +import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.categories.model.CategoriesRVAdapter +import foundation.e.apps.utils.loadDataOnce @AndroidEntryPoint class GamesFragment : Fragment(R.layout.fragment_games) { @@ -37,6 +40,7 @@ class GamesFragment : Fragment(R.layout.fragment_games) { private val binding get() = _binding!! private val categoriesViewModel: CategoriesViewModel by viewModels() + private val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val loginViewModel: LoginViewModel by lazy { ViewModelProvider(requireActivity())[LoginViewModel::class.java] @@ -46,10 +50,11 @@ class GamesFragment : Fragment(R.layout.fragment_games) { super.onViewCreated(view, savedInstanceState) _binding = FragmentGamesBinding.bind(view) - loginViewModel.loginState.observe(viewLifecycleOwner) { - if (it.isLoggedIn) { - // TODO : check for network and wait if network is unavailable - loadData() + mainActivityViewModel.internetConnection.loadDataOnce(this) { + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + loadData() + } } } diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt index 78b759f2b..617857054 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt @@ -46,6 +46,7 @@ import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.home.model.HomeChildRVAdapter import foundation.e.apps.ui.home.model.HomeParentRVAdapter +import foundation.e.apps.utils.loadDataOnce import kotlinx.coroutines.launch import javax.inject.Inject @@ -117,11 +118,12 @@ class HomeFragment : Fragment(R.layout.fragment_home), FusedAPIInterface { } private fun loadHomePageData() { - loginViewModel.loginState.observe(viewLifecycleOwner) { - if (it.isLoggedIn) { - // TODO : check for network and wait if network is unavailable - this.authData = it.authData - loadData() + mainActivityViewModel.internetConnection.loadDataOnce(this) { + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.isLoggedIn) { + this.authData = it.authData + loadData() + } } } } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt index e43367bda..013dd4275 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt @@ -59,6 +59,7 @@ import foundation.e.apps.ui.PrivacyInfoViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.applicationlist.ApplicationListRVAdapter import foundation.e.apps.utils.isNetworkAvailable +import foundation.e.apps.utils.loadDataOnce import kotlinx.coroutines.launch import javax.inject.Inject @@ -121,17 +122,17 @@ class SearchFragment : observeSearchResult(listAdapter) - loginViewModel.loginState.observe(viewLifecycleOwner) { - val currentQuery = searchView?.query?.toString() ?: "" - if ((!it.isLoggedIn || (currentQuery.isNotEmpty() && lastSearch == currentQuery)) && - !searchViewModel.isLoginStateChanged(it) - ) { - return@observe + mainActivityViewModel.internetConnection.loadDataOnce(this) { + loginViewModel.loginState.observe(viewLifecycleOwner) { + val currentQuery = searchView?.query?.toString() ?: "" + if ((!it.isLoggedIn || (currentQuery.isNotEmpty() && lastSearch == currentQuery)) && + !searchViewModel.isLoginStateChanged(it) + ) { + return@observe + } + this.authData = it.authData + loadData() } - - // TODO : check for network and wait if network is unavailable - this.authData = it.authData - loadData() } binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { diff --git a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt index 936c4cd4c..5ceade8b3 100644 --- a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt @@ -57,6 +57,7 @@ import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.applicationlist.ApplicationListRVAdapter import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus +import foundation.e.apps.utils.loadDataOnce import foundation.e.apps.utils.toast import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filter @@ -93,12 +94,14 @@ class UpdatesFragment : Fragment(R.layout.fragment_updates), FusedAPIInterface { binding.button.isEnabled = false - loginViewModel.loginState.observe(viewLifecycleOwner) { - if (!it.isLoggedIn) return@observe - if (!updatesViewModel.updatesList.value?.first.isNullOrEmpty()) { - return@observe + mainActivityViewModel.internetConnection.loadDataOnce(this) { + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (!it.isLoggedIn) return@observe + if (!updatesViewModel.updatesList.value?.first.isNullOrEmpty()) { + return@observe + } + loadData() } - loadData() } val recyclerView = binding.recyclerView -- GitLab From 81b7063af0b403a4689551dc704117ea2d413323 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 3 Oct 2023 18:10:36 +0600 Subject: [PATCH 104/112] fixed: refresh session for 429 error --- .../java/foundation/e/apps/MainActivity.kt | 11 ++++- .../main/usecase/MainActivityUseCase.kt | 9 +++- .../apps/presentation/login/LoginViewModel.kt | 7 +-- .../ApplicationListViewModel.kt | 5 +++ .../e/apps/ui/search/SearchFragment.kt | 43 +++++++++---------- .../e/apps/ui/search/SearchViewModel.kt | 22 +++++----- .../anonymous/AnonymousUserRetrofitImpl.kt | 1 + 7 files changed, 60 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index d1107ad23..e69e71610 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -39,7 +39,6 @@ import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.domain.errors.RetryMechanism @@ -73,6 +72,10 @@ class MainActivity : AppCompatActivity() { @Inject lateinit var preferenceManagerModule: PreferenceManagerModule + companion object { + private const val STATUS_TOO_MANY_REQUESTS = "Status: 429" + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -301,6 +304,10 @@ class MainActivity : AppCompatActivity() { EventBus.events.filter { appEvent -> appEvent is AppEvent.DataLoadError<*> }.collectLatest { + if (it.data is ResultSupreme<*> && it.data.message.contains(STATUS_TOO_MANY_REQUESTS)) { + return@collectLatest + } + retryMechanism.wrapWithRetry( { loginViewModel.checkLogin() }, { @@ -328,7 +335,7 @@ class MainActivity : AppCompatActivity() { binding.sessionErrorLayout.visibility = View.VISIBLE binding.retrySessionButton.setOnClickListener { binding.sessionErrorLayout.visibility = View.GONE - loginViewModel.startLoginFlow(listOf(LoginSourceGPlay::class.java.simpleName)) + loginViewModel.getNewToken() } } } diff --git a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt index d0d704f07..5a35f9f85 100644 --- a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt @@ -18,6 +18,7 @@ package foundation.e.apps.domain.main.usecase +import com.aurora.gplayapi.data.models.AuthData import foundation.e.apps.domain.common.repository.CacheRepository import javax.inject.Inject @@ -26,5 +27,11 @@ class MainActivityUseCase @Inject constructor( ) { fun currentUser() = cacheRepository.currentUser() - fun currentAuthData() = cacheRepository.cacheAuthData() + fun currentAuthData(): AuthData { + return try { + cacheRepository.cacheAuthData() + } catch (e: RuntimeException) { + AuthData("", "") + } + } } diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 792a7fc8d..1884a14a3 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -68,7 +68,9 @@ class LoginViewModel @Inject constructor( fun startLoginFlow(clearList: List = listOf()) { viewModelScope.launch { val authObjectsLocal = loginSourceRepository.getAuthObjects(clearList) - authObjects.postValue(authObjectsLocal) + if (authObjectsLocal.isNotEmpty()) { + _loginState.postValue(LoginState(isLoggedIn = authObjectsLocal[0].result.isSuccess())) + } } } @@ -235,8 +237,7 @@ class LoginViewModel @Inject constructor( is Resource.Loading -> _loginState.value = LoginState(isLoading = true) is Resource.Success -> { // TODO - it.data?.let { it1 -> updateAuthObjectForAnonymousUser(it1) } - + it.data?.let { authData -> updateAuthObjectForAnonymousUser(authData) } _loginState.value = LoginState(isLoggedIn = true, authData = it.data, user = User.ANONYMOUS) } diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt index 29dae7c1a..82ea3cc4a 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListViewModel.kt @@ -58,6 +58,7 @@ class ApplicationListViewModel @Inject constructor( return } + this.nextPageUrl = null viewModelScope.launch(Dispatchers.IO) { isLoading = true val result = fusedAPIRepository.getAppsListBasedOnCategory( @@ -74,6 +75,10 @@ class ApplicationListViewModel @Inject constructor( updateNextPageUrl(it.second) } + if (result.isSuccess()) { + loadMore(authData, category) + } + if (!result.isSuccess()) EventBus.invokeEvent(AppEvent.DataLoadError(result)) } } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt index 013dd4275..15213d9e3 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt @@ -40,7 +40,6 @@ import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.aurora.gplayapi.SearchSuggestEntry -import com.aurora.gplayapi.data.models.AuthData import com.facebook.shimmer.ShimmerFrameLayout import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R @@ -87,8 +86,6 @@ class SearchFragment : ViewModelProvider(requireActivity())[LoginViewModel::class.java] } - private var authData: AuthData? = null - private val SUGGESTION_KEY = "suggestion" private var lastSearch = "" @@ -104,6 +101,24 @@ class SearchFragment : */ private var searchText = "" + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mainActivityViewModel.internetConnection.loadDataOnce(this) { + observeLoginState() + } + } + + private fun observeLoginState() { + loginViewModel.loginState.observe(viewLifecycleOwner) { + if (it.authData == null || !it.isLoggedIn) { + return@observe + } + + searchViewModel.authData = it.authData + loadData() + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentSearchBinding.bind(view) @@ -122,26 +137,10 @@ class SearchFragment : observeSearchResult(listAdapter) - mainActivityViewModel.internetConnection.loadDataOnce(this) { - loginViewModel.loginState.observe(viewLifecycleOwner) { - val currentQuery = searchView?.query?.toString() ?: "" - if ((!it.isLoggedIn || (currentQuery.isNotEmpty() && lastSearch == currentQuery)) && - !searchViewModel.isLoginStateChanged(it) - ) { - return@observe - } - this.authData = it.authData - loadData() - } - } - binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) - if (!recyclerView.canScrollVertically(1)) { - if (!requireContext().isNetworkAvailable()) { - return - } + if (!recyclerView.canScrollVertically(1) && requireContext().isNetworkAvailable()) { searchViewModel.loadMore(searchText) } } @@ -288,7 +287,7 @@ class SearchFragment : private fun loadData() { showLoadingUI() - searchViewModel.loadData(searchText, viewLifecycleOwner, authData) + searchViewModel.loadData(searchText, viewLifecycleOwner) } private fun showLoadingUI() { @@ -379,7 +378,7 @@ class SearchFragment : override fun onQueryTextChange(newText: String?): Boolean { newText?.let { text -> - searchViewModel.getSearchSuggestions(text, authData) + searchViewModel.getSearchSuggestions(text) } return true } diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 6dfcffd69..44a7eaf0d 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -30,7 +30,6 @@ import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.GplaySearchResult import foundation.e.apps.data.fused.data.FusedApp -import foundation.e.apps.presentation.login.LoginState import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.CoroutineScope @@ -49,7 +48,7 @@ class SearchViewModel @Inject constructor( val searchResult: MutableLiveData, Boolean>>> = MutableLiveData() - private var lastLoginState: LoginState? = null + var authData: AuthData? = null private var nextSubBundle: Set? = null @@ -59,13 +58,13 @@ class SearchViewModel @Inject constructor( private const val DATA_LOAD_ERROR = "Data load error" } - fun getSearchSuggestions(query: String, authData: AuthData?) { + fun getSearchSuggestions(query: String) { viewModelScope.launch(Dispatchers.IO) { if (query.isNotBlank() && authData != null) { searchSuggest.postValue( fusedAPIRepository.getSearchSuggestions( query, - authData + this@SearchViewModel.getNonNullAuthData() ) ) } @@ -74,13 +73,14 @@ class SearchViewModel @Inject constructor( fun loadData( query: String, - lifecycleOwner: LifecycleOwner, - authData: AuthData? + lifecycleOwner: LifecycleOwner ) { if (query.isBlank()) return - getSearchResults(query, authData ?: AuthData("", ""), lifecycleOwner) + getSearchResults(query, getNonNullAuthData(), lifecycleOwner) } + private fun getNonNullAuthData() = authData ?: AuthData("", "") + /* * Observe data from Fused API and publish the result in searchResult. * This allows us to show apps as they are being fetched from the network, @@ -101,6 +101,11 @@ class SearchViewModel @Inject constructor( EventBus.invokeEvent(AppEvent.DataLoadError(searchResultSupreme)) } + // if authadata is not available or valid, no need to fetch gplay data + if (authData.email.isEmpty() || authData.authToken.isEmpty()) { + return@launch + } + nextSubBundle = null fetchGplayData(query) } @@ -120,7 +125,6 @@ class SearchViewModel @Inject constructor( private suspend fun fetchGplayData(query: String) { isLoading = true val gplaySearchResult = fusedAPIRepository.getGplaySearchResults(query, nextSubBundle) - if (!gplaySearchResult.isSuccess()) { EventBus.invokeEvent(AppEvent.DataLoadError(gplaySearchResult)) } @@ -159,6 +163,4 @@ class SearchViewModel @Inject constructor( newFusedApps: List, oldFusedApps: List ) = fusedAPIRepository.isAnyFusedAppUpdated(newFusedApps, oldFusedApps) - - fun isLoginStateChanged(loginState: LoginState) = this.lastLoginState == null || this.lastLoginState != loginState } diff --git a/modules/src/main/java/app/lounge/login/anonymous/AnonymousUserRetrofitImpl.kt b/modules/src/main/java/app/lounge/login/anonymous/AnonymousUserRetrofitImpl.kt index 0721d8bfd..20b9403ae 100644 --- a/modules/src/main/java/app/lounge/login/anonymous/AnonymousUserRetrofitImpl.kt +++ b/modules/src/main/java/app/lounge/login/anonymous/AnonymousUserRetrofitImpl.kt @@ -79,6 +79,7 @@ class AnonymousUserRetrofitImpl @Inject constructor( byteCount = result.size ) } + return fetch { eCloudRetrofitAPI.authDataRequest( requestBody = requestBody, -- GitLab From 2a949b2f21cdf746ec2d6a72f38e631bd7d1bb8e Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Thu, 5 Oct 2023 11:22:39 +0530 Subject: [PATCH 105/112] Dont loadMore if authData is null --- .../main/java/foundation/e/apps/ui/search/SearchViewModel.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 44a7eaf0d..84b36da50 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -117,6 +117,10 @@ class SearchViewModel @Inject constructor( return } + if (authData == null) { + return + } + viewModelScope.launch(Dispatchers.IO) { fetchGplayData(query) } -- GitLab From adf7ef421d36f645eb2a5913f3a61fd58f1aab83 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 10 Oct 2023 10:19:24 +0530 Subject: [PATCH 106/112] Call home screen directly without waiting for login result --- .../e/apps/ui/setup/signin/SignInFragment.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt index bd3092fd9..3affbbf7b 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt @@ -35,6 +35,11 @@ class SignInFragment : Fragment(R.layout.fragment_sign_in) { binding.anonymousBT.setOnClickListener { viewModel.authenticateAnonymousUser() + view.findNavController() + .safeNavigate( + R.id.signInFragment, + R.id.action_signInFragment_to_homeFragment + ) } binding.noGoogleBT.setOnClickListener { @@ -49,12 +54,6 @@ class SignInFragment : Fragment(R.layout.fragment_sign_in) { loginState.authData?.let { data -> viewModel.updateAuthObjectForAnonymousUser(data) - - view.findNavController() - .safeNavigate( - R.id.signInFragment, - R.id.action_signInFragment_to_homeFragment - ) } ?: run { Timber.d("Auth Data is null") } } } -- GitLab From 7b55b033163057ce66a707ff0f32be4565dba5c5 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 10 Oct 2023 12:59:07 +0600 Subject: [PATCH 107/112] fixed: installing pwa --- .../main/java/foundation/e/apps/data/fused/FusedApiImpl.kt | 6 +++--- .../main/java/foundation/e/apps/data/fused/data/FusedApp.kt | 2 +- .../foundation/e/apps/ui/application/ApplicationFragment.kt | 2 +- .../e/apps/ui/applicationlist/ApplicationDiffUtil.kt | 2 +- .../e/apps/ui/applicationlist/ApplicationListRVAdapter.kt | 2 +- .../e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt | 2 +- .../foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt | 2 +- .../e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt | 6 +++--- .../e/apps/exodus/PrivacyScoreRepositoryImplTest.kt | 6 +++--- .../test/java/foundation/e/apps/fused/FusedApiImplTest.kt | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index 240076d5b..c16e34c61 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -1229,7 +1229,7 @@ class FusedApiImpl @Inject constructor( * Recommended to use this instead of [PkgManagerModule.getPackageStatus]. */ override fun getFusedAppInstallationStatus(fusedApp: FusedApp): Status { - return if (fusedApp.isPwa) { + return if (fusedApp.is_pwa) { pwaManagerModule.getPwaStatus(fusedApp) } else { pkgManagerModule.getPackageStatus(fusedApp.package_name, fusedApp.latest_version_code) @@ -1243,12 +1243,12 @@ class FusedApiImpl @Inject constructor( } private fun FusedApp.updateType() { - this.type = if (this.isPwa) Type.PWA else Type.NATIVE + this.type = if (this.is_pwa) Type.PWA else Type.NATIVE } private fun FusedApp.updateSource() { this.apply { - source = if (origin == Origin.CLEANAPK && isPwa) { + source = if (origin == Origin.CLEANAPK && is_pwa) { context.getString(R.string.pwa) } else if (origin == Origin.CLEANAPK) { context.getString(R.string.open_source) diff --git a/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt b/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt index 1eb429c06..bda418240 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/data/FusedApp.kt @@ -52,7 +52,7 @@ data class FusedApp( var source: String = String(), val price: String = String(), val isFree: Boolean = true, - val isPwa: Boolean = false, + val is_pwa: Boolean = false, var pwaPlayerDbId: Long = -1, val url: String = String(), var type: Type = Type.NATIVE, diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index 4f6630102..872733c91 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -756,7 +756,7 @@ class ApplicationFragment : Fragment(R.layout.fragment_application) { backgroundTintList = ContextCompat.getColorStateList(view.context, R.color.colorAccent) setOnClickListener { - if (fusedApp.isPwa) { + if (fusedApp.is_pwa) { pwaManagerModule.launchPwa(fusedApp) } else { startActivity(pkgManagerModule.getLaunchIntent(fusedApp.package_name)) diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt index 5a56a1dce..4013e7095 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationDiffUtil.kt @@ -51,6 +51,6 @@ class ApplicationDiffUtil : DiffUtil.ItemCallback() { ((oldItem.trackers == LIST_OF_NULL && newItem.trackers.isEmpty()) || oldItem.trackers == newItem.trackers) && oldItem.url.contentEquals(newItem.url) && oldItem.isFree == newItem.isFree && - oldItem.isPwa == newItem.isPwa + oldItem.is_pwa == newItem.is_pwa } } diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt index 55c66616f..bdcad5036 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt @@ -527,7 +527,7 @@ class ApplicationListRVAdapter( enableInstallButton(Status.INSTALLED) text = context.getString(R.string.open) setOnClickListener { - if (searchApp.isPwa) { + if (searchApp.is_pwa) { mainActivityViewModel.launchPwa(searchApp) } else { context.startActivity( diff --git a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt index 891c7a6f6..61bba5097 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildFusedAppDiffUtil.kt @@ -50,6 +50,6 @@ class HomeChildFusedAppDiffUtil : DiffUtil.ItemCallback() { oldItem.trackers == newItem.trackers && oldItem.url.contentEquals(newItem.url) && oldItem.isFree == newItem.isFree && - oldItem.isPwa == newItem.isPwa + oldItem.is_pwa == newItem.is_pwa } } diff --git a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt index 07bac986e..9be2af9e6 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt @@ -236,7 +236,7 @@ class HomeChildRVAdapter( enableInstallButton(Status.INSTALLED) text = context.getString(R.string.open) setOnClickListener { - if (homeApp.isPwa) { + if (homeApp.is_pwa) { mainActivityViewModel.launchPwa(homeApp) } else { context.startActivity( diff --git a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt index 907335fdb..5b0893fe2 100644 --- a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt +++ b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt @@ -63,7 +63,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "foundation.e.demothree", latest_version_code = 123, - isPwa = true + is_pwa = true ) val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) assertEquals("getAppPrivacyInfo", true, result.isSuccess()) @@ -78,7 +78,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "", latest_version_code = 123, - isPwa = true + is_pwa = true ) val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) assertEquals("getAppPrivacyInfo", false, result.isSuccess()) @@ -92,7 +92,7 @@ class AppPrivacyInfoRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - isPwa = true + is_pwa = true ) fakeTrackerDao.trackers.clear() val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name) diff --git a/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt index e7062fe91..b6a8c25f8 100644 --- a/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt +++ b/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt @@ -43,7 +43,7 @@ class PrivacyScoreRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - isPwa = true, + is_pwa = true, permsFromExodus = listOf(), perms = listOf(), trackers = listOf() @@ -60,7 +60,7 @@ class PrivacyScoreRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - isPwa = true, + is_pwa = true, perms = listOf(), trackers = listOf() ) @@ -76,7 +76,7 @@ class PrivacyScoreRepositoryImplTest { name = "Demo Three", package_name = "a.b.c", latest_version_code = 123, - isPwa = true, + is_pwa = true, permsFromExodus = listOf(), perms = listOf(), trackers = CommonUtilsModule.LIST_OF_NULL diff --git a/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt b/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt index 3d7f65d0a..d4e75679f 100644 --- a/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt @@ -432,7 +432,7 @@ class FusedApiImplTest { name = "Demo Three", package_name = "foundation.e.demothree", latest_version_code = 123, - isPwa = true + is_pwa = true ) Mockito.`when`(pwaManagerModule.getPwaStatus(fusedApp)).thenReturn(fusedApp.status) -- GitLab From 93c8600a4744c744de935a38b88a623f6ac8cecd Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 10 Oct 2023 17:53:07 +0600 Subject: [PATCH 108/112] fixed: memory leak from mainactivity --- app/src/main/java/foundation/e/apps/MainActivity.kt | 2 +- app/src/main/java/foundation/e/apps/data/fused/UpdatesDao.kt | 4 ++++ app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index e69e71610..a31f46ced 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -109,7 +109,7 @@ class MainActivity : AppCompatActivity() { } } - viewModel.setupConnectivityManager(this) + viewModel.setupConnectivityManager(applicationContext) viewModel.internetConnection.observe(this) { isInternetAvailable -> hasInternet = isInternetAvailable diff --git a/app/src/main/java/foundation/e/apps/data/fused/UpdatesDao.kt b/app/src/main/java/foundation/e/apps/data/fused/UpdatesDao.kt index 65e5edc9f..4fc69e67c 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/UpdatesDao.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/UpdatesDao.kt @@ -43,4 +43,8 @@ object UpdatesDao { fun clearSuccessfullyUpdatedApps() { _successfulUpdatedApps.clear() } + + fun clearAllUpdatableApps() { + _appsAwaitingForUpdate.clear() + } } diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt b/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt index e1fd528de..302a16cc1 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginCommon.kt @@ -19,6 +19,7 @@ package foundation.e.apps.data.login import foundation.e.apps.data.Constants import foundation.e.apps.data.enums.User +import foundation.e.apps.data.fused.UpdatesDao import javax.inject.Inject import javax.inject.Singleton @@ -58,5 +59,6 @@ class LoginCommon @Inject constructor( loginDataStore.setSource(Constants.PREFERENCE_SHOW_FOSS, true) loginDataStore.setSource(Constants.PREFERENCE_SHOW_PWA, true) loginDataStore.setSource(Constants.PREFERENCE_SHOW_GPLAY, true) + UpdatesDao.clearAllUpdatableApps() } } -- GitLab From 5129608ac702c692f2ceb97db646e27922275735 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 12 Oct 2023 20:37:25 +0530 Subject: [PATCH 109/112] Modified as per review comment --- app/src/main/java/foundation/e/apps/data/Constants.kt | 3 +++ .../e/apps/presentation/login/LoginViewModel.kt | 9 +++++---- .../e/apps/presentation/settings/SettingsViewModel.kt | 9 ++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/Constants.kt b/app/src/main/java/foundation/e/apps/data/Constants.kt index 20e9c0bc8..f9d73d75d 100644 --- a/app/src/main/java/foundation/e/apps/data/Constants.kt +++ b/app/src/main/java/foundation/e/apps/data/Constants.kt @@ -9,4 +9,7 @@ object Constants { const val ACTION_AUTHDATA_DUMP = "foundation.e.apps.action.DUMP_GACCOUNT_INFO" const val TAG_AUTHDATA_DUMP = "AUTHDATA_DUMP" + + const val GOOGLE_LOGIN_FAIL = "Google login failed" + const val UNEXPECTED_ERROR = "An unexpected error occurred" } diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 1884a14a3..31c92c2da 100644 --- a/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -23,6 +23,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel +import foundation.e.apps.data.Constants import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.enums.User.NO_GOOGLE @@ -112,7 +113,7 @@ class LoginViewModel @Inject constructor( LoginState( isLoading = false, isLoggedIn = false, - error = "Google login failed" + error = Constants.GOOGLE_LOGIN_FAIL ) } } else { @@ -120,7 +121,7 @@ class LoginViewModel @Inject constructor( LoginState( isLoading = false, isLoggedIn = false, - error = "Google login failed" + error = Constants.GOOGLE_LOGIN_FAIL ) } @@ -197,7 +198,7 @@ class LoginViewModel @Inject constructor( is Resource.Error -> { _loginState.value = LoginState( - error = result.message ?: "An unexpected error occured" + error = result.message ?: Constants.UNEXPECTED_ERROR ) } @@ -228,7 +229,7 @@ class LoginViewModel @Inject constructor( is Resource.Error -> { val error = it.message.let { message -> when (message) { - null -> "An unexpected error occurred" + null -> Constants.UNEXPECTED_ERROR else -> message } } diff --git a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt index c838ad5c9..36dcbffe0 100644 --- a/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/settings/SettingsViewModel.kt @@ -23,6 +23,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import foundation.e.apps.data.Constants import foundation.e.apps.domain.settings.usecase.SettingsUseCase import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.collect @@ -49,7 +50,7 @@ class SettingsViewModel @Inject constructor( } is Resource.Error -> { _currentUserState.value = - settingUserState.apply { error = result.message ?: "An unexpected error occurred" } + settingUserState.apply { error = result.message ?: Constants.UNEXPECTED_ERROR } } is Resource.Loading -> TODO() @@ -68,10 +69,12 @@ class SettingsViewModel @Inject constructor( } is Resource.Error -> { _currentUserState.value = - settingUserState.apply { error = result.message ?: "An unexpected error occurred" } + settingUserState.apply { error = result.message ?: Constants.UNEXPECTED_ERROR } } - is Resource.Loading -> TODO() + is Resource.Loading -> { + // NO NEED + } } }.collect() } -- GitLab From a76430ebb397a31c86cf5e83c1204af51cff5b5c Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Thu, 12 Oct 2023 20:42:42 +0530 Subject: [PATCH 110/112] Use Timber for exception --- .../main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt b/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt index d86e3b87a..165dcecbe 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt +++ b/app/src/main/java/foundation/e/apps/data/login/LoginSourceGPlay.kt @@ -121,7 +121,7 @@ class LoginSourceGPlay @Inject constructor( try { gson.fromJson(authJson, AuthData::class.java) } catch (e: Exception) { - e.printStackTrace() + Timber.e(e) null } } -- GitLab From 57e08887414a27707880198ca58fe43adaa84a65 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 13 Oct 2023 12:42:13 +0530 Subject: [PATCH 111/112] Modified as per review comment --- .../java/foundation/e/apps/domain/errors/RetryMechanism.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt b/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt index 0afdc953f..593131043 100644 --- a/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt +++ b/app/src/main/java/foundation/e/apps/domain/errors/RetryMechanism.kt @@ -53,11 +53,11 @@ class RetryMechanism { ): Boolean { if (shouldFailRetry()) return false retryBlock() - updateAutoRetryCondition() + incrementAutoRetryCounter() return true } - private fun updateAutoRetryCondition() { + private fun incrementAutoRetryCounter() { autoRetryCount++ } -- GitLab From 48a62ca23b8d43ab1cfad6ebd9f8e95882ed82f5 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 13 Oct 2023 13:10:37 +0530 Subject: [PATCH 112/112] Modified as per review comment --- .../e/apps/domain/common/repository/CacheRepository.kt | 2 +- .../e/apps/domain/common/repository/CacheRepositoryImpl.kt | 2 +- .../e/apps/domain/install/usecase/AppInstallerUseCase.kt | 2 +- .../e/apps/domain/login/usecase/UserLoginUseCase.kt | 4 ++-- .../e/apps/domain/main/usecase/MainActivityUseCase.kt | 2 +- .../e/apps/domain/settings/usecase/SettingsUseCase.kt | 2 +- .../e/apps/domain/updates/usecase/UpdatesUseCase.kt | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt index 33891e8bd..ef35d2d63 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepository.kt @@ -23,7 +23,7 @@ import foundation.e.apps.data.enums.User interface CacheRepository { fun currentUser(): User - fun cacheAuthData(): AuthData + fun cachedAuthData(): AuthData fun resetCachedData() fun clearAuthData() } diff --git a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt index 3de408f1a..dab4d730b 100644 --- a/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/common/repository/CacheRepositoryImpl.kt @@ -49,7 +49,7 @@ class CacheRepositoryImpl @Inject constructor( } } - override fun cacheAuthData(): AuthData = + override fun cachedAuthData(): AuthData = applicationContext.configurations.authData.let { data -> if (data.isEmpty()) throw RuntimeException("Auth Data not available") return data.toAuthData() diff --git a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt index 76d432ec3..b7a41ec1f 100644 --- a/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/install/usecase/AppInstallerUseCase.kt @@ -30,7 +30,7 @@ class AppInstallerUseCase@Inject constructor( fun currentAuthData(): AuthData? { return try { - cacheRepository.cacheAuthData() + cacheRepository.cachedAuthData() } catch (e: RuntimeException) { if (cacheRepository.currentUser() == User.NO_GOOGLE) { return AuthData("", "").apply { diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt index 57dfbb273..0f920d886 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -56,7 +56,7 @@ class UserLoginUseCase @Inject constructor( fun retrieveCachedAuthData(): Flow> = flow { kotlin.runCatching { emit(Resource.Loading()) - emit(Resource.Success(cacheRepository.cacheAuthData())) + emit(Resource.Success(cacheRepository.cachedAuthData())) }.onFailure { failure -> emit(Resource.Error(failure.localizedMessage)) } } @@ -82,7 +82,7 @@ class UserLoginUseCase @Inject constructor( } is Resource.Loading -> emit(Resource.Loading()) is Resource.Success -> { - emit(Resource.Success(cacheRepository.cacheAuthData())) + emit(Resource.Success(cacheRepository.cachedAuthData())) } } }.collect() diff --git a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt index 5a35f9f85..348c5bb61 100644 --- a/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/main/usecase/MainActivityUseCase.kt @@ -29,7 +29,7 @@ class MainActivityUseCase @Inject constructor( fun currentAuthData(): AuthData { return try { - cacheRepository.cacheAuthData() + cacheRepository.cachedAuthData() } catch (e: RuntimeException) { AuthData("", "") } diff --git a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt index a5c80bfc6..3a3e3c057 100644 --- a/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/settings/usecase/SettingsUseCase.kt @@ -38,7 +38,7 @@ class SettingsUseCase @Inject constructor( fun currentAuthData(): Flow> = flow { kotlin.runCatching { - emit(Resource.Success(cacheRepository.cacheAuthData())) + emit(Resource.Success(cacheRepository.cachedAuthData())) }.onFailure { emit(Resource.Error("Something went wrong in fun currentUser()")) } } diff --git a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt index 6295c75e2..d26d6718a 100644 --- a/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/updates/usecase/UpdatesUseCase.kt @@ -26,5 +26,5 @@ class UpdatesUseCase @Inject constructor( ) { fun currentUser() = cacheRepository.currentUser() - fun currentAuthData() = cacheRepository.cacheAuthData() + fun currentAuthData() = cacheRepository.cachedAuthData() } -- GitLab