diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index f688d83e6dfad0d482dceb9de3f52acdf7ff5c02..1163011a843b7f063bc50b00348b6c35350a3158 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -483,7 +483,12 @@ class FusedApiImpl @Inject constructor( } override suspend fun getSearchSuggestions(query: String): List { - return gplayRepository.getSearchSuggestions(query) + var searchSuggesions = listOf() + runCodeWithTimeout({ + searchSuggesions = gplayRepository.getSearchSuggestions(query) + }) + + return searchSuggesions } override suspend fun getOnDemandModule( diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index d72884689b64d5eeb0da3f8f7fdacbc538e032b9..071e8df6eec7b6c68d960de5c3528c8d9a0c7719 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -124,7 +124,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { companion object { private const val PRIVACY_SCORE_SOURCE_CODE_URL = - "https://gitlab.e.foundation/e/os/apps/-/blob/main/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt#L31" + "https://gitlab.e.foundation/e/os/apps/-/blob/main/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt" private const val EXODUS_URL = "https://exodus-privacy.eu.org" private const val EXODUS_REPORT_URL = "https://reports.exodus-privacy.eu.org/" private const val PRIVACY_GUIDELINE_URL = "https://doc.e.foundation/privacy_score" diff --git a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt index 79e0a3081ba47baffaaf4716f56eb246d8eef72a..f13657eeb353573748e69d0253977715e298dfb3 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/subFrags/ApplicationDialogFragment.kt @@ -64,8 +64,8 @@ class ApplicationDialogFragment() : DialogFragment() { val positiveButtonText = positiveButtonText?.ifEmpty { getString(R.string.ok) } val materialAlertDialogBuilder = MaterialAlertDialogBuilder(requireContext()) - .setTitle(Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT)) - .setMessage(Html.fromHtml(message, Html.FROM_HTML_MODE_COMPACT)) + .setTitle(Html.fromHtml(title ?: "", Html.FROM_HTML_MODE_COMPACT)) + .setMessage(Html.fromHtml(message ?: "", Html.FROM_HTML_MODE_COMPACT)) .setPositiveButton(positiveButtonText) { _, _ -> positiveButtonAction?.invoke() this.dismiss() diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 2d8161c3a6fca8112e5164c5622ee791c1272552..d6dc63743d62601a4f13ea027f3fe75e61d61203 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -162,4 +162,8 @@ Þvingun uppsetningar mun gerir þér kleift að sækja og setja hana upp, en tryggir ekki endilega að hún muni virka rétt.

Tilraunir til að setja upp óstuddan hugbúnað geta valdi hruni eða hægt á kerfinu.

Við erum að vinna í að bæta samhæfni við þetta forrit.
Einhver vandamál\? Halda áfram yfir í Google-innskráningu + Við mælum með því að notaður sé sértakur Google-reikningur til að: +\n +\n\t• minnka líkur á sérsniðnu snuðri (micro-targeting) +\n\t• minnka áhrifin sem takmörkun reikningsins af hálfu Google myndi hafa \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 5dd0ee8004cba8f021e9bbcfd595c0ed4b9d9a2b..fb6205356b9602084733819efc659eda74ab2d72 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -104,7 +104,6 @@ L\'app verrà scaricata automaticamente su questo dispositivo Acquisto completato! Le app a pagamento non possono essere installate in modalità anonima. Per installarle, accedi al tuo account Google. - ANNULLA CONFERMA Facendo click su conferma, si accede alla pagina Google Play della app per completare l\'acquisto con il browser. Fai click su conferma per acquistare %1$s per %2$s. Acquista %1$s @@ -161,4 +160,10 @@ Aggiorna le app installate da altri store Aggiorna le app installate da altri app store. \nTali app proveremo ad aggiornarle dalla categoria app comuni e open source. + Problemi\? + Prosegui con l\'accesso a Google + Consigliamo di utilizzare un account Google dedicato al fine di: +\n +\n\t• ridurre il micro-targeting +\n\t• limitare l\'impatto nel caso in cui l\'account venga bloccato da Google \ No newline at end of file diff --git a/modules/.gitignore b/modules/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..42afabfd2abebf31384ca7797186a27a4b7dbee8 --- /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 0000000000000000000000000000000000000000..43b102cc56f64bf731e422239a0e1031e6f5cdcf --- /dev/null +++ b/modules/build.gradle @@ -0,0 +1,50 @@ +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' + gson_version = '2.9.0' + kotlin_reflection = '1.8.10' +} + +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_11 + targetCompatibility JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = '11' + } +} + +dependencies { + + implementation "androidx.core:core-ktx:$core_version" + 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' +} \ No newline at end of file diff --git a/modules/consumer-rules.pro b/modules/consumer-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/proguard-rules.pro b/modules/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce --- /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/PersistentStorageTest.kt b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..6161b1cfba3294ba56a5addb2b6477083c11a26a --- /dev/null +++ b/modules/src/androidTest/java/app/lounge/PersistentStorageTest.kt @@ -0,0 +1,116 @@ +package app.lounge + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import app.lounge.storage.cache.PersistentConfiguration +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 +import org.junit.runner.RunWith +import kotlin.reflect.KClassifier + +@RunWith(AndroidJUnit4::class) +class PersistentStorageTest { + + private lateinit var testConfiguration: PersistentConfiguration + + @Before + fun setupPersistentConfiguration(){ + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + testConfiguration = appContext.configurations + } + + @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) + } + } + } + + @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) + } + } + + @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 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 +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) + 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) + } +} + +// 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 diff --git a/modules/src/main/AndroidManifest.xml b/modules/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5918e68abcdde7f61ccae4f0ad4885b764573fd --- /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 0000000000000000000000000000000000000000..6c089a458f138e040cf03a5ce48d8849dd2542b5 --- /dev/null +++ b/modules/src/main/java/app/lounge/storage/cache/Persistence.kt @@ -0,0 +1,85 @@ +package app.lounge.storage.cache + +import android.content.Context +import kotlin.reflect.KProperty + + +val Context.configurations: PersistentConfiguration get() = PersistentConfiguration(context = this) + +internal enum class PersistenceKey { + updateInstallAuto, + updateCheckIntervals, + updateAppsFromOtherStores, + showAllApplications, + showPWAApplications, + showFOSSApplications, + // OLD datastore + authData, + email, + oauthtoken, + userType, + tocStatus, + 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, "") +} + +internal class PersistentItem( + 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() + } + } +} + +internal fun Context.persistent(key: PersistenceKey, defaultValue: T) : PersistentItem { + return PersistentItem(context = this, key = key, defaultValue = defaultValue) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4f3c63f186ad758687c7fae4fc47ab773fbd7f17..011889897e706e50891d341cfc1bfc79cc37946b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -62,3 +62,4 @@ dependencyResolutionManagement { } rootProject.name = "App Lounge" include ':app' +include ':modules'