1 /* 2 * Copyright (C) 2017 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.car.settings.datetime; 17 18 import android.app.timedetector.ManualTimeSuggestion; 19 import android.app.timedetector.TimeDetector; 20 import android.app.timedetector.TimeDetectorHelper; 21 import android.car.drivingstate.CarUxRestrictions; 22 import android.content.Intent; 23 import android.os.Bundle; 24 import android.util.Log; 25 import android.view.ViewGroup; 26 import android.widget.DatePicker; 27 import android.widget.NumberPicker; 28 29 import androidx.annotation.LayoutRes; 30 import androidx.annotation.StringRes; 31 32 import com.android.car.settings.R; 33 import com.android.car.settings.common.BaseFragment; 34 import com.android.car.settings.common.rotary.DirectManipulationHandler; 35 import com.android.car.settings.common.rotary.DirectManipulationState; 36 import com.android.car.settings.common.rotary.NumberPickerNudgeHandler; 37 import com.android.car.settings.common.rotary.NumberPickerRotationHandler; 38 import com.android.car.settings.common.rotary.NumberPickerUtils; 39 40 import java.util.ArrayList; 41 import java.util.Calendar; 42 import java.util.GregorianCalendar; 43 import java.util.List; 44 45 /** 46 * Sets the system date. 47 */ 48 public class DatePickerFragment extends BaseFragment { 49 private static final String TAG = "DatePickerFragment"; 50 51 private DirectManipulationState mDirectManipulationMode; 52 private DatePicker mDatePicker; 53 private List<NumberPicker> mNumberPickers; 54 55 @Override onStop()56 public void onStop() { 57 super.onStop(); 58 Calendar c = Calendar.getInstance(); 59 c.set(Calendar.YEAR, mDatePicker.getYear()); 60 c.set(Calendar.MONTH, mDatePicker.getMonth()); 61 c.set(Calendar.DAY_OF_MONTH, mDatePicker.getDayOfMonth()); 62 63 long when = c.getTimeInMillis(); 64 TimeDetector timeDetector = getContext().getSystemService(TimeDetector.class); 65 ManualTimeSuggestion manualTimeSuggestion = 66 TimeDetector.createManualTimeSuggestion(when, "Settings: Set date"); 67 boolean success = timeDetector.suggestManualTime(manualTimeSuggestion); 68 if (success) { 69 getContext().sendBroadcast(new Intent(Intent.ACTION_TIME_CHANGED)); 70 } else { 71 // This implies the system server is applying tighter bounds than the settings app or 72 // the date/time cannot be set for other reasons, e.g. perhaps "auto time" is turned on. 73 Log.w(TAG, "Unable to set date with suggestion=" + manualTimeSuggestion); 74 } 75 } 76 77 @Override 78 @LayoutRes getLayoutId()79 protected int getLayoutId() { 80 return R.layout.date_picker; 81 } 82 83 @Override 84 @StringRes getTitleId()85 protected int getTitleId() { 86 return R.string.date_picker_title; 87 } 88 89 @Override onActivityCreated(Bundle savedInstanceState)90 public void onActivityCreated(Bundle savedInstanceState) { 91 super.onActivityCreated(savedInstanceState); 92 93 mDirectManipulationMode = new DirectManipulationState(); 94 mDatePicker = getView().findViewById(R.id.date_picker); 95 mDatePicker.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 96 97 // Set lower and upper bounds for the date that the user is able to select. 98 GregorianCalendar calendar = new GregorianCalendar(); 99 calendar.clear(); 100 calendar.set(Calendar.DAY_OF_MONTH, 1); 101 calendar.set(Calendar.MONTH, Calendar.JANUARY); 102 calendar.set(Calendar.YEAR, TimeDetectorHelper.INSTANCE.getManualDateSelectionYearMin()); 103 mDatePicker.setMinDate(calendar.getTimeInMillis()); 104 105 calendar.clear(); 106 calendar.set(Calendar.DAY_OF_MONTH, 31); 107 calendar.set(Calendar.MONTH, Calendar.DECEMBER); 108 calendar.set(Calendar.YEAR, TimeDetectorHelper.INSTANCE.getManualDateSelectionYearMax()); 109 mDatePicker.setMaxDate(calendar.getTimeInMillis()); 110 111 mNumberPickers = new ArrayList<>(); 112 NumberPickerUtils.getNumberPickerDescendants(mNumberPickers, mDatePicker); 113 114 DirectManipulationHandler.setDirectManipulationHandler(mDatePicker, 115 new DirectManipulationHandler.Builder(mDirectManipulationMode) 116 // Use no-op nudge handler, since we never stay on this view in direct 117 // manipulation mode. 118 .setNudgeHandler((v, keyCode, event) -> true) 119 .setCenterButtonHandler(inDirectManipulationMode -> { 120 if (inDirectManipulationMode) { 121 return true; 122 } 123 124 NumberPicker picker = mNumberPickers.get(0); 125 if (picker != null) { 126 picker.requestFocus(); 127 } 128 return true; 129 }) 130 .setBackHandler(inDirectManipulationMode -> { 131 // Only handle back if we weren't previously in direct manipulation 132 // mode. 133 if (!inDirectManipulationMode) { 134 onBackPressed(); 135 } 136 return true; 137 }) 138 .build()); 139 140 DirectManipulationHandler numberPickerListener = 141 new DirectManipulationHandler.Builder(mDirectManipulationMode) 142 .setNudgeHandler(new NumberPickerNudgeHandler()) 143 .setCenterButtonHandler(inDirectManipulationMode -> { 144 if (!inDirectManipulationMode) { 145 return true; 146 } 147 148 mDatePicker.requestFocus(); 149 return true; 150 }) 151 .setBackHandler(inDirectManipulationMode -> { 152 mDatePicker.requestFocus(); 153 return true; 154 }) 155 .setRotationHandler(new NumberPickerRotationHandler()) 156 .build(); 157 158 for (int i = 0; i < mNumberPickers.size(); i++) { 159 DirectManipulationHandler.setDirectManipulationHandler(mNumberPickers.get(i), 160 numberPickerListener); 161 } 162 } 163 164 @Override onUxRestrictionsChanged(CarUxRestrictions restrictionInfo)165 public void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo) { 166 if (canBeShown(restrictionInfo)) { 167 return; 168 } 169 if (mDirectManipulationMode != null && mDirectManipulationMode.isActive()) { 170 mDirectManipulationMode.disable(); 171 } 172 } 173 174 @Override onDestroy()175 public void onDestroy() { 176 DirectManipulationHandler.setDirectManipulationHandler(mDatePicker, /* handler= */ null); 177 for (int i = 0; i < mNumberPickers.size(); i++) { 178 DirectManipulationHandler.setDirectManipulationHandler(mNumberPickers.get(i), 179 /* handler= */ null); 180 } 181 182 super.onDestroy(); 183 } 184 } 185