Skip to content
This repository has been archived by the owner on Mar 6, 2024. It is now read-only.

Commit

Permalink
Update open source branch to include changes through 2020-05-21.
Browse files Browse the repository at this point in the history
Features:
- Adds debug UI for matching:
- View screen to see temporary exposure keys including a QR code
  encoding of this data in JSON.
- Provide screen to allow you to add keys:
  - Single keys either manually or via QR code and signed randomly
  - JSON of batches signed randomly
  - A pre-signed file
- Minor UI optimizations and improvements
- Minor bug fixes and cleanup

Known Issues:
- QR code scanner sometimes not available on a device.
- Providing single or batch keys will sign them with a random key.
  If signature verification is on, this will fail to provide keys.
  • Loading branch information
Google committed May 22, 2020
1 parent bc67322 commit 17b5c6e
Show file tree
Hide file tree
Showing 26 changed files with 2,823 additions and 65 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -24,5 +24,5 @@ This project uses the Gradle build system. To build this code, use the
- [Frequently Asked Questions](https://www.blog.google/documents/63/Exposure_Notification_-_FAQ_v1.0.pdf)
- [Exposure Notification Bluetooth Specification](https://www.blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf)
- [Exposure Notification Cryptography Specification](https://www.blog.google/documents/69/Exposure_Notification_-_Cryptography_Specification_v1.2.1.pdf)
- [Android Exposure Notifications API](https://static.googleusercontent.com/media/www.google.com/en//covid19/exposurenotifications/pdfs/Android-Exposure-Notification-API-documentation-v1.3.1.pdf)
- [Android Exposure Notifications API](https://static.googleusercontent.com/media/www.google.com/en//covid19/exposurenotifications/pdfs/Android-Exposure-Notification-API-documentation-v1.3.2.pdf)
- [Exposure Key Export and File Format](https://static.googleusercontent.com/media/www.google.com/en//covid19/exposurenotifications/pdfs/Exposure-Key-File-Format-and-Verification.pdf)
71 changes: 41 additions & 30 deletions app/build.gradle
Expand Up @@ -25,6 +25,8 @@ ext.key_server_upload_uri = project.hasProperty('UPLOAD_URI') ? project.getPrope
ext.key_server_download_base_uri = project.hasProperty('DOWNLOAD_URI') ? project.getProperty('DOWNLOAD_URI') : "http://example.com"
ext.safetynet_api_key = project.hasProperty('SAFETYNET_KEY') ? project.getProperty('SAFETYNET_KEY') : "REPLACE-ME"

apply plugin: 'com.google.protobuf'

android {
compileSdkVersion 29
defaultConfig {
Expand Down Expand Up @@ -57,6 +59,19 @@ android {
}
}

sourceSets {
debug {
proto {
srcDirs 'src/main/java/com/google/android/apps/exposurenotification/debug/proto'
}
}
release {
proto {
srcDir 'src/main/java/com/google/android/apps/exposurenotification/debug/proto'
}
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand All @@ -77,63 +92,59 @@ android {
}
}

protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.11.4'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {}
}
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])

implementation 'com.google.auto.value:auto-value-annotations:1.7'
annotationProcessor 'androidx.room:room-compiler:2.2.5'
annotationProcessor 'com.google.auto.value:auto-value:1.7'

implementation 'androidx.appcompat:appcompat:1.2.0-beta01'
implementation 'androidx.fragment:fragment:1.2.4'

def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
debugImplementation 'androidx.fragment:fragment-testing:1.2.4'

implementation 'androidx.appcompat:appcompat:1.2.0-beta01'
implementation 'androidx.concurrent:concurrent-futures:1.0.0'

implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

implementation 'androidx.fragment:fragment:1.2.4'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
implementation 'androidx.multidex:multidex:2.0.1'

implementation 'androidx.room:room-guava:2.2.5'
implementation 'androidx.room:room-runtime:2.2.5'
annotationProcessor 'androidx.room:room-compiler:2.2.5'

implementation 'androidx.work:work-runtime:2.3.4'

implementation 'commons-io:commons-io:2.5'

implementation 'com.android.volley:volley:1.1.1'

implementation 'com.google.android.gms:play-services-base:17.2.1'
implementation 'com.google.android.gms:play-services-basement:17.2.1'
implementation 'com.google.android.gms:play-services-safetynet:17.0.0'
implementation 'com.google.android.gms:play-services-tasks:17.0.2'

implementation 'com.google.android.gms:play-services-vision:20.0.0'
implementation 'com.google.android.material:material:1.1.0'

implementation 'com.google.auto.value:auto-value-annotations:1.7'
implementation 'com.google.guava:guava:29.0-android'

implementation 'com.google.android.gms:play-services-tasks:17.0.2'
implementation 'com.google.android.gms:play-services-base:17.2.1'
implementation 'com.google.android.gms:play-services-basement:17.2.1'

implementation 'com.google.protobuf:protobuf-java:3.11.4'

implementation 'com.google.zxing:core:3.3.0'
implementation 'com.jakewharton.threetenabp:threetenabp:1.2.4'
implementation 'commons-io:commons-io:2.5'

debugImplementation 'androidx.fragment:fragment-testing:1.2.4'

testImplementation 'junit:junit:4.13'
//noinspection FragmentGradleConfiguration
testImplementation 'androidx.fragment:fragment-testing:1.2.4'
testImplementation 'androidx.test.ext:junit:1.1.2-beta01'
testImplementation 'androidx.test:core:1.3.0-beta01'
testImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
testImplementation 'com.android.support.test:runner:1.0.2'
testImplementation 'com.google.guava:guava-testlib:29.0-jre'
testImplementation 'com.google.truth:truth:1.0.1'
testImplementation 'commons-io:commons-io:2.5'
testImplementation 'junit:junit:4.13'
testImplementation 'org.robolectric:robolectric:4.3.1'
//noinspection FragmentGradleConfiguration
testImplementation 'androidx.fragment:fragment-testing:1.2.4'
}
13 changes: 13 additions & 0 deletions app/src/main/AndroidManifest.xml
Expand Up @@ -21,9 +21,12 @@

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
<uses-feature android:name="android.hardware.bluetooth" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />

<application
android:allowBackup="false"
Expand Down Expand Up @@ -62,6 +65,16 @@
android:exported="false"
android:parentActivityName=".home.ExposureNotificationActivity" />

<activity
android:name=".debug.MatchingDebugActivity"
android:exported="false"
android:parentActivityName=".home.ExposureNotificationActivity" />

<activity android:name=".debug.QRScannerActivity"
android:exported="false"
android:label="Scan QR Code"
android:parentActivityName=".debug.MatchingDebugActivity"/>

<!-- Receivers -->
<receiver
android:name=".nearby.ExposureNotificationBroadcastReceiver"
Expand Down
Expand Up @@ -17,19 +17,24 @@

package com.google.android.apps.exposurenotification.debug;

import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.apps.exposurenotification.R;
import com.google.android.apps.exposurenotification.home.ExposureNotificationViewModel;
import com.google.android.apps.exposurenotification.network.Uris;
import com.google.android.apps.exposurenotification.storage.ExposureNotificationSharedPreferences.NetworkMode;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.switchmaterial.SwitchMaterial;

Expand Down Expand Up @@ -72,13 +77,30 @@ public void onViewCreated(View view, Bundle savedInstanceState) {

exposureNotificationViewModel
.getApiErrorLiveEvent()
.observe(getViewLifecycleOwner(), unused -> {
View rootView = getView();
if (rootView != null) {
Snackbar.make(rootView, R.string.generic_error_message, Snackbar.LENGTH_LONG).show();
}
});
.observe(
getViewLifecycleOwner(),
unused -> maybeShowSnackbar(getString(R.string.generic_error_message)));

// Version
TextView appVersion = view.findViewById(R.id.debug_app_version);
appVersion.setText(
getString(
R.string.debug_version_app, getVersionNameForPackage(getContext().getPackageName())));

TextView gmsVersion = view.findViewById(R.id.debug_gms_version);
gmsVersion.setText(
getString(
R.string.debug_version_gms,
getVersionNameForPackage(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE)));

// Master switch
SwitchMaterial masterSwitch = view.findViewById(R.id.master_switch);
masterSwitch.setOnCheckedChangeListener(masterSwitchChangeListener);
exposureNotificationViewModel
.getInFlightLiveData()
.observe(getViewLifecycleOwner(), isInFlight -> masterSwitch.setEnabled(!isInFlight));

// Test exposure notification
view.findViewById(R.id.debug_test_exposure_notify_button)
.setOnClickListener(
v -> debugHomeViewModel.addTestExposures(getString(R.string.generic_error_message)));
Expand All @@ -90,28 +112,23 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
getString(R.string.debug_test_exposure_reset_success),
getString(R.string.generic_error_message)));

// Matching
Button manualMatching = view.findViewById(R.id.debug_matching_manual_button);
manualMatching.setOnClickListener(
v -> startActivity(new Intent(requireContext(), MatchingDebugActivity.class)));

view.findViewById(R.id.debug_provide_keys_button)
.setOnClickListener(
v -> {
debugHomeViewModel.provideKeys();
View rootView = getView();
if (rootView == null) {
return;
}
Snackbar.make(rootView, R.string.debug_provide_keys_enqueued, Snackbar.LENGTH_LONG)
.show();
maybeShowSnackbar(getString(R.string.debug_provide_keys_enqueued));
});

// Network
SwitchMaterial networkSwitch = view.findViewById(R.id.network_mode);
networkSwitch.setOnCheckedChangeListener(networkModeChangeListener);
networkSwitch.setChecked(
debugHomeViewModel.getNetworkMode(NetworkMode.FAKE).equals(NetworkMode.TEST));

SwitchMaterial masterSwitch = view.findViewById(R.id.master_switch);
masterSwitch.setOnCheckedChangeListener(masterSwitchChangeListener);
exposureNotificationViewModel
.getInFlightLiveData()
.observe(getViewLifecycleOwner(), isInFlight -> masterSwitch.setEnabled(!isInFlight));
}

@Override
Expand Down Expand Up @@ -140,11 +157,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Uris uris = new Uris(requireContext());
if (uris.hasDefaultUris()) {
debugHomeViewModel.setNetworkMode(NetworkMode.FAKE);
Snackbar.make(
requireView(),
R.string.debug_network_mode_default_uri,
BaseTransientBottomBar.LENGTH_LONG)
.show();
maybeShowSnackbar(getString(R.string.debug_network_mode_default_uri));
((SwitchMaterial) requireView().findViewById(R.id.network_mode)).setChecked(false);
return;
}
Expand Down Expand Up @@ -175,4 +188,21 @@ private void refreshUiForEnabled(Boolean currentlyEnabled) {
masterSwitch.setChecked(currentlyEnabled);
masterSwitch.setOnCheckedChangeListener(masterSwitchChangeListener);
}

/** Gets the version name for a specified package. Returns a debug string if not found. */
private String getVersionNameForPackage(String packageName) {
try {
return getContext().getPackageManager().getPackageInfo(packageName, 0).versionName;
} catch (NameNotFoundException e) {
Log.e(TAG, "Couldn't get the app version", e);
}
return getString(R.string.debug_version_not_available);
}

private void maybeShowSnackbar(String message) {
View rootView = getView();
if (rootView != null) {
Snackbar.make(rootView, message, Snackbar.LENGTH_LONG).show();
}
}
}

0 comments on commit 17b5c6e

Please sign in to comment.