diff --git a/res/values/strings.xml b/res/values/strings.xml index f96e0052bde..c3c2fe4c16a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -710,6 +710,10 @@ Use network-provided time zone + + Automatic 24\u2011hour format + + Use locale default 24\u2011hour format diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml index c9ad0dddb23..ddc685cb313 100644 --- a/res/xml/date_time_prefs.xml +++ b/res/xml/date_time_prefs.xml @@ -20,38 +20,53 @@ android:title="@string/date_and_time" settings:keywords="@string/keywords_date_and_time"> - + + - + - + + - + + - + + - + + + + + diff --git a/src/com/android/settings/DateTimeSettings.java b/src/com/android/settings/DateTimeSettings.java index 2afbdb23aef..18a7ee1eb4f 100644 --- a/src/com/android/settings/DateTimeSettings.java +++ b/src/com/android/settings/DateTimeSettings.java @@ -26,6 +26,7 @@ import android.provider.SearchIndexableResource; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.SummaryLoader; +import com.android.settings.datetime.AutoTimeFormatPreferenceController; import com.android.settings.datetime.AutoTimePreferenceController; import com.android.settings.datetime.AutoTimeZonePreferenceController; import com.android.settings.datetime.DatePreferenceController; @@ -84,8 +85,12 @@ public class DateTimeSettings extends DashboardFragment implements final AutoTimePreferenceController autoTimePreferenceController = new AutoTimePreferenceController( activity, this /* UpdateTimeAndDateCallback */); + final AutoTimeFormatPreferenceController autoTimeFormatPreferenceController = + new AutoTimeFormatPreferenceController( + activity, this /* UpdateTimeAndDateCallback */); controllers.add(autoTimeZonePreferenceController); controllers.add(autoTimePreferenceController); + controllers.add(autoTimeFormatPreferenceController); controllers.add(new TimeFormatPreferenceController( activity, this /* UpdateTimeAndDateCallback */, isFromSUW)); diff --git a/src/com/android/settings/datetime/AutoTimeFormatPreferenceController.java b/src/com/android/settings/datetime/AutoTimeFormatPreferenceController.java new file mode 100644 index 00000000000..15b74026c58 --- /dev/null +++ b/src/com/android/settings/datetime/AutoTimeFormatPreferenceController.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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.android.settings.datetime; + +import android.content.Context; +import android.provider.Settings; +import android.provider.Settings.System; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.TwoStatePreference; +import android.text.TextUtils; +import android.text.format.DateFormat; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.core.AbstractPreferenceController; +import java.util.Locale; + +public class AutoTimeFormatPreferenceController extends AbstractPreferenceController + implements PreferenceControllerMixin { + + private static final String KEY_AUTO_24_HOUR = "auto_24hour"; + + public AutoTimeFormatPreferenceController(Context context, UpdateTimeAndDateCallback callback) { + super(context); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_AUTO_24_HOUR; + } + + @Override + public void updateState(Preference preference) { + if (!(preference instanceof SwitchPreference)) { + return; + } + ((SwitchPreference) preference).setChecked(isAutoTimeFormatSelection(mContext)); + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!(preference instanceof TwoStatePreference) + || !TextUtils.equals(KEY_AUTO_24_HOUR, preference.getKey())) { + return false; + } + boolean auto24HourEnabled = ((SwitchPreference) preference).isChecked(); + Boolean is24Hour; + if (auto24HourEnabled) { + is24Hour = null; + } else { + is24Hour = is24HourLocale(mContext.getResources().getConfiguration().locale); + } + TimeFormatPreferenceController.update24HourFormat(mContext, is24Hour); + return true; + } + + boolean is24HourLocale(Locale locale) { + return DateFormat.is24HourLocale(locale); + } + + /** + * Returns if the system is currently configured to pick the time format automatically based on + * the locale. + */ + static boolean isAutoTimeFormatSelection(Context context) { + return Settings.System.getString(context.getContentResolver(), System.TIME_12_24) == null; + } +} diff --git a/src/com/android/settings/datetime/TimeFormatPreferenceController.java b/src/com/android/settings/datetime/TimeFormatPreferenceController.java index 92b371667f0..c594b264ac8 100644 --- a/src/com/android/settings/datetime/TimeFormatPreferenceController.java +++ b/src/com/android/settings/datetime/TimeFormatPreferenceController.java @@ -63,6 +63,8 @@ public class TimeFormatPreferenceController extends AbstractPreferenceController if (!(preference instanceof TwoStatePreference)) { return; } + preference.setEnabled( + !AutoTimeFormatPreferenceController.isAutoTimeFormatSelection(mContext)); ((TwoStatePreference) preference).setChecked(is24Hour()); final Calendar now = Calendar.getInstance(); mDummyDate.setTimeZone(now.getTimeZone()); @@ -80,8 +82,7 @@ public class TimeFormatPreferenceController extends AbstractPreferenceController return false; } final boolean is24Hour = ((SwitchPreference) preference).isChecked(); - set24Hour(is24Hour); - timeUpdated(is24Hour); + update24HourFormat(mContext, is24Hour); mUpdateTimeAndDateCallback.updateTimeAndDateDisplay(mContext); return true; } @@ -95,18 +96,28 @@ public class TimeFormatPreferenceController extends AbstractPreferenceController return DateFormat.is24HourFormat(mContext); } - private void timeUpdated(boolean is24Hour) { - Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED); - int timeFormatPreference = - is24Hour ? Intent.EXTRA_TIME_PREF_VALUE_USE_24_HOUR - : Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR; - timeChanged.putExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, timeFormatPreference); - mContext.sendBroadcast(timeChanged); + static void update24HourFormat(Context context, Boolean is24Hour) { + set24Hour(context, is24Hour); + timeUpdated(context, is24Hour); } - private void set24Hour(boolean is24Hour) { - Settings.System.putString(mContext.getContentResolver(), - Settings.System.TIME_12_24, - is24Hour ? HOURS_24 : HOURS_12); + static void timeUpdated(Context context, Boolean is24Hour) { + Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED); + int timeFormatPreference; + if (is24Hour == null) { + timeFormatPreference = Intent.EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT; + } else { + timeFormatPreference = is24Hour ? Intent.EXTRA_TIME_PREF_VALUE_USE_24_HOUR + : Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR; + } + timeChanged.putExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, timeFormatPreference); + context.sendBroadcast(timeChanged); + } + + static void set24Hour(Context context, Boolean is24Hour) { + String value = is24Hour == null ? null : + is24Hour ? HOURS_24 : HOURS_12; + Settings.System.putString(context.getContentResolver(), + Settings.System.TIME_12_24, value); } } diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeFormatPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeFormatPreferenceControllerTest.java new file mode 100644 index 00000000000..82d3dd90337 --- /dev/null +++ b/tests/robotests/src/com/android/settings/datetime/AutoTimeFormatPreferenceControllerTest.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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.android.settings.datetime; + + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Answers.RETURNS_DEEP_STUBS; + +import android.content.Context; +import android.content.Intent; +import android.provider.Settings; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.PreferenceScreen; +import android.text.format.DateFormat; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import java.util.List; +import java.util.Locale; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; +import org.robolectric.shadows.ShadowApplication; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AutoTimeFormatPreferenceControllerTest { + + @Mock(answer = RETURNS_DEEP_STUBS) + private PreferenceScreen mScreen; + @Mock + private UpdateTimeAndDateCallback mCallback; + + private ShadowApplication mApplication; + private Context mContext; + private SwitchPreference mPreference; + private TestAutoTimeFormatPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mApplication = ShadowApplication.getInstance(); + mContext = mApplication.getApplicationContext(); + } + + @Test + public void updateState_24HourSet_shouldCheckPreference() { + mController = new TestAutoTimeFormatPreferenceController(mContext, mCallback); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24, + TimeFormatPreferenceController.HOURS_24); + + mController.updateState(mPreference); + + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void updateState_12HourSet_shouldCheckPreference() { + mController = new TestAutoTimeFormatPreferenceController(mContext, mCallback); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24, + TimeFormatPreferenceController.HOURS_12); + + mController.updateState(mPreference); + + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void updateState_autoSet_shouldNotCheckPreference() { + mController = new TestAutoTimeFormatPreferenceController(mContext, mCallback); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24, null); + + mController.updateState(mPreference); + + assertThat(mPreference.isChecked()).isTrue(); + } + + @Test + public void updatePreference_autoSet_shouldSendIntent_12HourLocale() { + mController = new TestAutoTimeFormatPreferenceController(mContext, mCallback); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + mPreference.setChecked(false); + + boolean result = mController.handlePreferenceTreeClick(mPreference); + + assertThat(result).isTrue(); + + List intentsFired = mApplication.getBroadcastIntents(); + assertThat(intentsFired.size()).isEqualTo(1); + Intent intentFired = intentsFired.get(0); + assertThat(intentFired.getAction()).isEqualTo(Intent.ACTION_TIME_CHANGED); + assertThat(intentFired.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, -1)) + .isEqualTo(Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR); + } + + @Test + public void updatePreference_autoSet_shouldSendIntent_24HourLocale() { + mController = new TestAutoTimeFormatPreferenceController(mContext, mCallback); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + mPreference.setChecked(false); + + mController.setIs24HourLocale(true); + boolean result = mController.handlePreferenceTreeClick(mPreference); + + assertThat(result).isTrue(); + + List intentsFired = mApplication.getBroadcastIntents(); + assertThat(intentsFired.size()).isEqualTo(1); + Intent intentFired = intentsFired.get(0); + assertThat(intentFired.getAction()).isEqualTo(Intent.ACTION_TIME_CHANGED); + assertThat(intentFired.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, -1)) + .isEqualTo(Intent.EXTRA_TIME_PREF_VALUE_USE_24_HOUR); + } + + @Test + public void updatePreference_24HourSet_shouldSendIntent() { + mController = new TestAutoTimeFormatPreferenceController(mContext, mCallback); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + mPreference.setChecked(true); + + mController.setIs24HourLocale(false); + boolean result = mController.handlePreferenceTreeClick(mPreference); + + assertThat(result).isTrue(); + + List intentsFired = mApplication.getBroadcastIntents(); + assertThat(intentsFired.size()).isEqualTo(1); + Intent intentFired = intentsFired.get(0); + assertThat(intentFired.getAction()).isEqualTo(Intent.ACTION_TIME_CHANGED); + assertThat(intentFired.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, -1)) + .isEqualTo(Intent.EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT); + } + + /** + * Extend class under test to change {@link #is24HourLocale()} to not call + * {@link DateFormat#is24HourLocale(Locale)} because that's not available in roboelectric. + */ + private static class TestAutoTimeFormatPreferenceController + extends AutoTimeFormatPreferenceController { + + private boolean is24HourLocale = false; + + public TestAutoTimeFormatPreferenceController(Context context, + UpdateTimeAndDateCallback callback) { + super(context, callback); + } + + void setIs24HourLocale(boolean value) { + is24HourLocale = value; + } + + @Override + boolean is24HourLocale(Locale locale) { + return is24HourLocale; + } + } +} diff --git a/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java index 58ecd62e712..31b6841dc2e 100644 --- a/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java @@ -39,6 +39,7 @@ import org.robolectric.shadows.ShadowApplication; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @@ -101,6 +102,18 @@ public class TimeFormatPreferenceControllerTest { assertThat(mPreference.isChecked()).isFalse(); } + @Test + public void updateState_autoSet_shouldNotEnablePreference() { + mController = new TimeFormatPreferenceController(mContext, mCallback, false); + Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24, null); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + + mController.updateState(mPreference); + + assertThat(mPreference.isEnabled()).isFalse(); + } + @Test public void updatePreference_12HourSet_shouldSendIntent() { mController = new TimeFormatPreferenceController(mContext, mCallback, false);