1b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri/* 2b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * Copyright (C) 2017 The Android Open Source Project 3b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * 4b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * Licensed under the Apache License, Version 2.0 (the "License"); 5b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * you may not use this file except in compliance with the License. 6b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * You may obtain a copy of the License at 7b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * 8b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * http://www.apache.org/licenses/LICENSE-2.0 9b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * 10b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * Unless required by applicable law or agreed to in writing, software 11b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * distributed under the License is distributed on an "AS IS" BASIS, 12b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * See the License for the specific language governing permissions and 14b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri * limitations under the License. 15b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri */ 16b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 17b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiripackage android.support.v17.leanback.widget; 18b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 19b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport static org.hamcrest.CoreMatchers.is; 20b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport static org.hamcrest.MatcherAssert.assertThat; 217041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiriimport static org.junit.Assert.assertTrue; 22b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 23b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.content.Context; 24b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.content.Intent; 25b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.support.test.InstrumentationRegistry; 26b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.support.test.filters.MediumTest; 27b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.support.test.rule.ActivityTestRule; 28b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.support.test.runner.AndroidJUnit4; 29b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.support.v17.leanback.test.R; 30b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.support.v17.leanback.widget.picker.DatePicker; 31b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.util.Log; 32b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.view.KeyEvent; 33b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.view.View; 34b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport android.view.ViewGroup; 35b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 36b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport org.junit.Rule; 37b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport org.junit.Test; 38b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiriimport org.junit.runner.RunWith; 39b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 40b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri@MediumTest 41b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri@RunWith(AndroidJUnit4.class) 42b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiripublic class DatePickerTest { 43b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 44b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri private static final String TAG = "DatePickerTest"; 45b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri private static final long TRANSITION_LENGTH = 1000; 46b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 47b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Context mContext; 48b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri View mViewAbove; 49b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri DatePicker mDatePickerView; 50b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri ViewGroup mDatePickerInnerView; 51b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri View mViewBelow; 52b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 53b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Rule 54b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public ActivityTestRule<DatePickerActivity> mActivityTestRule = 55b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri new ActivityTestRule<>(DatePickerActivity.class, false, false); 56b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri private DatePickerActivity mActivity; 57b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 58b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void initActivity(Intent intent) throws Throwable { 59b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mActivity = mActivityTestRule.launchActivity(intent); 60b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 61b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView = (DatePicker) mActivity.findViewById(R.id.date_picker); 62b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView = (ViewGroup) mDatePickerView.findViewById(R.id.picker); 63b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setActivatedVisibleItemCount(3); 64b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setOnClickListener(new View.OnClickListener() { 65b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Override 66b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void onClick(View v) { 67b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setActivated(!mDatePickerView.isActivated()); 68b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 69b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri }); 70b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri if (intent.getIntExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 71b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_with_other_widgets) == R.layout.datepicker_with_other_widgets) { 72b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mViewAbove = mActivity.findViewById(R.id.above_picker); 73b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mViewBelow = mActivity.findViewById(R.id.below_picker); 74b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } else if (intent.getIntExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 75b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_with_other_widgets) == R.layout.datepicker_alone) { 76b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // A layout with only a DatePicker widget that is initially activated. 77b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mActivityTestRule.runOnUiThread(new Runnable() { 78b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Override 79b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void run() { 80b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setActivated(true); 81b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 82b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri }); 83b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(500); 84b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 85b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 86b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 87b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Test 88b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void testFocusTravel() throws Throwable { 89b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Intent intent = new Intent(); 90b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 91b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_with_other_widgets); 92b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri initActivity(intent); 93b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 94b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("TextView above should have focus initially", mViewAbove.hasFocus(), is(true)); 95b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 96b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 97b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 98b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("DatePicker should have focus now", mDatePickerView.isFocused(), is(true)); 99b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 100b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 101b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 102b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The first column of DatePicker should hold focus", 103b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(0).hasFocus(), is(true)); 104b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 105b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // skipping the separator in the child indices 106b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 107b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 108b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The second column of DatePicker should hold focus", 109b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(2).hasFocus(), is(true)); 110b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 111b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 112b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 113b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The third column of DatePicker should hold focus", 114b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(4).hasFocus(), is(true)); 115b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 116b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 117b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Test 118b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void testFocusRetainedForASelectedColumn() 119b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri throws Throwable { 120b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Intent intent = new Intent(); 121b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 122b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_with_other_widgets); 123b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri initActivity(intent); 124b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setFocusable(true); 125b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 126b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 127b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 128b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("DatePicker should have focus when it's focusable", 129b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.isFocused(), is(true)); 130b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 131b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 132b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 133b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 134b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("After the first activation, the first column of DatePicker should hold focus", 135b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(0).hasFocus(), is(true)); 136b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 137b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 138b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 139b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 140b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 141b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 142b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The third column of DatePicker should hold focus", 143b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(4).hasFocus(), is(true)); 144b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 145b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 146b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 147b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("After the first deactivation, the DatePicker itself should hold focus", 148b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.isFocused(), is(true)); 149b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 150b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 151b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 152b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("After the second activation, the last selected column (3rd) should hold focus", 153b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(4).hasFocus(), is(true)); 154b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 155b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 156b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Test 157b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void testFocusSkippedWhenDatePickerUnFocusable() 158b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri throws Throwable { 159b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Intent intent = new Intent(); 160b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 161b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_with_other_widgets); 162b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri initActivity(intent); 163b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 164b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setFocusable(false); 165b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("TextView above should have focus initially.", mViewAbove.hasFocus(), is(true)); 166b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 167b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 168b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 169b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 170b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("DatePicker should be skipped and TextView below should have focus.", 171b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mViewBelow.hasFocus(), is(true)); 172b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 173b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 174b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Test 175b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void testTemporaryFocusLossWhenDeactivated() 176b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri throws Throwable { 177b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Intent intent = new Intent(); 178b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 179b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_with_other_widgets); 180b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri initActivity(intent); 181b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 182b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri final int[] currentFocusChangeCountForViewAbove = {0}; 183b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setFocusable(true); 184b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Log.d(TAG, "view above: " + mViewAbove); 185b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mViewAbove.setOnFocusChangeListener(new View.OnFocusChangeListener(){ 186b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Override 187b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void onFocusChange(View v, boolean hasFocus) { 188b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForViewAbove[0]++; 189b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 190b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri }); 191b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("TextView above should have focus initially.", mViewAbove.hasFocus(), is(true)); 192b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 193b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Traverse to the third column of date picker 194b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 195b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 196b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Click once to activate 197b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 198b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 199b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Traverse to the third column 200b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 201b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 202b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 203b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 204b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Click to deactivate. Before that we remember the focus change count for the view above. 205b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // This view should NOT receive temporary focus when DatePicker is deactivated, and 206b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // DatePicker itself should capture the focus. 207b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri int[] lastFocusChangeCountForViewAbove = {currentFocusChangeCountForViewAbove[0]}; 208b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 209b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 210b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("DatePicker should have focus now since it's focusable", 211b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.isFocused(), is(true)); 212b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("Focus change count of view above should not be changed after last click.", 213b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForViewAbove[0], is(lastFocusChangeCountForViewAbove[0])); 214b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 215b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 216b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Test 217b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void testTemporaryFocusLossWhenActivated() throws Throwable { 218b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Intent intent = new Intent(); 219b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 220b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_with_other_widgets); 221b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri initActivity(intent); 222b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri final int[] currentFocusChangeCountForColumns = {0, 0, 0}; 223b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerView.setFocusable(true); 224b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(0).setOnFocusChangeListener( 225b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri new View.OnFocusChangeListener() { 226b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Override 227b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void onFocusChange(View v, boolean hasFocus) { 228b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForColumns[0]++; 229b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 230b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri }); 231b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 232b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(2).setOnFocusChangeListener( 233b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri new View.OnFocusChangeListener() { 234b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Override 235b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void onFocusChange(View v, boolean hasFocus) { 236b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForColumns[1]++; 237b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 238b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri }); 239b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 240b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(4).setOnFocusChangeListener( 241b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri new View.OnFocusChangeListener() { 242b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Override 243b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void onFocusChange(View v, boolean hasFocus) { 244b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForColumns[2]++; 245b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 246b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri }); 247b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 248b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Traverse to the third column of date picker 249b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 250b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 251b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Click once to activate 252b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 253b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 254b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Traverse to the third column 255b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 256b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 257b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 258b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 259b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Click to deactivate 260b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 261b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 262b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Click again. The focus should NOT be temporarily moved to the other columns and the third 263b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // column should receive focus. 264b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // Before that we will remember the last focus change count to compare it against after the 265b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // click. 266b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri int[] lastFocusChangeCountForColumns = {currentFocusChangeCountForColumns[0], 267b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForColumns[1], currentFocusChangeCountForColumns[2]}; 268b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 269b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 270b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("Focus change count of column 0 should not be changed after last click.", 271b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForColumns[0], is(lastFocusChangeCountForColumns[0])); 272b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("Focus change count of column 1 should not be changed after last click.", 273b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForColumns[1], is(lastFocusChangeCountForColumns[1])); 274b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("Focus change count of column 2 should not be changed after last click.", 275b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri currentFocusChangeCountForColumns[2], is(lastFocusChangeCountForColumns[2])); 276b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 277b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 278b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri @Test 279b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri public void testInitiallyActiveDatePicker() 280b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri throws Throwable { 281b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Intent intent = new Intent(); 282b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 283b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri R.layout.datepicker_alone); 284b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri initActivity(intent); 285b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 286b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The first column of DatePicker should initially hold focus", 287b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(0).hasFocus(), is(true)); 288b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 289b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // focus on first column 290b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 291b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 292b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The first column of DatePicker should still hold focus after scrolling down", 293b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(0).hasFocus(), is(true)); 294b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 295b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // focus on second column 296b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 297b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 298b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The second column of DatePicker should hold focus after scrolling right", 299b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(2).hasFocus(), is(true)); 300b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 301b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 302b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 303b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The second column of DatePicker should still hold focus after scrolling down", 304b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(2).hasFocus(), is(true)); 305b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 306b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri // focus on third column 307b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 308b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 309b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The third column of DatePicker should hold focus after scrolling right", 310b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(4).hasFocus(), is(true)); 311b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 312b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 313b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri Thread.sleep(TRANSITION_LENGTH); 314b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri assertThat("The third column of DatePicker should still hold focus after scrolling down", 315b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri mDatePickerInnerView.getChildAt(4).hasFocus(), is(true)); 316b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 317b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri 3187041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri @Test 3197041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri public void testInvisibleColumnsAlpha() throws Throwable { 3207041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri Intent intent = new Intent(); 3217041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 3227041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri R.layout.datepicker_with_other_widgets); 3237041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri initActivity(intent); 3247041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri 3257041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri mActivityTestRule.runOnUiThread(new Runnable() { 3267041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri @Override 3277041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri public void run() { 3287041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri mDatePickerView.updateDate(2017, 2, 21, false); 3297041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3307041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri }); 3317041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri 3327041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri Thread.sleep(TRANSITION_LENGTH); 3337041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri mActivityTestRule.runOnUiThread(new Runnable() { 3347041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri @Override 3357041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri public void run() { 3367041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri mDatePickerView.updateDate(2017, 2, 20, false); 3377041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3387041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri }); 3397041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri Thread.sleep(TRANSITION_LENGTH); 3407041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri 3417041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 3427041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri Thread.sleep(TRANSITION_LENGTH); 3437041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri // Click once to activate 3447041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 3457041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri Thread.sleep(TRANSITION_LENGTH); 3467041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri 3477041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri int activeColumn = 0; 3487041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri // For the inactive columns: the alpha for all the rows except the selected row should be 3497041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri // zero: Picker#mInvisibleColumnAlpha (they should all be invisible). 3507041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri for (int i = 0; i < 3; i++) { 3517041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri ViewGroup gridView = (ViewGroup) mDatePickerInnerView.getChildAt(2 * i); 3527041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri int childCount = gridView.getChildCount(); 3537041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri int alpha1RowsCount = 0; 3547041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri int alphaNonZeroRowsCount = 0; 3557041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri for (int j = 0; j < childCount; j++) { 3567041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri View pickerItem = gridView.getChildAt(j); 3577041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri if (pickerItem.getAlpha() > 0) { 3587041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri alphaNonZeroRowsCount++; 3597041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3607041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri if (pickerItem.getAlpha() == 1) { 3617041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri alpha1RowsCount++; 3627041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3637041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3647041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri if (i == activeColumn) { 3657041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri assertThat("The active column " + i + " should have only one row with an alpha of " 3667041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri + "1", alpha1RowsCount, is(1)); 3677041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri assertTrue("The active column " + i + " should have more than one view with alpha " 3687041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri + "greater than 1", alphaNonZeroRowsCount > 1); 3697041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } else { 3707041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri assertThat("The inactive column " + i + " should have only one row with an alpha of" 3717041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri + " 1", alpha1RowsCount, is(1)); 3727041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri assertThat("The inactive column " + i + " should have only one row with a non-zero" 3737041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri + " alpha", alphaNonZeroRowsCount, is(1)); 3747041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3757041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3767041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri } 3777041ebbfa7b73695f5c8e831fa6f14233c95d9efKeyvan Amiri 378b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri private void sendKeys(int ...keys) { 379b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri for (int i = 0; i < keys.length; i++) { 380b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]); 381b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 382b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri } 383b0818cc234e9df4312732f95fd6849922bfa0fa1Keyvan Amiri} 384