Loading res/xml/date_time_prefs.xml +12 −13 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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> src/com/android/settings/datetime/TimeFeedbackPreferenceController.java +32 −3 Original line number Diff line number Diff line Loading @@ -19,9 +19,12 @@ package com.android.settings.datetime; 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; Loading @@ -40,17 +43,22 @@ public class TimeFeedbackPreferenceController 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; } Loading @@ -70,6 +78,9 @@ public class TimeFeedbackPreferenceController if (!DateTimeLaunchUtils.isFeedbackFeatureSupported()) { return UNSUPPORTED_ON_DEVICE; } if (!isTimeFeedbackTargetAvailable()) { return CONDITIONALLY_UNAVAILABLE; } return mAvailabilityStatus; } Loading @@ -89,7 +100,25 @@ public class TimeFeedbackPreferenceController 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; } } tests/robotests/src/com/android/settings/datetime/TimeFeedbackPreferenceControllerTest.java +78 −7 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -52,21 +85,47 @@ public class TimeFeedbackPreferenceControllerTest { 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"); Loading @@ -87,4 +146,16 @@ public class TimeFeedbackPreferenceControllerTest { "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; } } Loading
res/xml/date_time_prefs.xml +12 −13 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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>
src/com/android/settings/datetime/TimeFeedbackPreferenceController.java +32 −3 Original line number Diff line number Diff line Loading @@ -19,9 +19,12 @@ package com.android.settings.datetime; 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; Loading @@ -40,17 +43,22 @@ public class TimeFeedbackPreferenceController 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; } Loading @@ -70,6 +78,9 @@ public class TimeFeedbackPreferenceController if (!DateTimeLaunchUtils.isFeedbackFeatureSupported()) { return UNSUPPORTED_ON_DEVICE; } if (!isTimeFeedbackTargetAvailable()) { return CONDITIONALLY_UNAVAILABLE; } return mAvailabilityStatus; } Loading @@ -89,7 +100,25 @@ public class TimeFeedbackPreferenceController 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; } }
tests/robotests/src/com/android/settings/datetime/TimeFeedbackPreferenceControllerTest.java +78 −7 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -52,21 +85,47 @@ public class TimeFeedbackPreferenceControllerTest { 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"); Loading @@ -87,4 +146,16 @@ public class TimeFeedbackPreferenceControllerTest { "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; } }