1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.ui; 17 18 import static com.android.settings.ui.testutils.SettingsTestUtils.SETTINGS_PACKAGE; 19 import static com.android.settings.ui.testutils.SettingsTestUtils.TIMEOUT; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertTrue; 24 25 import android.os.RemoteException; 26 import android.os.SystemProperties; 27 import android.provider.Settings; 28 import android.support.test.uiautomator.By; 29 import android.support.test.uiautomator.BySelector; 30 import android.support.test.uiautomator.UiDevice; 31 import android.support.test.uiautomator.UiObject; 32 import android.support.test.uiautomator.UiObject2; 33 import android.support.test.uiautomator.UiObjectNotFoundException; 34 import android.support.test.uiautomator.UiScrollable; 35 import android.support.test.uiautomator.UiSelector; 36 import android.support.test.uiautomator.Until; 37 import android.system.helpers.SettingsHelper; 38 import android.system.helpers.SettingsHelper.SettingsType; 39 40 import androidx.test.InstrumentationRegistry; 41 import androidx.test.filters.MediumTest; 42 import androidx.test.runner.AndroidJUnit4; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 import java.util.TimeZone; 50 51 @MediumTest 52 @RunWith(AndroidJUnit4.class) 53 public class ZonePickerSettingsTest { 54 55 private static final BySelector SELECTOR_SELECT_TIME_ZONE = 56 By.hasChild(By.text("Select time zone")); 57 58 private UiDevice mDevice; 59 private SettingsHelper mHelper; 60 private String mIsV2EnabledByDefault; 61 private int mIsAutoZoneEnabled; 62 63 @Before setUp()64 public void setUp() throws Exception { 65 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 66 mHelper = SettingsHelper.getInstance(); 67 try { 68 mDevice.setOrientationNatural(); 69 } catch (RemoteException e) { 70 throw new RuntimeException("failed to freeze device orientation", e); 71 } 72 mIsV2EnabledByDefault = mHelper.getStringSetting(SettingsType.GLOBAL, 73 "settings_zone_picker_v2"); 74 mHelper.setStringSetting(SettingsType.GLOBAL, "settings_zone_picker_v2", "true"); 75 mIsAutoZoneEnabled = mHelper.getIntSetting(SettingsType.GLOBAL, 76 Settings.Global.AUTO_TIME_ZONE); 77 } 78 79 @After tearDown()80 public void tearDown() throws Exception { 81 // Go back to home for next test. 82 mDevice.pressBack(); 83 mDevice.pressBack(); 84 mDevice.pressHome(); 85 mDevice.waitForIdle(TIMEOUT * 2); 86 mHelper.setStringSetting(SettingsType.GLOBAL, "settings_zone_picker_v2", 87 mIsV2EnabledByDefault); 88 mHelper.setIntSetting(SettingsType.GLOBAL, Settings.Global.AUTO_TIME_ZONE, 89 mIsAutoZoneEnabled); 90 } 91 92 @Test zonePickerDisabled()93 public void zonePickerDisabled() throws Exception { 94 mHelper.setIntSetting(SettingsType.GLOBAL, Settings.Global.AUTO_TIME_ZONE, 1); 95 96 SettingsHelper.launchSettingsPage( 97 InstrumentationRegistry.getContext(), Settings.ACTION_DATE_SETTINGS); 98 UiObject2 selectTimeZone = wait(SELECTOR_SELECT_TIME_ZONE); 99 assertFalse(selectTimeZone.isEnabled()); 100 } 101 102 // Test 2 time zones with no DST 103 @Test testSelectReykjavik()104 public void testSelectReykjavik() throws Exception { 105 testSelectTimeZone("Iceland", "Reykjavik", "GMT+00:00", "Atlantic/Reykjavik", true); 106 } 107 108 @Test testSelectPhoenix()109 public void testSelectPhoenix() throws Exception { 110 testSelectTimeZone("United States", "Phoenix", "GMT-07:00", "America/Phoenix", false); 111 } 112 testSelectTimeZone(String region, String timezone, String expectedTimeZoneOffset, String expectedTimeZoneId, boolean assumeOneTimeZoneInRegion)113 private void testSelectTimeZone(String region, String timezone, String expectedTimeZoneOffset, 114 String expectedTimeZoneId, boolean assumeOneTimeZoneInRegion) throws Exception { 115 mHelper.setIntSetting(SettingsType.GLOBAL, Settings.Global.AUTO_TIME_ZONE, 0); 116 117 SettingsHelper.launchSettingsPage( 118 InstrumentationRegistry.getContext(), Settings.ACTION_DATE_SETTINGS); 119 120 UiObject2 selectTimeZone = wait(SELECTOR_SELECT_TIME_ZONE); 121 assertTrue(selectTimeZone.isEnabled()); 122 selectTimeZone.click(); 123 124 wait(By.text("Region")).click(); 125 // Speed-up the test by searching with the first 2 characters of the region name 126 wait(By.res("android", "search_src_text")).setText(region.substring(0, 2)); 127 // Select region in the list 128 selectItemInList(new UiSelector().textContains(region)) 129 .click(); 130 131 // Only select time zone explicitly if there are more than one time zones in a region 132 if (!assumeOneTimeZoneInRegion) { 133 wait(By.text("Time zone")); 134 selectItemInList(new UiSelector().textContains(timezone)) 135 .click(); 136 } 137 138 mDevice.pressBack(); 139 // The select button should include the GMT offset in the summary 140 BySelector summarySelector = By.res("android:id/summary"); 141 UiObject2 selectedTimeZone = selectTimeZone.findObject(summarySelector); 142 assertUiObjectFound(selectedTimeZone, summarySelector); 143 assertTrue("Expect " + expectedTimeZoneOffset + " is shown for " + timezone, 144 selectedTimeZone.getText().startsWith(expectedTimeZoneOffset)); 145 146 waitAndAssertTimeGetDefault(expectedTimeZoneId); 147 assertEquals("Time zone change in Settings should update persist.sys.timezone", 148 expectedTimeZoneId, SystemProperties.get("persist.sys.timezone")); 149 } 150 151 private static final long CHECK_DEFAULT_TIMEZONE_INTERVAL = 200L; 152 private static final long CHECK_DEFAULT_TIMEZONE_TIMEOUT = 3000L; 153 154 /** 155 * Wait for the broadcast ACTION_TIMEZONE_CHANGED propagated, and update the default TimeZone 156 * by ApplicationThread. 157 */ waitAndAssertTimeGetDefault(String expectedTimeZoneId)158 private static void waitAndAssertTimeGetDefault(String expectedTimeZoneId) 159 throws InterruptedException { 160 for (int i = 0; i < CHECK_DEFAULT_TIMEZONE_TIMEOUT / CHECK_DEFAULT_TIMEZONE_INTERVAL; i++) { 161 if (expectedTimeZoneId.equals(TimeZone.getDefault().getID())) { 162 return; 163 } 164 Thread.sleep(CHECK_DEFAULT_TIMEZONE_INTERVAL); 165 } 166 167 assertEquals(expectedTimeZoneId, TimeZone.getDefault().getID()); 168 } 169 selectItemInList(UiSelector childSelector)170 private UiObject selectItemInList(UiSelector childSelector) throws UiObjectNotFoundException { 171 UiScrollable recyclerView = new UiScrollable( 172 new UiSelector().resourceId(SETTINGS_PACKAGE + ":id/recycler_view")); 173 return selectScrollableItem(recyclerView, childSelector); 174 } 175 176 /** 177 * Select the child object in the UiScrollable 178 * @throws UiObjectNotFoundException if scrollable or child is not found 179 */ selectScrollableItem(UiScrollable scrollable, UiSelector childSelector)180 private UiObject selectScrollableItem(UiScrollable scrollable, UiSelector childSelector) 181 throws UiObjectNotFoundException { 182 if (!scrollable.waitForExists(TIMEOUT)) { 183 throw newUiObjectNotFoundException(scrollable.getSelector()); 184 } 185 scrollable.scrollIntoView(childSelector); 186 187 UiObject child = mDevice.findObject(childSelector); 188 assertUiObjectFound(child, childSelector); 189 return child; 190 } 191 192 /** 193 * @throws UiObjectNotFoundException if UiDevice.wait returns null 194 */ wait(BySelector selector)195 private UiObject2 wait(BySelector selector) throws UiObjectNotFoundException { 196 UiObject2 item = mDevice.wait(Until.findObject(selector), TIMEOUT); 197 assertUiObjectFound(item, selector); 198 return item; 199 } 200 assertUiObjectFound(UiObject2 obj, BySelector selector)201 private static void assertUiObjectFound(UiObject2 obj, BySelector selector) 202 throws UiObjectNotFoundException { 203 if (obj == null) { 204 throw newUiObjectNotFoundException(selector); 205 } 206 } 207 208 assertUiObjectFound(UiObject obj, UiSelector selector)209 private static void assertUiObjectFound(UiObject obj, UiSelector selector) 210 throws UiObjectNotFoundException { 211 if (obj == null) { 212 throw newUiObjectNotFoundException(selector); 213 } 214 } 215 newUiObjectNotFoundException(Object selector)216 private static UiObjectNotFoundException newUiObjectNotFoundException(Object selector) { 217 return new UiObjectNotFoundException( 218 String.format("UI object not found: %s", selector.toString())); 219 } 220 } 221