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 
17 package com.android.tv.settings.system;
18 
19 import android.app.timedetector.ManualTimeSuggestion;
20 import android.app.timedetector.TimeDetector;
21 import android.content.Context;
22 import android.os.Bundle;
23 import android.text.TextUtils;
24 import android.util.TypedValue;
25 import android.view.ContextThemeWrapper;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.TextView;
30 
31 import androidx.leanback.preference.LeanbackPreferenceDialogFragment;
32 import androidx.leanback.preference.LeanbackSettingsFragment;
33 import androidx.leanback.widget.picker.DatePicker;
34 import androidx.leanback.widget.picker.Picker;
35 import androidx.leanback.widget.picker.TimePicker;
36 import androidx.preference.DialogPreference;
37 
38 import com.android.tv.settings.R;
39 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment;
40 
41 import java.util.Calendar;
42 
43 /**
44  * A DialogFragment started for either setting date or setting time purposes. The type of
45  * fragment launched is controlled by the type of {@link LeanbackPickerDialogPreference}
46  * that's clicked. Launching of these two fragments is done inside
47  * {@link com.android.tv.settings.BaseSettingsFragment#onPreferenceDisplayDialog}.
48  */
49 public class LeanbackPickerDialogFragment extends LeanbackPreferenceDialogFragment implements
50         TwoPanelSettingsFragment.NavigationCallback {
51 
52     private static final String EXTRA_PICKER_TYPE = "LeanbackPickerDialogFragment.PickerType";
53     private static final String TYPE_DATE = "date";
54     private static final String TYPE_TIME = "time";
55     private static final String SAVE_STATE_TITLE = "LeanbackPickerDialogFragment.title";
56 
57     private CharSequence mDialogTitle;
58     private Calendar mCalendar;
59     private Picker mPicker;
60 
61     /**
62      * Generated a new DialogFragment displaying a Leanback DatePicker widget.
63      *
64      * @param key The preference key starting this DialogFragment.
65      * @return The fragment to be started displaying a DatePicker widget for setting date.
66      */
newDatePickerInstance(String key)67     public static LeanbackPickerDialogFragment newDatePickerInstance(String key) {
68         final Bundle args = new Bundle(1);
69         args.putString(ARG_KEY, key);
70         args.putString(EXTRA_PICKER_TYPE, TYPE_DATE);
71 
72         final LeanbackPickerDialogFragment fragment = new LeanbackPickerDialogFragment();
73         fragment.setArguments(args);
74         return fragment;
75     }
76 
77     /**
78      * Generated a new DialogFragment displaying a Leanback TimePicker widget.
79      *
80      * @param key The preference key starting this DialogFragment.
81      * @return The fragment to be started displaying a TimePicker widget for setting time.
82      */
newTimePickerInstance(String key)83     public static LeanbackPickerDialogFragment newTimePickerInstance(String key) {
84         final Bundle args = new Bundle(1);
85         args.putString(ARG_KEY, key);
86         args.putString(EXTRA_PICKER_TYPE, TYPE_TIME);
87 
88         final LeanbackPickerDialogFragment fragment = new LeanbackPickerDialogFragment();
89         fragment.setArguments(args);
90         return fragment;
91     }
92 
93     @Override
onCreate(Bundle savedInstanceState)94     public void onCreate(Bundle savedInstanceState) {
95         super.onCreate(savedInstanceState);
96 
97         if (savedInstanceState == null) {
98             final DialogPreference preference = getPreference();
99             mDialogTitle = preference.getDialogTitle();
100         } else {
101             mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE);
102         }
103         mCalendar = Calendar.getInstance();
104     }
105 
106     @Override
onSaveInstanceState(Bundle outState)107     public void onSaveInstanceState(Bundle outState) {
108         super.onSaveInstanceState(outState);
109         outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle);
110     }
111 
112     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)113     public View onCreateView(LayoutInflater inflater, ViewGroup container,
114             Bundle savedInstanceState) {
115         final String pickerType = getArguments().getString(EXTRA_PICKER_TYPE);
116 
117         final TypedValue tv = new TypedValue();
118         getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true);
119         int theme = tv.resourceId;
120         if (theme == 0) {
121             // Fallback to default theme.
122             theme = R.style.PreferenceThemeOverlayLeanback;
123         }
124         Context styledContext = new ContextThemeWrapper(getActivity(), theme);
125         LayoutInflater styledInflater = inflater.cloneInContext(styledContext);
126         final View view = styledInflater.inflate(R.layout.picker_dialog_fragment, container, false);
127         ViewGroup pickerContainer = view.findViewById(R.id.picker_container);
128         if (pickerType.equals(TYPE_DATE)) {
129             styledInflater.inflate(R.layout.date_picker_widget, pickerContainer, true);
130             DatePicker datePicker = pickerContainer.findViewById(R.id.date_picker);
131             mPicker = datePicker;
132             if (getParentFragment() instanceof LeanbackSettingsFragment) {
133                 datePicker.setActivated(true);
134             }
135             datePicker.setOnClickListener(v -> {
136                 // Setting the new system date
137                 long whenMillis = datePicker.getDate();
138                 TimeDetector timeDetector = getContext().getSystemService(TimeDetector.class);
139                 ManualTimeSuggestion manualTimeSuggestion = TimeDetector.createManualTimeSuggestion(
140                         whenMillis, "Settings: Set date");
141                 timeDetector.suggestManualTime(manualTimeSuggestion);
142                 // a) Two Panel: Navigate back, setActivated(false) in the callback
143                 // b) Side Panel: Finish the fragment/activity when clicked.
144                 if (getParentFragment() instanceof TwoPanelSettingsFragment) {
145                     ((TwoPanelSettingsFragment) getParentFragment()).navigateBack();
146                 } else if (!getFragmentManager().popBackStackImmediate()) {
147                     getActivity().finish();
148                 }
149             });
150 
151         } else {
152             styledInflater.inflate(R.layout.time_picker_widget, pickerContainer, true);
153             TimePicker timePicker = pickerContainer.findViewById(R.id.time_picker);
154             if (getParentFragment() instanceof LeanbackSettingsFragment) {
155                 timePicker.setActivated(true);
156             }
157             mPicker = timePicker;
158             timePicker.setOnClickListener(v -> {
159                 // Setting the new system time
160                 mCalendar.set(Calendar.HOUR_OF_DAY, timePicker.getHour());
161                 mCalendar.set(Calendar.MINUTE, timePicker.getMinute());
162                 mCalendar.set(Calendar.SECOND, 0);
163                 mCalendar.set(Calendar.MILLISECOND, 0);
164                 long whenMillis = mCalendar.getTimeInMillis();
165 
166                 TimeDetector timeDetector = getContext().getSystemService(TimeDetector.class);
167                 ManualTimeSuggestion manualTimeSuggestion = TimeDetector.createManualTimeSuggestion(
168                         whenMillis, "Settings: Set time");
169                 timeDetector.suggestManualTime(manualTimeSuggestion);
170                 // a) Two Panel: Navigate back, setActivated(false) in the callback.
171                 // b) Side Panel: Finish the fragment/activity when clicked.
172                 if (getParentFragment() instanceof TwoPanelSettingsFragment) {
173                     ((TwoPanelSettingsFragment) getParentFragment()).navigateBack();
174                 } else if (!getFragmentManager().popBackStackImmediate()) {
175                     getActivity().finish();
176                 }
177             });
178         }
179 
180         final CharSequence title = mDialogTitle;
181         if (!TextUtils.isEmpty(title)) {
182             final TextView titleView = view.findViewById(R.id.decor_title);
183             titleView.setText(title);
184         }
185         return view;
186     }
187 
188     @Override
canNavigateBackOnDPAD()189     public boolean canNavigateBackOnDPAD() {
190         if (mPicker.isActivated() && mPicker.hasFocus()) {
191             if (mPicker.getSelectedColumn() == 0) {
192                 return true;
193             }
194             return false;
195         }
196         return true;
197     }
198 
199     @Override
onNavigateToPreview()200     public void onNavigateToPreview() {
201         mPicker.setActivated(true);
202         mPicker.requestFocus();
203     }
204 
205     @Override
onNavigateBack()206     public void onNavigateBack() {
207         mPicker.setActivated(false);
208     }
209 }
210