Skip to content

Handle the case where we can't identify the release. #4331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class FeedbackActivity extends AppCompatActivity {
"com.google.firebase.appdistribution.FeedbackActivity.SCREENSHOT_URI";

private FeedbackSender feedbackSender;
private String releaseName;
@Nullable private String releaseName; // in development-mode the releaseName might be null
private CharSequence infoText;
@Nullable private Uri screenshotUri;

Expand Down Expand Up @@ -112,6 +112,12 @@ private Bitmap readScreenshot() {
}

public void submitFeedback(View view) {
if (releaseName == null) {
// Don't actually send feedback in development-mode
Toast.makeText(this, R.string.feedback_no_release, Toast.LENGTH_LONG).show();
finish();
return;
}
setSubmittingStateEnabled(true);
EditText feedbackText = findViewById(R.id.feedbackText);
CheckBox screenshotCheckBox = findViewById(R.id.screenshotCheckBox);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand Down Expand Up @@ -313,8 +314,6 @@ private UpdateTask updateApp(boolean showDownloadInNotificationManager) {

@Override
public void startFeedback(@StringRes int infoTextResourceId) {
// TODO(lkellogg): Once we have the real FeedbackActivity view implemented, we should write a
// test that checks that <a> tags are preserved
startFeedback(firebaseApp.getApplicationContext().getText(infoTextResourceId));
}

Expand Down Expand Up @@ -375,26 +374,51 @@ private void doStartFeedback(CharSequence infoText, @Nullable Uri screenshotUri)
.signInTester()
.addOnFailureListener(
taskExecutor,
e ->
LogWrapper.getInstance()
.e("Failed to sign in tester. Could not collect feedback.", e))
.onSuccessTask(taskExecutor, unused -> releaseIdentifier.identifyRelease())
.onSuccessTask(
taskExecutor,
releaseName -> launchFeedbackActivity(releaseName, infoText, screenshotUri))
.addOnFailureListener(
e -> {
LogWrapper.getInstance().e("Failed to launch feedback flow", e);
feedbackInProgress.set(false);
});
LogWrapper.getInstance()
.e("Failed to sign in tester. Could not collect feedback.", e);
})
.onSuccessTask(
taskExecutor,
unused ->
releaseIdentifier
.identifyRelease()
.addOnFailureListener(
e -> {
feedbackInProgress.set(false);
LogWrapper.getInstance().e("Failed to identify release", e);
Toast.makeText(
firebaseApp.getApplicationContext(),
R.string.feedback_unidentified_release,
Toast.LENGTH_LONG)
.show();
})
.onSuccessTask(
taskExecutor,
releaseName ->
// in development-mode the releaseName might be null
launchFeedbackActivity(releaseName, infoText, screenshotUri)
.addOnFailureListener(
e -> {
feedbackInProgress.set(false);
LogWrapper.getInstance()
.e("Failed to launch feedback flow", e);
Toast.makeText(
firebaseApp.getApplicationContext(),
R.string.feedback_launch_failed,
Toast.LENGTH_LONG)
.show();
})));
}

private Task<Void> launchFeedbackActivity(
String releaseName, CharSequence infoText, @Nullable Uri screenshotUri) {
@Nullable String releaseName, CharSequence infoText, @Nullable Uri screenshotUri) {
return lifecycleNotifier.consumeForegroundActivity(
activity -> {
LogWrapper.getInstance().i("Launching feedback activity");
Intent intent = new Intent(activity, FeedbackActivity.class);
// in development-mode the releaseName might be null
intent.putExtra(FeedbackActivity.RELEASE_NAME_EXTRA_KEY, releaseName);
intent.putExtra(FeedbackActivity.INFO_TEXT_EXTRA_KEY, infoText);
if (screenshotUri != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.firebase.appdistribution.FirebaseAppDistributionException.Status;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Expand All @@ -46,17 +47,26 @@ class ReleaseIdentifier {
static final String IAS_ARTIFACT_ID_METADATA_KEY = "com.android.vending.internal.apk.id";

private final ConcurrentMap<String, String> cachedApkHashes = new ConcurrentHashMap<>();
private FirebaseApp firebaseApp;
FirebaseAppDistributionTesterApiClient testerApiClient;
private final FirebaseApp firebaseApp;
private final FirebaseAppDistributionTesterApiClient testerApiClient;

ReleaseIdentifier(
FirebaseApp firebaseApp, FirebaseAppDistributionTesterApiClient testerApiClient) {
this.firebaseApp = firebaseApp;
this.testerApiClient = testerApiClient;
}

/** Identify the currently installed release, returning the release name. */
/**
* Identify the currently installed release, returning the release name.
*
* <p>Will return {@code Task} with a {@code null} result in "developer mode" which allows the UI
* to be used, but no actual feedback to be submitted.
*/
Task<String> identifyRelease() {
if (developmentModeEnabled()) {
return Tasks.forResult(null);
}

// Attempt to find release using IAS artifact ID, which identifies app bundle releases
String iasArtifactId = null;
try {
Expand Down Expand Up @@ -193,4 +203,25 @@ private static byte[] arrayListToByteArray(ArrayList<Byte> list) {
}
return result;
}

private static boolean developmentModeEnabled() {
return Boolean.valueOf(getSystemProperty("debug.firebase.appdistro.devmode"));
}

@Nullable
@SuppressWarnings({"unchecked", "PrivateApi"})
private static String getSystemProperty(String propertyName) {
String className = "android.os.SystemProperties";
try {
Class<?> sysProps = Class.forName(className);
Method method = sysProps.getDeclaredMethod("get", String.class);
Object result = method.invoke(null, propertyName);
if (result != null && String.class.isAssignableFrom(result.getClass())) {
return (String) result;
}
} catch (Exception e) {
// do nothing
}
return null;
}
}
3 changes: 3 additions & 0 deletions firebase-appdistribution/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
<string name="notifications_group_name">Firebase App Distribution</string>
<string name="app_update_notification_channel_name">App updates</string>
<string name="app_update_notification_channel_description">Shows progress of in-app updates</string>
<string name="feedback_launch_failed">Failed to launch feedback</string>
<string name="feedback_unidentified_release">Release not found. This app may not have been installed by App Distribution, or you may no longer have access.</string>
<string name="feedback_no_release">Would have sent feedback, but did not due to development mode.</string>
<string name="feedback_notification_channel_name">Feedback</string>
<string name="feedback_notification_channel_description">Tap to leave feedback</string>
<string name="feedback_notification_title">We want your feedback!</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ public void startFeedback_signInTesterFails_logsAndSetsInProgressToFalse()
TestUtils.awaitAsyncOperations(taskExecutor);

assertThat(firebaseAppDistribution.isFeedbackInProgress()).isFalse();
assertLoggedError("Failed to launch feedback flow", exception);
assertLoggedError("Failed to sign in tester", exception);
}

@Test
Expand All @@ -772,7 +772,7 @@ public void startFeedback_cantIdentifyRelease_logsAndSetsInProgressToFalse()
TestUtils.awaitAsyncOperations(taskExecutor);

assertThat(firebaseAppDistribution.isFeedbackInProgress()).isFalse();
assertLoggedError("Failed to launch feedback flow", exception);
assertLoggedError("Failed to identify release", exception);
}

private static void assertLoggedError(String partialMessage, Throwable e) {
Expand Down