From c13cfdd1639c27b684e4fbcc2ba8d41457a82662 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Mon, 19 Jun 2023 12:28:29 +0530 Subject: [PATCH 01/57] 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 02/57] 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 03/57] 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 04/57] 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 05/57] 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 06/57] 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 07/57] 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 08/57] 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 09/57] 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 10/57] 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 11/57] 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 12/57] 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 13/57] 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 14/57] 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 15/57] 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 16/57] 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 17/57] 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 18/57] 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 19/57] 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 20/57] 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 21/57] 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 22/57] 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 23/57] 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 24/57] 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 25/57] 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 26/57] 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 27/57] 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 28/57] 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 29/57] 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 30/57] 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 31/57] 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 32/57] 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 33/57] 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 34/57] 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 35/57] 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 36/57] 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 37/57] 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 38/57] 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 a02e217fd021f9413628dcc237ae96284fe16e76 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 1 Aug 2023 13:53:17 +0600 Subject: [PATCH 39/57] GplayHttpClient is added --- modules/build.gradle | 3 + .../java/app/lounge/gplay/GplayHttpClient.kt | 124 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt diff --git a/modules/build.gradle b/modules/build.gradle index 48fab303e..c233bd53e 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -86,6 +86,9 @@ dependencies { implementation("com.google.dagger:hilt-android:$dagger_hilt_version") kapt("com.google.dagger:hilt-android-compiler:$dagger_hilt_version") + //logger + implementation 'com.jakewharton.timber:timber:5.0.1' + 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/gplay/GplayHttpClient.kt b/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt new file mode 100644 index 000000000..0bc6aa0c4 --- /dev/null +++ b/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt @@ -0,0 +1,124 @@ +package app.lounge.gplay + +import com.aurora.gplayapi.data.models.PlayResponse +import com.aurora.gplayapi.network.IHttpClient +import okhttp3.Headers.Companion.toHeaders +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import timber.log.Timber +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import javax.inject.Inject +import javax.inject.Named + +class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHttpClient: OkHttpClient): IHttpClient { + + companion object { + private const val POST = "POST" + private const val GET = "GET" + } + + + override fun get(url: String, headers: Map): PlayResponse { + return get(url, headers, mapOf()) + } + + override fun get(url: String, headers: Map, paramString: String): PlayResponse { + val request = Request.Builder() + .url(url + paramString) + .headers(headers.toHeaders()) + .method(GET, null) + .build() + return processRequest(request) + } + + override fun get( + url: String, + headers: Map, + params: Map + ): PlayResponse { + val request = Request.Builder() + .url(buildUrlWithQueryParameters(url, params)) + .headers(headers.toHeaders()) + .method(GET, null) + .build() + return processRequest(request) + } + + override fun getAuth(url: String): PlayResponse { + val request = Request.Builder() + .url(url) + .method(GET, null) + .build() + Timber.d("get auth request", request.toString()) + return processRequest(request) + } + + override fun post(url: String, headers: Map, body: ByteArray): PlayResponse { + TODO("Not yet implemented") + } + + override fun post( + url: String, + headers: Map, + params: Map + ): PlayResponse { + TODO("Not yet implemented") + } + + override fun postAuth(url: String, body: ByteArray): PlayResponse { + TODO("Not yet implemented") + } + + private fun buildUrlWithQueryParameters(url: String, params: Map): HttpUrl { + val urlBuilder = url.toHttpUrl().newBuilder() + params.forEach { + urlBuilder.addQueryParameter(it.key, it.value) + } + return urlBuilder.build() + } + + private fun processRequest(request: Request): PlayResponse { + return try { + val call = okHttpClient.newCall(request) + buildPlayResponse(call.execute()) + } catch (e: Exception) { + when (e) { + is UnknownHostException, + is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e) + else -> handleExceptionOnGooglePlayRequest(e) + } + } + } + + private fun handleExceptionOnGooglePlayRequest(e: Exception): PlayResponse { + Timber.e("processRequest: ${e.localizedMessage}") + return PlayResponse().apply { + errorString = "${this@GplayHttpClient::class.java.simpleName}: ${e.localizedMessage}" + } + } + + private fun buildPlayResponse(response: Response): PlayResponse { + return PlayResponse().apply { + isSuccessful = response.isSuccessful + code = response.code + + Timber.d("Url: ${response.request.url}\nStatus: $code") + + if (code == 401) { + // TODO Handle the case of Unauthorized user + } + + if (response.body != null) { + responseBytes = response.body!!.bytes() + } + + if (!isSuccessful) { + errorString = response.message + } + } + } +} \ No newline at end of file -- GitLab From 7d34835954a8e3fe8150fbef8a2840aeaec53bac Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 1 Aug 2023 23:29:20 +0530 Subject: [PATCH 40/57] 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 e9a0d6dc9c25ce5191b9ca8d558de327d333489d Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Fri, 4 Aug 2023 12:17:06 +0600 Subject: [PATCH 41/57] exception handling is added --- .../foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt | 6 ++++++ 1 file changed, 6 insertions(+) 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 33a3b8370..0dd6be93c 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 @@ -185,6 +185,10 @@ class GPlayHttpClient @Inject constructor( Timber.d("$TAG: Url: ${response.request.url}\nStatus: $code") + if (code != 200) { + throw GplayException(code, response.message) + } + if (code == 401) { MainScope().launch { EventBus.invokeEvent( @@ -203,3 +207,5 @@ class GPlayHttpClient @Inject constructor( } } } + +class GplayException (val errorCode: Int, message: String) : Exception(message) -- GitLab From 2abcc41eac3f23972626120a3b3c1669ed4f72c1 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Fri, 4 Aug 2023 23:54:24 +0530 Subject: [PATCH 42/57] 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 43/57] 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 44/57] 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 20b6c0a9ebf6d2edc6ca6b7b87ca382b5058e5fd Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sun, 6 Aug 2023 23:00:14 +0530 Subject: [PATCH 45/57] Fix CI Klint issues --- .../java/foundation/e/apps/di/LoginModule.kt | 1 - .../login/repository/LoginRepository.kt | 5 +- .../login/repository/LoginRepositoryImpl.kt | 6 +-- .../domain/login/usecase/UserLoginUseCase.kt | 7 +-- .../presentation/login/LoginViewModelTest.kt | 47 +++++++++---------- 5 files changed, 27 insertions(+), 39 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/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt index 94533699a..19ff48c34 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 @@ -16,17 +16,14 @@ * 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 - -} \ 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 b5f36c1be..ad61667d8 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 @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - package foundation.e.apps.domain.login.repository import android.content.Context @@ -31,14 +30,14 @@ import javax.inject.Inject class LoginRepositoryImpl @Inject constructor( private val networkFetching: AnonymousUser, @ApplicationContext val applicationContext: Context -): LoginRepository { +) : LoginRepository { override suspend fun anonymousUser(authDataRequestBody: AnonymousAuthDataRequestBody): AuthData { val result = networkFetching.requestAuthData( anonymousAuthDataRequestBody = authDataRequestBody ) - when(result) { + when (result) { is NetworkResult.Error -> throw Exception(result.errorMessage, result.exception) is NetworkResult.Success -> { @@ -47,5 +46,4 @@ class LoginRepositoryImpl @Inject constructor( } } } - } 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 ee936b572..a62ee1961 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 @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - package foundation.e.apps.domain.login.usecase import app.lounge.model.AnonymousAuthDataRequestBody @@ -25,8 +24,6 @@ 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 retrofit2.HttpException -import java.io.IOException import java.util.Properties import javax.inject.Inject @@ -47,8 +44,8 @@ class UserLoginUseCase @Inject constructor( ) ) emit(Resource.Success(userResponse)) - } catch(e: Exception) { + } 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/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt index cc14f35c2..b4e083e30 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,14 +16,12 @@ * 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 +import foundation.e.apps.testAnonymousRequestBodyData import foundation.e.apps.testAnonymousResponseData import foundation.e.apps.util.getOrAwaitValue import foundation.e.apps.utils.Resource @@ -36,21 +34,16 @@ 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 @@ -64,11 +57,13 @@ 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.invoke( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + ) + ).thenReturn(Resource.Success(testAnonymousResponseData)) val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) loginViewModel.authenticateAnonymousUser( @@ -82,11 +77,13 @@ 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.invoke( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + ) + ).thenReturn(Resource.Error(loginFailureMessage)) val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) loginViewModel.authenticateAnonymousUser( @@ -101,11 +98,13 @@ 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.invoke( + properties = testAnonymousRequestBodyData.properties, + userAgent = testAnonymousRequestBodyData.userAgent + ) + ).thenReturn(Resource.Loading()) val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) loginViewModel.authenticateAnonymousUser( @@ -122,6 +121,4 @@ class LoginViewModelTest { fun tearDown() { Dispatchers.resetMain() } - } - -- GitLab From 6a567219433f46918ea4e2cca4a08388daf01b32 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sun, 6 Aug 2023 23:09:10 +0530 Subject: [PATCH 46/57] Fix CI Klint issues --- app/src/test/java/foundation/e/apps/Shared.kt | 4 +-- .../usecase/repository/LoginRepositoryTest.kt | 34 +++++++++---------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/app/src/test/java/foundation/e/apps/Shared.kt b/app/src/test/java/foundation/e/apps/Shared.kt index b82344dfa..2199b514f 100644 --- a/app/src/test/java/foundation/e/apps/Shared.kt +++ b/app/src/test/java/foundation/e/apps/Shared.kt @@ -16,14 +16,12 @@ * 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" @@ -33,4 +31,4 @@ val testAnonymousResponseData = AuthData("eOS@murena.io", "") const val loginFailureMessage = "Fail to login" -val testFailureException: Exception = Exception(loginFailureMessage) \ No newline at end of file +val testFailureException: Exception = Exception(loginFailureMessage) 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 25a9f06d5..5ae8d57a4 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 @@ -16,11 +16,17 @@ * along with this program. If not, see . */ - package foundation.e.apps.usecase.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.domain.login.repository.LoginRepositoryImpl +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 @@ -31,28 +37,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 -import foundation.e.apps.loginFailureMessage -import foundation.e.apps.testFailureException - @RunWith(RobolectricTestRunner::class) class LoginRepositoryTest { @Mock - lateinit var anonymousUser: AnonymousUser + lateinit var anonymousUser: AnonymousUser private lateinit var instrumentationContext: Context - @Before fun setUp() { MockitoAnnotations.openMocks(this) - instrumentationContext= ApplicationProvider.getApplicationContext() + instrumentationContext = ApplicationProvider.getApplicationContext() } @Test @@ -72,11 +68,13 @@ class LoginRepositoryTest { @Test fun testOnFailureReturnErrorWithException() = runTest { Mockito.`when`(anonymousUser.requestAuthData(testAnonymousRequestBodyData)) - .thenReturn(NetworkResult.Error( - exception = testFailureException, - code = 1, - errorMessage = loginFailureMessage - )) + .thenReturn( + NetworkResult.Error( + exception = testFailureException, + code = 1, + errorMessage = loginFailureMessage + ) + ) runCatching { LoginRepositoryImpl(anonymousUser, instrumentationContext) .run { anonymousUser(testAnonymousRequestBodyData) } -- GitLab From 2c68e643c2244edf6345c9f64f22ed89edb80a0a Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Sun, 6 Aug 2023 23:17:32 +0530 Subject: [PATCH 47/57] Fix CI Klint issues --- .../java/foundation/e/apps/presentation/login/LoginState.kt | 2 -- .../java/foundation/e/apps/presentation/login/LoginViewModel.kt | 2 -- app/src/main/java/foundation/e/apps/utils/Resource.kt | 2 -- 3 files changed, 6 deletions(-) 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 e4fec4e76..529d34f96 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 @@ -140,11 +140,9 @@ class LoginViewModel @Inject constructor( } } - private val _loginState: MutableLiveData = MutableLiveData() val loginState: LiveData = _loginState - fun authenticateAnonymousUser( properties: Properties, userAgent: String = SystemInfoProvider.getAppBuildInfo() 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 7c468ab47..7a471a5f1 100644 --- a/app/src/main/java/foundation/e/apps/utils/Resource.kt +++ b/app/src/main/java/foundation/e/apps/utils/Resource.kt @@ -16,10 +16,8 @@ * 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) -- GitLab From 57611451366b4025d735f7098a0465780b6f798c Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Tue, 8 Aug 2023 09:16:01 +0530 Subject: [PATCH 48/57] 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 49/57] 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 50/57] 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 62565af20437dcb15126b23f5dfb4d928afd8ed8 Mon Sep 17 00:00:00 2001 From: Nishant Dande Date: Wed, 9 Aug 2023 22:20:17 +0530 Subject: [PATCH 51/57] Fix view model looper issue --- .../presentation/login/LoginViewModelTest.kt | 5 ++ .../usecase/repository/LoginRepositoryTest.kt | 85 ------------------- 2 files changed, 5 insertions(+), 85 deletions(-) delete mode 100644 app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt 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 b4e083e30..993638af5 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 @@ -18,6 +18,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.UserLoginUseCase import foundation.e.apps.loginFailureMessage @@ -34,6 +35,7 @@ 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 @@ -44,6 +46,9 @@ class LoginViewModelTest { private val testDispatcher = StandardTestDispatcher() + @get:Rule + val rule = InstantTaskExecutorRule() + @Mock lateinit var mockUserLoginUseCase: UserLoginUseCase 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 5ae8d57a4..000000000 --- a/app/src/test/java/foundation/e/apps/usecase/repository/LoginRepositoryTest.kt +++ /dev/null @@ -1,85 +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 app.lounge.login.anonymous.AnonymousUser -import app.lounge.networking.NetworkResult -import foundation.e.apps.domain.login.repository.LoginRepositoryImpl -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 b7d344f463fa686e6313b0a410dc79c8c13e9fe3 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Thu, 10 Aug 2023 22:58:05 +0600 Subject: [PATCH 52/57] added google login components --- .../java/app/lounge/gplay/GplayException.kt | 3 ++ .../java/app/lounge/gplay/GplayHttpClient.kt | 29 ++++++++++-- .../lounge/login/google/AuthTokenFetchApi.kt | 46 +++++++++++++++++++ .../app/lounge/login/google/GoogeLoginApi.kt | 11 +++++ .../lounge/login/google/GoogleLoginApiImpl.kt | 32 +++++++++++++ .../src/main/java/app/lounge/model/Model.kt | 6 +++ .../lounge/networking/PlayResponseHandler.kt | 18 ++++++++ 7 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 modules/src/main/java/app/lounge/gplay/GplayException.kt create mode 100644 modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt create mode 100644 modules/src/main/java/app/lounge/login/google/GoogeLoginApi.kt create mode 100644 modules/src/main/java/app/lounge/login/google/GoogleLoginApiImpl.kt create mode 100644 modules/src/main/java/app/lounge/networking/PlayResponseHandler.kt diff --git a/modules/src/main/java/app/lounge/gplay/GplayException.kt b/modules/src/main/java/app/lounge/gplay/GplayException.kt new file mode 100644 index 000000000..9d64c79d1 --- /dev/null +++ b/modules/src/main/java/app/lounge/gplay/GplayException.kt @@ -0,0 +1,3 @@ +package app.lounge.gplay + +class GplayException(var errorCode: Int, message: String) : Exception(message) \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt b/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt index 0bc6aa0c4..eb9f0c657 100644 --- a/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt +++ b/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt @@ -5,8 +5,10 @@ import com.aurora.gplayapi.network.IHttpClient import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import timber.log.Timber import java.net.SocketTimeoutException @@ -58,7 +60,17 @@ class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHt } override fun post(url: String, headers: Map, body: ByteArray): PlayResponse { - TODO("Not yet implemented") + val requestBody = body.toRequestBody( + "application/x-protobuf".toMediaType(), + 0, + body.size + ) + val request = Request.Builder() + .url(url) + .headers(headers.toHeaders()) + .method(POST, requestBody) + .build() + return processRequest(request) } override fun post( @@ -66,7 +78,12 @@ class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHt headers: Map, params: Map ): PlayResponse { - TODO("Not yet implemented") + val request = Request.Builder() + .url(buildUrlWithQueryParameters(url, params)) + .headers(headers.toHeaders()) + .method(POST, "".toRequestBody(null)) + .build() + return processRequest(request) } override fun postAuth(url: String, body: ByteArray): PlayResponse { @@ -91,6 +108,7 @@ class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHt is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e) else -> handleExceptionOnGooglePlayRequest(e) } + throw e } } @@ -108,8 +126,8 @@ class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHt Timber.d("Url: ${response.request.url}\nStatus: $code") - if (code == 401) { - // TODO Handle the case of Unauthorized user + if (code != 200) { + throw GplayException(code, response.message) } if (response.body != null) { @@ -121,4 +139,5 @@ class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHt } } } -} \ No newline at end of file +} + diff --git a/modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt b/modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt new file mode 100644 index 000000000..ef91d1aa2 --- /dev/null +++ b/modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt @@ -0,0 +1,46 @@ +package app.lounge.login.google + +import app.lounge.gplay.GplayHttpClient +import com.aurora.gplayapi.data.models.PlayResponse +import java.util.Locale +import javax.inject.Inject + +class AuthTokenFetchApi @Inject constructor(private val gplayHttpClient: GplayHttpClient) { + companion object { + private const val TOKEN_AUTH_URL = "https://android.clients.google.com/auth" + private const val BUILD_VERSION_SDK = 28 + private const val PLAY_SERVICES_VERSION_CODE = 19629032 + } + + fun getAuthTokenPlayResponse(email: String?, oAuthToken: String?): PlayResponse { + if (email == null || oAuthToken == null) + return PlayResponse() + + val params: MutableMap = hashMapOf() + params["lang"] = Locale.getDefault().toString().replace("_", "-") + params["google_play_services_version"] = PLAY_SERVICES_VERSION_CODE + params["sdk_version"] = BUILD_VERSION_SDK + params["device_country"] = Locale.getDefault().country.lowercase(Locale.US) + params["Email"] = email + params["service"] = "ac2dm" + params["get_accountid"] = 1 + params["ACCESS_TOKEN"] = 1 + params["callerPkg"] = "com.google.android.gms" + params["add_account"] = 1 + params["Token"] = oAuthToken + params["callerSig"] = "38918a453d07199354f8b19af05ec6562ced5788" + + val body = params.map { "${it.key}=${it.value}" }.joinToString(separator = "&") + val header = mapOf( + "app" to "com.google.android.gms", + "User-Agent" to "", + "Content-Type" to "application/x-www-form-urlencoded" + ) + + /* + * Returning PlayResponse instead of map so that we can get the network response code. + * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709 + */ + return gplayHttpClient.post(TOKEN_AUTH_URL, header, body.toByteArray()) + } +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/login/google/GoogeLoginApi.kt b/modules/src/main/java/app/lounge/login/google/GoogeLoginApi.kt new file mode 100644 index 000000000..fad4e80c6 --- /dev/null +++ b/modules/src/main/java/app/lounge/login/google/GoogeLoginApi.kt @@ -0,0 +1,11 @@ +package app.lounge.login.google + +import app.lounge.model.GoogleAuthDataRequestBody +import app.lounge.networking.NetworkResult +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.models.PlayResponse + +interface GoogleLoginApi { + suspend fun getAuthTokenPlayResponse(email: String, oauthToken: String): NetworkResult + suspend fun validate(authData: AuthData): NetworkResult +} \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/login/google/GoogleLoginApiImpl.kt b/modules/src/main/java/app/lounge/login/google/GoogleLoginApiImpl.kt new file mode 100644 index 000000000..e98d680bc --- /dev/null +++ b/modules/src/main/java/app/lounge/login/google/GoogleLoginApiImpl.kt @@ -0,0 +1,32 @@ +package app.lounge.login.google + +import app.lounge.gplay.GplayHttpClient +import app.lounge.networking.fetchPlayResponse +import app.lounge.networking.NetworkResult +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.models.PlayResponse +import com.aurora.gplayapi.helpers.AuthValidator +import javax.inject.Inject + +class GoogleLoginApiImpl @Inject constructor( + private val gplayHttpClient: GplayHttpClient, + private val authTokenFetchApi: AuthTokenFetchApi +) : GoogleLoginApi { + + override suspend fun getAuthTokenPlayResponse( + email: String, + oauthToken: String + ): NetworkResult { + return fetchPlayResponse { + authTokenFetchApi.getAuthTokenPlayResponse(email, oauthToken) + } + } + + override suspend fun validate(authData: AuthData): NetworkResult { + return fetchPlayResponse { + val authValidator = AuthValidator(authData).using(gplayHttpClient) + authValidator.isValid() + } + } + +} \ 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 index e77a91db4..3d7273721 100644 --- a/modules/src/main/java/app/lounge/model/Model.kt +++ b/modules/src/main/java/app/lounge/model/Model.kt @@ -25,4 +25,10 @@ import java.util.Properties data class AnonymousAuthDataRequestBody( val properties: Properties, val userAgent: String +) + +data class GoogleAuthDataRequestBody( + val email: String, + val token: String, + val properties: Properties ) \ No newline at end of file diff --git a/modules/src/main/java/app/lounge/networking/PlayResponseHandler.kt b/modules/src/main/java/app/lounge/networking/PlayResponseHandler.kt new file mode 100644 index 000000000..87dc1cc51 --- /dev/null +++ b/modules/src/main/java/app/lounge/networking/PlayResponseHandler.kt @@ -0,0 +1,18 @@ +package app.lounge.networking + +import app.lounge.gplay.GplayException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +suspend fun fetchPlayResponse(call: suspend () -> T) : NetworkResult { + return withContext(Dispatchers.IO) { + try { + val result = call() + NetworkResult.Success(result) + } catch (e: GplayException) { + NetworkResult.Error(e, e.errorCode, e.localizedMessage) + } catch (e: Exception) { + NetworkResult.Error(e, -1, e.localizedMessage) + } + } +} \ No newline at end of file -- GitLab From db970a787294949b5abff0aad4738e464797b7d9 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Fri, 11 Aug 2023 21:52:16 +0600 Subject: [PATCH 53/57] Google login repository is introduced --- app/build.gradle | 2 +- .../login/AuthTokenPlayResponseParser.kt | 17 +++++++ .../domain/login/GoogleLoginRepository.kt | 8 ++++ .../domain/login/GoogleLoginRepositoryImpl.kt | 46 +++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/foundation/e/apps/domain/login/AuthTokenPlayResponseParser.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepository.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepositoryImpl.kt diff --git a/app/build.gradle b/app/build.gradle index 1d0b1c0d7..43c255645 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -146,7 +146,7 @@ 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/AuthTokenPlayResponseParser.kt b/app/src/main/java/foundation/e/apps/domain/login/AuthTokenPlayResponseParser.kt new file mode 100644 index 000000000..b29c62c33 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/AuthTokenPlayResponseParser.kt @@ -0,0 +1,17 @@ +package foundation.e.apps.domain.login + +import java.util.StringTokenizer + +object AuthTokenPlayResponseParser { + fun parseResponse(response: String?): Map { + val keyValueMap: MutableMap = HashMap() + val st = StringTokenizer(response, "\n\r") + while (st.hasMoreTokens()) { + val keyValue = st.nextToken().split("=") + if (keyValue.size >= 2) { + keyValueMap[keyValue[0]] = keyValue[1] + } + } + return keyValueMap + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepository.kt new file mode 100644 index 000000000..01dde8cf4 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepository.kt @@ -0,0 +1,8 @@ +package foundation.e.apps.domain.login + +import com.aurora.gplayapi.data.models.AuthData + +interface GoogleLoginRepository { + suspend fun getGoogleLoginAuthData(email: String, oauthToken: String): AuthData? + suspend fun validate(): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepositoryImpl.kt new file mode 100644 index 000000000..b75650427 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepositoryImpl.kt @@ -0,0 +1,46 @@ +package foundation.e.apps.domain.login + +import app.lounge.login.google.GoogleLoginApi +import app.lounge.networking.NetworkResult +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.data.models.PlayResponse +import com.aurora.gplayapi.helpers.AuthHelper +import timber.log.Timber +import java.lang.Exception +import java.util.Properties +import javax.inject.Inject + +class GoogleLoginRepositoryImpl @Inject constructor( + private val googleLoginApi: GoogleLoginApi, + private val properties: Properties +) : GoogleLoginRepository { + + override suspend fun getGoogleLoginAuthData(email: String, oauthToken: String): AuthData? { + val result = googleLoginApi.getAuthTokenPlayResponse(email, oauthToken) + return when (result) { + is NetworkResult.Success -> handleAuthTokenPlayResponseSuccess(result, email) + is NetworkResult.Error -> throw Exception(result.errorMessage, result.exception) + } + } + + private fun handleAuthTokenPlayResponseSuccess( + result: NetworkResult.Success, + email: String + ): AuthData? { + if (result.data.isSuccessful) { + val parsedResult = + AuthTokenPlayResponseParser.parseResponse(String(result.data.responseBytes)) + val token = parsedResult["Token"] ?: "" + Timber.d("Parsed token: $token") + //TODO save token in the preferences + return AuthHelper.build(email, token, properties) + } + return null + } + + override suspend fun validate(): Boolean { + val authData = AuthHelper.build("", "") // TODO authdata will be fetched from preferences + val result = googleLoginApi.validate(authData) + return result is NetworkResult.Success && result.data + } +} \ No newline at end of file -- GitLab From 7c511b9978f5a96d3662419c7328ae2d6d20049f Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Thu, 24 Aug 2023 17:14:06 +0600 Subject: [PATCH 54/57] Google sign in integrated --- .../java/foundation/e/apps/di/LoginModule.kt | 10 +++ .../AuthTokenPlayResponseParser.kt | 2 +- .../{ => repository}/GoogleLoginRepository.kt | 4 +- .../GoogleLoginRepositoryImpl.kt | 33 +++++++-- .../apps/domain/login/usecase/BaseUseCase.kt | 3 + .../domain/login/usecase/GplayLoginUseCase.kt | 30 +++++++++ .../apps/domain/login/usecase/LoginUseCase.kt | 34 ++++++++++ .../domain/login/usecase/UserLoginUseCase.kt | 10 ++- .../apps/presentation/login/LoginViewModel.kt | 67 +++++++++++++++++-- .../main/java/app/lounge/di/NetworkModule.kt | 31 ++++++++- .../app/lounge/storage/cache/Persistence.kt | 4 +- 11 files changed, 206 insertions(+), 22 deletions(-) rename app/src/main/java/foundation/e/apps/domain/login/{ => repository}/AuthTokenPlayResponseParser.kt (90%) rename app/src/main/java/foundation/e/apps/domain/login/{ => repository}/GoogleLoginRepository.kt (71%) rename app/src/main/java/foundation/e/apps/domain/login/{ => repository}/GoogleLoginRepositoryImpl.kt (56%) create mode 100644 app/src/main/java/foundation/e/apps/domain/login/usecase/BaseUseCase.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt create mode 100644 app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.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 3df9857af..83bbe7c7d 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,10 @@ 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.GoogleLoginRepository +import foundation.e.apps.domain.login.repository.GoogleLoginRepositoryImpl import foundation.e.apps.domain.login.repository.LoginRepositoryImpl +import foundation.e.apps.domain.login.usecase.GplayLoginUseCase import foundation.e.apps.domain.login.usecase.UserLoginUseCase @InstallIn(SingletonComponent::class) @@ -45,4 +48,11 @@ object LoginModule { ): UserLoginUseCase { return UserLoginUseCase(loginRepositoryImpl) } + + @Provides + fun provideGoogleLoginUseCase( + loginRepositoryImpl: GoogleLoginRepositoryImpl + ): GplayLoginUseCase { + return GplayLoginUseCase(loginRepositoryImpl) + } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/AuthTokenPlayResponseParser.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/AuthTokenPlayResponseParser.kt similarity index 90% rename from app/src/main/java/foundation/e/apps/domain/login/AuthTokenPlayResponseParser.kt rename to app/src/main/java/foundation/e/apps/domain/login/repository/AuthTokenPlayResponseParser.kt index b29c62c33..b8af011d6 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/AuthTokenPlayResponseParser.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/AuthTokenPlayResponseParser.kt @@ -1,4 +1,4 @@ -package foundation.e.apps.domain.login +package foundation.e.apps.domain.login.repository import java.util.StringTokenizer diff --git a/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepository.kt similarity index 71% rename from app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepository.kt rename to app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepository.kt index 01dde8cf4..3f1f2791c 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepository.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepository.kt @@ -1,8 +1,8 @@ -package foundation.e.apps.domain.login +package foundation.e.apps.domain.login.repository import com.aurora.gplayapi.data.models.AuthData interface GoogleLoginRepository { - suspend fun getGoogleLoginAuthData(email: String, oauthToken: String): AuthData? + suspend fun getGoogleLoginAuthData(email: String, oauthToken: String?): AuthData? suspend fun validate(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt similarity index 56% rename from app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepositoryImpl.kt rename to app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt index b75650427..061305a92 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/GoogleLoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt @@ -1,26 +1,44 @@ -package foundation.e.apps.domain.login +package foundation.e.apps.domain.login.repository +import android.content.Context import app.lounge.login.google.GoogleLoginApi import app.lounge.networking.NetworkResult +import app.lounge.storage.cache.configurations import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.helpers.AuthHelper +import dagger.hilt.android.qualifiers.ApplicationContext import timber.log.Timber import java.lang.Exception import java.util.Properties import javax.inject.Inject class GoogleLoginRepositoryImpl @Inject constructor( + @ApplicationContext private val context: Context, private val googleLoginApi: GoogleLoginApi, private val properties: Properties ) : GoogleLoginRepository { - override suspend fun getGoogleLoginAuthData(email: String, oauthToken: String): AuthData? { - val result = googleLoginApi.getAuthTokenPlayResponse(email, oauthToken) - return when (result) { - is NetworkResult.Success -> handleAuthTokenPlayResponseSuccess(result, email) - is NetworkResult.Error -> throw Exception(result.errorMessage, result.exception) + override suspend fun getGoogleLoginAuthData(email: String, oauthToken: String?): AuthData? { + val aasToken = context.configurations.aasToken + + if (oauthToken.isNullOrEmpty() && aasToken.isNotEmpty()) { + return AuthHelper.build(email, aasToken, properties) + } + + return fetchAuthData(oauthToken, email) + } + + private suspend fun fetchAuthData(oauthToken: String?, email: String): AuthData? { + oauthToken?.let { + val result = googleLoginApi.getAuthTokenPlayResponse(email, oauthToken) + return when (result) { + is NetworkResult.Success -> handleAuthTokenPlayResponseSuccess(result, email) + is NetworkResult.Error -> throw Exception(result.errorMessage, result.exception) + } } + + return null } private fun handleAuthTokenPlayResponseSuccess( @@ -30,9 +48,10 @@ class GoogleLoginRepositoryImpl @Inject constructor( if (result.data.isSuccessful) { val parsedResult = AuthTokenPlayResponseParser.parseResponse(String(result.data.responseBytes)) + val token = parsedResult["Token"] ?: "" Timber.d("Parsed token: $token") - //TODO save token in the preferences + context.configurations.aasToken = token return AuthHelper.build(email, token, properties) } return null diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/BaseUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/BaseUseCase.kt new file mode 100644 index 000000000..72a37d1bb --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/BaseUseCase.kt @@ -0,0 +1,3 @@ +package foundation.e.apps.domain.login.usecase + +open class BaseUseCase \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt new file mode 100644 index 000000000..ecdf54b90 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt @@ -0,0 +1,30 @@ +package foundation.e.apps.domain.login.usecase + +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.ResultSupreme +import foundation.e.apps.data.enums.User +import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.domain.login.repository.GoogleLoginRepository +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.single +import javax.inject.Inject + +class GplayLoginUseCase @Inject constructor(private val googleLoginRepository: GoogleLoginRepository) : + BaseUseCase() { + + suspend operator fun invoke( + email: String, + oauthToken: String, + ): Resource = flow { + try { + emit(Resource.Loading()) + val authData = googleLoginRepository.getGoogleLoginAuthData(email, oauthToken) + val authObject = AuthObject.GPlayAuth(ResultSupreme.Success(authData), User.ANONYMOUS) as AuthObject + + emit(Resource.Success(authObject)) + } catch (e: Exception) { + emit(Resource.Error(e.localizedMessage ?: "")) + } + }.single() +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt new file mode 100644 index 000000000..1a8446e8c --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt @@ -0,0 +1,34 @@ +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.enums.User +import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.utils.Resource +import timber.log.Timber +import java.lang.Exception +import java.util.Properties +import javax.inject.Inject + +class LoginUseCase @Inject constructor( + @ApplicationContext private val context: Context, + private val userLoginUseCase: UserLoginUseCase, + private val gplayLoginUseCase: GplayLoginUseCase, + private val properties: Properties +) { + suspend fun getAuthObject(user: User?, email: String = "", oauthToken: String = ""): Resource? { + return try { + val currentUser = user ?: User.valueOf(context.configurations.userType) + + when (currentUser) { + User.ANONYMOUS -> userLoginUseCase.invoke(properties, "") + User.GOOGLE -> gplayLoginUseCase.invoke(email, oauthToken) + else -> null + } + } catch (e: Exception) { + Timber.w(e) + null + } + } +} \ 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 a62ee1961..3b4e6ef63 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 @@ -20,6 +20,9 @@ package foundation.e.apps.domain.login.usecase import app.lounge.model.AnonymousAuthDataRequestBody import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.ResultSupreme +import foundation.e.apps.data.enums.User +import foundation.e.apps.data.login.AuthObject import foundation.e.apps.domain.login.repository.LoginRepository import foundation.e.apps.utils.Resource import kotlinx.coroutines.flow.flow @@ -29,12 +32,12 @@ import javax.inject.Inject class UserLoginUseCase @Inject constructor( private val loginRepository: LoginRepository, -) { +): BaseUseCase() { suspend operator fun invoke( properties: Properties, userAgent: String - ): Resource = flow { + ): Resource = flow { try { emit(Resource.Loading()) val userResponse = loginRepository.anonymousUser( @@ -43,7 +46,8 @@ class UserLoginUseCase @Inject constructor( userAgent = userAgent ) ) - emit(Resource.Success(userResponse)) + val authObject = AuthObject.GPlayAuth(ResultSupreme.Success(userResponse), User.ANONYMOUS) as AuthObject + emit(Resource.Success(authObject)) } catch (e: Exception) { emit(Resource.Error(e.localizedMessage)) } 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 529d34f96..f24215159 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 @@ -17,18 +17,25 @@ package foundation.e.apps.presentation.login +import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import app.lounge.storage.cache.configurations import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext 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.BaseUseCase +import foundation.e.apps.domain.login.usecase.GplayLoginUseCase +import foundation.e.apps.domain.login.usecase.LoginUseCase 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.Dispatchers import kotlinx.coroutines.launch import java.util.Properties import javax.inject.Inject @@ -40,7 +47,9 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, - private val userLoginUseCase: UserLoginUseCase + private val userLoginUseCase: UserLoginUseCase, + private val gplayLoginUseCase: GplayLoginUseCase, + private val loginUseCase: LoginUseCase ) : ViewModel() { /** @@ -58,10 +67,18 @@ class LoginViewModel @Inject constructor( /** * Main point of starting of entire authentication process. */ - fun startLoginFlow(clearList: List = listOf()) { + fun startLoginFlow( + clearList: List = listOf(), + user: User? = null, + email: String = "", + oauthToken: String = "" + ) { viewModelScope.launch { - val authObjectsLocal = loginSourceRepository.getAuthObjects(clearList) - authObjects.postValue(authObjectsLocal) + val authObject = loginUseCase.getAuthObject(user, email, oauthToken) +// val authObjectsLocal = loginSourceRepository.getAuthObjects(clearList) + authObject?.data?.let { + authObjects.postValue(listOf(it)) + } } } @@ -145,7 +162,7 @@ class LoginViewModel @Inject constructor( fun authenticateAnonymousUser( properties: Properties, - userAgent: String = SystemInfoProvider.getAppBuildInfo() + userAgent: String = SystemInfoProvider.getAppBuildInfo(), ) { viewModelScope.launch { userLoginUseCase( @@ -156,11 +173,35 @@ class LoginViewModel @Inject constructor( 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) + } + } + } + } + } + + fun authenticateGoogleUser(email: String, oauthToken: String) { + viewModelScope.launch(Dispatchers.IO) { + gplayLoginUseCase(email, oauthToken).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) } @@ -169,3 +210,19 @@ class LoginViewModel @Inject constructor( } } } + +class LoginFactory @Inject constructor( + @ApplicationContext private val context: Context, + private val userLoginUseCase: UserLoginUseCase, + private val gplayLoginUseCase: GplayLoginUseCase +) { + suspend fun getLoginUseCase(user: User?): BaseUseCase? { + val currentUser = user ?: User.valueOf(context.configurations.userType) + + return when (currentUser) { + User.ANONYMOUS -> userLoginUseCase + User.GOOGLE -> gplayLoginUseCase + else -> null + } + } +} diff --git a/modules/src/main/java/app/lounge/di/NetworkModule.kt b/modules/src/main/java/app/lounge/di/NetworkModule.kt index 63d354cf1..973cecc0a 100644 --- a/modules/src/main/java/app/lounge/di/NetworkModule.kt +++ b/modules/src/main/java/app/lounge/di/NetworkModule.kt @@ -19,9 +19,13 @@ package app.lounge.di +import app.lounge.gplay.GplayHttpClient import app.lounge.login.anonymous.AnonymousUser import app.lounge.login.anonymous.AnonymousUserRetrofitAPI import app.lounge.login.anonymous.AnonymousUserRetrofitImpl +import app.lounge.login.google.AuthTokenFetchApi +import app.lounge.login.google.GoogleLoginApi +import app.lounge.login.google.GoogleLoginApiImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -43,7 +47,7 @@ internal object NetworkModule { private fun retrofit( okHttpClient: OkHttpClient, baseUrl: String - ) : Retrofit { + ): Retrofit { return Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) @@ -77,7 +81,7 @@ internal object NetworkModule { @Provides @Singleton - internal fun providesHttpLogger() : HttpLoggingInterceptor { + internal fun providesHttpLogger(): HttpLoggingInterceptor { return run { val httpLoggingInterceptor = HttpLoggingInterceptor() httpLoggingInterceptor.apply { @@ -86,14 +90,35 @@ internal object NetworkModule { } } + @Provides + @Singleton + fun provideGplayHttpClient(@Named("privateOkHttpClient") okHttpClient: OkHttpClient): GplayHttpClient { + return GplayHttpClient(okHttpClient) + } + @Provides @Singleton fun provideAnonymousUser( @Named("ECloudRetrofit") ecloud: Retrofit - ) : AnonymousUser { + ): AnonymousUser { return AnonymousUserRetrofitImpl( eCloud = ecloud ) } + @Provides + @Singleton + fun provideAuthTokenFetchApi(gplayHttpClient: GplayHttpClient): AuthTokenFetchApi { + return AuthTokenFetchApi(gplayHttpClient) + } + + @Provides + @Singleton + fun provideGoogleLoginApi( + gplayHttpClient: GplayHttpClient, + authTokenFetchApi: AuthTokenFetchApi + ): GoogleLoginApi { + return GoogleLoginApiImpl(gplayHttpClient, authTokenFetchApi) + } + } \ 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 6c089a458..fa3a93daa 100644 --- a/modules/src/main/java/app/lounge/storage/cache/Persistence.kt +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -19,7 +19,8 @@ internal enum class PersistenceKey { oauthtoken, userType, tocStatus, - tosversion + tosversion, + aastoken } class PersistentConfiguration(context: Context) { @@ -35,6 +36,7 @@ class PersistentConfiguration(context: Context) { var userType by context.persistent(PersistenceKey.userType, "") var tocStatus by context.persistent(PersistenceKey.tocStatus, false) var tosversion by context.persistent(PersistenceKey.tosversion, "") + var aasToken by context.persistent(PersistenceKey.aastoken, "") } internal class PersistentItem( -- GitLab From c9e70f76d11d33312fa7b85262cb12ee48074082 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Thu, 24 Aug 2023 23:23:52 +0600 Subject: [PATCH 55/57] fixed: post method for gplay login --- .../e/apps/presentation/login/LoginViewModel.kt | 15 ++++++++------- .../setup/signin/google/GoogleSignInFragment.kt | 6 ++++++ .../java/app/lounge/gplay/GplayHttpClient.kt | 17 ++++++++++++----- .../lounge/login/google/AuthTokenFetchApi.kt | 3 ++- 4 files changed, 28 insertions(+), 13 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 f24215159..8275e95a9 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 @@ -74,11 +74,12 @@ class LoginViewModel @Inject constructor( oauthToken: String = "" ) { viewModelScope.launch { - val authObject = loginUseCase.getAuthObject(user, email, oauthToken) -// val authObjectsLocal = loginSourceRepository.getAuthObjects(clearList) - authObject?.data?.let { - authObjects.postValue(listOf(it)) + val authObjectList = mutableListOf() + loginUseCase.getAuthObject(user, email, oauthToken)?.data?.let { + authObjectList.add(it) } +// val authObjectsLocal = loginSourceRepository.getAuthObjects(clearList) + authObjects.postValue(authObjectList) } } @@ -103,10 +104,10 @@ class LoginViewModel @Inject constructor( */ fun initialGoogleLogin(email: String, oauthToken: String, onUserSaved: () -> Unit) { viewModelScope.launch { - loginSourceRepository.saveGoogleLogin(email, oauthToken) - loginSourceRepository.saveUserType(User.GOOGLE) +// loginSourceRepository.saveGoogleLogin(email, oauthToken) +// loginSourceRepository.saveUserType(User.GOOGLE) onUserSaved() - startLoginFlow() + startLoginFlow(email = email, oauthToken = oauthToken) } } 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..fffd36c76 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 @@ -29,8 +29,10 @@ import android.webkit.WebViewClient import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController +import app.lounge.storage.cache.configurations import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R +import foundation.e.apps.data.enums.User import foundation.e.apps.data.gplay.utils.AC2DMUtil import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.databinding.FragmentGoogleSigninBinding @@ -80,6 +82,10 @@ class GoogleSignInFragment : Fragment(R.layout.fragment_google_signin) { ) { val email = it.replace("\"".toRegex(), "") viewModel.initialGoogleLogin(email, oauthToken) { + requireContext().configurations.email = email + requireContext().configurations.oauthtoken = oauthToken + requireContext().configurations.userType = User.GOOGLE.name + view.findNavController() .safeNavigate( R.id.googleSignInFragment, diff --git a/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt b/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt index eb9f0c657..d144081df 100644 --- a/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt +++ b/modules/src/main/java/app/lounge/gplay/GplayHttpClient.kt @@ -8,9 +8,11 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import timber.log.Timber +import java.io.IOException import java.net.SocketTimeoutException import java.net.UnknownHostException import javax.inject.Inject @@ -86,6 +88,16 @@ class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHt return processRequest(request) } + @Throws(IOException::class) + fun post(url: String, headers: Map, requestBody: RequestBody): PlayResponse { + val request = Request.Builder() + .url(url) + .headers(headers.toHeaders()) + .method(POST, requestBody) + .build() + return processRequest(request) + } + override fun postAuth(url: String, body: ByteArray): PlayResponse { TODO("Not yet implemented") } @@ -103,11 +115,6 @@ class GplayHttpClient @Inject constructor(@Named("privateOkHttpClient") val okHt val call = okHttpClient.newCall(request) buildPlayResponse(call.execute()) } catch (e: Exception) { - when (e) { - is UnknownHostException, - is SocketTimeoutException -> handleExceptionOnGooglePlayRequest(e) - else -> handleExceptionOnGooglePlayRequest(e) - } throw e } } diff --git a/modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt b/modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt index ef91d1aa2..c20e836bd 100644 --- a/modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt +++ b/modules/src/main/java/app/lounge/login/google/AuthTokenFetchApi.kt @@ -2,6 +2,7 @@ package app.lounge.login.google import app.lounge.gplay.GplayHttpClient import com.aurora.gplayapi.data.models.PlayResponse +import okhttp3.RequestBody.Companion.toRequestBody import java.util.Locale import javax.inject.Inject @@ -41,6 +42,6 @@ class AuthTokenFetchApi @Inject constructor(private val gplayHttpClient: GplayHt * Returning PlayResponse instead of map so that we can get the network response code. * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5709 */ - return gplayHttpClient.post(TOKEN_AUTH_URL, header, body.toByteArray()) + return gplayHttpClient.post(TOKEN_AUTH_URL, header, body.toRequestBody()) } } \ No newline at end of file -- GitLab From 6320fe9f175302e55f289b968d00e0bc05aa4b24 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Fri, 25 Aug 2023 12:52:57 +0600 Subject: [PATCH 56/57] handled google signin in flow --- .../java/foundation/e/apps/di/LoginModule.kt | 1 - .../repository/GoogleLoginRepositoryImpl.kt | 8 +- .../domain/login/usecase/GplayLoginUseCase.kt | 5 +- .../apps/domain/login/usecase/LoginUseCase.kt | 86 ++++++++++++++++--- .../apps/presentation/login/LoginViewModel.kt | 47 +++++----- .../signin/google/GoogleSignInFragment.kt | 3 - 6 files changed, 106 insertions(+), 44 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 83bbe7c7d..23b32bcd3 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,6 @@ 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.GoogleLoginRepository import foundation.e.apps.domain.login.repository.GoogleLoginRepositoryImpl import foundation.e.apps.domain.login.repository.LoginRepositoryImpl import foundation.e.apps.domain.login.usecase.GplayLoginUseCase diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt index 061305a92..8136eb55b 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt @@ -8,6 +8,7 @@ import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.helpers.AuthHelper import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.data.enums.User import timber.log.Timber import java.lang.Exception import java.util.Properties @@ -20,8 +21,13 @@ class GoogleLoginRepositoryImpl @Inject constructor( ) : GoogleLoginRepository { override suspend fun getGoogleLoginAuthData(email: String, oauthToken: String?): AuthData? { - val aasToken = context.configurations.aasToken + context.configurations.email = email + context.configurations.userType = User.GOOGLE.name + oauthToken?.let { + context.configurations.oauthtoken = oauthToken + } + val aasToken = context.configurations.aasToken if (oauthToken.isNullOrEmpty() && aasToken.isNotEmpty()) { return AuthHelper.build(email, aasToken, properties) } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt index ecdf54b90..31969099d 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/GplayLoginUseCase.kt @@ -6,6 +6,7 @@ import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.AuthObject import foundation.e.apps.domain.login.repository.GoogleLoginRepository 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 @@ -16,7 +17,7 @@ class GplayLoginUseCase @Inject constructor(private val googleLoginRepository: G suspend operator fun invoke( email: String, oauthToken: String, - ): Resource = flow { + ): Flow> = flow { try { emit(Resource.Loading()) val authData = googleLoginRepository.getGoogleLoginAuthData(email, oauthToken) @@ -26,5 +27,5 @@ class GplayLoginUseCase @Inject constructor(private val googleLoginRepository: G } catch (e: Exception) { emit(Resource.Error(e.localizedMessage ?: "")) } - }.single() + } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt index 1a8446e8c..f7a2e358d 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt @@ -1,11 +1,22 @@ package foundation.e.apps.domain.login.usecase import android.content.Context +import app.lounge.model.AnonymousAuthDataRequestBody import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.helpers.AuthHelper import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.R +import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.domain.login.repository.GoogleLoginRepository +import foundation.e.apps.domain.login.repository.GoogleLoginRepositoryImpl +import foundation.e.apps.domain.login.repository.LoginRepositoryImpl import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach import timber.log.Timber import java.lang.Exception import java.util.Properties @@ -13,22 +24,71 @@ import javax.inject.Inject class LoginUseCase @Inject constructor( @ApplicationContext private val context: Context, - private val userLoginUseCase: UserLoginUseCase, - private val gplayLoginUseCase: GplayLoginUseCase, + private val googleLoginRepository: GoogleLoginRepositoryImpl, + private val loginRepositoryImpl: LoginRepositoryImpl, private val properties: Properties ) { - suspend fun getAuthObject(user: User?, email: String = "", oauthToken: String = ""): Resource? { - return try { - val currentUser = user ?: User.valueOf(context.configurations.userType) - - when (currentUser) { - User.ANONYMOUS -> userLoginUseCase.invoke(properties, "") - User.GOOGLE -> gplayLoginUseCase.invoke(email, oauthToken) - else -> null + suspend fun getAuthObject( + user: User?, + email: String = "", + oauthToken: String = "" + ): Flow> { + return flow { + try { + val currentUser = user ?: User.valueOf(context.configurations.userType) + val currentEmail = email.ifEmpty { context.configurations.email } + + if (currentUser == User.GOOGLE && oauthToken.isNotEmpty()) { + fetchGplayAuthObject(currentEmail, oauthToken, currentUser)?.let { + emit(Resource.Success(it)) + return@flow + } + } + + val currentToken = context.configurations.aasToken + + if (currentUser == User.ANONYMOUS && currentToken.isEmpty()) { + loginRepositoryImpl.anonymousUser(authDataRequestBody = AnonymousAuthDataRequestBody( + properties = properties, + userAgent = "" + ) + ) + } + + val authData = AuthHelper.build(currentEmail, currentToken, properties) + + if (currentUser == User.GOOGLE || currentUser == User.ANONYMOUS) { + emit( + Resource.Success( + AuthObject.GPlayAuth( + ResultSupreme.Success(authData), + currentUser + ) + ) + ) + } else { + emit(Resource.Error("No User Available!")) + } + } catch (e: Exception) { + Timber.w(e) + emit( + Resource.Error( + e.localizedMessage ?: context.getString(R.string.unknown_error) + ) + ) } - } catch (e: Exception) { - Timber.w(e) - null } } + + private suspend fun fetchGplayAuthObject( + currentEmail: String, + oauthToken: String, + user: User + ): AuthObject? { + var authObject: AuthObject? = null + googleLoginRepository.getGoogleLoginAuthData(currentEmail, oauthToken)?.let { + authObject = AuthObject.GPlayAuth(ResultSupreme.Success(it), user) + } + return authObject + } } \ 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 8275e95a9..4ff50933d 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 @@ -36,6 +36,7 @@ import foundation.e.apps.ui.parentFragment.LoadingViewModel import foundation.e.apps.utils.Resource import foundation.e.apps.utils.SystemInfoProvider import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import java.util.Properties import javax.inject.Inject @@ -75,11 +76,27 @@ class LoginViewModel @Inject constructor( ) { viewModelScope.launch { val authObjectList = mutableListOf() - loginUseCase.getAuthObject(user, email, oauthToken)?.data?.let { - authObjectList.add(it) + loginUseCase.getAuthObject(user, email, oauthToken).onEach { resutl -> + when (resutl) { + is Resource.Success -> { + _loginState.value = LoginState(isLoggedIn = true) + resutl.data?.let { + authObjectList.add(it) + authObjects.postValue(authObjectList) + } + } + + is Resource.Error -> { + _loginState.value = LoginState( + error = resutl.message ?: "An unexpected error occured" + ) + } + + is Resource.Loading -> { + _loginState.value = LoginState(isLoading = true) + } + } } -// val authObjectsLocal = loginSourceRepository.getAuthObjects(clearList) - authObjects.postValue(authObjectList) } } @@ -104,10 +121,8 @@ class LoginViewModel @Inject constructor( */ fun initialGoogleLogin(email: String, oauthToken: String, onUserSaved: () -> Unit) { viewModelScope.launch { -// loginSourceRepository.saveGoogleLogin(email, oauthToken) -// loginSourceRepository.saveUserType(User.GOOGLE) - onUserSaved() startLoginFlow(email = email, oauthToken = oauthToken) + onUserSaved() } } @@ -191,7 +206,7 @@ class LoginViewModel @Inject constructor( fun authenticateGoogleUser(email: String, oauthToken: String) { viewModelScope.launch(Dispatchers.IO) { - gplayLoginUseCase(email, oauthToken).also { result -> + gplayLoginUseCase(email, oauthToken).onEach { result -> when (result) { is Resource.Success -> { _loginState.value = LoginState(isLoggedIn = true) @@ -211,19 +226,3 @@ class LoginViewModel @Inject constructor( } } } - -class LoginFactory @Inject constructor( - @ApplicationContext private val context: Context, - private val userLoginUseCase: UserLoginUseCase, - private val gplayLoginUseCase: GplayLoginUseCase -) { - suspend fun getLoginUseCase(user: User?): BaseUseCase? { - val currentUser = user ?: User.valueOf(context.configurations.userType) - - return when (currentUser) { - User.ANONYMOUS -> userLoginUseCase - User.GOOGLE -> gplayLoginUseCase - else -> null - } - } -} 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 fffd36c76..4e7204cec 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 @@ -82,9 +82,6 @@ class GoogleSignInFragment : Fragment(R.layout.fragment_google_signin) { ) { val email = it.replace("\"".toRegex(), "") viewModel.initialGoogleLogin(email, oauthToken) { - requireContext().configurations.email = email - requireContext().configurations.oauthtoken = oauthToken - requireContext().configurations.userType = User.GOOGLE.name view.findNavController() .safeNavigate( -- GitLab From dc6e2ad2af59f1c29d9bbfbb07bde8563299a4d3 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Sun, 27 Aug 2023 13:59:56 +0600 Subject: [PATCH 57/57] handled all types of login --- .../data/gplay/GplayStoreRepositoryImpl.kt | 8 +- .../repository/GoogleLoginRepositoryImpl.kt | 3 +- .../login/repository/LoginRepositoryImpl.kt | 5 +- .../apps/domain/login/usecase/LoginUseCase.kt | 78 ++++++++++++------- .../apps/presentation/login/LoginViewModel.kt | 22 ++++-- 5 files changed, 76 insertions(+), 40 deletions(-) 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 23d4e8427..4da1870d7 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 @@ -19,6 +19,7 @@ package foundation.e.apps.data.gplay import android.content.Context +import app.lounge.storage.cache.configurations import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.AuthData @@ -33,6 +34,7 @@ import com.aurora.gplayapi.helpers.Chart import com.aurora.gplayapi.helpers.PurchaseHelper import com.aurora.gplayapi.helpers.SearchHelper import com.aurora.gplayapi.helpers.TopChartsHelper +import com.google.gson.Gson import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.fused.utils.CategoryType @@ -55,7 +57,7 @@ class GplayStoreRepositoryImpl @Inject constructor( override suspend fun getHomeScreenData(): Any { val homeScreenData = mutableMapOf>() val homeElements = createTopChartElements() - val authData = loginSourceRepository.gplayAuth ?: return homeScreenData + val authData = getAuthData()//loginSourceRepository.gplayAuth ?: return homeScreenData homeElements.forEach { val chart = it.value.keys.iterator().next() @@ -311,4 +313,8 @@ class GplayStoreRepositoryImpl @Inject constructor( } return downloadData } + + private fun getAuthData(): AuthData { + return Gson().fromJson(context.configurations.authData, AuthData::class.java) + } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt index 8136eb55b..eedcf1e7d 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/GoogleLoginRepositoryImpl.kt @@ -28,7 +28,7 @@ class GoogleLoginRepositoryImpl @Inject constructor( } val aasToken = context.configurations.aasToken - if (oauthToken.isNullOrEmpty() && aasToken.isNotEmpty()) { + if (aasToken.isNotEmpty()) { return AuthHelper.build(email, aasToken, properties) } @@ -56,7 +56,6 @@ class GoogleLoginRepositoryImpl @Inject constructor( AuthTokenPlayResponseParser.parseResponse(String(result.data.responseBytes)) val token = parsedResult["Token"] ?: "" - Timber.d("Parsed token: $token") context.configurations.aasToken = token return AuthHelper.build(email, token, properties) } 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..6477e7e69 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 @@ -24,7 +24,9 @@ import app.lounge.model.AnonymousAuthDataRequestBody import app.lounge.networking.NetworkResult import app.lounge.storage.cache.configurations 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 javax.inject.Inject class LoginRepositoryImpl @Inject constructor( @@ -41,7 +43,8 @@ class LoginRepositoryImpl @Inject constructor( is NetworkResult.Error -> throw Exception(result.errorMessage, result.exception) is NetworkResult.Success -> { - applicationContext.configurations.authData = result.data.toString() + applicationContext.configurations.userType = User.ANONYMOUS.toString() + applicationContext.configurations.authData = Gson().toJson(result.data) return result.data } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt index f7a2e358d..f45400373 100644 --- a/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/LoginUseCase.kt @@ -5,22 +5,24 @@ import app.lounge.model.AnonymousAuthDataRequestBody import app.lounge.storage.cache.configurations import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.helpers.AuthHelper +import com.google.gson.Gson +import com.google.gson.JsonSyntaxException import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.domain.login.repository.GoogleLoginRepository import foundation.e.apps.domain.login.repository.GoogleLoginRepositoryImpl import foundation.e.apps.domain.login.repository.LoginRepositoryImpl import foundation.e.apps.utils.Resource +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.flowOn import timber.log.Timber -import java.lang.Exception import java.util.Properties import javax.inject.Inject +import kotlin.Exception class LoginUseCase @Inject constructor( @ApplicationContext private val context: Context, @@ -32,43 +34,44 @@ class LoginUseCase @Inject constructor( user: User?, email: String = "", oauthToken: String = "" - ): Flow> { + ): Flow> { return flow { try { - val currentUser = user ?: User.valueOf(context.configurations.userType) - val currentEmail = email.ifEmpty { context.configurations.email } + emit(Resource.Loading()) + val currentUser = + user ?: User.valueOf(context.configurations.userType.ifEmpty { "UNAVAILABLE" }) + val currentAuthData = getAuthData() + Timber.d("currentAuthData: $(currentAuthData != null) currentUser: $currentUser") - if (currentUser == User.GOOGLE && oauthToken.isNotEmpty()) { + if (currentAuthData != null && currentUser != User.UNAVAILABLE) { + Timber.d("User is already available!") + emit( + createResourceSuccess(currentAuthData, currentUser) + ) + return@flow + } + + if (currentUser == User.GOOGLE) { + val currentEmail = email.ifEmpty { context.configurations.email } fetchGplayAuthObject(currentEmail, oauthToken, currentUser)?.let { emit(Resource.Success(it)) - return@flow } + return@flow } - val currentToken = context.configurations.aasToken - - if (currentUser == User.ANONYMOUS && currentToken.isEmpty()) { - loginRepositoryImpl.anonymousUser(authDataRequestBody = AnonymousAuthDataRequestBody( - properties = properties, - userAgent = "" - ) + if (currentUser == User.ANONYMOUS) { + val authData = loginRepositoryImpl.anonymousUser( + authDataRequestBody = AnonymousAuthDataRequestBody( + properties = properties, + userAgent = "" + ) ) + emit(createResourceSuccess(authData, currentUser)) + return@flow } - val authData = AuthHelper.build(currentEmail, currentToken, properties) + emit(Resource.Error("User is not available!")) - if (currentUser == User.GOOGLE || currentUser == User.ANONYMOUS) { - emit( - Resource.Success( - AuthObject.GPlayAuth( - ResultSupreme.Success(authData), - currentUser - ) - ) - ) - } else { - emit(Resource.Error("No User Available!")) - } } catch (e: Exception) { Timber.w(e) emit( @@ -77,9 +80,28 @@ class LoginUseCase @Inject constructor( ) ) } + }.flowOn(Dispatchers.IO) + } + + private fun getAuthData(): AuthData? { + return try { + Gson().fromJson(context.configurations.authData, AuthData::class.java) + } catch (e: Exception) { + null } } + private fun createResourceSuccess( + authData: AuthData, + currentUser: User + ): Resource.Success = + Resource.Success( + AuthObject.GPlayAuth( + ResultSupreme.Success(authData), + currentUser + ) + ) + private suspend fun fetchGplayAuthObject( currentEmail: String, oauthToken: String, 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 4ff50933d..9adc1b140 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.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.data.enums.User @@ -36,8 +37,10 @@ import foundation.e.apps.ui.parentFragment.LoadingViewModel import foundation.e.apps.utils.Resource import foundation.e.apps.utils.SystemInfoProvider import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import timber.log.Timber import java.util.Properties import javax.inject.Inject @@ -76,27 +79,30 @@ class LoginViewModel @Inject constructor( ) { viewModelScope.launch { val authObjectList = mutableListOf() - loginUseCase.getAuthObject(user, email, oauthToken).onEach { resutl -> - when (resutl) { + loginUseCase.getAuthObject(user, email, oauthToken).onEach { result -> + when (result) { is Resource.Success -> { _loginState.value = LoginState(isLoggedIn = true) - resutl.data?.let { + result.data?.let { authObjectList.add(it) - authObjects.postValue(authObjectList) } + authObjects.postValue(authObjectList) } is Resource.Error -> { + Timber.d("AuthObject Error: ${result.message}") + _loginState.value = LoginState( - error = resutl.message ?: "An unexpected error occured" + error = result.message ?: "An unexpected error occurred!" ) + authObjects.postValue(authObjectList) } is Resource.Loading -> { _loginState.value = LoginState(isLoading = true) } } - } + }.collect() } } @@ -109,7 +115,7 @@ class LoginViewModel @Inject constructor( viewModelScope.launch { loginSourceRepository.saveUserType(User.ANONYMOUS) onUserSaved() - startLoginFlow() + startLoginFlow(user = User.ANONYMOUS) } } @@ -121,7 +127,7 @@ class LoginViewModel @Inject constructor( */ fun initialGoogleLogin(email: String, oauthToken: String, onUserSaved: () -> Unit) { viewModelScope.launch { - startLoginFlow(email = email, oauthToken = oauthToken) + startLoginFlow(user = User.GOOGLE, email = email, oauthToken = oauthToken) onUserSaved() } } -- GitLab