Merge changes from topic "feedback_and_help" into main
* changes:
Hide "Send feedback" button if no target exists.
Move "Send feedback" button to the end of the list
diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml
index 678b1e2..3a4bb84 100644
--- a/res/xml/date_time_prefs.xml
+++ b/res/xml/date_time_prefs.xml
@@ -90,7 +90,18 @@
</PreferenceCategory>
- <!-- An optional preference category for feedback. Only displayed up if enabled via flags and config. -->
+ <PreferenceCategory
+ android:key="time_format_preference_category"
+ android:title="@string/time_format_category_title"
+ settings:keywords="@string/keywords_time_format">
+
+ <SwitchPreferenceCompat
+ android:key="24 hour"
+ android:title="@string/date_time_24hour"
+ settings:controller="com.android.settings.datetime.TimeFormatPreferenceController" />
+ </PreferenceCategory>
+
+ <!-- An optional preference category for feedback. Only displayed up if enabled via flags and config. Always put time feedback at the end. -->
<PreferenceCategory
android:key="time_feedback_preference_category"
android:title="@string/time_feedback_category_title"
@@ -102,18 +113,6 @@
android:title="@string/time_feedback_title"
settings:controller="com.android.settings.datetime.TimeFeedbackPreferenceController"
settings:keywords="@string/keywords_time_feedback" />
-
- </PreferenceCategory>
-
- <PreferenceCategory
- android:key="time_format_preference_category"
- android:title="@string/time_format_category_title"
- settings:keywords="@string/keywords_time_format">
-
- <SwitchPreferenceCompat
- android:key="24 hour"
- android:title="@string/date_time_24hour"
- settings:controller="com.android.settings.datetime.TimeFormatPreferenceController" />
</PreferenceCategory>
</PreferenceScreen>
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;
+ }
}