From 91c8e43ab947482f7c3ab55eca53d4f476f591bc Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 27 Dec 2020 17:09:51 +0100 Subject: [PATCH 01/29] EN: Make service more robust against exceptions while processing app input --- .../ExposureNotificationServiceImpl.kt | 63 +++++++++++-------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt index 0f04427e..2b3b1bc7 100644 --- a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt +++ b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt @@ -12,6 +12,7 @@ import android.content.* import android.os.* import android.util.Log import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import com.google.android.gms.common.api.Status @@ -19,6 +20,8 @@ import com.google.android.gms.nearby.exposurenotification.* import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes.* import com.google.android.gms.nearby.exposurenotification.internal.* import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.withTimeout import org.json.JSONArray import org.json.JSONObject @@ -37,6 +40,8 @@ import kotlin.random.Random class ExposureNotificationServiceImpl(private val context: Context, private val lifecycle: Lifecycle, private val packageName: String) : INearbyExposureNotificationService.Stub(), LifecycleOwner { + private fun LifecycleCoroutineScope.launchSafely(block: suspend CoroutineScope.() -> Unit): Job = launchWhenStarted { try { block() } catch (e: Exception) { Log.w(TAG, "Error in coroutine", e) } } + override fun getLifecycle(): Lifecycle = lifecycle private fun pendingConfirm(permission: String): PendingIntent { @@ -102,7 +107,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun start(params: StartParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val isAuthorized = ExposureDatabase.with(context) { it.isAppAuthorized(packageName) } val adapter = BluetoothAdapter.getDefaultAdapter() val status = if (isAuthorized && ExposurePreferences(context).enabled) { @@ -131,7 +136,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun stop(params: StopParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val isAuthorized = ExposureDatabase.with(context) { database -> database.isAppAuthorized(packageName).also { if (it) database.noteAppAction(packageName, "stop") @@ -149,7 +154,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun isEnabled(params: IsEnabledParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val isAuthorized = ExposureDatabase.with(context) { database -> database.isAppAuthorized(packageName) } @@ -162,7 +167,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun getTemporaryExposureKeyHistory(params: GetTemporaryExposureKeyHistoryParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val status = confirmPermission(CONFIRM_ACTION_KEYS) val response = when { status.isSuccess -> ExposureDatabase.with(context) { database -> @@ -243,7 +248,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val override fun provideDiagnosisKeys(params: ProvideDiagnosisKeysParams) { val token = params.token ?: TOKEN_A Log.w(TAG, "provideDiagnosisKeys() with $packageName/$token") - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val tid = ExposureDatabase.with(context) { database -> val configuration = params.configuration if (configuration != null) { @@ -259,7 +264,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } catch (e: Exception) { Log.w(TAG, "Callback failed", e) } - return@launchWhenStarted + return@launchSafely } ExposureDatabase.with(context) { database -> val start = System.currentTimeMillis() @@ -289,21 +294,25 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } params.keyFileSupplier?.let { keyFileSupplier -> Log.d(TAG, "Using key file supplier") - while (keyFileSupplier.isAvailable && keyFileSupplier.hasNext()) { - try { - val cacheFile = File(context.cacheDir, "en-keyfile-${System.currentTimeMillis()}-${Random.nextInt()}.zip") - ParcelFileDescriptor.AutoCloseInputStream(keyFileSupplier.next()).use { it.copyToFile(cacheFile) } - val hash = MessageDigest.getInstance("SHA-256").digest(cacheFile) - val storedKeys = database.storeDiagnosisFileUsed(tid, hash) - if (storedKeys != null) { - keys += storedKeys.toInt() - cacheFile.delete() - } else { - todoKeyFiles.add(cacheFile to hash) + try { + while (keyFileSupplier.isAvailable && keyFileSupplier.hasNext()) { + try { + val cacheFile = File(context.cacheDir, "en-keyfile-${System.currentTimeMillis()}-${Random.nextInt()}.zip") + ParcelFileDescriptor.AutoCloseInputStream(keyFileSupplier.next()).use { it.copyToFile(cacheFile) } + val hash = MessageDigest.getInstance("SHA-256").digest(cacheFile) + val storedKeys = database.storeDiagnosisFileUsed(tid, hash) + if (storedKeys != null) { + keys += storedKeys.toInt() + cacheFile.delete() + } else { + todoKeyFiles.add(cacheFile to hash) + } + } catch (e: Exception) { + Log.w(TAG, "Failed parsing file", e) } - } catch (e: Exception) { - Log.w(TAG, "Failed parsing file", e) } + } catch (e: Exception) { + Log.w(TAG, "Disconnected from key file supplier", e) } } @@ -389,7 +398,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun getExposureSummary(params: GetExposureSummaryParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val response = buildExposureSummary(params.token) ExposureDatabase.with(context) { database -> @@ -413,7 +422,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun getExposureInformation(params: GetExposureInformationParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { ExposureDatabase.with(context) { database -> val pair = database.loadConfiguration(packageName, params.token) val response = if (pair != null && database.isAppAuthorized(packageName)) { @@ -494,7 +503,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun getExposureWindows(params: GetExposureWindowsParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val response = getExposureWindowsInternal(params.token ?: TOKEN_A) ExposureDatabase.with(context) { database -> @@ -529,7 +538,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun getDailySummaries(params: GetDailySummariesParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val response = getExposureWindowsInternal().groupBy { it.dateMillisSinceEpoch }.map { val map = arrayListOf() for (i in 0 until ReportType.VALUES) { @@ -564,7 +573,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun setDiagnosisKeysDataMapping(params: SetDiagnosisKeysDataMappingParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { ExposureDatabase.with(context) { database -> database.storeConfiguration(packageName, TOKEN_A, params.mapping) database.noteAppAction(packageName, "setDiagnosisKeysDataMapping") @@ -578,7 +587,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun getDiagnosisKeysDataMapping(params: GetDiagnosisKeysDataMappingParams) { - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { val mapping = ExposureDatabase.with(context) { database -> val triple = database.loadConfiguration(packageName, TOKEN_A) database.noteAppAction(packageName, "getDiagnosisKeysDataMapping") @@ -594,7 +603,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val override fun getPackageConfiguration(params: GetPackageConfigurationParams) { Log.w(TAG, "Not yet implemented: getPackageConfiguration") - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { ExposureDatabase.with(context) { database -> database.noteAppAction(packageName, "getPackageConfiguration") } @@ -608,7 +617,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val override fun getStatus(params: GetStatusParams) { Log.w(TAG, "Not yet implemented: getStatus") - lifecycleScope.launchWhenStarted { + lifecycleScope.launchSafely { ExposureDatabase.with(context) { database -> database.noteAppAction(packageName, "getStatus") } From 8e7544bd14c9169cc2040e275f46adf0ae3cebb9 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 6 Jan 2021 12:00:59 +0100 Subject: [PATCH 02/29] EN: Correctly set since last scan in seconds not milliseconds --- .../exposurenotification/ExposureNotificationServiceImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt index 2b3b1bc7..3756bfce 100644 --- a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt +++ b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt @@ -450,7 +450,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val private fun ScanInstance.Builder.apply(subExposure: MergedSubExposure): ScanInstance.Builder { return this - .setSecondsSinceLastScan(subExposure.duration.coerceAtMost(5 * 60 * 1000L).toInt()) + .setSecondsSinceLastScan(subExposure.duration.coerceAtMost(5 * 60).toInt()) .setMinAttenuationDb(subExposure.attenuation) // FIXME: We use the average for both, because we don't store the minimum attenuation yet .setTypicalAttenuationDb(subExposure.attenuation) } @@ -460,7 +460,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val for (subExposure in this) { res.add(ScanInstance.Builder().apply(subExposure).build()) if (subExposure.duration > 5 * 60 * 1000L) { - res.add(ScanInstance.Builder().apply(subExposure).setSecondsSinceLastScan((subExposure.duration - 5 * 60 * 1000L).coerceAtMost(5 * 60 * 1000L).toInt()).build()) + res.add(ScanInstance.Builder().apply(subExposure).setSecondsSinceLastScan((subExposure.duration - 5 * 60).coerceAtMost(5 * 60).toInt()).build()) } } return res From f12536e6ceb43060463886d50d6c76107e93003b Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 6 Jan 2021 12:11:57 +0100 Subject: [PATCH 03/29] EN: Catch errors in zip file processing --- .../ExposureNotificationServiceImpl.kt | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt index 3756bfce..42cfad17 100644 --- a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt +++ b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt @@ -19,10 +19,7 @@ import com.google.android.gms.common.api.Status import com.google.android.gms.nearby.exposurenotification.* import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes.* import com.google.android.gms.nearby.exposurenotification.internal.* -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.* import org.json.JSONArray import org.json.JSONObject import org.microg.gms.common.Constants @@ -296,19 +293,21 @@ class ExposureNotificationServiceImpl(private val context: Context, private val Log.d(TAG, "Using key file supplier") try { while (keyFileSupplier.isAvailable && keyFileSupplier.hasNext()) { - try { - val cacheFile = File(context.cacheDir, "en-keyfile-${System.currentTimeMillis()}-${Random.nextInt()}.zip") - ParcelFileDescriptor.AutoCloseInputStream(keyFileSupplier.next()).use { it.copyToFile(cacheFile) } - val hash = MessageDigest.getInstance("SHA-256").digest(cacheFile) - val storedKeys = database.storeDiagnosisFileUsed(tid, hash) - if (storedKeys != null) { - keys += storedKeys.toInt() - cacheFile.delete() - } else { - todoKeyFiles.add(cacheFile to hash) + withContext(Dispatchers.IO) { + try { + val cacheFile = File(context.cacheDir, "en-keyfile-${System.currentTimeMillis()}-${Random.nextLong()}.zip") + ParcelFileDescriptor.AutoCloseInputStream(keyFileSupplier.next()).use { it.copyToFile(cacheFile) } + val hash = MessageDigest.getInstance("SHA-256").digest(cacheFile) + val storedKeys = database.storeDiagnosisFileUsed(tid, hash) + if (storedKeys != null) { + keys += storedKeys.toInt() + cacheFile.delete() + } else { + todoKeyFiles.add(cacheFile to hash) + } + } catch (e: Exception) { + Log.w(TAG, "Failed parsing file", e) } - } catch (e: Exception) { - Log.w(TAG, "Failed parsing file", e) } } } catch (e: Exception) { @@ -331,32 +330,38 @@ class ExposureNotificationServiceImpl(private val context: Context, private val var newKeys = if (params.keys != null) database.finishSingleMatching(tid) else 0 for ((cacheFile, hash) in todoKeyFiles) { - ZipFile(cacheFile).use { zip -> - for (entry in zip.entries()) { - if (entry.name == "export.bin") { - val stream = zip.getInputStream(entry) - val prefix = ByteArray(16) - var totalBytesRead = 0 - var bytesRead = 0 - while (bytesRead != -1 && totalBytesRead < prefix.size) { - bytesRead = stream.read(prefix, totalBytesRead, prefix.size - totalBytesRead) - if (bytesRead > 0) { - totalBytesRead += bytesRead + withContext(Dispatchers.IO) { + try { + ZipFile(cacheFile).use { zip -> + for (entry in zip.entries()) { + if (entry.name == "export.bin") { + val stream = zip.getInputStream(entry) + val prefix = ByteArray(16) + var totalBytesRead = 0 + var bytesRead = 0 + while (bytesRead != -1 && totalBytesRead < prefix.size) { + bytesRead = stream.read(prefix, totalBytesRead, prefix.size - totalBytesRead) + if (bytesRead > 0) { + totalBytesRead += bytesRead + } + } + if (totalBytesRead == prefix.size && String(prefix).trim() == "EK Export v1") { + val export = TemporaryExposureKeyExport.ADAPTER.decode(stream) + database.finishFileMatching(tid, hash, export.end_timestamp?.let { it * 1000 } + ?: System.currentTimeMillis(), export.keys.map { it.toKey() }, export.revised_keys.map { it.toKey() }) + keys += export.keys.size + export.revised_keys.size + newKeys += export.keys.size + } else { + Log.d(TAG, "export.bin had invalid prefix") + } } } - if (totalBytesRead == prefix.size && String(prefix).trim() == "EK Export v1") { - val export = TemporaryExposureKeyExport.ADAPTER.decode(stream) - database.finishFileMatching(tid, hash, export.end_timestamp?.let { it * 1000 } - ?: System.currentTimeMillis(), export.keys.map { it.toKey() }, export.revised_keys.map { it.toKey() }) - keys += export.keys.size + export.revised_keys.size - newKeys += export.keys.size - } else { - Log.d(TAG, "export.bin had invalid prefix") - } } + cacheFile.delete() + } catch (e: Exception) { + Log.w(TAG, "Failed parsing file", e) } } - cacheFile.delete() } val time = (System.currentTimeMillis() - start).coerceAtLeast(1).toDouble() / 1000.0 From af0aa2288acfd6b9bfa3c48b700eed4640b9d0c9 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 6 Jan 2021 12:13:20 +0100 Subject: [PATCH 04/29] Auth: Log and handle invalid package names --- .../gms/auth/AskPermissionActivity.java | 69 +++++++------------ 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java index 701e24ee..94c5ea5a 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AskPermissionActivity.java @@ -25,7 +25,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Bundle; import android.text.Html; import android.util.Log; @@ -39,7 +38,6 @@ import android.widget.ListView; import android.widget.TextView; import com.google.android.gms.R; -import com.squareup.wire.Wire; import org.microg.gms.common.PackageUtils; import org.microg.gms.people.PeopleManager; @@ -107,7 +105,7 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { try { applicationInfo = packageManager.getApplicationInfo(packageName, 0); } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, e); + Log.w(TAG, "Failed to find package " + packageName, e); finish(); return; } @@ -119,18 +117,9 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { if (profileIcon != null) { ((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon); } else { - new Thread(new Runnable() { - @Override - public void run() { - final Bitmap profileIcon = PeopleManager.getOwnerAvatarBitmap(AskPermissionActivity.this, account.name, true); - runOnUiThread(new Runnable() { - @Override - public void run() { - ((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon); - } - }); - - } + new Thread(() -> { + final Bitmap profileIcon1 = PeopleManager.getOwnerAvatarBitmap(AskPermissionActivity.this, account.name, true); + runOnUiThread(() -> ((ImageView) findViewById(R.id.account_photo)).setImageBitmap(profileIcon1)); }).start(); } @@ -140,18 +129,8 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { } else { ((TextView) findViewById(R.id.title)).setText(getString(R.string.ask_service_permission_title, appLabel)); } - findViewById(android.R.id.button1).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onAllow(); - } - }); - findViewById(android.R.id.button2).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onDeny(); - } - }); + findViewById(android.R.id.button1).setOnClickListener(v -> onAllow()); + findViewById(android.R.id.button2).setOnClickListener(v -> onDeny()); ((ListView) findViewById(R.id.permissions)).setAdapter(new PermissionAdapter()); } @@ -161,24 +140,20 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { findViewById(android.R.id.button2).setEnabled(false); findViewById(R.id.progress_bar).setVisibility(VISIBLE); findViewById(R.id.no_progress_bar).setVisibility(GONE); - new Thread(new Runnable() { - @Override - public void run() { - try { - AuthResponse response = authManager.requestAuth(fromAccountManager); - Bundle result = new Bundle(); - result.putString(KEY_AUTHTOKEN, response.auth); - result.putString(KEY_ACCOUNT_NAME, account.name); - result.putString(KEY_ACCOUNT_TYPE, account.type); - result.putString(KEY_ANDROID_PACKAGE_NAME, packageName); - result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); - setAccountAuthenticatorResult(result); - } catch (IOException e) { - Log.w(TAG, e); - } - finish(); - + new Thread(() -> { + try { + AuthResponse response = authManager.requestAuth(fromAccountManager); + Bundle result = new Bundle(); + result.putString(KEY_AUTHTOKEN, response.auth); + result.putString(KEY_ACCOUNT_NAME, account.name); + result.putString(KEY_ACCOUNT_TYPE, account.type); + result.putString(KEY_ANDROID_PACKAGE_NAME, packageName); + result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); + setAccountAuthenticatorResult(result); + } catch (IOException e) { + Log.w(TAG, e); } + finish(); }).start(); } @@ -189,8 +164,10 @@ public class AskPermissionActivity extends AccountAuthenticatorActivity { @Override public void finish() { - NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(packageName.hashCode()); + if (packageName != null) { + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel(packageName.hashCode()); + } super.finish(); } From 6e176cceed88214d9c797e74af8e30e98fd0e87a Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 6 Jan 2021 12:14:53 +0100 Subject: [PATCH 05/29] EN: Move permission to not interfere with existing permission when used as a library --- play-services-core/src/main/AndroidManifest.xml | 5 +++++ play-services-nearby-core/src/main/AndroidManifest.xml | 10 ++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 56a4269b..93d8a6c5 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -56,6 +56,11 @@ android:description="@string/permission_service_writely_description" android:label="@string/permission_service_writely_label" android:protectionLevel="dangerous" /> + + + - - - - + + + + From 11a86d9169677e39578d52727597c7a680f16580 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Fri, 8 Jan 2021 15:43:05 +0100 Subject: [PATCH 06/29] EN: Display historgram of collected IDs using hourly heat map --- play-services-nearby-core-ui/build.gradle | 2 - .../src/main/AndroidManifest.xml | 2 - .../gms/nearby/core/ui/BarChartPreference.kt | 56 ------- .../gms/nearby/core/ui/DotChartPreference.kt | 39 +++++ .../microg/gms/nearby/core/ui/DotChartView.kt | 157 ++++++++++++++++++ .../ui/ExposureNotificationsRpisFragment.kt | 41 +---- .../main/res/layout/preference_bar_chart.xml | 18 -- .../main/res/layout/preference_dot_chart.xml | 12 ++ ...references_exposure_notifications_rpis.xml | 9 +- .../exposurenotification/ExposureDatabase.kt | 21 +-- .../exposurenotification/MeasuredExposure.kt | 2 + 11 files changed, 225 insertions(+), 134 deletions(-) delete mode 100644 play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/BarChartPreference.kt create mode 100644 play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartPreference.kt create mode 100644 play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt delete mode 100644 play-services-nearby-core-ui/src/main/res/layout/preference_bar_chart.xml create mode 100644 play-services-nearby-core-ui/src/main/res/layout/preference_dot_chart.xml diff --git a/play-services-nearby-core-ui/build.gradle b/play-services-nearby-core-ui/build.gradle index 38c65af0..6ad704ad 100644 --- a/play-services-nearby-core-ui/build.gradle +++ b/play-services-nearby-core-ui/build.gradle @@ -14,8 +14,6 @@ dependencies { implementation project(':play-services-nearby-core') implementation project(':play-services-base-core-ui') - implementation "com.diogobernardino:williamchart:3.7.1" - // AndroidX UI implementation "androidx.multidex:multidex:$multidexVersion" implementation "androidx.appcompat:appcompat:$appcompatVersion" diff --git a/play-services-nearby-core-ui/src/main/AndroidManifest.xml b/play-services-nearby-core-ui/src/main/AndroidManifest.xml index d7ac93e7..fd0cbb27 100644 --- a/play-services-nearby-core-ui/src/main/AndroidManifest.xml +++ b/play-services-nearby-core-ui/src/main/AndroidManifest.xml @@ -7,8 +7,6 @@ xmlns:tools="http://schemas.android.com/tools" package="org.microg.gms.nearby.core.ui"> - - diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/BarChartPreference.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/BarChartPreference.kt deleted file mode 100644 index c887c223..00000000 --- a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/BarChartPreference.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.microg.gms.nearby.core.ui - -import android.content.Context -import android.util.AttributeSet -import androidx.preference.Preference -import androidx.preference.PreferenceViewHolder -import com.db.williamchart.data.Scale -import com.db.williamchart.view.BarChartView - -class BarChartPreference : Preference { - constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) - constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) - constructor(context: Context?) : super(context) - - init { - layoutResource = R.layout.preference_bar_chart - } - - private lateinit var chart: BarChartView - var labelsFormatter: (Float) -> String = { it.toString() } - set(value) { - field = value - if (this::chart.isInitialized) { - chart.labelsFormatter = value - } - } - var scale: Scale? = null - set(value) { - field = value - if (value != null && this::chart.isInitialized) { - chart.scale = value - } - } - var data: LinkedHashMap = linkedMapOf() - set(value) { - field = value - if (this::chart.isInitialized) { - chart.animate(data) - } - } - - override fun onBindViewHolder(holder: PreferenceViewHolder) { - super.onBindViewHolder(holder) - chart = holder.itemView as? BarChartView ?: holder.findViewById(R.id.bar_chart) as BarChartView - chart.labelsFormatter = labelsFormatter - scale?.let { chart.scale = it } - chart.animate(data) - } - -} diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartPreference.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartPreference.kt new file mode 100644 index 00000000..2bdb12e3 --- /dev/null +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartPreference.kt @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.nearby.core.ui + +import android.content.Context +import android.util.AttributeSet +import androidx.preference.Preference +import androidx.preference.PreferenceViewHolder +import org.microg.gms.nearby.exposurenotification.ExposureScanSummary + +class DotChartPreference : Preference { + constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context?) : super(context) + + init { + layoutResource = R.layout.preference_dot_chart + } + + private lateinit var chart: DotChartView + var data: Set = emptySet() + set(value) { + field = value + if (this::chart.isInitialized) { + chart.data = data + } + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + chart = holder.itemView as? DotChartView ?: holder.findViewById(R.id.dot_chart) as DotChartView + chart.data = data + } + +} diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt new file mode 100644 index 00000000..dbcd4f95 --- /dev/null +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt @@ -0,0 +1,157 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.nearby.core.ui + +import android.annotation.SuppressLint +import android.annotation.TargetApi +import android.content.Context +import android.content.res.TypedArray +import android.graphics.* +import android.provider.Settings +import android.text.TextUtils +import android.util.AttributeSet +import android.util.Log +import android.util.TypedValue +import android.view.View +import org.microg.gms.nearby.exposurenotification.ExposureScanSummary +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.max + + +class DotChartView : View { + @TargetApi(21) + constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) + constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context?) : super(context) + + var data: Set? = null + @SuppressLint("SimpleDateFormat") + set(value) { + field = value + val displayData = hashMapOf>>() + val now = System.currentTimeMillis() + val min = now - 14 * 24 * 60 * 60 * 1000L + val date = Date(min) + val format = Settings.System.getString(context.contentResolver, Settings.System.DATE_FORMAT); + val dateFormat = if (TextUtils.isEmpty(format)) { + android.text.format.DateFormat.getMediumDateFormat(context) + } else { + SimpleDateFormat(format) + } + val lowest = dateFormat.parse(dateFormat.format(date))?.time ?: date.time + for (day in 0 until 15) { + date.time = now - (14 - day) * 24 * 60 * 60 * 1000L + displayData[day] = dateFormat.format(date) to hashMapOf() + } + if (value != null) { + for (summary in value) { + val off = summary.time - lowest + if (off < 0) continue + val totalHours = (off / 1000 / 60 / 60).toInt() + val day = totalHours / 24 + val hour = totalHours % 24 + displayData[day]?.second?.set(hour, (displayData[day]?.second?.get(hour) ?: 0) + summary.rpis) + } + } + for (hour in 0..((min-lowest)/1000/60/60).toInt()) { + displayData[0]?.second?.set(hour, displayData[0]?.second?.get(hour) ?: -1) + } + for (hour in ((min-lowest)/1000/60/60).toInt() until 24) { + displayData[14]?.second?.set(hour, displayData[14]?.second?.get(hour) ?: -1) + } + this.displayData = displayData + invalidate() + } + + private var displayData: Map>> = emptyMap() + private val paint = Paint() + private val tempRect = Rect() + private val tempRectF = RectF() + + private fun fetchAccentColor(): Int { + val typedValue = TypedValue() + val a: TypedArray = context.obtainStyledAttributes(typedValue.data, intArrayOf(androidx.appcompat.R.attr.colorAccent)) + val color = a.getColor(0, 0) + a.recycle() + return color + } + + override fun onDraw(canvas: Canvas) { + if (data == null) data = emptySet() + paint.textSize = 10 * resources.displayMetrics.scaledDensity + paint.isAntiAlias = true + paint.strokeWidth = 2f + var maxTextWidth = 0 + var maxTextHeight = 0 + for (dateString in displayData.values.map { it.first }) { + paint.getTextBounds(dateString, 0, dateString.length, tempRect) + maxTextWidth = max(maxTextWidth, tempRect.width()) + maxTextHeight = max(maxTextHeight, tempRect.height()) + } + + val legendLeft = maxTextWidth + 4 * resources.displayMetrics.scaledDensity + val legendBottom = maxTextHeight + 4 * resources.displayMetrics.scaledDensity + + val distHeight = (height - 28 - paddingTop - paddingBottom - legendBottom).toDouble() + val distWidth = (width - 46 - paddingLeft - paddingRight - legendLeft).toDouble() + val perHeight = distHeight / 15.0 + val perWidth = distWidth / 24.0 + + paint.textAlign = Paint.Align.RIGHT + val maxValue = displayData.values.mapNotNull { it.second.values.maxOrNull() }.maxOrNull() ?: 0 + val accentColor = fetchAccentColor() + val accentRed = Color.red(accentColor) + val accentGreen = Color.green(accentColor) + val accentBlue = Color.blue(accentColor) + for (day in 0 until 15) { + val (dateString, hours) = displayData[day] ?: "" to emptyMap() + val top = day * (perHeight + 2) + paddingTop + if (day % 2 == 0) { + paint.setARGB(255, 100, 100, 100) + canvas.drawText(dateString, (paddingLeft + legendLeft - 4 * resources.displayMetrics.scaledDensity), (top + perHeight / 2.0 + maxTextHeight / 2.0).toFloat(), paint) + } + for (hour in 0 until 24) { + val value = hours[hour] ?: 0 // TODO: Actually allow null to display offline state as soon as we properly record it + val left = hour * (perWidth + 2) + paddingLeft + legendLeft + tempRectF.set(left.toFloat() + 2f, top.toFloat() + 2f, (left + perWidth).toFloat() - 2f, (top + perHeight).toFloat() - 2f) + when { + value == null -> { + paint.style = Paint.Style.FILL_AND_STROKE + paint.setARGB(30, 100, 100, 100) + canvas.drawRoundRect(tempRectF, 2f, 2f, paint) + paint.style = Paint.Style.FILL + } + maxValue == 0 -> { + paint.setARGB(50, accentRed, accentGreen, accentBlue) + paint.style = Paint.Style.STROKE + canvas.drawRoundRect(tempRectF, 2f, 2f, paint) + paint.style = Paint.Style.FILL + } + value >= 0 -> { + val alpha = ((value.toDouble() / maxValue.toDouble()) * 255).toInt() + paint.setARGB(max(50, alpha), accentRed, accentGreen, accentBlue) + paint.style = Paint.Style.STROKE + canvas.drawRoundRect(tempRectF, 2f, 2f, paint) + paint.style = Paint.Style.FILL + paint.setARGB(alpha, accentRed, accentGreen, accentBlue) + canvas.drawRoundRect(tempRectF, 2f, 2f, paint) + } + } + } + } + val legendTop = 15 * (perHeight + 2) + paddingTop + maxTextHeight + 4 * resources.displayMetrics.scaledDensity + paint.textAlign = Paint.Align.CENTER + paint.setARGB(255, 100, 100, 100) + for (hour in 0 until 24) { + if (hour % 3 == 0) { + val left = hour * (perWidth + 2) + paddingLeft + legendLeft + perWidth / 2.0 + canvas.drawText("${hour}:00", left.toFloat(), legendTop.toFloat(), paint) + } + } + } +} diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt index d643a1c3..339bc88a 100644 --- a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/ExposureNotificationsRpisFragment.kt @@ -7,22 +7,17 @@ package org.microg.gms.nearby.core.ui import android.annotation.TargetApi import android.os.Bundle -import android.text.format.DateFormat import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat -import com.db.williamchart.data.Scale import org.microg.gms.nearby.exposurenotification.ExposureDatabase -import java.util.* -import kotlin.math.roundToInt -import kotlin.math.roundToLong @TargetApi(21) class ExposureNotificationsRpisFragment : PreferenceFragmentCompat() { private lateinit var histogramCategory: PreferenceCategory - private lateinit var histogram: BarChartPreference + private lateinit var histogram: DotChartPreference private lateinit var deleteAll: Preference private lateinit var exportDb: Preference @@ -63,37 +58,11 @@ class ExposureNotificationsRpisFragment : PreferenceFragmentCompat() { fun updateChart() { lifecycleScope.launchWhenResumed { - val (totalRpiCount, rpiHistogram) = ExposureDatabase.with(requireContext()) { database -> - val map = linkedMapOf() - val lowestDate = (System.currentTimeMillis() / 24 / 60 / 60 / 1000 - 13).toDouble().roundToLong() * 24 * 60 * 60 * 1000 - for (i in 0..13) { - val date = Calendar.getInstance().apply { this.time = Date(lowestDate + i * 24 * 60 * 60 * 1000) }.get(Calendar.DAY_OF_MONTH) - val str = when (i) { - 0, 13 -> DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "MMMd"), lowestDate + i * 24 * 60 * 60 * 1000).toString() - else -> IntArray(date).joinToString("").replace("0", "\u200B") - } - map[str] = 0f - } - val refDateLow = Calendar.getInstance().apply { this.time = Date(lowestDate) }.get(Calendar.DAY_OF_MONTH) - val refDateHigh = Calendar.getInstance().apply { this.time = Date(lowestDate + 13 * 24 * 60 * 60 * 1000) }.get(Calendar.DAY_OF_MONTH) - for (entry in database.rpiHistogram) { - val time = Date(entry.key * 24 * 60 * 60 * 1000) - if (time.time < lowestDate) continue // Ignore old data - val date = Calendar.getInstance().apply { this.time = time }.get(Calendar.DAY_OF_MONTH) - val str = when (date) { - refDateLow, refDateHigh -> DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "MMMd"), entry.key * 24 * 60 * 60 * 1000).toString() - else -> IntArray(date).joinToString("").replace("0", "\u200B") - } - map[str] = entry.value.toFloat() - } - val totalRpiCount = database.totalRpiCount - totalRpiCount to map - } - deleteAll.isEnabled = totalRpiCount != 0L + val rpiHourHistogram = ExposureDatabase.with(requireContext()) { database -> database.rpiHourHistogram } + val totalRpiCount = rpiHourHistogram.map { it.rpis }.sum() + deleteAll.isEnabled = totalRpiCount > 0 histogramCategory.title = getString(R.string.prefcat_exposure_rpis_histogram_title, totalRpiCount) - histogram.labelsFormatter = { it.roundToInt().toString() } - histogram.scale = Scale(0f, rpiHistogram.values.max()?.coerceAtLeast(0.1f) ?: 0.1f) - histogram.data = rpiHistogram + histogram.data = rpiHourHistogram } } } diff --git a/play-services-nearby-core-ui/src/main/res/layout/preference_bar_chart.xml b/play-services-nearby-core-ui/src/main/res/layout/preference_bar_chart.xml deleted file mode 100644 index 51b45d33..00000000 --- a/play-services-nearby-core-ui/src/main/res/layout/preference_bar_chart.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/play-services-nearby-core-ui/src/main/res/layout/preference_dot_chart.xml b/play-services-nearby-core-ui/src/main/res/layout/preference_dot_chart.xml new file mode 100644 index 00000000..574a335e --- /dev/null +++ b/play-services-nearby-core-ui/src/main/res/layout/preference_dot_chart.xml @@ -0,0 +1,12 @@ + + + + diff --git a/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml b/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml index 37168c0e..66658dec 100644 --- a/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml +++ b/play-services-nearby-core-ui/src/main/res/xml/preferences_exposure_notifications_rpis.xml @@ -9,15 +9,16 @@ - + android:selectable="false" + tools:layout="@layout/preference_dot_chart" /> + android:summary="@string/pref_exposure_rpi_export_summary" + android:title="@string/pref_exposure_rpi_export_title" /> + val rpiHourHistogram: Set get() = readableDatabase.run { - rawQuery("SELECT round(timestamp/(24*60*60*1000)), COUNT(*) FROM $TABLE_ADVERTISEMENTS WHERE timestamp > ? GROUP BY round(timestamp/(24*60*60*1000)) ORDER BY timestamp ASC;", arrayOf((Date().time - (14 * 24 * 60 * 60 * 1000)).toString())).use { cursor -> - val map = linkedMapOf() + rawQuery("SELECT round(timestamp/(60*60*1000))*60*60*1000, COUNT(*), COUNT(*) FROM $TABLE_ADVERTISEMENTS WHERE timestamp > ? GROUP BY round(timestamp/(60*60*1000)) ORDER BY timestamp ASC;", arrayOf((System.currentTimeMillis() - (14 * 24 * 60 * 60 * 1000L)).toString())).use { cursor -> + val set = hashSetOf() while (cursor.moveToNext()) { - map[cursor.getLong(0)] = cursor.getLong(1) - } - map - } - } - - val totalRpiCount: Long - get() = readableDatabase.run { - rawQuery("SELECT COUNT(*) FROM $TABLE_ADVERTISEMENTS WHERE timestamp > ?;", arrayOf((Date().time - (14 * 24 * 60 * 60 * 1000)).toString())).use { cursor -> - if (cursor.moveToNext()) { - cursor.getLong(0) - } else { - 0L + set.add(ExposureScanSummary(cursor.getLong(0), cursor.getInt(1), cursor.getInt(2))) } + set } } diff --git a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/MeasuredExposure.kt b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/MeasuredExposure.kt index 456738d4..4bb7f2de 100644 --- a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/MeasuredExposure.kt +++ b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/MeasuredExposure.kt @@ -9,6 +9,8 @@ import android.util.Log import com.google.android.gms.nearby.exposurenotification.* import java.util.concurrent.TimeUnit +data class ExposureScanSummary(val time: Long, val rpis: Int, val records: Int) + data class PlainExposure(val rpi: ByteArray, val aem: ByteArray, val timestamp: Long, val duration: Long, val rssi: Int) data class MeasuredExposure(val timestamp: Long, val duration: Long, val rssi: Int, val txPower: Int, @CalibrationConfidence val confidence: Int, val key: TemporaryExposureKey) { From 65032fbb3cfb196fb1755354796fcf13c07eeaa2 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Fri, 8 Jan 2021 19:34:33 +0100 Subject: [PATCH 07/29] EN-UI: Fix chart in dark mode, add legend --- .../microg/gms/nearby/core/ui/DotChartView.kt | 131 ++++++++++-------- 1 file changed, 76 insertions(+), 55 deletions(-) diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt index dbcd4f95..dff5f504 100644 --- a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt @@ -8,15 +8,13 @@ package org.microg.gms.nearby.core.ui import android.annotation.SuppressLint import android.annotation.TargetApi import android.content.Context -import android.content.res.TypedArray import android.graphics.* import android.provider.Settings import android.text.TextUtils import android.util.AttributeSet -import android.util.Log -import android.util.TypedValue import android.view.View import org.microg.gms.nearby.exposurenotification.ExposureScanSummary +import org.microg.gms.ui.resolveColor import java.text.SimpleDateFormat import java.util.* import kotlin.math.max @@ -58,10 +56,10 @@ class DotChartView : View { displayData[day]?.second?.set(hour, (displayData[day]?.second?.get(hour) ?: 0) + summary.rpis) } } - for (hour in 0..((min-lowest)/1000/60/60).toInt()) { + for (hour in 0..((min - lowest) / 1000 / 60 / 60).toInt()) { displayData[0]?.second?.set(hour, displayData[0]?.second?.get(hour) ?: -1) } - for (hour in ((min-lowest)/1000/60/60).toInt() until 24) { + for (hour in ((min - lowest) / 1000 / 60 / 60).toInt() until 24) { displayData[14]?.second?.set(hour, displayData[14]?.second?.get(hour) ?: -1) } this.displayData = displayData @@ -69,89 +67,112 @@ class DotChartView : View { } private var displayData: Map>> = emptyMap() - private val paint = Paint() - private val tempRect = Rect() - private val tempRectF = RectF() + private val drawPaint = Paint() + private val drawTempRect = RectF() + private val fontPaint = Paint() + private val fontTempRect = Rect() - private fun fetchAccentColor(): Int { - val typedValue = TypedValue() - val a: TypedArray = context.obtainStyledAttributes(typedValue.data, intArrayOf(androidx.appcompat.R.attr.colorAccent)) - val color = a.getColor(0, 0) - a.recycle() - return color + fun Canvas.drawMyRect(x: Float, y: Float, width: Float, height: Float, color: Int) { + drawTempRect.set(x + drawPaint.strokeWidth, y + drawPaint.strokeWidth, x + width - drawPaint.strokeWidth, y + height - drawPaint.strokeWidth) + if (Color.alpha(color) >= 80) { + drawPaint.style = Paint.Style.FILL_AND_STROKE + drawPaint.color = color + drawRoundRect(drawTempRect, 2f, 2f, drawPaint) + drawPaint.style = Paint.Style.FILL + } else { + drawPaint.color = color or (80 shl 24) and (80 shl 24 or 0xffffff) + drawPaint.style = Paint.Style.STROKE + drawRoundRect(drawTempRect, 2f, 2f, drawPaint) + drawPaint.style = Paint.Style.FILL + drawPaint.color = color + drawRoundRect(drawTempRect, 2f, 2f, drawPaint) + } } override fun onDraw(canvas: Canvas) { if (data == null) data = emptySet() - paint.textSize = 10 * resources.displayMetrics.scaledDensity - paint.isAntiAlias = true - paint.strokeWidth = 2f + val d = resources.displayMetrics.scaledDensity + fontPaint.textSize = 10 * d + fontPaint.isAntiAlias = true + drawPaint.isAntiAlias = true + drawPaint.strokeWidth = 2f + val innerPadding = 2 * d var maxTextWidth = 0 var maxTextHeight = 0 for (dateString in displayData.values.map { it.first }) { - paint.getTextBounds(dateString, 0, dateString.length, tempRect) - maxTextWidth = max(maxTextWidth, tempRect.width()) - maxTextHeight = max(maxTextHeight, tempRect.height()) + fontPaint.getTextBounds(dateString, 0, dateString.length, fontTempRect) + maxTextWidth = max(maxTextWidth, fontTempRect.width()) + maxTextHeight = max(maxTextHeight, fontTempRect.height()) } - val legendLeft = maxTextWidth + 4 * resources.displayMetrics.scaledDensity - val legendBottom = maxTextHeight + 4 * resources.displayMetrics.scaledDensity + val legendLeft = maxTextWidth + 4 * d + val legendBottom = maxTextHeight + 4 * d + val subHeight = maxTextHeight + 4 * d + paddingBottom - val distHeight = (height - 28 - paddingTop - paddingBottom - legendBottom).toDouble() - val distWidth = (width - 46 - paddingLeft - paddingRight - legendLeft).toDouble() + val distHeight = (height - innerPadding * 14 - paddingTop - paddingBottom - legendBottom - subHeight).toDouble() + val distWidth = (width - innerPadding * 23 - paddingLeft - paddingRight - legendLeft).toDouble() val perHeight = distHeight / 15.0 val perWidth = distWidth / 24.0 - paint.textAlign = Paint.Align.RIGHT val maxValue = displayData.values.mapNotNull { it.second.values.maxOrNull() }.maxOrNull() ?: 0 - val accentColor = fetchAccentColor() - val accentRed = Color.red(accentColor) - val accentGreen = Color.green(accentColor) - val accentBlue = Color.blue(accentColor) + val averageValue = (displayData.values.mapNotNull { it.second.values.average().takeIf { !it.isNaN() } }.average().takeIf { !it.isNaN() } + ?: 0.0).toInt() + val accentColor = context.resolveColor(androidx.appcompat.R.attr.colorAccent) ?: 0 + val fontColor = context.resolveColor(android.R.attr.textColorSecondary) ?: 0 + val grayBoxColor = fontColor or (255 shl 24) and (80 shl 24 or 0xffffff) + fontPaint.textAlign = Paint.Align.RIGHT + fontPaint.color = fontColor for (day in 0 until 15) { val (dateString, hours) = displayData[day] ?: "" to emptyMap() - val top = day * (perHeight + 2) + paddingTop + val top = day * (perHeight + innerPadding) + paddingTop if (day % 2 == 0) { - paint.setARGB(255, 100, 100, 100) - canvas.drawText(dateString, (paddingLeft + legendLeft - 4 * resources.displayMetrics.scaledDensity), (top + perHeight / 2.0 + maxTextHeight / 2.0).toFloat(), paint) + canvas.drawText(dateString, (paddingLeft + legendLeft - 4 * d), (top + perHeight / 2.0 + maxTextHeight / 2.0).toFloat(), fontPaint) } for (hour in 0 until 24) { - val value = hours[hour] ?: 0 // TODO: Actually allow null to display offline state as soon as we properly record it - val left = hour * (perWidth + 2) + paddingLeft + legendLeft - tempRectF.set(left.toFloat() + 2f, top.toFloat() + 2f, (left + perWidth).toFloat() - 2f, (top + perHeight).toFloat() - 2f) + val value = hours[hour] + val left = hour * (perWidth + innerPadding) + paddingLeft + legendLeft when { value == null -> { - paint.style = Paint.Style.FILL_AND_STROKE - paint.setARGB(30, 100, 100, 100) - canvas.drawRoundRect(tempRectF, 2f, 2f, paint) - paint.style = Paint.Style.FILL + canvas.drawMyRect(left.toFloat(), top.toFloat(), perWidth.toFloat(), perHeight.toFloat(), grayBoxColor) } maxValue == 0 -> { - paint.setARGB(50, accentRed, accentGreen, accentBlue) - paint.style = Paint.Style.STROKE - canvas.drawRoundRect(tempRectF, 2f, 2f, paint) - paint.style = Paint.Style.FILL + canvas.drawMyRect(left.toFloat(), top.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and 0xffffff) } value >= 0 -> { - val alpha = ((value.toDouble() / maxValue.toDouble()) * 255).toInt() - paint.setARGB(max(50, alpha), accentRed, accentGreen, accentBlue) - paint.style = Paint.Style.STROKE - canvas.drawRoundRect(tempRectF, 2f, 2f, paint) - paint.style = Paint.Style.FILL - paint.setARGB(alpha, accentRed, accentGreen, accentBlue) - canvas.drawRoundRect(tempRectF, 2f, 2f, paint) + val alpha = if (value < averageValue) { + value.toDouble() / averageValue.toDouble() * 127 + } else { + (value - averageValue).toDouble() / (maxValue - averageValue).toDouble() * 128 + 127 + }.toInt() + canvas.drawMyRect(left.toFloat(), top.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (alpha shl 24 or 0xffffff)) } } } } - val legendTop = 15 * (perHeight + 2) + paddingTop + maxTextHeight + 4 * resources.displayMetrics.scaledDensity - paint.textAlign = Paint.Align.CENTER - paint.setARGB(255, 100, 100, 100) + val legendTop = 15 * (perHeight + innerPadding) + paddingTop + maxTextHeight + 4 * d + fontPaint.textAlign = Paint.Align.CENTER for (hour in 0 until 24) { if (hour % 3 == 0) { - val left = hour * (perWidth + 2) + paddingLeft + legendLeft + perWidth / 2.0 - canvas.drawText("${hour}:00", left.toFloat(), legendTop.toFloat(), paint) + val left = hour * (perWidth + innerPadding) + paddingLeft + legendLeft + perWidth / 2.0 + canvas.drawText("${hour}:00", left.toFloat(), legendTop.toFloat(), fontPaint) } } + + val subTop = legendTop + paddingBottom + val subLeft = paddingLeft + legendLeft + + canvas.drawMyRect(subLeft, subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), grayBoxColor) + + val strNoRecords = "No records" + fontPaint.textAlign = Paint.Align.LEFT + fontPaint.getTextBounds(strNoRecords, 0, strNoRecords.length, fontTempRect) + canvas.drawText(strNoRecords, (subLeft + perWidth + 4 * d).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) + + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 1 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and 0xffffff) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 2 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (128 shl 24 or 0xffffff)) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 3 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor) + + val strRecords = "0 / $averageValue / $maxValue IDs per hour" + canvas.drawText(strRecords, (subLeft + (perWidth + innerPadding) * 3 + 16 * d + fontTempRect.width() + perWidth).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) } } From 2f29b93a9999e05018877ac3e7a424f1ee71db19 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 9 Jan 2021 14:39:08 +0100 Subject: [PATCH 08/29] EN-UI: Make legend translateable --- .../main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt | 4 ++-- .../src/main/res/values-de/strings.xml | 2 ++ play-services-nearby-core-ui/src/main/res/values/strings.xml | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt index dff5f504..ab4882c5 100644 --- a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt @@ -163,7 +163,7 @@ class DotChartView : View { canvas.drawMyRect(subLeft, subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), grayBoxColor) - val strNoRecords = "No records" + val strNoRecords = context.getString(R.string.pref_exposure_rpis_histogram_legend_no_records) fontPaint.textAlign = Paint.Align.LEFT fontPaint.getTextBounds(strNoRecords, 0, strNoRecords.length, fontTempRect) canvas.drawText(strNoRecords, (subLeft + perWidth + 4 * d).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) @@ -172,7 +172,7 @@ class DotChartView : View { canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 2 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (128 shl 24 or 0xffffff)) canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 3 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor) - val strRecords = "0 / $averageValue / $maxValue IDs per hour" + val strRecords = context.getString(R.string.pref_exposure_rpis_histogram_legend_records, 0, averageValue, maxValue) canvas.drawText(strRecords, (subLeft + (perWidth + innerPadding) * 3 + 16 * d + fontTempRect.width() + perWidth).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) } } diff --git a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml index a80b38f4..cb705e43 100644 --- a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml @@ -30,6 +30,8 @@ Nutzung der API in den letzten 14 Tagen %1$d Aufrufe von %2$s %1$d gesammelte IDs + Keine Daten + %1$d / %2$d / %3$d IDs pro Stunde Löschen Alle gesammelten IDs löschen Nach dem Löschen der gesammelten IDs kannst du nicht mehr informiert werden, falls einer deiner Kontakte der letzten 14 Tage positiv getested wurde. diff --git a/play-services-nearby-core-ui/src/main/res/values/strings.xml b/play-services-nearby-core-ui/src/main/res/values/strings.xml index 8b14db24..e639afd3 100644 --- a/play-services-nearby-core-ui/src/main/res/values/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values/strings.xml @@ -40,6 +40,8 @@ API usage in the last 14 days %1$d calls to %2$s %1$d IDs collected + No records + %1$d / %2$d / %3$d IDs per hour Delete Delete all collected IDs Deleting collected IDs will make it impossible to notify you in case any of your contacts of the last 14 days is diagnosed. From 2fe12d8788b7035927a063b8f5ddd31163e5d723 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Mon, 11 Jan 2021 21:04:08 +0100 Subject: [PATCH 09/29] EN-UI: Add popup to ID chart --- .../microg/gms/nearby/core/ui/DotChartView.kt | 126 ++++++++++++++++-- .../src/main/res/values-de/strings.xml | 2 +- .../src/main/res/values/strings.xml | 2 +- 3 files changed, 116 insertions(+), 14 deletions(-) diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt index ab4882c5..9946dc3b 100644 --- a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt @@ -9,15 +9,18 @@ import android.annotation.SuppressLint import android.annotation.TargetApi import android.content.Context import android.graphics.* +import android.os.Build import android.provider.Settings import android.text.TextUtils import android.util.AttributeSet +import android.view.MotionEvent import android.view.View import org.microg.gms.nearby.exposurenotification.ExposureScanSummary import org.microg.gms.ui.resolveColor import java.text.SimpleDateFormat import java.util.* import kotlin.math.max +import kotlin.math.min class DotChartView : View { @@ -63,10 +66,12 @@ class DotChartView : View { displayData[14]?.second?.set(hour, displayData[14]?.second?.get(hour) ?: -1) } this.displayData = displayData + this.displayDataList = displayData.values.map { it.second.values }.flatten().sorted() invalidate() } private var displayData: Map>> = emptyMap() + private var displayDataList: List = emptyList() private val drawPaint = Paint() private val drawTempRect = RectF() private val fontPaint = Paint() @@ -89,6 +94,9 @@ class DotChartView : View { } } + private fun List.relativePosition(element: T): Double? = indexOf(element).takeIf { it >= 0 }?.toDouble()?.let { it / size.toDouble() } + + private var focusPoint: PointF? = null override fun onDraw(canvas: Canvas) { if (data == null) data = emptySet() val d = resources.displayMetrics.scaledDensity @@ -114,20 +122,21 @@ class DotChartView : View { val perHeight = distHeight / 15.0 val perWidth = distWidth / 24.0 - val maxValue = displayData.values.mapNotNull { it.second.values.maxOrNull() }.maxOrNull() ?: 0 - val averageValue = (displayData.values.mapNotNull { it.second.values.average().takeIf { !it.isNaN() } }.average().takeIf { !it.isNaN() } - ?: 0.0).toInt() + val maxValue = displayDataList.last() val accentColor = context.resolveColor(androidx.appcompat.R.attr.colorAccent) ?: 0 val fontColor = context.resolveColor(android.R.attr.textColorSecondary) ?: 0 val grayBoxColor = fontColor or (255 shl 24) and (80 shl 24 or 0xffffff) fontPaint.textAlign = Paint.Align.RIGHT fontPaint.color = fontColor + var focusDay = -1 + var focusHour = -1 for (day in 0 until 15) { val (dateString, hours) = displayData[day] ?: "" to emptyMap() val top = day * (perHeight + innerPadding) + paddingTop if (day % 2 == 0) { canvas.drawText(dateString, (paddingLeft + legendLeft - 4 * d), (top + perHeight / 2.0 + maxTextHeight / 2.0).toFloat(), fontPaint) } + focusPoint?.let { if (it.y > top && it.y < top + perHeight) focusDay = day } for (hour in 0 until 24) { val value = hours[hour] val left = hour * (perWidth + innerPadding) + paddingLeft + legendLeft @@ -139,14 +148,18 @@ class DotChartView : View { canvas.drawMyRect(left.toFloat(), top.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and 0xffffff) } value >= 0 -> { - val alpha = if (value < averageValue) { - value.toDouble() / averageValue.toDouble() * 127 - } else { - (value - averageValue).toDouble() / (maxValue - averageValue).toDouble() * 128 + 127 - }.toInt() + val byBucket = ((displayDataList.relativePosition(value) ?: 0.0) * 180.0).toInt() + val byMax = (value.toDouble() / maxValue.toDouble() * 80.0).toInt() + val alpha = max(0, min(byBucket + byMax, 255)) canvas.drawMyRect(left.toFloat(), top.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (alpha shl 24 or 0xffffff)) } } + if (focusDay == day && (value == null || value >= 0)) focusPoint?.let { + if (it.x > left && it.x < left + perWidth) { + focusHour = hour + canvas.drawMyRect(left.toFloat(), top.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and 0xffffff) + } + } } } val legendTop = 15 * (perHeight + innerPadding) + paddingTop + maxTextHeight + 4 * d @@ -169,10 +182,99 @@ class DotChartView : View { canvas.drawText(strNoRecords, (subLeft + perWidth + 4 * d).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 1 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and 0xffffff) - canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 2 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (128 shl 24 or 0xffffff)) - canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 3 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 2 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (80 shl 24 or 0xffffff)) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 3 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (170 shl 24 or 0xffffff)) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 4 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor) - val strRecords = context.getString(R.string.pref_exposure_rpis_histogram_legend_records, 0, averageValue, maxValue) - canvas.drawText(strRecords, (subLeft + (perWidth + innerPadding) * 3 + 16 * d + fontTempRect.width() + perWidth).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) + val strRecords = context.getString(R.string.pref_exposure_rpis_histogram_legend_records, "0 - $maxValue") + canvas.drawText(strRecords, (subLeft + (perWidth + innerPadding) * 4 + 16 * d + fontTempRect.width() + perWidth).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) + + if (focusHour != -1 && Build.VERSION.SDK_INT >= 23) { + val floatingColor = context.resolveColor(androidx.appcompat.R.attr.colorBackgroundFloating) ?: 0 + val line1 = "${displayData[focusDay]?.first}, $focusHour:00" + val line2 = displayData[focusDay]?.second?.get(focusHour)?.let { context.getString(R.string.pref_exposure_rpis_histogram_legend_records, it.toString()) } + ?: strNoRecords + fontPaint.textSize = 14 * d + fontPaint.isFakeBoldText = true + fontPaint.getTextBounds(line1, 0, line1.length, fontTempRect) + var fontWidth = fontTempRect.width() + val line1Height = fontTempRect.height() + innerPadding + fontPaint.isFakeBoldText = false + fontPaint.getTextBounds(line2, 0, line2.length, fontTempRect) + fontWidth = max(fontWidth, fontTempRect.width()) + val totalHeight = line1Height + innerPadding + fontTempRect.height() + drawPaint.color = floatingColor + drawPaint.style = Paint.Style.FILL_AND_STROKE + val refTop = focusDay * (perHeight + innerPadding) + paddingTop + perHeight / 2 + val refLeft = focusHour * (perWidth + innerPadding) + paddingLeft + legendLeft + if (refLeft - fontWidth < 50 * d) { + // To the right + drawTempRect.set((refLeft + perWidth + innerPadding).toFloat(), (refTop - innerPadding - 5 * d - totalHeight / 2f).toFloat(), (refLeft + perWidth + innerPadding + 10 * d + fontWidth).toFloat(), (refTop + innerPadding + 5 * d + totalHeight / 2f).toFloat()) + } else { + // To the left + drawTempRect.set((refLeft - innerPadding - 10 * d - fontWidth).toFloat(), (refTop - innerPadding - 5 * d - totalHeight / 2f).toFloat(), (refLeft - innerPadding).toFloat(), (refTop + innerPadding + 5 * d + totalHeight / 2f).toFloat()) + } + canvas.drawRoundRect(drawTempRect, drawPaint.strokeWidth, drawPaint.strokeWidth, drawPaint) + val path = Path() + if (refLeft - fontWidth < 50 * d) { + // To the right + val off = refLeft + perWidth + innerPadding + drawPaint.strokeWidth + val corr = (perWidth / 2 - innerPadding + drawPaint.strokeWidth) + path.moveTo(off.toFloat(), (refTop - corr).toFloat()) + path.lineTo((refLeft + perWidth / 2).toFloat(), refTop.toFloat()) + path.lineTo(off.toFloat(), (refTop + corr).toFloat()) + } else { + // To the left + val off = refLeft - innerPadding - drawPaint.strokeWidth + val corr = (perWidth / 2 - innerPadding + drawPaint.strokeWidth) + path.moveTo(off.toFloat(), (refTop - corr).toFloat()) + path.lineTo((refLeft + perWidth / 2).toFloat(), refTop.toFloat()) + path.lineTo(off.toFloat(), (refTop + corr).toFloat()) + } + drawPaint.style = Paint.Style.STROKE + drawPaint.color = accentColor and (130 shl 24 or 0xffffff) + canvas.drawRoundRect(drawTempRect, drawPaint.strokeWidth, drawPaint.strokeWidth, drawPaint) + + drawPaint.color = floatingColor + drawPaint.style = Paint.Style.FILL_AND_STROKE + path.fillType = Path.FillType.EVEN_ODD + canvas.drawPath(path, drawPaint) + drawPaint.style = Paint.Style.STROKE + drawPaint.color = accentColor and (130 shl 24 or 0xffffff) + path.fillType = Path.FillType.EVEN_ODD + canvas.drawPath(path, drawPaint) + + canvas.drawText(line2, drawTempRect.left + 5 * d, drawTempRect.top + 5 * d + totalHeight, fontPaint) + fontPaint.isFakeBoldText = true + canvas.drawText(line1, drawTempRect.left + 5 * d, drawTempRect.top + 5 * d + line1Height, fontPaint) + fontPaint.isFakeBoldText = false + } + } + + private val removeFocusPoint = Runnable { unfocus() } + + fun unfocus() { + focusPoint = null + invalidate() + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + when (event.actionMasked) { + MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_CANCEL -> { + postDelayed(removeFocusPoint, POPUP_DELAY) + return true + } + MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_MOVE -> { + removeCallbacks(removeFocusPoint) + focusPoint = PointF(event.x, event.y) + invalidate() + return true + } + } + return false + } + + companion object { + const val POPUP_DELAY = 3000L } } diff --git a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml index cb705e43..f53eb12f 100644 --- a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml @@ -31,7 +31,7 @@ %1$d Aufrufe von %2$s %1$d gesammelte IDs Keine Daten - %1$d / %2$d / %3$d IDs pro Stunde + %1$s IDs pro Stunde Löschen Alle gesammelten IDs löschen Nach dem Löschen der gesammelten IDs kannst du nicht mehr informiert werden, falls einer deiner Kontakte der letzten 14 Tage positiv getested wurde. diff --git a/play-services-nearby-core-ui/src/main/res/values/strings.xml b/play-services-nearby-core-ui/src/main/res/values/strings.xml index e639afd3..ddf2dfcc 100644 --- a/play-services-nearby-core-ui/src/main/res/values/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values/strings.xml @@ -41,7 +41,7 @@ %1$d calls to %2$s %1$d IDs collected No records - %1$d / %2$d / %3$d IDs per hour + %1$s IDs per hour Delete Delete all collected IDs Deleting collected IDs will make it impossible to notify you in case any of your contacts of the last 14 days is diagnosed. From 29b39a861e9d2bfaccb89272ba48471c676e726a Mon Sep 17 00:00:00 2001 From: heyarne Date: Mon, 11 Jan 2021 21:05:50 +0100 Subject: [PATCH 10/29] Fix typo for short exposure notifications (#1355) --- play-services-nearby-core-ui/src/main/res/values-de/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml index f53eb12f..6be1d2f9 100644 --- a/play-services-nearby-core-ui/src/main/res/values-de/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values-de/strings.xml @@ -17,7 +17,7 @@ Aktuell verwendete ID Gemeldete Begegnungen Aktualisiert: %1$s - Kürzer als 5 Minutem + Kürzer als 5 Minuten Etwa %1$d Minuten nahe Begegnung entfernte Begegnung From 75ae299b5451c470e0156bafbea7fc6eab6323f5 Mon Sep 17 00:00:00 2001 From: Vladimir Filatov <31875619+Vavun@users.noreply.github.com> Date: Mon, 11 Jan 2021 23:15:36 +0300 Subject: [PATCH 11/29] Improve translations (#1330) --- .../src/main/res/values-be/strings.xml | 11 +++++++++++ .../src/main/res/values-ru/strings.xml | 11 +++++++++++ .../src/main/res/values-be/strings.xml | 18 +++++++++--------- .../src/main/res/values-ru/strings.xml | 4 ++-- .../src/main/res/values-be/strings.xml | 7 ++++++- .../src/main/res/values-ru/strings.xml | 7 ++++++- 6 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 play-services-base-core/src/main/res/values-be/strings.xml create mode 100644 play-services-base-core/src/main/res/values-ru/strings.xml diff --git a/play-services-base-core/src/main/res/values-be/strings.xml b/play-services-base-core/src/main/res/values-be/strings.xml new file mode 100644 index 00000000..23983ba8 --- /dev/null +++ b/play-services-base-core/src/main/res/values-be/strings.xml @@ -0,0 +1,11 @@ + + + + + Фонавая актыўнасць + %1$s працуе ў фонавым рэжыме. + Адключыце эканомію выкарыстання акумулятара для %1$s, каб ўбраць гэтае паведамленне. + diff --git a/play-services-base-core/src/main/res/values-ru/strings.xml b/play-services-base-core/src/main/res/values-ru/strings.xml new file mode 100644 index 00000000..e6fb4115 --- /dev/null +++ b/play-services-base-core/src/main/res/values-ru/strings.xml @@ -0,0 +1,11 @@ + + + + + Фоновая активность + %1$s работает в фоновом режиме. + Отключите экономию заряда батареи для %1$s, чтобы убрать это уведомление. + diff --git a/play-services-core/src/main/res/values-be/strings.xml b/play-services-core/src/main/res/values-be/strings.xml index 9b617956..8cd7f17c 100644 --- a/play-services-core/src/main/res/values-be/strings.xml +++ b/play-services-core/src/main/res/values-be/strings.xml @@ -16,8 +16,8 @@ microG Services Core - Налады MicroG - Канфігураванне сэрвісаў microG. + Налады microG + Канфігураванне сэрвісаў microG Калі ласка, пачакайце... Google @@ -56,7 +56,7 @@ Дазволіць прыкладанню настраіваць сэрвісы microG без ўмяшання карыстальніка Рэгістрацыя прылады ў Google - Google Cloud Messaging + Воблачны абмен паведамленнямі Google SafetyNet Выключана @@ -119,7 +119,7 @@ Тэст Актыўная аптымізацыя энергаспажывання - Вы ўключылі Google Cloud Messaging, але ў вас актыўная аптымізацыя энергаспажывання для microG Services Core. Каб атрымліваць push-паведамленні вам неабходна дазволіць прыкладанню працаваць у фонавым рэжыме. + Вы ўключылі воблачны абмен паведамленнямі, але ў вас актыўная аптымізацыя энергаспажывання для microG Services Core. Каб атрымліваць push-паведамленні вам неабходна дазволіць прыкладанню працаваць у фонавым рэжыме. Дазволіць работу ў фоне Адсутнічаюць дазволы @@ -144,11 +144,11 @@ Уліковы запіс Дадаць уліковы запіс Google - Google Cloud Messaging прадастаўляе push-паведамленні, якія выкарыстоўваюцца ў многіх у іншых прыкладаннях. Каб выкарыстоўваць іх, вы павінны ўключыць рэгістрацыю прылады. - Інтэрвал злучэння Cloud Messaging - "Інтэрвал у секундах, для выкарыстання сервераў Google. Павелічэнне гэтага ліку скароціць спажыванне батарэі, але можа прывесці да затрымак push-паведамленняў.\nУстарело, будзе зменена ў наступным рэлізе." - Прыкладанні, якія выкарыстоўваюць Google Cloud Messaging - Спіс прыкладанняў, якія прывязаныя да Google Cloud Messaging. + Воблачны абмен паведамленнямі прадастаўляе push-паведамленні, якія выкарыстоўваюцца ў многіх у іншых прыкладаннях. Каб выкарыстоўваць іх, вы павінны ўключыць рэгістрацыю прылады. + Інтэрвал злучэння воблачных паведамленняў + "Інтэрвал у секундах, для выкарыстання сервераў Google. Павелічэнне гэтага ліку скароціць спажыванне батарэі, але можа прывесці да затрымак push-паведамленняў.\nСастарэла, будзе зменена ў наступным рэлізе." + Прыкладанні, якія выкарыстоўваюць воблачны абмен паведамленнямі + Спіс прыкладанняў, якія прывязаныя да воблачнага абмену паведамленнямі. Пацверджваць новыя прыкладанні Пытаць дазвол перад прывязкай новых прыкладанняў для атрымання push-паведамленняў Інтэрвал праверкі: %1$s diff --git a/play-services-core/src/main/res/values-ru/strings.xml b/play-services-core/src/main/res/values-ru/strings.xml index 3106c591..2eec92ba 100644 --- a/play-services-core/src/main/res/values-ru/strings.xml +++ b/play-services-core/src/main/res/values-ru/strings.xml @@ -16,8 +16,8 @@ microG Services Core - Настройки MicroG - Конфигурирование сервисов microG. + Настройки microG + Конфигурирование сервисов microG Пожалуйста, подождите... Google diff --git a/play-services-nearby-core-ui/src/main/res/values-be/strings.xml b/play-services-nearby-core-ui/src/main/res/values-be/strings.xml index 056b71de..66e03580 100644 --- a/play-services-nearby-core-ui/src/main/res/values-be/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values-be/strings.xml @@ -40,9 +40,14 @@ Выкарыстанне API за апошнія 14 дзён %1$d запытаў да %2$s %1$d ідэнтыфікатараў сабрана - Выдаліць усе сабраныя ідэнтыфікатары + Няма запісаў + %1$s Ідэнтыфікатараў у гадзіну + Выдаліць + Выдаліць усе сабраныя ідэнтыфікатары Выдаленне сабраных ідэнтыфікатараў прывядзе да немагчымасці інфармавання, у выпадку калі ў аднаго з вашых кантактаў за апошнія 14 дзён будзе пацверджаны дыягназ. Усё роўна выдаліць + Экспартаваць + Экспартаваць сабраныя ідэнтыфікатары для пашыранага аналізу ў іншым прыкладанні. "API паведамленняў аб рызыцы інфікавання (Exposure Notifications API) дазваляе прыкладанням апавяшчаць вас, калі вы сутыкнуліся з кімсьці, у каго быў пацверджаны дыягназ. Дата, працягласць і магутнасць сігналу, звязаныя з уздзеяннем, будуць перададзены адпаведным прыкладанням." diff --git a/play-services-nearby-core-ui/src/main/res/values-ru/strings.xml b/play-services-nearby-core-ui/src/main/res/values-ru/strings.xml index 58f5f52d..f6fa13a0 100644 --- a/play-services-nearby-core-ui/src/main/res/values-ru/strings.xml +++ b/play-services-nearby-core-ui/src/main/res/values-ru/strings.xml @@ -40,9 +40,14 @@ Использование API за последние 14 дней %1$d запросов к %2$s %1$d идентификаторов собрано - Удалить все собранные идентификаторы + Нет записей + %1$s Идентификаторов в час + Удалить + Удалить все собранные идентификаторы Удаление собранных идентификаторов приведёт к невозможности информирования, в случае если у одного из ваших контактов за последние 14 дней окажется положительный диагноз. Всё равно удалить + Экспортировать + Экспортировать собранные идентификаторы для расширенного анализа в стороннем приложении. "API Уведомлений о риске инфицирования (Exposure Notifications API) позволяет приложениям уведомлять вас, если вы столкнулись с кем-то, у кого был положительный диагноз. Дата, продолжительность и мощность сигнала, связанные с воздействием, будут переданы соответствующему приложению." From 19118d0b31a3603376578bf2e5f61a5da7ec90b4 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Mon, 11 Jan 2021 21:35:34 +0100 Subject: [PATCH 12/29] Enable GitHub Actions --- .github/workflows/build.yml | 13 +++++++++++++ .travis.yml | 15 --------------- extern/RemoteDroidGuard | 1 - extern/Wearable | 1 - play-services-maps-core-mapbox/build.gradle | 6 +++++- 5 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml delete mode 160000 extern/RemoteDroidGuard delete mode 160000 extern/Wearable diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..febda7e2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,13 @@ +name: Build +on: [pull_request, push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - run: ./gradlew --no-daemon build + env: + TERM: dumb + JAVA_OPTS: -Xmx2048m diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 02f9e12f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: java -jdk: openjdk8 -install: - - mkdir $HOME/android-cmdline-tools - - curl https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip > $HOME/android-cmdline-tools/cmdline-tools.zip - - unzip -qq -n $HOME/android-cmdline-tools/cmdline-tools.zip -d $HOME/android-cmdline-tools - - echo y | $HOME/android-cmdline-tools/tools/bin/sdkmanager --sdk_root=$HOME/android-sdk 'platform-tools' - - echo y | $HOME/android-cmdline-tools/tools/bin/sdkmanager --sdk_root=$HOME/android-sdk 'build-tools;29.0.3' - - echo y | $HOME/android-cmdline-tools/tools/bin/sdkmanager --sdk_root=$HOME/android-sdk 'platforms;android-30' -env: - - ANDROID_HOME=$HOME/android-sdk TERM=dumb JAVA_OPTS="-Xmx2048m" -before_script: - - echo sdk.dir $ANDROID_HOME > local.properties -script: - - ./gradlew --no-daemon build diff --git a/extern/RemoteDroidGuard b/extern/RemoteDroidGuard deleted file mode 160000 index 47073dd7..00000000 --- a/extern/RemoteDroidGuard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 47073dd7a2a039593fe556af8f9f33e325febfa7 diff --git a/extern/Wearable b/extern/Wearable deleted file mode 160000 index 617a43e1..00000000 --- a/extern/Wearable +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 617a43e139c6470361e44015bf1964026f3107e7 diff --git a/play-services-maps-core-mapbox/build.gradle b/play-services-maps-core-mapbox/build.gradle index e60171ea..331516c9 100644 --- a/play-services-maps-core-mapbox/build.gradle +++ b/play-services-maps-core-mapbox/build.gradle @@ -41,7 +41,11 @@ def execResult(...args) { def mapboxKey() { Properties properties = new Properties() - properties.load(project.rootProject.file('local.properties').newDataInputStream()) + try { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + } catch (ignored) { + // Ignore + } return properties.getProperty("mapbox.key", "invalid") } From b0fae31c4b6f70704223ffb72e7d871558045f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Fern=C3=A1ndez=20D=C3=ADaz?= Date: Mon, 11 Jan 2021 22:14:48 +0000 Subject: [PATCH 13/29] Update Spanish translation (#1315) --- .../src/main/res/values-es/strings.xml | 14 ++ .../src/main/res/values-es/strings.xml | 29 +++- .../src/main/res/values-es/permissions.xml | 163 +++++++++++++++++- .../src/main/res/values-es/plurals.xml | 20 ++- .../src/main/res/values-es/strings.xml | 134 ++++++++++++-- .../src/main/res/values-es/strings.xml | 77 +++++++++ .../src/main/res/values-es/strings.xml | 10 ++ 7 files changed, 426 insertions(+), 21 deletions(-) create mode 100644 play-services-base-core-ui/src/main/res/values-es/strings.xml create mode 100644 play-services-nearby-core-ui/src/main/res/values-es/strings.xml create mode 100644 play-services-nearby-core/src/main/res/values-es/strings.xml diff --git a/play-services-base-core-ui/src/main/res/values-es/strings.xml b/play-services-base-core-ui/src/main/res/values-es/strings.xml new file mode 100644 index 00000000..a380438a --- /dev/null +++ b/play-services-base-core-ui/src/main/res/values-es/strings.xml @@ -0,0 +1,14 @@ + + + + + Avanzado + + Ninguno + Ver todo + + Abrir + diff --git a/play-services-core/microg-ui-tools/src/main/res/values-es/strings.xml b/play-services-core/microg-ui-tools/src/main/res/values-es/strings.xml index 04758356..39b65b3f 100644 --- a/play-services-core/microg-ui-tools/src/main/res/values-es/strings.xml +++ b/play-services-core/microg-ui-tools/src/main/res/values-es/strings.xml @@ -1,6 +1,6 @@ - + Herramientas de IU de microG + Apache License 2.0, Equipo de microG + + Versión %1$s + %1$s %2$s + Todos los derechos reservados. + + Configuración + + Autocomprobación + Comprueba si el sistema está correctamente configurado para usar microG. + + Permisos concedidos + Permiso para %1$s: + Toque aquí para conceder el permiso. No conceder el permiso puede resultar en comportamientos incorrectos de las aplicaciones. + + Demo de IU de microG + Resumen + Versión v0.1.0 + Librerías incluidas + + v4 Support Library + v7 appcompat Support Library + v7 preference Support Library + Apache License 2.0, The Android Open Source Project + diff --git a/play-services-core/src/main/res/values-es/permissions.xml b/play-services-core/src/main/res/values-es/permissions.xml index 0292f489..d63f0559 100644 --- a/play-services-core/src/main/res/values-es/permissions.xml +++ b/play-services-core/src/main/res/values-es/permissions.xml @@ -1,6 +1,6 @@ - \ No newline at end of file + Todos los servicios de Google + Permite a la aplicación acceder a todos los servicios de Google a través de cualquier cuenta de Google asociada. + + Servicios de Android + Permite a la aplicación acceder a todos los servicios de Google a través de cualquier cuenta de Google asociada. + AdSense + Permite a la aplicación acceder a todos los servicios de AdSense a través de cualquier cuenta de Google asociada. + AdWords + Permite a la aplicación acceder a todos los servicios de AdWords a través de cualquier cuenta de Google asociada. + Google App Engine + Permite a la aplicación acceder a todos los servicios de Google App Engine a través de cualquier cuenta de Google asociada. + Blogger + Permite a la aplicación acceder a todos los servicios de Blogger a través de cualquier cuenta de Google asociada. + Google Calendar + Permite a la aplicación acceder a todos los servicios de Google Calendar a través de cualquier cuenta de Google asociada. + Contactos + Permite a la aplicación acceder a todos los servicios de Contactos a través de cualquier cuenta de Google asociada. + Dodgeball + Permite a la aplicación acceder a todos los servicios de Dodgeball a través de cualquier cuenta de Google asociada. + Google Finance + Permite a la aplicación acceder a todos los servicios de Google Finance a través de cualquier cuenta de Google asociada. + Google Base + Permite a la aplicación acceder a todos los servicios de Google Base a través de cualquier cuenta de Google asociada. + Google Voice + Permite a la aplicación acceder a todos los servicios de Google Voice a través de cualquier cuenta de Google asociada. + Grupos de Google + Permite a la aplicación acceder a todos los servicios de Grupos de Google a través de cualquier cuenta de Google asociada. + Google Health + Permite a la aplicación acceder a todos los servicios de Google Health a través de cualquier cuenta de Google asociada. + iGoogle + Permite a la aplicación acceder a todos los servicios de iGoogle a través de cualquier cuenta de Google asociada. + JotSpot + Permite a la aplicación acceder a todos los servicios de JotSpot a través de cualquier cuenta de Google asociada. + Knol + Permite a la aplicación acceder a todos los servicios de Knol a través de cualquier cuenta de Google asociada. + Picasa Web Albums + Permite a la aplicación acceder a todos los servicios de Picasa Web Albums a través de cualquier cuenta de Google asociada. + Google Maps + Permite a la aplicación acceder a todos los servicios de Google Maps a través de cualquier cuenta de Google asociada. + Google Mail + Permite a la aplicación acceder a todos los servicios de Google Mail a través de cualquier cuenta de Google asociada. + Google Noticias + Permite a la aplicación acceder a todos los servicios de Google Noticias a través de cualquier cuenta de Google asociada. + Google Notebook + Permite a la aplicación acceder a todos los servicios de Google Notebook a través de cualquier cuenta de Google asociada. + Orkut + Permite a la aplicación acceder a todos los servicios de Orkut a través de cualquier cuenta de Google asociada. + Búsqueda de Libros de Google + Permite a la aplicación acceder a todos los servicios de Búsqueda de Libros de Google a través de cualquier cuenta de Google asociada. + Cuentas de Google Checkout + Permite a la aplicación acceder a todos los servicios de Cuentas de Google Checkout a través de cualquier cuenta de Google asociada. + Cuentas de QA de Google Checkout + Permite a la aplicación acceder a todos los servicios de Cuentas de QA de Google Checkout a través de cualquier cuenta de Google asociada. + Cuentas de Sandbox de Google Checkout + Permite a la aplicación acceder a todos los servicios de Cuentas de Sandbox de Google Checkout a través de cualquier cuenta de Google asociada. + Herramientas de Google Webmaster + Permite a la aplicación acceder a todos los servicios de Herramientas de Google Webmaster a través de cualquier cuenta de Google asociada. + Búsqueda por voz + Permite a la aplicación acceder a todos los servicios de Búsqueda por voz a través de cualquier cuenta de Google asociada. + Personalized Speech Recognition + Permite a la aplicación acceder a todos los servicios de Personalized Speech Recognition a través de cualquier cuenta de Google asociada. + Google Talk + Permite a la aplicación acceder a todos los servicios de Google Talk a través de cualquier cuenta de Google asociada. + Google Wi-Fi + Permite a la aplicación acceder a todos los servicios de Google Wi-Fi a través de cualquier cuenta de Google asociada. + Hojas de cálculo de Google + Permite a la aplicación acceder a todos los servicios de Hojas de cálculo de Google a través de cualquier cuenta de Google asociada. + Documentos de Google + Permite a la aplicación acceder a todos los servicios de Documentos de Google a través de cualquier cuenta de Google asociada. + YouTube + Permite a la aplicación acceder a todos los servicios de YouTube a través de cualquier cuenta de Google asociada. + Nombres de usuario de YouTube + Permite a la aplicación acceder a todos los servicios de nombres de usuario(s) de YouTube a través de cualquier cuenta de Google asociada. + + Ver el historial de actividad de tus Google Apps + Administrar la configuración de su cuenta de comprador de Ad Exchange + Ver tus datos de Ad Exchange + Ver y administrar tus datos de Ad Exchange + Ver y administar tus datos del host de AdSense y las cuentas asociadas + Ver tus datos de AdSense + Ver y administrar tus datos de AdSense + Ver tus datos de Google Analytics + Ver y administrar tus datos de Google Analytics + Acceder a Google Play Android Developer + Alcance de la administración del motor de la aplicación. + Acceso de lectura y escritura a la API de Groups Migration. + Ver y administrar la configuración de un grupo de Google Apps + Acceso de lectura y escritura a la API de License Manager. + Para administradores de revendedores y usuarios acceso de lectura/escritura cuando se realizan pruebas en la API de la sandbox, o acceso de lectura/escritura cuando se llama directamente a una operación de la API. + Además del alcance general de lectura/escritura de OAuth, utilice el alcance de sólo lectura de OAuth al recuperar los datos del cliente. + Acceder a la Admin Audit API ReadOnly + Posibilidad de utilizar el servicio de App State. + Ver tus datos en Google BigQuery + Ver y administrar tus datos en Google BigQuery + Administar tu cuenta de Blogger + Ver tu cuenta de Blogger + Administrar tus libros + Administrar tus calendarios + Ver tus calendarios + Ver y administrar tus datos de Google Cloud Print + Ver tus recursos de Google Compute Engine + Ver y administrar tus recursos de Google Compute Engine + Ver tus trabajos de Google Coordinate + Ver y administrar tus trabajos de Google Maps Coordinate + Administrar tus datos y permisos en Google Cloud Storage + Ver tus datos en Google Cloud Storage + Administrar tus datos en Google Cloud Storage + Ver y administrar los informes de DoubleClick para Advertisers + Permite el acceso a la carpeta de Application Data + Ver tus aplicaciones de Google Drive + Ver y administrar los archivos de Google Drive que has abierto o creado con esta aplicación + Ámbito especial utilizado para que los usuarios aprueben la instalación de una aplicación + Ver metadatos de archivos y documentos en tu Google Drive + Ver archivos y documentos en tu Google Drive + Modificar el comportamiento de tus scripts de Google Apps Script + Ver y administrar archivos y documentos en tu Google Drive + ver tu cuenta de Freebase + Inicia sesión en Freebase con tu cuenta + Administrar tus Fusion Tables + Ver tus Fusion Tables + Ámbito para acceder a los datos de Google Play Juegos. + Administar tus datos de GAN + Ver tus datos de GAN + CloudMessaging para Chrome + Ámbito de la cronología de Glass + Crea, lee, actualiza, y elimina borradores. Envia mensajes y borradores. + Todas las operaciones de lectura/escritura excepto el borrado inmediato y permanente de hilos y mensajes, evitando la basura. + Lea todos los recursos y sus metadatos—no escriba las operaciones. + Administra tu mejor ubicación disponible y tu historial de ubicación + Administra la ubicación de la ciudad y el historial de la ubicación + Administra tu mejor ubicación posible + Administra tu ubicación en la ciudad + Ver y administrar los datos del motor de Google Maps + Ver tus datos del motor de Google Maps + Ver y administrar tu experiencia móvil de Google Maps + Administrar tu actividad de Orkut + Ver tus datos de Orkut + Conocer tu nombre, información básica y la lista de personas con las que estás conectado en Google+ + Conocer quién eres en Google + Administrar tus datos en la API de Google Prediction + Ver los datos de tu producto + Administrar la lista de sitios y dominios que controlas + Administrar las verificaciones de tu nuevo sitio con Google + Acceso de lectura y escritura a la API de Shopping Content. + Consume las tareas de tus Taskqueues + Administrar tus Tareas + Administrar tus tareas + Ver tus tareas + La API de Google Maps Tracks, Este alcance permite el acceso de lectura y escritura a los datos de tu proyecto. + Administrar tus URLs cortas de goo.gl + Ver tu dirección de correo electrónico + Ver información básica sobre tu cuenta + Administrar tu cuenta de YouTube + Ver y administrar tus activos y contenido asociado en YouTube + Ver tu cuenta de YouTube + Administrar tus vídeos de YouTube + Ver los informes monetarios de YouTube Analytics para su contenido de YouTube + Ver los informes de YouTube Analytics para su contenido de YouTube + + diff --git a/play-services-core/src/main/res/values-es/plurals.xml b/play-services-core/src/main/res/values-es/plurals.xml index b7228cab..6ebbcaea 100644 --- a/play-services-core/src/main/res/values-es/plurals.xml +++ b/play-services-core/src/main/res/values-es/plurals.xml @@ -1,6 +1,6 @@ - \ No newline at end of file + + %1$d backend configurado + %1$d backends configurados + + + %1$d aplicación registrada + %1$d aplicaciones registradas + + + No se concede el permiso necesario para el correcto funcionamiento del núcleo de servicio de microG. + No se conceden los permisos necesarios para el correcto funcionamiento del núcleo de servicio de microG. + + + Solicitar el permiso que falta + Solicitar los permisos que faltan + + diff --git a/play-services-core/src/main/res/values-es/strings.xml b/play-services-core/src/main/res/values-es/strings.xml index c94a29da..28e9a118 100644 --- a/play-services-core/src/main/res/values-es/strings.xml +++ b/play-services-core/src/main/res/values-es/strings.xml @@ -1,5 +1,5 @@ Soporte para spoofing de firmas Aplicaciones instaladas + Sistema + Soporte de spoofing: Tu ROM no tiene soporte nativo para spoofing de firmas. Todavía puedes usar Xposed o otros sistemas para conseguirlo. Por favor, compruebe la documentación en que ROMs soportan spoofing de firmas y como usar microG en las que no. Se concede el permiso de spoofing de firmas: @@ -70,26 +97,103 @@ Esto podría tardar algunos minutos." Play Services (GmsCore) Play Store (Phonesky) Services Framework (GSF) - %1$s instalado: - Instala la aplicación %1$s o una compatible. Por favor comprueba la documentación en que aplicaciones son compatibles. - %1$s tiene una firma correcta: - Puede que el %1$s instalado no sea compatible o el spoofing de firmas no este activo. Por favor compruebe la documentación en que aplicaciones y ROMs son compatibles. + %1$s instalado: + Instala la aplicación %1$s o una compatible. Por favor comprueba la documentación en que aplicaciones son compatibles. + %1$s tiene una firma correcta: + Puede que el %1$s instalado no sea compatible o el spoofing de firmas no este activo. Por favor compruebe la documentación en que aplicaciones y ROMs son compatibles. + + Optimizaciones de la batería ignoradas: + Toca aquí para desactivar las optimizaciones de la batería. Si no lo haces, puede que las aplicaciones se comporten incorrectamente. Acerca Componentes + Configuración + Servicios de Google Servicio de localización - Servicios de segundo plano + Modo de operación + Servicios + Prueba + + Optimizaciones de la batería activadas + Has habilitado Cloud Messaging pero tienes las optimizaciones de la batería activas para el núcleo de servicios de microG. Para que las notificaciones push lleguen, debes ignorar las optimizaciones de la batería. + Ignorar optimizaciones + Falta el permiso + + Preferencias de la cuenta + Información personal & privacidad + Inicio de sesión & seguridad + Confiar en Google para los permisos de las aplicaciones Cuando este desactivado, se preguntara al usuario antes de que una aplicación envíe una solicitud de autorización a Google. Algunas aplicaciones fallaran al usar la cuenta de Google si esta desactivado. + Permitir que las aplicaciones encuentren cuentas + Cuando esté habilitado, todas las aplicaciones de este dispositivo podrán ver la dirección de correo electrónico de sus cuentas de Google sin necesidad de autorización previa. El registro del dispositivo es un proceso oculto que se usa para crear un identificador único para los servicios de Google. microG quita información identificativa aparte del nombre en tu cuenta de Google de estos datos. + Identificador de Android + No registrado + Último registro: %1$s + Registrar el dispositivo + + Estado + Más + + Cuenta + Añadir cuenta de Google Cloud Messaging es un proveedor de notificaciones push usado por muchas aplicaciones. Para usarlo debes habilitar el registro del dispositivo. Intervalo del Cloud Messaging heartbeat El intervalo en segundos para el heartbeat a los servidores de Google. Aumentar este número reducirá el gasto de la batería, pero podría causar retrasos en mensajes push. + Aplicaciones usando Cloud Messaging + Lista de aplicaciones registradas actualmente para Cloud Messagging. + Confirmar nuevas aplicaciones + Preguntar antes de registrar una nueva aplicación para recibir notificaciones push + Intervalo de ping: %1$s + Acerca de los Servicios de microG Core Información de la versión y librerías usadas - + Error al cancelar el registro + Ya no está instalada + Cancelar el registro + No registrado + No se han recibido mensajes hasta ahora + Último mensaje: %1$s + Registrado + Registrado desde: %1$s + ¿Cancelar el registro %1$s? + Algunas aplicaciones no se registran automáticamente y/o no ofrecen la opción de hacerlo manualmente. Es posible que estas aplicaciones no funcionen correctamente después de la cancelación del registro. ¿Continuar? + Negaste a una aplicación para registrarse en las notificaciones push que ya está registrada. ¿Quieres cancelar el registro ahora para que no reciba mensajes push en el futuro? + Mensajes: %1$d (%2$d bytes) + Desconectado + Conectado desde %1$s + Recibir notificaciones push + + Permitir el registro + Permite que la aplicación se registre para las notificaciones push. + Iniciar la aplicación en el mensaje push + Inicie la aplicación en segundo plano para recibir los mensajes entrantes. + Aplicaciones que utilizan notificaciones push + Aplicaciones registradas + Aplicaciones no registradas + Redes a utilizar para las notificaciones push + + Google SafetyNet es un sistema de certificación de dispositivos, que garantiza que el dispositivo está correctamente asegurado y es compatible con Android CTS. Algunas aplicaciones utilizan SafetyNet por razones de seguridad o como un prerrequisito para la protección contra manipulaciones.\n\nmicroG GmsCore contiene una implementación gratuita de SafetyNet, pero el servidor oficial requiere que las solicitudes de SafetyNet sean firmadas utilizando el sistema propietario DroidGuard. Una versión en sandbox de DroidGuard está disponible como una aplicación separada "DroidGuard Helper". + Permitir la certificación del dispositivo + + Probar el certificado de SafetyNet + + Usar el servidor oficial + Requiere un sistema no root y un microG DroidGuard Helper instalado + servidor oficial + Usar un servidor de terceros + Los servidores de terceros podrían responder a las solicitudes de SafetyNet sin la firma de DroidGuard + servidor de terceros + URL del servidor personalizada + URL completa del servidor de terceros que responde a las solicitudes de certificación de SafetyNet + Usar un certificado autofirmado + En lugar de solicitar un servidor, firma las respuestas de SafetyNet localmente usando un certificado autofirmado. La mayoría de las aplicaciones se negarán a usar respuestas autofirmadas. + certificado autofirmado + + diff --git a/play-services-nearby-core-ui/src/main/res/values-es/strings.xml b/play-services-nearby-core-ui/src/main/res/values-es/strings.xml new file mode 100644 index 00000000..0a381f9b --- /dev/null +++ b/play-services-nearby-core-ui/src/main/res/values-es/strings.xml @@ -0,0 +1,77 @@ + + + + Notificaciones de Exposición + Para habilitar las Notificaciones de Exposición, abre cualquier aplicación que lo soporte. + Habilitar el Bluetooth + Abrir Configuración de la ubicación + Desafortunadamente, su dispositivo no es compatible con las Notificaciones de Exposición. + Desafortunadamente, su dispositivo sólo es parcialmente compatible con las Notificaciones de Exposición. Puedes ser notificado para contactos de riesgo pero no podrás notificar a otros. + Aplicaciones que utilizan Notificaciones de Exposición + Identificaciones recogidas + %1$d identificaciones en la última hora + Identificación actualmente emitida + Exposiciones reportadas + Actualizado: %1$s + Menos de 5 minutos + exposición cercana + exposición lejana + %1$s, %2$s + Alrededor de %1$d minutos + Procesadas %1$d claves de diagnóstico. + No se ha informado de ningún encuentro de exposición. + Reportados %1$d encuentros de exposición: + %1$s, puntuación de riesgo %2$d + Nota: La puntuación de riesgo está definida por la aplicación. Los números altos pueden referirse a un riesgo bajo o viceversa. + El uso de la API en los últimos 14 días + %1$d peticiones a %2$s + %1$d identificaciones recogidas + Eliminar + Eliminar todas las identificaciones recogidas + Eliminar las identificaciones recogidas hará imposible notificarle en caso de que alguno de sus contactos de los últimos 14 días sea diagnosticado. + Eliminar de todas formas + Exportar + Exportar identificaciones recogidas para su análisis ampliado con otra aplicación. + La API de Notificaciones de Exposición permite que las aplicaciones le notifiquen si estuvo expuesto a alguien que informó haber sido diagnosticado como positivo. + +La fecha, la duración y la intensidad de la señal asociadas a una exposición se compartirán con la aplicación correspondiente. + Mientras la API de Notificación de Exposición está activada, el dispositivo recoge pasivamente identificaciones (llamadas identificadores de proximidad rodante o RPI) de los dispositivos cercanos. + +Cuando los propietarios de los dispositivos informan que el diagnóstico es positivo, se pueden compartir sus identificaciones. Su dispositivo comprueba si alguna de las identificaciones diagnosticadas conocidas coincide con alguna de las identificaciones recogidas y calcula su riesgo de infección. + + Usar Notificaciones de Exposición + ¿Encender las Notificaciones de Exposición? + El teléfono debe usar el Bluetooth para recopilar y compartir de manera segura las identificaciones con otros teléfonos que estén cerca. + +%1$s puede notificarte si estuviste expuesto a alguien que informó ser diagnosticado como positivo. + +La fecha, la duración y la intensidad de la señal asociadas a una exposición se compartirán con la aplicación. + Encender + ¿Apagar las Notificaciones de Exposición? + Después de desactivar las Notificaciones de Exposición, ya no se le notificará cuando haya estado expuesto a alguien que haya informado de que ha sido diagnosticado como positivo. + Apagar + Comparte tus identificaciones con %1$s? + Sus identificaciones de los últimos 14 días se usarán para ayudar a notificar a otros que usted ha estado cerca de una posible exposición. + +Su identidad o el resultado de la prueba no será compartida con otras personas. + Compartir + %1$s necesita permisos adicionales. + Conceder + El Bluetooth debe estar activado. + Se requiere acceso a la ubicación. + Habilitar + diff --git a/play-services-nearby-core/src/main/res/values-es/strings.xml b/play-services-nearby-core/src/main/res/values-es/strings.xml new file mode 100644 index 00000000..efeea3d7 --- /dev/null +++ b/play-services-nearby-core/src/main/res/values-es/strings.xml @@ -0,0 +1,10 @@ + + + Notificaciones de Exposición inactivas + El Bluetooth debe estar habilitado para recibir Notificaciones de Exposición. + El acceso a la ubicación es necesario para recibir las Notificaciones de Exposición. + El acceso al Bluetooth y a la localización debe estar habilitado para recibir Notificaciones de Exposición. + From baf890cf1840aed507eeb3b0e77ec3942f32cb07 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 12 Jan 2021 14:24:51 +0100 Subject: [PATCH 14/29] Bump version --- README.md | 2 +- build.gradle | 2 +- .../google/android/gms/common/GoogleApiAvailability.java | 2 +- .../google/android/gms/common/GooglePlayServicesUtil.java | 2 +- play-services-basement/build.gradle | 1 + .../android/gms/common/internal/GetServiceRequest.java | 4 ++-- .../src/main/java/org/microg/gms/common/Constants.java | 6 +++++- .../src/main/java/org/microg/gms/auth/AuthRequest.java | 2 +- .../main/java/org/microg/gms/auth/login/LoginActivity.java | 4 ++-- .../src/main/java/org/microg/gms/snet/Attestation.java | 4 ++-- .../src/main/java/org/microg/gms/iid/InstanceIdRpc.java | 4 ++-- .../microg/gms/location/GoogleLocationManagerClient.java | 2 +- .../exposurenotification/ExposureNotificationServiceImpl.kt | 2 +- 13 files changed, 21 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 17dc20a2..48d7bca1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ microG Services Core (GmsCore) ======= -[![Build Status](https://travis-ci.com/microg/GmsCore.svg?branch=master)](https://travis-ci.com/microg/GmsCore) +[![Build Status](https://github.com/microg/GmsCore/workflows/Build/badge.svg)](https://travis-ci.com/microg/GmsCore) microG GmsCore is a FLOSS (Free/Libre Open Source Software) framework to allow applications designed for Google Play Services to run on systems, where Play Services is not available. diff --git a/build.gradle b/build.gradle index 1c0e0952..c42bb3b4 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ def execResult(...args) { return stdout.toString().trim() } -def gmsVersion = "20.47.13" +def gmsVersion = "20.47.14" def gmsVersionCode = Integer.parseInt(gmsVersion.replaceAll('\\.', '')) def gitVersionBase = execResult('git', 'describe', '--tags', '--abbrev=0', '--match=v[0-9]*').substring(1) def gitCommitCount = Integer.parseInt(execResult('git', 'rev-list', '--count', "v$gitVersionBase..HEAD")) diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java index 0439619e..889cc7f6 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java @@ -55,7 +55,7 @@ public class GoogleApiAvailability { /** * Google Play services client library version (declared in library's AndroidManifest.xml android:versionCode). */ - public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION; + public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.GMS_VERSION_CODE; private static GoogleApiAvailability instance; diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java index 5fb995b5..4850b816 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GooglePlayServicesUtil.java @@ -52,7 +52,7 @@ public class GooglePlayServicesUtil { * Google Play services client library version (declared in library's AndroidManifest.xml android:versionCode). */ @Deprecated - public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.MAX_REFERENCE_VERSION; + public static final int GOOGLE_PLAY_SERVICES_VERSION_CODE = Constants.GMS_VERSION_CODE; /** * Package name for Google Play Store. diff --git a/play-services-basement/build.gradle b/play-services-basement/build.gradle index 9fb09e1e..8c7ec067 100644 --- a/play-services-basement/build.gradle +++ b/play-services-basement/build.gradle @@ -40,6 +40,7 @@ android { versionName version minSdkVersion androidMinSdk targetSdkVersion androidTargetSdk + buildConfigField "int", "VERSION_CODE", "$appVersionCode" } sourceSets { diff --git a/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java b/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java index 6abc7687..fbd84dba 100644 --- a/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java +++ b/play-services-basement/src/main/java/com/google/android/gms/common/internal/GetServiceRequest.java @@ -60,12 +60,12 @@ public class GetServiceRequest extends AutoSafeParcelable { private GetServiceRequest() { serviceId = -1; - gmsVersion = Constants.MAX_REFERENCE_VERSION; + gmsVersion = Constants.GMS_VERSION_CODE; } public GetServiceRequest(int serviceId) { this.serviceId = serviceId; - this.gmsVersion = Constants.MAX_REFERENCE_VERSION; + this.gmsVersion = Constants.GMS_VERSION_CODE; this.field12 = true; } diff --git a/play-services-basement/src/main/java/org/microg/gms/common/Constants.java b/play-services-basement/src/main/java/org/microg/gms/common/Constants.java index 0ac6aae5..b2bc6353 100644 --- a/play-services-basement/src/main/java/org/microg/gms/common/Constants.java +++ b/play-services-basement/src/main/java/org/microg/gms/common/Constants.java @@ -16,9 +16,13 @@ package org.microg.gms.common; +import org.microg.gms.basement.BuildConfig; + public class Constants { - public static final int MAX_REFERENCE_VERSION = 204713 * 1000; + public static final int GMS_VERSION_CODE = (BuildConfig.VERSION_CODE / 1000) * 1000; public static final String GMS_PACKAGE_NAME = "com.google.android.gms"; public static final String GSF_PACKAGE_NAME = "com.google.android.gsf"; public static final String GMS_PACKAGE_SIGNATURE_SHA1 = "38918a453d07199354f8b19af05ec6562ced5788"; + @Deprecated + public static final int MAX_REFERENCE_VERSION = GMS_VERSION_CODE; } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java index 6de2e815..8365adb9 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java @@ -58,7 +58,7 @@ public class AuthRequest extends HttpFormClient.Request { @RequestContent("lang") public String locale; @RequestContent("google_play_services_version") - public int gmsVersion = Constants.MAX_REFERENCE_VERSION; + public int gmsVersion = Constants.GMS_VERSION_CODE; @RequestContent("accountType") public String accountType; @RequestContent("Email") diff --git a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java index 7a292df3..13a26b2e 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java @@ -73,7 +73,7 @@ import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; -import static org.microg.gms.common.Constants.MAX_REFERENCE_VERSION; +import static org.microg.gms.common.Constants.GMS_VERSION_CODE; public class LoginActivity extends AssistantActivity { public static final String TMPL_NEW_ACCOUNT = "new_account"; @@ -463,7 +463,7 @@ public class LoginActivity extends AssistantActivity { @JavascriptInterface public final int getPlayServicesVersionCode() { - return MAX_REFERENCE_VERSION; + return GMS_VERSION_CODE; } @JavascriptInterface diff --git a/play-services-core/src/main/java/org/microg/gms/snet/Attestation.java b/play-services-core/src/main/java/org/microg/gms/snet/Attestation.java index 36dc6776..f1993ca4 100644 --- a/play-services-core/src/main/java/org/microg/gms/snet/Attestation.java +++ b/play-services-core/src/main/java/org/microg/gms/snet/Attestation.java @@ -76,7 +76,7 @@ public class Attestation { .packageName(packageName) .fileDigest(getPackageFileDigest()) .signatureDigest(getPackageSignatures()) - .gmsVersionCode(Constants.MAX_REFERENCE_VERSION) + .gmsVersionCode(Constants.GMS_VERSION_CODE) //.googleCn(false) .seLinuxState(new SELinuxState.Builder().enabled(true).supported(true).build()) .suCandidates(Collections.emptyList()) @@ -155,7 +155,7 @@ public class Attestation { connection.setRequestProperty("content-type", "application/x-protobuf"); connection.setRequestProperty("Accept-Encoding", "gzip"); Build build = Utils.getBuild(context); - connection.setRequestProperty("User-Agent", "SafetyNet/" + Constants.MAX_REFERENCE_VERSION + " (" + build.device + " " + build.id + "); gzip"); + connection.setRequestProperty("User-Agent", "SafetyNet/" + Constants.GMS_VERSION_CODE + " (" + build.device + " " + build.id + "); gzip"); OutputStream os = connection.getOutputStream(); os.write(request.encode()); diff --git a/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java b/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java index 77f65f5e..cce6616f 100644 --- a/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java +++ b/play-services-iid/src/main/java/org/microg/gms/iid/InstanceIdRpc.java @@ -57,7 +57,7 @@ import static com.google.android.gms.iid.InstanceID.ERROR_SERVICE_NOT_AVAILABLE; import static com.google.android.gms.iid.InstanceID.ERROR_TIMEOUT; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import static org.microg.gms.common.Constants.GSF_PACKAGE_NAME; -import static org.microg.gms.common.Constants.MAX_REFERENCE_VERSION; +import static org.microg.gms.common.Constants.GMS_VERSION_CODE; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; import static org.microg.gms.gcm.GcmConstants.ACTION_INSTANCE_ID; @@ -284,7 +284,7 @@ public class InstanceIdRpc { data.putString(EXTRA_OS_VERSION, Integer.toString(Build.VERSION.SDK_INT)); data.putString(EXTRA_APP_VERSION_CODE, Integer.toString(getSelfVersionCode(context))); data.putString(EXTRA_APP_VERSION_NAME, getSelfVersionName(context)); - data.putString(EXTRA_CLIENT_VERSION, "iid-" + MAX_REFERENCE_VERSION); + data.putString(EXTRA_CLIENT_VERSION, "iid-" + GMS_VERSION_CODE); data.putString(EXTRA_APP_ID, InstanceID.sha1KeyPair(keyPair)); String pub = base64encode(keyPair.getPublic().getEncoded()); data.putString(EXTRA_PUBLIC_KEY, pub); diff --git a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java index deb26d47..3d542782 100644 --- a/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java +++ b/play-services-location/src/main/java/org/microg/gms/location/GoogleLocationManagerClient.java @@ -46,7 +46,7 @@ public abstract class GoogleLocationManagerClient extends GmsClient Date: Thu, 21 Jan 2021 11:41:07 +0100 Subject: [PATCH 15/29] Fix Android Studio not reading symbols of proto generated classes --- play-services-core-proto/build.gradle | 4 ++++ play-services-nearby-core-proto/build.gradle | 4 ++++ play-services-wearable-proto/build.gradle | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/play-services-core-proto/build.gradle b/play-services-core-proto/build.gradle index 3ae24b59..68f2a91f 100644 --- a/play-services-core-proto/build.gradle +++ b/play-services-core-proto/build.gradle @@ -16,6 +16,10 @@ wire { } } +sourceSets { + main.java.srcDirs += "$buildDir/generated/source/wire" +} + compileKotlin { kotlinOptions.jvmTarget = 1.8 } diff --git a/play-services-nearby-core-proto/build.gradle b/play-services-nearby-core-proto/build.gradle index ac8889ee..c99a4c61 100644 --- a/play-services-nearby-core-proto/build.gradle +++ b/play-services-nearby-core-proto/build.gradle @@ -16,6 +16,10 @@ wire { kotlin {} } +sourceSets { + main.java.srcDirs += "$buildDir/generated/source/wire" +} + compileKotlin { kotlinOptions.jvmTarget = 1.8 } diff --git a/play-services-wearable-proto/build.gradle b/play-services-wearable-proto/build.gradle index 3ae24b59..68f2a91f 100644 --- a/play-services-wearable-proto/build.gradle +++ b/play-services-wearable-proto/build.gradle @@ -16,6 +16,10 @@ wire { } } +sourceSets { + main.java.srcDirs += "$buildDir/generated/source/wire" +} + compileKotlin { kotlinOptions.jvmTarget = 1.8 } From 2fbe0a09287d169cdcd6bd9c0dde51e29557d332 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Thu, 21 Jan 2021 11:41:49 +0100 Subject: [PATCH 16/29] Mapbox: use specialized location layer Less buggy and more performant, but lacks pulsing animation. Fixes #1293 --- .../src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index 5af3e40f..133bb7f8 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -708,6 +708,7 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) val mapContext = MapContext(context) map.locationComponent.apply { activateLocationComponent(LocationComponentActivationOptions.builder(mapContext, it) + .useSpecializedLocationLayer(true) .locationComponentOptions(LocationComponentOptions.builder(mapContext).pulseEnabled(true).build()) .build()) cameraMode = CameraMode.TRACKING From 3d2c7e95237ee6bc8308f57627fb3530f3dbaf85 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 31 Jan 2021 12:13:03 -0600 Subject: [PATCH 17/29] Add DroidGuard service API and client --- play-services-droidguard-api/build.gradle | 35 +++++++ .../src/main/AndroidManifest.xml | 7 ++ .../internal/DroidGuardInitReply.aidl | 3 + .../internal/DroidGuardResultsRequest.aidl | 3 + .../internal/IDroidGuardCallbacks.aidl | 5 + .../internal/IDroidGuardHandle.aidl | 13 +++ .../internal/IDroidGuardService.aidl | 14 +++ .../internal/DroidGuardInitReply.java | 48 ++++++++++ .../internal/DroidGuardResultsRequest.java | 93 +++++++++++++++++++ play-services-droidguard/build.gradle | 42 +++++++++ .../src/main/AndroidManifest.xml | 7 ++ .../gms/droidguard/DroidGuardApiClient.kt | 25 +++++ .../microg/gms/droidguard/DroidGuardClient.kt | 13 +++ .../gms/droidguard/DroidGuardClientImpl.kt | 33 +++++++ .../microg/gms/droidguard/DroidGuardHandle.kt | 54 +++++++++++ settings.gradle | 2 + 16 files changed, 397 insertions(+) create mode 100644 play-services-droidguard-api/build.gradle create mode 100644 play-services-droidguard-api/src/main/AndroidManifest.xml create mode 100644 play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardInitReply.aidl create mode 100644 play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.aidl create mode 100644 play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardCallbacks.aidl create mode 100644 play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardHandle.aidl create mode 100644 play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardService.aidl create mode 100644 play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardInitReply.java create mode 100644 play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.java create mode 100644 play-services-droidguard/build.gradle create mode 100644 play-services-droidguard/src/main/AndroidManifest.xml create mode 100644 play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardApiClient.kt create mode 100644 play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClient.kt create mode 100644 play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClientImpl.kt create mode 100644 play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardHandle.kt diff --git a/play-services-droidguard-api/build.gradle b/play-services-droidguard-api/build.gradle new file mode 100644 index 00000000..41829087 --- /dev/null +++ b/play-services-droidguard-api/build.gradle @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } +} + +apply from: '../gradle/publish-android.gradle' + +description = 'microG API for play-services-droidguard' + +dependencies { + api project(':play-services-basement') + api project(':play-services-base-api') + + implementation "androidx.annotation:annotation:$annotationVersion" +} diff --git a/play-services-droidguard-api/src/main/AndroidManifest.xml b/play-services-droidguard-api/src/main/AndroidManifest.xml new file mode 100644 index 00000000..30563293 --- /dev/null +++ b/play-services-droidguard-api/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardInitReply.aidl b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardInitReply.aidl new file mode 100644 index 00000000..48cd159f --- /dev/null +++ b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardInitReply.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.droidguard.internal; + +parcelable DroidGuardInitReply; diff --git a/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.aidl b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.aidl new file mode 100644 index 00000000..9fff3a8d --- /dev/null +++ b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.droidguard.internal; + +parcelable DroidGuardResultsRequest; diff --git a/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardCallbacks.aidl b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardCallbacks.aidl new file mode 100644 index 00000000..7e4aa680 --- /dev/null +++ b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardCallbacks.aidl @@ -0,0 +1,5 @@ +package com.google.android.gms.droidguard.internal; + +interface IDroidGuardCallbacks { + void onResult(in byte[] res); +} diff --git a/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardHandle.aidl b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardHandle.aidl new file mode 100644 index 00000000..649f2fd8 --- /dev/null +++ b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardHandle.aidl @@ -0,0 +1,13 @@ +package com.google.android.gms.droidguard.internal; + +import com.google.android.gms.droidguard.internal.DroidGuardInitReply; +import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest; + +interface IDroidGuardHandle { + void init(String flow) = 0; + DroidGuardInitReply initWithRequest(String flow, in DroidGuardResultsRequest request) = 4; + + byte[] guard(in Map map) = 1; + + void close() = 2; +} diff --git a/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardService.aidl b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardService.aidl new file mode 100644 index 00000000..2e604453 --- /dev/null +++ b/play-services-droidguard-api/src/main/aidl/com/google/android/gms/droidguard/internal/IDroidGuardService.aidl @@ -0,0 +1,14 @@ +package com.google.android.gms.droidguard.internal; + +import com.google.android.gms.droidguard.internal.IDroidGuardCallbacks; +import com.google.android.gms.droidguard.internal.IDroidGuardHandle; +import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest; + +interface IDroidGuardService { + void guard(IDroidGuardCallbacks callbacks, String flow, in Map map) = 0; + void guardWithRequest(IDroidGuardCallbacks callbacks, String flow, in Map map, in DroidGuardResultsRequest request) = 3; + + IDroidGuardHandle getHandle() = 1; + + int getClientTimeoutMillis() = 2; +} diff --git a/play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardInitReply.java b/play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardInitReply.java new file mode 100644 index 00000000..02b93b0d --- /dev/null +++ b/play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardInitReply.java @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.droidguard.internal; + +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; + +public class DroidGuardInitReply implements Parcelable { + public ParcelFileDescriptor pfd; + public Parcelable object; + + public DroidGuardInitReply(ParcelFileDescriptor pfd, Parcelable object) { + this.pfd = pfd; + this.object = object; + } + + @Override + public int describeContents() { + return (pfd != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0) | (object != null ? object.describeContents() : 0); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(pfd, flags); + dest.writeParcelable(object, flags); + } + + public final static Creator CREATOR = new Creator() { + @Override + public DroidGuardInitReply createFromParcel(Parcel source) { + ParcelFileDescriptor pfd = source.readParcelable(ParcelFileDescriptor.class.getClassLoader()); + Parcelable object = source.readParcelable(getClass().getClassLoader()); + if (pfd != null && object != null) { + return new DroidGuardInitReply(pfd, object); + } + return null; + } + + @Override + public DroidGuardInitReply[] newArray(int size) { + return new DroidGuardInitReply[size]; + } + }; +} diff --git a/play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.java b/play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.java new file mode 100644 index 00000000..162a6bfc --- /dev/null +++ b/play-services-droidguard-api/src/main/java/com/google/android/gms/droidguard/internal/DroidGuardResultsRequest.java @@ -0,0 +1,93 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.droidguard.internal; + +import android.net.Network; +import android.os.Build; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +import androidx.annotation.RequiresApi; + +import org.microg.gms.common.Constants; +import org.microg.safeparcel.AutoSafeParcelable; + +public class DroidGuardResultsRequest extends AutoSafeParcelable { + private static final String KEY_APP_ARCHITECTURE = "appArchitecture"; + private static final String KEY_CLIENT_VERSION = "clientVersion"; + private static final String KEY_FD = "fd"; + private static final String KEY_NETWORK_TO_USE = "networkToUse"; + private static final String KEY_TIMEOUT_MS = "timeoutMs"; + public static final String KEY_OPEN_HANDLES = "openHandles"; + + @Field(2) + public Bundle bundle; + + public DroidGuardResultsRequest() { + bundle = new Bundle(); + String arch; + try { + arch = System.getProperty("os.arch"); + } catch (Exception ignored) { + arch = "?"; + } + bundle.putString(KEY_APP_ARCHITECTURE, arch); + setClientVersion(Constants.GMS_VERSION_CODE); + } + + public String getAppArchitecture() { + return bundle.getString(KEY_APP_ARCHITECTURE); + } + + public int getTimeoutMillis() { + return bundle.getInt(KEY_TIMEOUT_MS, 60000); + } + + public DroidGuardResultsRequest setTimeoutMillis(int millis) { + bundle.putInt(KEY_TIMEOUT_MS, millis); + return this; + } + + public int getClientVersion() { + return bundle.getInt(KEY_CLIENT_VERSION); + } + + public DroidGuardResultsRequest setClientVersion(int clientVersion) { + bundle.putInt(KEY_CLIENT_VERSION, clientVersion); + return this; + } + + public ParcelFileDescriptor getFd() { + return bundle.getParcelable(KEY_FD); + } + + public DroidGuardResultsRequest setFd(ParcelFileDescriptor fd) { + bundle.putParcelable(KEY_FD, fd); + return this; + } + + public int getOpenHandles() { + return bundle.getInt(KEY_OPEN_HANDLES); + } + + public DroidGuardResultsRequest setOpenHandles(int openHandles) { + bundle.putInt(KEY_OPEN_HANDLES, openHandles); + return this; + } + + @RequiresApi(api = 21) + public Network getNetworkToUse() { + return bundle.getParcelable(KEY_NETWORK_TO_USE); + } + + @RequiresApi(api = 21) + public DroidGuardResultsRequest setNetworkToUse(Network networkToUse) { + bundle.putParcelable(KEY_NETWORK_TO_USE, networkToUse); + return this; + } + + public static final Creator CREATOR = new AutoCreator<>(DroidGuardResultsRequest.class); +} diff --git a/play-services-droidguard/build.gradle b/play-services-droidguard/build.gradle new file mode 100644 index 00000000..a473f2d5 --- /dev/null +++ b/play-services-droidguard/build.gradle @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +android { + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } +} + +apply from: '../gradle/publish-android.gradle' + +description = 'microG implementation of play-services-droidguard' + +dependencies { + api project(':play-services-base') + api project(':play-services-droidguard-api') + + implementation "androidx.annotation:annotation:$annotationVersion" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" +} diff --git a/play-services-droidguard/src/main/AndroidManifest.xml b/play-services-droidguard/src/main/AndroidManifest.xml new file mode 100644 index 00000000..4d4b7873 --- /dev/null +++ b/play-services-droidguard/src/main/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardApiClient.kt b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardApiClient.kt new file mode 100644 index 00000000..a9aa7a85 --- /dev/null +++ b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardApiClient.kt @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.droidguard + +import android.content.Context +import android.os.IBinder +import com.google.android.gms.droidguard.internal.IDroidGuardHandle +import com.google.android.gms.droidguard.internal.IDroidGuardService +import org.microg.gms.common.GmsClient +import org.microg.gms.common.GmsService +import org.microg.gms.common.api.ConnectionCallbacks +import org.microg.gms.common.api.OnConnectionFailedListener + +class DroidGuardApiClient(context: Context, connectionCallbacks: ConnectionCallbacks, onConnectionFailedListener: OnConnectionFailedListener) : GmsClient(context, connectionCallbacks, onConnectionFailedListener, GmsService.DROIDGUARD.ACTION) { + init { + serviceId = GmsService.DROIDGUARD.SERVICE_ID + } + + override fun interfaceFromBinder(binder: IBinder): IDroidGuardService = IDroidGuardService.Stub.asInterface(binder) + + fun getHandle() = serviceInterface.handle +} diff --git a/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClient.kt b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClient.kt new file mode 100644 index 00000000..adcaa45b --- /dev/null +++ b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClient.kt @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.droidguard + +import com.google.android.gms.droidguard.internal.IDroidGuardHandle +import com.google.android.gms.tasks.Task + +interface DroidGuardClient { + fun getHandle(): Task +} diff --git a/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClientImpl.kt b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClientImpl.kt new file mode 100644 index 00000000..49f3fc63 --- /dev/null +++ b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardClientImpl.kt @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.droidguard + +import android.content.Context +import android.os.Looper +import com.google.android.gms.common.api.Api +import com.google.android.gms.common.api.Api.ApiOptions.NoOptions +import com.google.android.gms.common.api.GoogleApi +import com.google.android.gms.tasks.Task +import org.microg.gms.common.api.ApiClientBuilder +import org.microg.gms.common.api.ApiClientSettings +import org.microg.gms.common.api.ConnectionCallbacks +import org.microg.gms.common.api.OnConnectionFailedListener + +class DroidGuardClientImpl(context: Context) : GoogleApi(context, API), DroidGuardClient { + companion object { + private val API = Api(ApiClientBuilder { _: NoOptions?, context: Context, _: Looper?, _: ApiClientSettings?, callbacks: ConnectionCallbacks, connectionFailedListener: OnConnectionFailedListener -> DroidGuardApiClient(context, callbacks, connectionFailedListener) }) + } + + override fun getHandle(): Task { + return scheduleTask { client: DroidGuardApiClient, completionSource -> + try { + completionSource.setResult(DroidGuardHandle(client.getHandle())) + } catch (e: Exception) { + completionSource.setException(e) + } + } + } +} diff --git a/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardHandle.kt b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardHandle.kt new file mode 100644 index 00000000..07e4fd39 --- /dev/null +++ b/play-services-droidguard/src/main/kotlin/org/microg/gms/droidguard/DroidGuardHandle.kt @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.droidguard + +import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest +import com.google.android.gms.droidguard.internal.IDroidGuardHandle + +class DroidGuardHandle(private val handle: IDroidGuardHandle) { + private var state = 0 + + fun init(flow: String) { + if (state != 0) throw IllegalStateException("init() already called") + try { + handle.initWithRequest(flow, DroidGuardResultsRequest().setOpenHandles(openHandles++)) + state = 1 + } catch (e: Exception) { + state = -1 + throw e + } + } + + fun guard(map: Map): ByteArray { + if (state != 1) throw IllegalStateException("init() must be called before guard()") + try { + return handle.guard(map) + } catch (e: Exception) { + state = -1 + throw e + } + } + + fun close() { + if (state != 1) throw IllegalStateException("init() must be called before close()") + try { + handle.close() + openHandles-- + state = 2 + } catch (e: Exception) { + state = -1 + throw e + } + } + + fun finalize() { + if (state == 1) close() + } + + companion object { + private var openHandles = 0 + } +} diff --git a/settings.gradle b/settings.gradle index 1ed21081..cdbee860 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,7 @@ include ':play-services-appinvite-api' include ':play-services-base-api' include ':play-services-cast-api' include ':play-services-cast-framework-api' +include ':play-services-droidguard-api' include ':play-services-iid-api' include ':play-services-location-api' include ':play-services-nearby-api' @@ -41,6 +42,7 @@ include ':play-services-core' include ':play-services-base' include ':play-services-cast' +include ':play-services-droidguard' include ':play-services-gcm' include ':play-services-iid' include ':play-services-location' From 83a150b1287d0a615b9a725270ba6d7847c20731 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 21 Feb 2021 16:24:22 -0600 Subject: [PATCH 18/29] EN: Add API for API version 1.8 --- .../INearbyExposureNotificationService.aidl | 4 ++ ...izedTemporaryExposureKeyHistoryParams.aidl | 8 ++++ ...izedTemporaryExposureKeyReleaseParams.aidl | 8 ++++ ...izedTemporaryExposureKeyHistoryParams.java | 24 ++++++++++ ...izedTemporaryExposureKeyReleaseParams.java | 24 ++++++++++ .../exposurenotification/Constants.java | 2 + .../ExposureNotificationServiceImpl.kt | 14 ++++++ .../ExposureNotificationClient.java | 22 ++++++++++ .../nearby/ExposureNotificationApiClient.java | 10 +++++ .../ExposureNotificationClientImpl.java | 44 +++++++++++++++++++ 10 files changed, 160 insertions(+) create mode 100644 play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.aidl create mode 100644 play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.aidl create mode 100644 play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.java create mode 100644 play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.java diff --git a/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/INearbyExposureNotificationService.aidl b/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/INearbyExposureNotificationService.aidl index fad4f590..80f7e6ae 100644 --- a/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/INearbyExposureNotificationService.aidl +++ b/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/INearbyExposureNotificationService.aidl @@ -20,6 +20,8 @@ import com.google.android.gms.nearby.exposurenotification.internal.SetDiagnosisK import com.google.android.gms.nearby.exposurenotification.internal.GetDiagnosisKeysDataMappingParams; import com.google.android.gms.nearby.exposurenotification.internal.GetStatusParams; import com.google.android.gms.nearby.exposurenotification.internal.GetPackageConfigurationParams; +import com.google.android.gms.nearby.exposurenotification.internal.RequestPreAuthorizedTemporaryExposureKeyHistoryParams; +import com.google.android.gms.nearby.exposurenotification.internal.RequestPreAuthorizedTemporaryExposureKeyReleaseParams; interface INearbyExposureNotificationService{ void start(in StartParams params) = 0; @@ -39,4 +41,6 @@ interface INearbyExposureNotificationService{ void getDiagnosisKeysDataMapping(in GetDiagnosisKeysDataMappingParams params) = 17; void getStatus(in GetStatusParams params) = 18; void getPackageConfiguration(in GetPackageConfigurationParams params) = 19; + void requestPreAuthorizedTemporaryExposureKeyHistory(in RequestPreAuthorizedTemporaryExposureKeyHistoryParams params) = 20; + void requestPreAuthorizedTemporaryExposureKeyRelease(in RequestPreAuthorizedTemporaryExposureKeyReleaseParams params) = 21; } diff --git a/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.aidl b/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.aidl new file mode 100644 index 00000000..49846674 --- /dev/null +++ b/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.nearby.exposurenotification.internal; + +parcelable RequestPreAuthorizedTemporaryExposureKeyHistoryParams; diff --git a/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.aidl b/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.aidl new file mode 100644 index 00000000..21973e29 --- /dev/null +++ b/play-services-nearby-api/src/main/aidl/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.aidl @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.nearby.exposurenotification.internal; + +parcelable RequestPreAuthorizedTemporaryExposureKeyReleaseParams; diff --git a/play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.java b/play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.java new file mode 100644 index 00000000..ebd807bf --- /dev/null +++ b/play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyHistoryParams.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.nearby.exposurenotification.internal; + +import com.google.android.gms.common.api.internal.IStatusCallback; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class RequestPreAuthorizedTemporaryExposureKeyHistoryParams extends AutoSafeParcelable { + @Field(1) + public IStatusCallback callback; + + private RequestPreAuthorizedTemporaryExposureKeyHistoryParams() { + } + + public RequestPreAuthorizedTemporaryExposureKeyHistoryParams(IStatusCallback callback) { + this.callback = callback; + } + + public static final Creator CREATOR = new AutoCreator<>(RequestPreAuthorizedTemporaryExposureKeyHistoryParams.class); +} diff --git a/play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.java b/play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.java new file mode 100644 index 00000000..428cd642 --- /dev/null +++ b/play-services-nearby-api/src/main/java/com/google/android/gms/nearby/exposurenotification/internal/RequestPreAuthorizedTemporaryExposureKeyReleaseParams.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.nearby.exposurenotification.internal; + +import com.google.android.gms.common.api.internal.IStatusCallback; + +import org.microg.safeparcel.AutoSafeParcelable; + +public class RequestPreAuthorizedTemporaryExposureKeyReleaseParams extends AutoSafeParcelable { + @Field(1) + public IStatusCallback callback; + + private RequestPreAuthorizedTemporaryExposureKeyReleaseParams() { + } + + public RequestPreAuthorizedTemporaryExposureKeyReleaseParams(IStatusCallback callback) { + this.callback = callback; + } + + public static final Creator CREATOR = new AutoCreator<>(RequestPreAuthorizedTemporaryExposureKeyReleaseParams.class); +} diff --git a/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java b/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java index b87dbfbe..11d7fdc2 100644 --- a/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java +++ b/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java @@ -9,9 +9,11 @@ public class Constants { public static final String ACTION_EXPOSURE_NOTIFICATION_SETTINGS = "com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS"; public static final String ACTION_EXPOSURE_NOT_FOUND = "com.google.android.gms.exposurenotification.ACTION_EXPOSURE_NOT_FOUND"; public static final String ACTION_EXPOSURE_STATE_UPDATED = "com.google.android.gms.exposurenotification.ACTION_EXPOSURE_STATE_UPDATED"; + public static final String ACTION_PRE_AUTHORIZE_RELEASE_PHONE_UNLOCKED = "com.google.android.gms.exposurenotification.ACTION_PRE_AUTHORIZE_RELEASE_PHONE_UNLOCKED"; public static final String ACTION_SERVICE_STATE_UPDATED = "com.google.android.gms.exposurenotification.ACTION_SERVICE_STATE_UPDATED"; public static final String EXTRA_EXPOSURE_SUMMARY = "com.google.android.gms.exposurenotification.EXTRA_EXPOSURE_SUMMARY"; public static final String EXTRA_SERVICE_STATE = "com.google.android.gms.exposurenotification.EXTRA_SERVICE_STATE"; + public static final String EXTRA_TEMPORARY_EXPOSURE_KEY_LIST = "com.google.android.gms.exposurenotification.EXTRA_TEMPORARY_EXPOSURE_KEY_LIST"; public static final String EXTRA_TOKEN = "com.google.android.gms.exposurenotification.EXTRA_TOKEN"; public static final String TOKEN_A = "TYZWQ32170AXEUVCDW7A"; public static final int DAYS_SINCE_ONSET_OF_SYMPTOMS_UNKNOWN = Integer.MAX_VALUE; diff --git a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt index ad14bf16..43bd8496 100644 --- a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt +++ b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt @@ -634,6 +634,20 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } } + override fun requestPreAuthorizedTemporaryExposureKeyHistory(params: RequestPreAuthorizedTemporaryExposureKeyHistoryParams) { + // TODO: Proper implementation + lifecycleScope.launchSafely { + params.callback.onResult(Status.CANCELED) + } + } + + override fun requestPreAuthorizedTemporaryExposureKeyRelease(params: RequestPreAuthorizedTemporaryExposureKeyReleaseParams) { + // TODO: Proper implementation + lifecycleScope.launchSafely { + params.callback.onResult(Status.CANCELED) + } + } + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { if (super.onTransact(code, data, reply, flags)) return true Log.d(TAG, "onTransact [unknown]: $code, $data, $flags") diff --git a/play-services-nearby/src/main/java/com/google/android/gms/nearby/exposurenotification/ExposureNotificationClient.java b/play-services-nearby/src/main/java/com/google/android/gms/nearby/exposurenotification/ExposureNotificationClient.java index 177295af..6fa55d94 100644 --- a/play-services-nearby/src/main/java/com/google/android/gms/nearby/exposurenotification/ExposureNotificationClient.java +++ b/play-services-nearby/src/main/java/com/google/android/gms/nearby/exposurenotification/ExposureNotificationClient.java @@ -38,6 +38,12 @@ public interface ExposureNotificationClient extends HasApiKey provideDiagnosisKeys(List keys, ExposureConfiguration configuration, String token); + /** + * Shows a dialog to the user asking for authorization to get {@link TemporaryExposureKey}s in the background. + *

+ * If approved, the client application will be able to call {@link #requestPreAuthorizedTemporaryExposureKeyRelease()} one time in the next 5 days to get a list of {@link TemporaryExposureKey}s for a user which has tested positive. + */ + Task requestPreAuthorizedTemporaryExposureKeyHistory(); + + /** + * If consent has previously been requested and granted by the user using {@link #requestPreAuthorizedTemporaryExposureKeyHistory()}, then this method will cause keys to be released to the client application after the screen is unlocked by the user. Keys will be delivered via a broadcast denoted with the {@link #ACTION_PRE_AUTHORIZE_RELEASE_PHONE_UNLOCKED} action. + */ + Task requestPreAuthorizedTemporaryExposureKeyRelease (); + /** * Sets the diagnosis keys data mapping if it wasn't already changed recently. *

diff --git a/play-services-nearby/src/main/java/org/microg/gms/nearby/ExposureNotificationApiClient.java b/play-services-nearby/src/main/java/org/microg/gms/nearby/ExposureNotificationApiClient.java index f8ecba7b..641fda01 100644 --- a/play-services-nearby/src/main/java/org/microg/gms/nearby/ExposureNotificationApiClient.java +++ b/play-services-nearby/src/main/java/org/microg/gms/nearby/ExposureNotificationApiClient.java @@ -22,6 +22,8 @@ import com.google.android.gms.nearby.exposurenotification.internal.GetVersionPar import com.google.android.gms.nearby.exposurenotification.internal.INearbyExposureNotificationService; import com.google.android.gms.nearby.exposurenotification.internal.IsEnabledParams; import com.google.android.gms.nearby.exposurenotification.internal.ProvideDiagnosisKeysParams; +import com.google.android.gms.nearby.exposurenotification.internal.RequestPreAuthorizedTemporaryExposureKeyHistoryParams; +import com.google.android.gms.nearby.exposurenotification.internal.RequestPreAuthorizedTemporaryExposureKeyReleaseParams; import com.google.android.gms.nearby.exposurenotification.internal.SetDiagnosisKeysDataMappingParams; import com.google.android.gms.nearby.exposurenotification.internal.StartParams; import com.google.android.gms.nearby.exposurenotification.internal.StopParams; @@ -101,4 +103,12 @@ public class ExposureNotificationApiClient extends GmsClient requestPreAuthorizedTemporaryExposureKeyHistory() { + return scheduleTask((PendingGoogleApiCall) (client, completionSource) -> { + RequestPreAuthorizedTemporaryExposureKeyHistoryParams params = new RequestPreAuthorizedTemporaryExposureKeyHistoryParams(new IStatusCallback.Stub() { + @Override + public void onResult(Status status) { + if (status.isSuccess()) { + completionSource.setResult(null); + } else { + completionSource.setException(new ApiException(status)); + } + } + }); + try { + client.requestPreAuthorizedTemporaryExposureKeyHistory(params); + } catch (Exception e) { + completionSource.setException(e); + } + }); + } + + @Override + public Task requestPreAuthorizedTemporaryExposureKeyRelease() { + return scheduleTask((PendingGoogleApiCall) (client, completionSource) -> { + RequestPreAuthorizedTemporaryExposureKeyReleaseParams params = new RequestPreAuthorizedTemporaryExposureKeyReleaseParams(new IStatusCallback.Stub() { + @Override + public void onResult(Status status) { + if (status.isSuccess()) { + completionSource.setResult(null); + } else { + completionSource.setException(new ApiException(status)); + } + } + }); + try { + client.requestPreAuthorizedTemporaryExposureKeyRelease(params); + } catch (Exception e) { + completionSource.setException(e); + } + }); + } + @Override public boolean deviceSupportsLocationlessScanning() { return false; From 412e513afe8e27c4d183c23e05f27ab752ead0b2 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 21 Feb 2021 16:39:12 -0600 Subject: [PATCH 19/29] EN: Implement new versioning scheme of API 1.8 --- .../org/microg/gms/nearby/exposurenotification/Constants.java | 4 ++++ .../exposurenotification/ExposureNotificationServiceImpl.kt | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java b/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java index 11d7fdc2..5c40b145 100644 --- a/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java +++ b/play-services-nearby-api/src/main/java/org/microg/gms/nearby/exposurenotification/Constants.java @@ -5,6 +5,8 @@ package org.microg.gms.nearby.exposurenotification; +import static org.microg.gms.common.Constants.GMS_VERSION_CODE; + public class Constants { public static final String ACTION_EXPOSURE_NOTIFICATION_SETTINGS = "com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS"; public static final String ACTION_EXPOSURE_NOT_FOUND = "com.google.android.gms.exposurenotification.ACTION_EXPOSURE_NOT_FOUND"; @@ -17,4 +19,6 @@ public class Constants { public static final String EXTRA_TOKEN = "com.google.android.gms.exposurenotification.EXTRA_TOKEN"; public static final String TOKEN_A = "TYZWQ32170AXEUVCDW7A"; public static final int DAYS_SINCE_ONSET_OF_SYMPTOMS_UNKNOWN = Integer.MAX_VALUE; + public static final int VERSION = 18; + public static final long VERSION_FULL = ((long)VERSION) * 1000000000L + GMS_VERSION_CODE; } diff --git a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt index 43bd8496..566a0f9a 100644 --- a/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt +++ b/play-services-nearby-core/src/main/kotlin/org/microg/gms/nearby/exposurenotification/ExposureNotificationServiceImpl.kt @@ -22,7 +22,6 @@ import com.google.android.gms.nearby.exposurenotification.internal.* import kotlinx.coroutines.* import org.json.JSONArray import org.json.JSONObject -import org.microg.gms.common.Constants import org.microg.gms.common.PackageUtils import org.microg.gms.nearby.exposurenotification.Constants.* import org.microg.gms.nearby.exposurenotification.proto.TemporaryExposureKeyExport @@ -96,7 +95,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val } override fun getVersion(params: GetVersionParams) { - params.callback.onResult(Status.SUCCESS, Constants.GMS_VERSION_CODE.toLong()) + params.callback.onResult(Status.SUCCESS, VERSION_FULL) } override fun getCalibrationConfidence(params: GetCalibrationConfidenceParams) { From 5b50e24636ef4963e0f43316050c4491cadc4d96 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Sat, 20 Feb 2021 18:06:58 +0100 Subject: [PATCH 20/29] EN: Hide legend if no data is to be displayed --- .../org/microg/gms/nearby/core/ui/DotChartView.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt index 9946dc3b..76e5590d 100644 --- a/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt +++ b/play-services-nearby-core-ui/src/main/kotlin/org/microg/gms/nearby/core/ui/DotChartView.kt @@ -181,13 +181,15 @@ class DotChartView : View { fontPaint.getTextBounds(strNoRecords, 0, strNoRecords.length, fontTempRect) canvas.drawText(strNoRecords, (subLeft + perWidth + 4 * d).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) - canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 1 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and 0xffffff) - canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 2 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (80 shl 24 or 0xffffff)) - canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 3 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (170 shl 24 or 0xffffff)) - canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 4 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor) + if (maxValue >= 0) { + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 1 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and 0xffffff) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 2 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (80 shl 24 or 0xffffff)) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 3 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor and (170 shl 24 or 0xffffff)) + canvas.drawMyRect((subLeft + (perWidth + innerPadding) * 4 + 12 * d + fontTempRect.width()).toFloat(), subTop.toFloat(), perWidth.toFloat(), perHeight.toFloat(), accentColor) - val strRecords = context.getString(R.string.pref_exposure_rpis_histogram_legend_records, "0 - $maxValue") - canvas.drawText(strRecords, (subLeft + (perWidth + innerPadding) * 4 + 16 * d + fontTempRect.width() + perWidth).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) + val strRecords = context.getString(R.string.pref_exposure_rpis_histogram_legend_records, "0 - $maxValue") + canvas.drawText(strRecords, (subLeft + (perWidth + innerPadding) * 4 + 16 * d + fontTempRect.width() + perWidth).toFloat(), (subTop + perHeight / 2.0 + fontTempRect.height() / 2.0).toFloat(), fontPaint) + } if (focusHour != -1 && Build.VERSION.SDK_INT >= 23) { val floatingColor = context.resolveColor(androidx.appcompat.R.attr.colorBackgroundFloating) ?: 0 From 87465cd1dda13af39bfbe9f3ca11f259458d2820 Mon Sep 17 00:00:00 2001 From: Fynn Godau Date: Sat, 20 Feb 2021 16:54:17 +0100 Subject: [PATCH 21/29] Add missing method to GoogleApiAvailability --- .../android/gms/common/GoogleApiAvailability.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java index 889cc7f6..30afec09 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java @@ -163,6 +163,19 @@ public class GoogleApiAvailability { return SUCCESS; } + /** + * Verifies that Google Play services is installed and enabled on this device, and that the version installed on + * this device is no older than the one required by this client or the version is not older than the one specified + * in minApkVersion. + * + * @return status code indicating whether there was an error. Can be one of following in + * {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_UPDATING, + * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID + */ + public int isGooglePlayServicesAvailable(Context context, int minApkVersion) { + return isGooglePlayServicesAvailable(context); + } + /** * Determines whether an error can be resolved via user action. If true, proceed by calling * {@link #getErrorDialog(Activity, int, int)} and showing the dialog. From ecfe3da3f7573e3d66a1ad9c12e8a08bbeae307b Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 9 Mar 2021 22:21:25 +0100 Subject: [PATCH 22/29] Wakeful receivers only handle non-null intents --- .../java/org/microg/gms/checkin/CheckinService.java | 4 +++- .../src/main/java/org/microg/gms/gcm/McsService.java | 2 +- .../kotlin/org/microg/gms/gcm/PushRegisterService.kt | 11 ++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java index 06d98af2..125d5ebb 100644 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java @@ -95,7 +95,9 @@ public class CheckinService extends IntentService { } catch (Exception e) { Log.w(TAG, e); } finally { - WakefulBroadcastReceiver.completeWakefulIntent(intent); + if (intent != null) { + WakefulBroadcastReceiver.completeWakefulIntent(intent); + } schedule(this); stopSelf(); } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java index e5c6bb03..55ee3b7a 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java @@ -320,7 +320,7 @@ public class McsService extends Service implements Handler.Callback { WakefulBroadcastReceiver.completeWakefulIntent(intent); } else if (connectIntent == null) { connectIntent = intent; - } else { + } else if (intent != null) { WakefulBroadcastReceiver.completeWakefulIntent(intent); } } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt index 01dd1a58..02fe6f0a 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt @@ -112,11 +112,12 @@ class PushRegisterService : LifecycleService() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - WakefulBroadcastReceiver.completeWakefulIntent(intent) - Log.d(TAG, "onStartCommand: $intent") - lifecycleScope.launchWhenStarted { - if (intent == null) return@launchWhenStarted - handleIntent(intent) + if (intent != null) { + WakefulBroadcastReceiver.completeWakefulIntent(intent) + Log.d(TAG, "onStartCommand: $intent") + lifecycleScope.launchWhenStarted { + handleIntent(intent) + } } return super.onStartCommand(intent, flags, startId) } From 1516af439557ca99a6cd860a36f1528b8decedbd Mon Sep 17 00:00:00 2001 From: Marvin W Date: Thu, 11 Mar 2021 10:14:29 +0100 Subject: [PATCH 23/29] Move EN permission to withNearby flavour --- play-services-core/src/main/AndroidManifest.xml | 4 ---- play-services-core/src/withNearby/AndroidManifest.xml | 10 ++++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 play-services-core/src/withNearby/AndroidManifest.xml diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 93d8a6c5..ac3b2254 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -57,10 +57,6 @@ android:label="@string/permission_service_writely_label" android:protectionLevel="dangerous" /> - - + + + + From c9e09e97140730ddd3d328b7f19536285f74da71 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 17 Mar 2021 19:21:59 +0100 Subject: [PATCH 24/29] Use Cursor.getColumnIndexOrThrow() --- .../java/org/microg/gms/gcm/GcmDatabase.java | 37 +++++++------------ .../org/microg/gms/people/PeopleManager.java | 2 +- .../wearable/ConfigurationDatabaseHelper.java | 12 +++--- .../gms/wearable/NodeDatabaseHelper.java | 4 +- 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java index bc524047..ce32d8d3 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java @@ -1,17 +1,6 @@ /* - * Copyright (C) 2013-2017 microG Project Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 */ package org.microg.gms.gcm; @@ -81,13 +70,13 @@ public class GcmDatabase extends SQLiteOpenHelper { public final boolean wakeForDelivery; private App(Cursor cursor) { - packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME)); - lastError = cursor.getString(cursor.getColumnIndex(FIELD_LAST_ERROR)); - lastMessageTimestamp = cursor.getLong(cursor.getColumnIndex(FIELD_LAST_MESSAGE_TIMESTAMP)); - totalMessageCount = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_COUNT)); - totalMessageBytes = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_BYTES)); - allowRegister = cursor.getLong(cursor.getColumnIndex(FIELD_ALLOW_REGISTER)) == 1; - wakeForDelivery = cursor.getLong(cursor.getColumnIndex(FIELD_WAKE_FOR_DELIVERY)) == 1; + packageName = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_PACKAGE_NAME)); + lastError = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_LAST_ERROR)); + lastMessageTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_LAST_MESSAGE_TIMESTAMP)); + totalMessageCount = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_TOTAL_MESSAGE_COUNT)); + totalMessageBytes = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_TOTAL_MESSAGE_BYTES)); + allowRegister = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_ALLOW_REGISTER)) == 1; + wakeForDelivery = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_WAKE_FOR_DELIVERY)) == 1; } public boolean hasError() { @@ -102,10 +91,10 @@ public class GcmDatabase extends SQLiteOpenHelper { public final String registerId; public Registration(Cursor cursor) { - packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME)); - signature = cursor.getString(cursor.getColumnIndex(FIELD_SIGNATURE)); - timestamp = cursor.getLong(cursor.getColumnIndex(FIELD_TIMESTAMP)); - registerId = cursor.getString(cursor.getColumnIndex(FIELD_REGISTER_ID)); + packageName = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_PACKAGE_NAME)); + signature = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_SIGNATURE)); + timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_TIMESTAMP)); + registerId = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_REGISTER_ID)); } } diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java index 1450fba3..2fa79314 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java @@ -51,7 +51,7 @@ public class PeopleManager { String url = null; if (cursor.moveToNext()) { int idx = cursor.getColumnIndex("avatar"); - if (!cursor.isNull(idx)) url = cursor.getString(idx); + if (idx >= 0 && !cursor.isNull(idx)) url = cursor.getString(idx); } cursor.close(); databaseHelper.close(); diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/ConfigurationDatabaseHelper.java b/play-services-core/src/main/java/org/microg/gms/wearable/ConfigurationDatabaseHelper.java index 01c8a1a8..4aa4b58b 100644 --- a/play-services-core/src/main/java/org/microg/gms/wearable/ConfigurationDatabaseHelper.java +++ b/play-services-core/src/main/java/org/microg/gms/wearable/ConfigurationDatabaseHelper.java @@ -49,12 +49,12 @@ public class ConfigurationDatabaseHelper extends SQLiteOpenHelper { } private static ConnectionConfiguration configFromCursor(final Cursor cursor) { - String name = cursor.getString(cursor.getColumnIndex("name")); - String pairedBtAddress = cursor.getString(cursor.getColumnIndex("pairedBtAddress")); - int connectionType = cursor.getInt(cursor.getColumnIndex("connectionType")); - int role = cursor.getInt(cursor.getColumnIndex("role")); - int enabled = cursor.getInt(cursor.getColumnIndex("connectionEnabled")); - String nodeId = cursor.getString(cursor.getColumnIndex("nodeId")); + String name = cursor.getString(cursor.getColumnIndexOrThrow("name")); + String pairedBtAddress = cursor.getString(cursor.getColumnIndexOrThrow("pairedBtAddress")); + int connectionType = cursor.getInt(cursor.getColumnIndexOrThrow("connectionType")); + int role = cursor.getInt(cursor.getColumnIndexOrThrow("role")); + int enabled = cursor.getInt(cursor.getColumnIndexOrThrow("connectionEnabled")); + String nodeId = cursor.getString(cursor.getColumnIndexOrThrow("nodeId")); if (NULL_STRING.equals(name)) name = null; if (NULL_STRING.equals(pairedBtAddress)) pairedBtAddress = null; return new ConnectionConfiguration(name, pairedBtAddress, connectionType, role, enabled > 0, nodeId); diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java b/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java index d0bfe058..8b86fee1 100644 --- a/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java +++ b/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java @@ -289,8 +289,8 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper { Cursor status = db.query("assetsReadyStatus", null, "nowReady != markedReady", null, null, null, null); while (status.moveToNext()) { cv = new ContentValues(); - cv.put("assetsPresent", status.getInt(status.getColumnIndex("nowReady"))); - db.update("dataitems", cv, "_id=?", new String[]{Integer.toString(status.getInt(status.getColumnIndex("dataitems_id")))}); + cv.put("assetsPresent", status.getInt(status.getColumnIndexOrThrow("nowReady"))); + db.update("dataitems", cv, "_id=?", new String[]{Integer.toString(status.getInt(status.getColumnIndexOrThrow("dataitems_id")))}); } status.close(); } From ff1f879ab6ffd940aaeaa1bf86640177c77fd880 Mon Sep 17 00:00:00 2001 From: Marcus Hoffmann Date: Mon, 8 Mar 2021 00:46:53 +0100 Subject: [PATCH 25/29] add deeplink to rpi information screen CCTG could use this when interacting with external microG. Instead of directly embedding the fragment through the bottomNavigationBar we can at least jump to the correct target location. Doing this through an implicit deeplink intent seems easiest as an explicit intent adds all the top level nav elements to the backstack (see: https://developer.android.com/guide/navigation/navigation-deep-link). Tested and this works pretty nicely :). --- play-services-core/src/main/AndroidManifest.xml | 7 +++++++ .../src/main/res/navigation/nav_nearby.xml | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index ac3b2254..8747be32 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -448,6 +448,13 @@ android:label="@string/gms_settings_name" android:process=":ui" android:roundIcon="@mipmap/ic_microg_settings"> + + + + + diff --git a/play-services-nearby-core-ui/src/main/res/navigation/nav_nearby.xml b/play-services-nearby-core-ui/src/main/res/navigation/nav_nearby.xml index 8b27a3d4..47e74b85 100644 --- a/play-services-nearby-core-ui/src/main/res/navigation/nav_nearby.xml +++ b/play-services-nearby-core-ui/src/main/res/navigation/nav_nearby.xml @@ -26,7 +26,11 @@ + android:label="@string/pref_exposure_collected_rpis_title"> + + Date: Thu, 18 Mar 2021 01:19:46 +0100 Subject: [PATCH 26/29] Fix typo --- .../src/main/java/org/microg/gms/people/PeopleManager.java | 4 ++-- .../main/java/org/microg/gms/people/PeopleServiceImpl.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java index 2fa79314..ccc2442a 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java @@ -45,7 +45,7 @@ public class PeopleManager { public static final String USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; public static final String REGEX_SEARCH_USER_PHOTO = "https?\\:\\/\\/lh([0-9]*)\\.googleusercontent\\.com/"; - public static File getOwnerAvaterFile(Context context, String accountName, boolean network) { + public static File getOwnerAvatarFile(Context context, String accountName, boolean network) { DatabaseHelper databaseHelper = new DatabaseHelper(context); Cursor cursor = databaseHelper.getOwner(accountName); String url = null; @@ -78,7 +78,7 @@ public class PeopleManager { } public static Bitmap getOwnerAvatarBitmap(Context context, String accountName, boolean network) { - File avaterFile = getOwnerAvaterFile(context, accountName, network); + File avaterFile = getOwnerAvatarFile(context, accountName, network); if (avaterFile == null) return null; return BitmapFactory.decodeFile(avaterFile.getPath()); } diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java index a2f10015..752ae928 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java @@ -127,7 +127,7 @@ public class PeopleServiceImpl extends IPeopleService.Stub { extras.putBoolean("rewindable", false); extras.putInt("width", 0); extras.putInt("height", 0); - File avaterFile = PeopleManager.getOwnerAvaterFile(context, account, true); + File avaterFile = PeopleManager.getOwnerAvatarFile(context, account, true); try { ParcelFileDescriptor fileDescriptor = null; if (avaterFile != null) { From 91b0f6893f9848354af7654b3cebc842615bcd7f Mon Sep 17 00:00:00 2001 From: Marvin W Date: Thu, 18 Mar 2021 01:19:54 +0100 Subject: [PATCH 27/29] GCM: Fix confirm dialog in dark theme --- .../org/microg/gms/ui/AskPushPermission.java | 14 +- .../src/main/res/layout/ask_gcm.xml | 123 +++++++++--------- .../src/main/res/values/strings.xml | 1 + 3 files changed, 74 insertions(+), 64 deletions(-) diff --git a/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java b/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java index 61a77f3e..f069cb4e 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java @@ -4,9 +4,16 @@ import android.app.Activity; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.graphics.Typeface; import android.os.Bundle; import android.os.ResultReceiver; import android.text.Html; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.SpannedString; +import android.text.style.StyleSpan; import android.view.View; import android.widget.TextView; @@ -58,9 +65,12 @@ public class AskPushPermission extends FragmentActivity { try { PackageManager pm = getPackageManager(); final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); - CharSequence label = pm.getApplicationLabel(info); + String label = pm.getApplicationLabel(info).toString(); + String raw = getString(R.string.gcm_allow_app_popup, label); + SpannableString s = new SpannableString(raw); + s.setSpan(new StyleSpan(Typeface.BOLD), raw.indexOf(label), raw.indexOf(label) + label.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); - ((TextView) findViewById(R.id.permission_message)).setText(Html.fromHtml("Allow " + label + " to register for push notifications?")); + ((TextView) findViewById(R.id.permission_message)).setText(s); findViewById(R.id.permission_allow_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/play-services-core/src/main/res/layout/ask_gcm.xml b/play-services-core/src/main/res/layout/ask_gcm.xml index f07c30cd..061e1ef8 100644 --- a/play-services-core/src/main/res/layout/ask_gcm.xml +++ b/play-services-core/src/main/res/layout/ask_gcm.xml @@ -14,88 +14,87 @@ limitations under the License. --> - + android:layout_width="match_parent" + android:layout_height="match_parent"> - - - - - - - - - - - - - - + android:gravity="center" + android:orientation="vertical" + android:paddingLeft="24dp" + android:paddingTop="18dp" + android:paddingRight="24dp" + android:paddingBottom="24dp"> - + android:gravity="center" + android:text="@string/gcm_allow_app_popup" + android:textSize="18sp"> + + + + + + + + + + + - + diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml index e7b12dc0..12ffbc25 100644 --- a/play-services-core/src/main/res/values/strings.xml +++ b/play-services-core/src/main/res/values/strings.xml @@ -171,6 +171,7 @@ This can take a couple of minutes." Disconnected Connected since %1$s Receive push notifications + Allow %1$s to register for push notifications? Allow registration Allow the app to register for push notifications. From dc77b759271a83a453306d5f78d9b53f4443e635 Mon Sep 17 00:00:00 2001 From: Oizaro <75915943+Oizaro@users.noreply.github.com> Date: Mon, 22 Mar 2021 04:04:09 +0100 Subject: [PATCH 28/29] Update to latest upstream --- build.gradle | 6 +- .../gms/common/GoogleApiAvailability.java | 13 ++++ play-services-basement/build.gradle | 5 +- play-services-core/build.gradle | 2 +- .../microg/gms/checkin/CheckinService.java | 4 +- .../java/org/microg/gms/gcm/GcmDatabase.java | 37 ++++------- .../java/org/microg/gms/gcm/McsService.java | 2 +- .../org/microg/gms/people/PeopleManager.java | 6 +- .../microg/gms/people/PeopleServiceImpl.java | 2 +- .../org/microg/gms/ui/AskPushPermission.java | 64 ------------------- .../org/microg/gms/gcm/PushRegisterService.kt | 12 ++-- 11 files changed, 48 insertions(+), 105 deletions(-) delete mode 100755 play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java diff --git a/build.gradle b/build.gradle index 8558d6c3..63d96ee5 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' + classpath 'com.android.tools.build:gradle:4.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "com.squareup.wire:wire-gradle-plugin:$wireVersion" } @@ -45,8 +45,8 @@ allprojects { apply plugin: 'idea' group = 'org.microg.gms' - version = "0.2.18.210390" - ext.appVersionCode = 210390001 + version = "0.2.18.210500" + ext.appVersionCode = 210500001 ext.isReleaseVersion = false } diff --git a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java index 889cc7f6..30afec09 100644 --- a/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java +++ b/play-services-base/src/main/java/com/google/android/gms/common/GoogleApiAvailability.java @@ -163,6 +163,19 @@ public class GoogleApiAvailability { return SUCCESS; } + /** + * Verifies that Google Play services is installed and enabled on this device, and that the version installed on + * this device is no older than the one required by this client or the version is not older than the one specified + * in minApkVersion. + * + * @return status code indicating whether there was an error. Can be one of following in + * {@link ConnectionResult}: SUCCESS, SERVICE_MISSING, SERVICE_UPDATING, + * SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID + */ + public int isGooglePlayServicesAvailable(Context context, int minApkVersion) { + return isGooglePlayServicesAvailable(context); + } + /** * Determines whether an error can be resolved via user action. If true, proceed by calling * {@link #getErrorDialog(Activity, int, int)} and showing the dialog. diff --git a/play-services-basement/build.gradle b/play-services-basement/build.gradle index 4f2f05c5..bc627a1b 100644 --- a/play-services-basement/build.gradle +++ b/play-services-basement/build.gradle @@ -16,7 +16,6 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' dependencies { api "org.microg:safe-parcel:1.7.0" @@ -41,6 +40,10 @@ android { buildConfigField "int", "VERSION_CODE", "$appVersionCode" } + buildFeatures { + viewBinding = true + } + sourceSets { main.java.srcDirs += 'src/main/kotlin' } diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle index 2a878698..7f3739f6 100644 --- a/play-services-core/build.gradle +++ b/play-services-core/build.gradle @@ -17,7 +17,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' dependencies { implementation 'com.squareup.wire:wire-runtime:3.6.1' @@ -70,6 +69,7 @@ android { buildFeatures { dataBinding = true + viewBinding = true } sourceSets { diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java index 56c273b9..dd32c1e1 100755 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java @@ -95,7 +95,9 @@ public class CheckinService extends IntentService { } catch (Exception e) { Log.w(TAG, e); } finally { - WakefulBroadcastReceiver.completeWakefulIntent(intent); + if (intent != null) { + WakefulBroadcastReceiver.completeWakefulIntent(intent); + } schedule(this); stopSelf(); } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java index bc524047..ce32d8d3 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/GcmDatabase.java @@ -1,17 +1,6 @@ /* - * Copyright (C) 2013-2017 microG Project Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * SPDX-FileCopyrightText: 2016, microG Project Team + * SPDX-License-Identifier: Apache-2.0 */ package org.microg.gms.gcm; @@ -81,13 +70,13 @@ public class GcmDatabase extends SQLiteOpenHelper { public final boolean wakeForDelivery; private App(Cursor cursor) { - packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME)); - lastError = cursor.getString(cursor.getColumnIndex(FIELD_LAST_ERROR)); - lastMessageTimestamp = cursor.getLong(cursor.getColumnIndex(FIELD_LAST_MESSAGE_TIMESTAMP)); - totalMessageCount = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_COUNT)); - totalMessageBytes = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_BYTES)); - allowRegister = cursor.getLong(cursor.getColumnIndex(FIELD_ALLOW_REGISTER)) == 1; - wakeForDelivery = cursor.getLong(cursor.getColumnIndex(FIELD_WAKE_FOR_DELIVERY)) == 1; + packageName = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_PACKAGE_NAME)); + lastError = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_LAST_ERROR)); + lastMessageTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_LAST_MESSAGE_TIMESTAMP)); + totalMessageCount = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_TOTAL_MESSAGE_COUNT)); + totalMessageBytes = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_TOTAL_MESSAGE_BYTES)); + allowRegister = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_ALLOW_REGISTER)) == 1; + wakeForDelivery = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_WAKE_FOR_DELIVERY)) == 1; } public boolean hasError() { @@ -102,10 +91,10 @@ public class GcmDatabase extends SQLiteOpenHelper { public final String registerId; public Registration(Cursor cursor) { - packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME)); - signature = cursor.getString(cursor.getColumnIndex(FIELD_SIGNATURE)); - timestamp = cursor.getLong(cursor.getColumnIndex(FIELD_TIMESTAMP)); - registerId = cursor.getString(cursor.getColumnIndex(FIELD_REGISTER_ID)); + packageName = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_PACKAGE_NAME)); + signature = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_SIGNATURE)); + timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(FIELD_TIMESTAMP)); + registerId = cursor.getString(cursor.getColumnIndexOrThrow(FIELD_REGISTER_ID)); } } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java index cdf5b3ca..8b3d45d7 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java @@ -320,7 +320,7 @@ public class McsService extends Service implements Handler.Callback { WakefulBroadcastReceiver.completeWakefulIntent(intent); } else if (connectIntent == null) { connectIntent = intent; - } else { + } else if (intent != null) { WakefulBroadcastReceiver.completeWakefulIntent(intent); } } diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java index c7ed2c0e..c50f2b09 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleManager.java @@ -42,13 +42,13 @@ public class PeopleManager { public static final String USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; public static final String REGEX_SEARCH_USER_PHOTO = "https?\\:\\/\\/lh([0-9]*)\\.googleusercontent\\.com/"; - public static File getOwnerAvaterFile(Context context, String accountName, boolean network) { + public static File getOwnerAvatarFile(Context context, String accountName, boolean network) { DatabaseHelper databaseHelper = new DatabaseHelper(context); Cursor cursor = databaseHelper.getOwner(accountName); String url = null; if (cursor.moveToNext()) { int idx = cursor.getColumnIndex("avatar"); - if (!cursor.isNull(idx)) url = cursor.getString(idx); + if (idx >= 0 && !cursor.isNull(idx)) url = cursor.getString(idx); } cursor.close(); databaseHelper.close(); @@ -75,7 +75,7 @@ public class PeopleManager { } public static Bitmap getOwnerAvatarBitmap(Context context, String accountName, boolean network) { - File avaterFile = getOwnerAvaterFile(context, accountName, network); + File avaterFile = getOwnerAvatarFile(context, accountName, network); if (avaterFile == null) return null; return BitmapFactory.decodeFile(avaterFile.getPath()); } diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java index dbccb1c8..8f0a88d3 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java @@ -121,7 +121,7 @@ public class PeopleServiceImpl extends IPeopleService.Stub { extras.putBoolean("rewindable", false); extras.putInt("width", 0); extras.putInt("height", 0); - File avaterFile = PeopleManager.getOwnerAvaterFile(context, account, true); + File avaterFile = PeopleManager.getOwnerAvatarFile(context, account, true); try { ParcelFileDescriptor fileDescriptor = null; if (avaterFile != null) { diff --git a/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java b/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java deleted file mode 100755 index 8fdef45f..00000000 --- a/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.microg.gms.ui; - -import android.app.Activity; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.os.ResultReceiver; -import android.text.Html; -import android.widget.TextView; - -import androidx.fragment.app.FragmentActivity; - -import com.mgoogle.android.gms.R; - -import org.microg.gms.gcm.GcmDatabase; -import org.microg.gms.gcm.PushRegisterService; - -import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; -import static org.microg.gms.gcm.GcmConstants.EXTRA_KID; -import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT; - -public class AskPushPermission extends FragmentActivity { - public static final String EXTRA_REQUESTED_PACKAGE = "package"; - public static final String EXTRA_RESULT_RECEIVER = "receiver"; - public static final String EXTRA_EXPLICIT = "explicit"; - - private GcmDatabase database; - - private String packageName; - private ResultReceiver resultReceiver; - private boolean answered; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - database = new GcmDatabase(this); - - packageName = getIntent().getStringExtra(EXTRA_REQUESTED_PACKAGE); - resultReceiver = getIntent().getParcelableExtra(EXTRA_RESULT_RECEIVER); - if (packageName == null || resultReceiver == null) { - answered = true; - finish(); - return; - } - - if (database.getApp(packageName) != null) { - resultReceiver.send(Activity.RESULT_OK, Bundle.EMPTY); - answered = true; - finish(); - return; - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (!answered) { - resultReceiver.send(Activity.RESULT_CANCELED, Bundle.EMPTY); - } - database.close(); - } -} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt index 1b0e0c94..00d0721e 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/gcm/PushRegisterService.kt @@ -24,7 +24,6 @@ import org.microg.gms.common.ForegroundServiceContext import org.microg.gms.common.PackageUtils import org.microg.gms.common.Utils import org.microg.gms.gcm.GcmConstants.* -import org.microg.gms.ui.AskPushPermission import java.util.concurrent.atomic.AtomicBoolean import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -97,11 +96,12 @@ class PushRegisterService : LifecycleService() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - WakefulBroadcastReceiver.completeWakefulIntent(intent) - Log.d(TAG, "onStartCommand: $intent") - lifecycleScope.launchWhenStarted { - if (intent == null) return@launchWhenStarted - handleIntent(intent) + if (intent != null) { + WakefulBroadcastReceiver.completeWakefulIntent(intent) + Log.d(TAG, "onStartCommand: $intent") + lifecycleScope.launchWhenStarted { + handleIntent(intent) + } } return super.onStartCommand(intent, flags, startId) } From a68d0ab98a505a528b9864b618f50487f9aca2c3 Mon Sep 17 00:00:00 2001 From: Oizaro <75915943+Oizaro@users.noreply.github.com> Date: Mon, 22 Mar 2021 04:28:53 +0100 Subject: [PATCH 29/29] Fix --- play-services-core/src/main/AndroidManifest.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 0f7699b3..8acb9c0d 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -331,12 +331,6 @@ android:resource="@string/gms_settings_summary" /> - -