Skip to content

Pass feedback trigger type to create feedback call #4631

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 11 commits into from
Feb 7, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public class FeedbackActivity extends AppCompatActivity {
"com.google.firebase.appdistribution.FeedbackActivity.INFO_TEXT";
public static final String SCREENSHOT_URI_KEY =
"com.google.firebase.appdistribution.FeedbackActivity.SCREENSHOT_URI";
public static final String FEEDBACK_TRIGGER_KEY =
"com.google.firebase.appdistribution.FeedbackActivity.FEEDBACK_TRIGGER";

private final ActivityResultLauncher<Intent> chooseScreenshotLauncher =
registerForActivityResult(new StartActivityForResult(), this::handleChooseScreenshotResult);
Expand All @@ -64,6 +66,7 @@ public class FeedbackActivity extends AppCompatActivity {
@Nullable private String releaseName; // in development-mode the releaseName might be null
private CharSequence infoText;
@Nullable private Uri screenshotUri;
private FeedbackTrigger feedbackTrigger;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -75,13 +78,17 @@ protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
releaseName = savedInstanceState.getString(RELEASE_NAME_KEY);
infoText = savedInstanceState.getCharSequence(INFO_TEXT_KEY);
feedbackTrigger =
FeedbackTrigger.fromString(savedInstanceState.getString(FEEDBACK_TRIGGER_KEY));
String screenshotUriString = savedInstanceState.getString(SCREENSHOT_URI_KEY);
if (screenshotUriString != null) {
screenshotUri = Uri.parse(screenshotUriString);
}
} else {
releaseName = getIntent().getStringExtra(RELEASE_NAME_KEY);
infoText = getIntent().getCharSequenceExtra(INFO_TEXT_KEY);
feedbackTrigger =
FeedbackTrigger.fromString(getIntent().getStringExtra(FEEDBACK_TRIGGER_KEY));
if (getIntent().hasExtra(SCREENSHOT_URI_KEY)) {
screenshotUri = Uri.parse(getIntent().getStringExtra(SCREENSHOT_URI_KEY));
}
Expand Down Expand Up @@ -197,7 +204,8 @@ public void submitFeedback(View view) {
.sendFeedback(
releaseName,
feedbackText.getText().toString(),
screenshotCheckBox.isChecked() ? screenshotUri : null)
screenshotCheckBox.isChecked() ? screenshotUri : null,
feedbackTrigger)
.addOnSuccessListener(
uiThreadExecutor,
unused -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ class FeedbackSender {
}

/** Send feedback text and optionally a screenshot to the Tester API for the given release. */
Task<Void> sendFeedback(String releaseName, String feedbackText, @Nullable Uri screenshotUri) {
Task<Void> sendFeedback(
String releaseName,
String feedbackText,
@Nullable Uri screenshotUri,
FeedbackTrigger trigger) {
return testerApiClient
.createFeedback(releaseName, feedbackText)
.createFeedback(releaseName, feedbackText, trigger)
.onSuccessTask(
lightweightExecutor, feedbackName -> attachScreenshot(feedbackName, screenshotUri))
.onSuccessTask(lightweightExecutor, testerApiClient::commitFeedback);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2021 Google LLC
//
// 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.

package com.google.firebase.appdistribution.impl;

/** Feedback trigger type */
enum FeedbackTrigger {
NOTIFICATION("notification"),
CUSTOM("custom"),
UNKNOWN("unknown");

private final String value;

FeedbackTrigger(String value) {
this.value = value;
}

@Override
public String toString() {
return value;
}

public static FeedbackTrigger fromString(String value) {
if (value.equals(NOTIFICATION.value)) {
return NOTIFICATION;
} else if (value.equals(CUSTOM.value)) {
return CUSTOM;
} else {
return UNKNOWN;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,15 @@ private UpdateTask updateApp(boolean showDownloadInNotificationManager) {

@Override
public void startFeedback(@StringRes int infoTextResourceId) {
startFeedback(applicationContext.getText(infoTextResourceId));
startFeedback(applicationContext.getText(infoTextResourceId), FeedbackTrigger.CUSTOM);
}

@Override
public void startFeedback(@NonNull CharSequence infoText) {
startFeedback(infoText, FeedbackTrigger.CUSTOM);
}

void startFeedback(@NonNull CharSequence infoText, FeedbackTrigger trigger) {
if (!feedbackInProgress.compareAndSet(/* expect= */ false, /* update= */ true)) {
LogWrapper.i(TAG, "Ignoring startFeedback() call because feedback is already in progress");
return;
Expand All @@ -356,24 +360,30 @@ public void startFeedback(@NonNull CharSequence infoText) {
lightweightExecutor,
e -> {
LogWrapper.w(TAG, "Failed to take screenshot for feedback", e);
doStartFeedback(infoText, null);
doStartFeedback(infoText, null, trigger);
})
.addOnSuccessListener(
lightweightExecutor, screenshotUri -> doStartFeedback(infoText, screenshotUri));
lightweightExecutor,
screenshotUri -> doStartFeedback(infoText, screenshotUri, trigger));
}

@Override
public void startFeedback(@StringRes int infoTextResourceId, @Nullable Uri screenshotUri) {
startFeedback(getText(infoTextResourceId), screenshotUri);
startFeedback(getText(infoTextResourceId), screenshotUri, FeedbackTrigger.CUSTOM);
}

@Override
public void startFeedback(@NonNull CharSequence infoText, @Nullable Uri screenshotUri) {
startFeedback(infoText, screenshotUri, FeedbackTrigger.CUSTOM);
}

private void startFeedback(
@NonNull CharSequence infoText, @Nullable Uri screenshotUri, FeedbackTrigger trigger) {
if (!feedbackInProgress.compareAndSet(/* expect= */ false, /* update= */ true)) {
LogWrapper.i(TAG, "Ignoring startFeedback() call because feedback is already in progress");
return;
}
doStartFeedback(infoText, screenshotUri);
doStartFeedback(infoText, screenshotUri, trigger);
}

@Override
Expand All @@ -393,7 +403,8 @@ public void cancelFeedbackNotification() {
notificationsManager.cancelFeedbackNotification();
}

private void doStartFeedback(CharSequence infoText, @Nullable Uri screenshotUri) {
private void doStartFeedback(
CharSequence infoText, @Nullable Uri screenshotUri, FeedbackTrigger trigger) {
testerSignInManager
.signInTester()
.addOnFailureListener(
Expand Down Expand Up @@ -422,7 +433,7 @@ private void doStartFeedback(CharSequence infoText, @Nullable Uri screenshotUri)
lightweightExecutor,
releaseName ->
// in development-mode the releaseName might be null
launchFeedbackActivity(releaseName, infoText, screenshotUri)
launchFeedbackActivity(releaseName, infoText, screenshotUri, trigger)
.addOnFailureListener(
uiThreadExecutor,
e -> {
Expand All @@ -437,14 +448,18 @@ private void doStartFeedback(CharSequence infoText, @Nullable Uri screenshotUri)
}

private Task<Void> launchFeedbackActivity(
@Nullable String releaseName, CharSequence infoText, @Nullable Uri screenshotUri) {
@Nullable String releaseName,
CharSequence infoText,
@Nullable Uri screenshotUri,
FeedbackTrigger trigger) {
return lifecycleNotifier.consumeForegroundActivity(
activity -> {
LogWrapper.i(TAG, "Launching feedback activity");
Intent intent = new Intent(activity, FeedbackActivity.class);
// in development-mode the releaseName might be null
intent.putExtra(FeedbackActivity.RELEASE_NAME_KEY, releaseName);
intent.putExtra(FeedbackActivity.INFO_TEXT_KEY, infoText);
intent.putExtra(FeedbackActivity.FEEDBACK_TRIGGER_KEY, trigger.toString());
if (screenshotUri != null) {
intent.putExtra(FeedbackActivity.SCREENSHOT_URI_KEY, screenshotUri.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import com.google.firebase.inject.Provider;
import com.google.firebase.installations.FirebaseInstallationsApi;
import com.google.firebase.installations.InstallationTokenResult;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import org.json.JSONArray;
Expand Down Expand Up @@ -60,6 +62,7 @@ private interface FidDependentJob<TResult> {
private static final String CREATE_FEEDBACK_TAG = "Creating feedback";
private static final String COMMIT_FEEDBACK_TAG = "Committing feedback";
private static final String UPLOAD_SCREENSHOT_TAG = "Uploading screenshot";
private static final String X_APP_DISTRO_FEEDBACK_TRIGGER = "X-APP-DISTRO-FEEDBACK-TRIGGER";

private final FirebaseOptions firebaseOptions;
private final Provider<FirebaseInstallationsApi> firebaseInstallationsApiProvider;
Expand Down Expand Up @@ -136,14 +139,18 @@ private String findRelease(String fid, String token, String binaryIdParam, Strin

/** Creates a new feedback from the given text, and returns the feedback name. */
@NonNull
Task<String> createFeedback(String testerReleaseName, String feedbackText) {
Task<String> createFeedback(
String testerReleaseName, String feedbackText, FeedbackTrigger trigger) {
return runWithFidAndToken(
(unused, token) -> {
LogWrapper.i(TAG, "Creating feedback for release: " + testerReleaseName);
String path = String.format("v1alpha/%s/feedbackReports", testerReleaseName);
String requestBody = buildCreateFeedbackBody(feedbackText).toString();
Map<String, String> extraHeaders = new HashMap<>();
extraHeaders.put(X_APP_DISTRO_FEEDBACK_TRIGGER, trigger.toString());
JSONObject responseBody =
testerApiHttpClient.makePostRequest(CREATE_FEEDBACK_TAG, path, token, requestBody);
testerApiHttpClient.makePostRequest(
CREATE_FEEDBACK_TAG, path, token, requestBody, extraHeaders);
return parseJsonFieldFromResponse(responseBody, "name");
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import android.app.Activity;
import android.os.Bundle;
import com.google.firebase.appdistribution.FirebaseAppDistribution;
import javax.inject.Inject;

public class TakeScreenshotAndStartFeedbackActivity extends Activity {
Expand All @@ -26,7 +25,7 @@ public class TakeScreenshotAndStartFeedbackActivity extends Activity {
public static final String INFO_TEXT_EXTRA_KEY =
"com.google.firebase.appdistribution.TakeScreenshotAndStartFeedbackActivity.INFO_TEXT";

@Inject FirebaseAppDistribution firebaseAppDistribution;
@Inject FirebaseAppDistributionImpl firebaseAppDistribution;

private CharSequence infoText;

Expand All @@ -38,7 +37,8 @@ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
infoText = getIntent().getCharSequenceExtra(INFO_TEXT_EXTRA_KEY);
LogWrapper.i(TAG, "Capturing screenshot and starting feedback");
firebaseAppDistribution.startFeedback(infoText);
firebaseAppDistribution.startFeedback(infoText, FeedbackTrigger.NOTIFICATION);

finish();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ JSONObject makeGetRequest(String tag, String path, String token)
*/
JSONObject makePostRequest(String tag, String path, String token, String requestBody)
throws FirebaseAppDistributionException {
return makePostRequest(tag, path, token, requestBody, new HashMap<>());
}

JSONObject makePostRequest(
String tag, String path, String token, String requestBody, Map<String, String> extraHeaders)
throws FirebaseAppDistributionException {
byte[] bytes;
try {
bytes = requestBody.getBytes(UTF_8);
Expand All @@ -112,7 +118,7 @@ JSONObject makePostRequest(String tag, String path, String token, String request
"Unsupported encoding: " + UTF_8, Status.UNKNOWN, e);
}
return makePostRequest(
tag, path, token, new HashMap<>(), outputStream -> outputStream.write(bytes));
tag, path, token, extraHeaders, outputStream -> outputStream.write(bytes));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,32 +59,38 @@ public void setup() {

@Test
public void sendFeedback_success() throws Exception {
when(mockTesterApiClient.createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT))
when(mockTesterApiClient.createFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, FeedbackTrigger.CUSTOM))
.thenReturn(Tasks.forResult(TEST_FEEDBACK_NAME));
when(mockTesterApiClient.attachScreenshot(TEST_FEEDBACK_NAME, TEST_SCREENSHOT_URI))
.thenReturn(Tasks.forResult(TEST_FEEDBACK_NAME));
when(mockTesterApiClient.commitFeedback(TEST_FEEDBACK_NAME)).thenReturn(Tasks.forResult(null));

Task<Void> task =
feedbackSender.sendFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI);
feedbackSender.sendFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI, FeedbackTrigger.CUSTOM);
TestUtils.awaitTask(task);

verify(mockTesterApiClient).createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT);
verify(mockTesterApiClient)
.createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, FeedbackTrigger.CUSTOM);
verify(mockTesterApiClient).attachScreenshot(TEST_FEEDBACK_NAME, TEST_SCREENSHOT_URI);
verify(mockTesterApiClient).commitFeedback(TEST_FEEDBACK_NAME);
}

@Test
public void sendFeedback_withoutScreenshot_success() throws Exception {
when(mockTesterApiClient.createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT))
when(mockTesterApiClient.createFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, FeedbackTrigger.CUSTOM))
.thenReturn(Tasks.forResult(TEST_FEEDBACK_NAME));
when(mockTesterApiClient.commitFeedback(TEST_FEEDBACK_NAME)).thenReturn(Tasks.forResult(null));

Task<Void> task =
feedbackSender.sendFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, /* screenshot= */ null);
feedbackSender.sendFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, /* screenshot= */ null, FeedbackTrigger.CUSTOM);
TestUtils.awaitTask(task);

verify(mockTesterApiClient).createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT);
verify(mockTesterApiClient)
.createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, FeedbackTrigger.CUSTOM);
verify(mockTesterApiClient).commitFeedback(TEST_FEEDBACK_NAME);
verify(mockTesterApiClient, never()).attachScreenshot(any(), any());
}
Expand All @@ -93,33 +99,38 @@ public void sendFeedback_withoutScreenshot_success() throws Exception {
public void sendFeedback_createFeedbackFails_failsTask() {
FirebaseAppDistributionException cause =
new FirebaseAppDistributionException("test ex", Status.AUTHENTICATION_FAILURE);
when(mockTesterApiClient.createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT))
when(mockTesterApiClient.createFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, FeedbackTrigger.CUSTOM))
.thenReturn(Tasks.forException(cause));

Task<Void> task =
feedbackSender.sendFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI);
feedbackSender.sendFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI, FeedbackTrigger.CUSTOM);

TestUtils.awaitTaskFailure(task, Status.AUTHENTICATION_FAILURE, "test ex");
}

@Test
public void sendFeedback_attachScreenshotFails_failsTask() {
when(mockTesterApiClient.createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT))
when(mockTesterApiClient.createFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, FeedbackTrigger.CUSTOM))
.thenReturn(Tasks.forResult(TEST_FEEDBACK_NAME));
FirebaseAppDistributionException cause =
new FirebaseAppDistributionException("test ex", Status.AUTHENTICATION_FAILURE);
when(mockTesterApiClient.attachScreenshot(TEST_FEEDBACK_NAME, TEST_SCREENSHOT_URI))
.thenReturn(Tasks.forException(cause));

Task<Void> task =
feedbackSender.sendFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI);
feedbackSender.sendFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI, FeedbackTrigger.CUSTOM);

TestUtils.awaitTaskFailure(task, Status.AUTHENTICATION_FAILURE, "test ex");
}

@Test
public void sendFeedback_commitFeedbackFails_failsTask() {
when(mockTesterApiClient.createFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT))
when(mockTesterApiClient.createFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, FeedbackTrigger.CUSTOM))
.thenReturn(Tasks.forResult(TEST_FEEDBACK_NAME));
when(mockTesterApiClient.attachScreenshot(TEST_FEEDBACK_NAME, TEST_SCREENSHOT_URI))
.thenReturn(Tasks.forResult(TEST_FEEDBACK_NAME));
Expand All @@ -129,7 +140,8 @@ public void sendFeedback_commitFeedbackFails_failsTask() {
.thenReturn(Tasks.forException(cause));

Task<Void> task =
feedbackSender.sendFeedback(TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI);
feedbackSender.sendFeedback(
TEST_RELEASE_NAME, TEST_FEEDBACK_TEXT, TEST_SCREENSHOT_URI, FeedbackTrigger.CUSTOM);

TestUtils.awaitTaskFailure(task, Status.AUTHENTICATION_FAILURE, "test ex");
}
Expand Down
Loading