Hide "Send feedback" button if no target exists.
This change hides the "Send feedback" button when a target for
the intent is not found. This will ensure the button is not
visible in cases when clicking it would have done nothing.
Bug: 283239837
Flag: com.android.settings.flags.datetime_feedback
Test: Test: atest TimeFeedbackPreferenceControllerTest.java
Change-Id: I8bb18c313925a7dc7ac07a1fb4c2f9e2d98352db
diff --git a/src/com/android/settings/datetime/TimeFeedbackPreferenceController.java b/src/com/android/settings/datetime/TimeFeedbackPreferenceController.java
index 907c202..ff3cb4b 100644
--- a/src/com/android/settings/datetime/TimeFeedbackPreferenceController.java
+++ b/src/com/android/settings/datetime/TimeFeedbackPreferenceController.java
@@ -19,9 +19,12 @@
import static android.content.Intent.URI_INTENT_SCHEME;
import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.text.TextUtils;
+import android.util.Log;
import androidx.preference.Preference;
@@ -40,17 +43,22 @@
extends BasePreferenceController
implements PreferenceControllerMixin {
+ private static final String TAG = "TimeFeedbackController";
+
+ private final PackageManager mPackageManager;
private final String mIntentUri;
private final int mAvailabilityStatus;
public TimeFeedbackPreferenceController(Context context, String preferenceKey) {
- this(context, preferenceKey, context.getResources().getString(
+ this(context, context.getPackageManager(), preferenceKey, context.getResources().getString(
R.string.config_time_feedback_intent_uri));
}
@VisibleForTesting
- TimeFeedbackPreferenceController(Context context, String preferenceKey, String intentUri) {
+ TimeFeedbackPreferenceController(Context context, PackageManager packageManager,
+ String preferenceKey, String intentUri) {
super(context, preferenceKey);
+ mPackageManager = packageManager;
mIntentUri = intentUri;
mAvailabilityStatus = TextUtils.isEmpty(mIntentUri) ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
}
@@ -70,6 +78,9 @@
if (!DateTimeLaunchUtils.isFeedbackFeatureSupported()) {
return UNSUPPORTED_ON_DEVICE;
}
+ if (!isTimeFeedbackTargetAvailable()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
return mAvailabilityStatus;
}
@@ -89,7 +100,25 @@
mContext.startActivity(intent);
return true;
} catch (URISyntaxException e) {
- throw new IllegalArgumentException("Bad intent configuration: " + mIntentUri, e);
+ Log.e(TAG, "Bad intent configuration: " + mIntentUri, e);
+ return false;
}
}
+
+ private boolean isTimeFeedbackTargetAvailable() {
+ Intent intent;
+ try {
+ intent = Intent.parseUri(mIntentUri, URI_INTENT_SCHEME);
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Bad intent configuration: " + mIntentUri, e);
+ return false;
+ }
+ ComponentName resolvedActivity = intent.resolveActivity(mPackageManager);
+
+ if (resolvedActivity == null) {
+ Log.w(TAG, "No valid target for the time feedback intent: " + intent);
+ return false;
+ }
+ return true;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/datetime/TimeFeedbackPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/TimeFeedbackPreferenceControllerTest.java
index f60e831..196aa36 100644
--- a/tests/robotests/src/com/android/settings/datetime/TimeFeedbackPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/TimeFeedbackPreferenceControllerTest.java
@@ -16,31 +16,64 @@
package com.android.settings.datetime;
+import static android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.DeviceConfig;
import androidx.preference.Preference;
+import com.android.settings.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class TimeFeedbackPreferenceControllerTest {
+ private static final String PACKAGE = "com.android.settings.test";
+ private static final String TEST_INTENT_URI =
+ "intent:#Intent;"
+ + "action=com.android.settings.test.LAUNCH_USER_FEEDBACK;"
+ + "package=com.android.settings.test.target;"
+ + "end";
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Mock
+ private PackageManager mMockPackageManager;
private Context mContext;
@Before
@@ -52,21 +85,47 @@
public void emptyIntentUri_controllerNotAvailable() {
String emptyIntentUri = "";
TimeFeedbackPreferenceController controller =
- new TimeFeedbackPreferenceController(mContext, "test_key", emptyIntentUri);
+ new TimeFeedbackPreferenceController(mContext, mContext.getPackageManager(),
+ "test_key", emptyIntentUri);
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
+ @EnableFlags({Flags.FLAG_DATETIME_FEEDBACK})
+ public void validIntentUri_targetHandlerNotFound_returnsConditionallyUnavailable() {
+ DeviceConfig.setProperty(NAMESPACE_SETTINGS_UI,
+ DateTimeLaunchUtils.KEY_HELP_AND_FEEDBACK_FEATURE_SUPPORTED, "true", true);
+ when(mMockPackageManager.resolveActivity(any(), anyInt())).thenReturn(null);
+
+ TimeFeedbackPreferenceController controller =
+ new TimeFeedbackPreferenceController(mContext, mMockPackageManager, "test_key",
+ TEST_INTENT_URI);
+
+ assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_DATETIME_FEEDBACK})
+ public void validIntentUri_targetHandlerAvailable_returnsAvailable() {
+ DeviceConfig.setProperty(NAMESPACE_SETTINGS_UI,
+ DateTimeLaunchUtils.KEY_HELP_AND_FEEDBACK_FEATURE_SUPPORTED, "true", true);
+ when(mMockPackageManager.resolveActivity(any(), anyInt())).thenReturn(
+ createDummyResolveInfo());
+
+ TimeFeedbackPreferenceController controller =
+ new TimeFeedbackPreferenceController(mContext, mMockPackageManager, "test_key",
+ TEST_INTENT_URI);
+
+ assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
public void clickPreference() {
Preference preference = new Preference(mContext);
- String intentUri =
- "intent:#Intent;"
- + "action=com.android.settings.test.LAUNCH_USER_FEEDBACK;"
- + "package=com.android.settings.test.target;"
- + "end";
TimeFeedbackPreferenceController controller =
- new TimeFeedbackPreferenceController(mContext, "test_key", intentUri);
+ new TimeFeedbackPreferenceController(mContext, mContext.getPackageManager(),
+ "test_key", TEST_INTENT_URI);
// Click a preference that's not controlled by this controller.
preference.setKey("fake_key");
@@ -87,4 +146,16 @@
"com.android.settings.test.LAUNCH_USER_FEEDBACK");
assertThat(actualIntent.getPackage()).isEqualTo("com.android.settings.test.target");
}
+
+ private static ResolveInfo createDummyResolveInfo() {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = PACKAGE;
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.applicationInfo = applicationInfo;
+ activityInfo.name = "TestActivity";
+
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = activityInfo;
+ return resolveInfo;
+ }
}