• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.deskclock;
18 
19 import android.app.Dialog;
20 import android.app.DialogFragment;
21 import android.app.Fragment;
22 import android.app.FragmentManager;
23 import android.app.FragmentTransaction;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.res.ColorStateList;
27 import android.graphics.Color;
28 import android.os.Bundle;
29 import androidx.annotation.NonNull;
30 import androidx.appcompat.app.AlertDialog;
31 import androidx.appcompat.widget.AppCompatEditText;
32 import android.text.Editable;
33 import android.text.InputType;
34 import android.text.TextUtils;
35 import android.text.TextWatcher;
36 import android.view.KeyEvent;
37 import android.view.Window;
38 import android.view.inputmethod.EditorInfo;
39 import android.widget.TextView;
40 
41 import com.android.deskclock.data.DataModel;
42 import com.android.deskclock.data.Timer;
43 import com.android.deskclock.provider.Alarm;
44 
45 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
46 
47 /**
48  * DialogFragment to edit label.
49  */
50 public class LabelDialogFragment extends DialogFragment {
51 
52     /**
53      * The tag that identifies instances of LabelDialogFragment in the fragment manager.
54      */
55     private static final String TAG = "label_dialog";
56 
57     private static final String ARG_LABEL = "arg_label";
58     private static final String ARG_ALARM = "arg_alarm";
59     private static final String ARG_TIMER_ID = "arg_timer_id";
60     private static final String ARG_TAG = "arg_tag";
61 
62     private AppCompatEditText mLabelBox;
63     private Alarm mAlarm;
64     private int mTimerId;
65     private String mTag;
66 
newInstance(Alarm alarm, String label, String tag)67     public static LabelDialogFragment newInstance(Alarm alarm, String label, String tag) {
68         final Bundle args = new Bundle();
69         args.putString(ARG_LABEL, label);
70         args.putParcelable(ARG_ALARM, alarm);
71         args.putString(ARG_TAG, tag);
72 
73         final LabelDialogFragment frag = new LabelDialogFragment();
74         frag.setArguments(args);
75         return frag;
76     }
77 
newInstance(Timer timer)78     public static LabelDialogFragment newInstance(Timer timer) {
79         final Bundle args = new Bundle();
80         args.putString(ARG_LABEL, timer.getLabel());
81         args.putInt(ARG_TIMER_ID, timer.getId());
82 
83         final LabelDialogFragment frag = new LabelDialogFragment();
84         frag.setArguments(args);
85         return frag;
86     }
87 
88     /**
89      * Replaces any existing LabelDialogFragment with the given {@code fragment}.
90      */
show(FragmentManager manager, LabelDialogFragment fragment)91     public static void show(FragmentManager manager, LabelDialogFragment fragment) {
92         if (manager == null || manager.isDestroyed()) {
93             return;
94         }
95 
96         // Finish any outstanding fragment work.
97         manager.executePendingTransactions();
98 
99         final FragmentTransaction tx = manager.beginTransaction();
100 
101         // Remove existing instance of LabelDialogFragment if necessary.
102         final Fragment existing = manager.findFragmentByTag(TAG);
103         if (existing != null) {
104             tx.remove(existing);
105         }
106         tx.addToBackStack(null);
107 
108         fragment.show(tx, TAG);
109     }
110 
111     @Override
onSaveInstanceState(@onNull Bundle outState)112     public void onSaveInstanceState(@NonNull Bundle outState) {
113         super.onSaveInstanceState(outState);
114         // As long as the label box exists, save its state.
115         if (mLabelBox != null) {
116             outState.putString(ARG_LABEL, mLabelBox.getText().toString());
117         }
118     }
119 
120     @Override
onCreateDialog(Bundle savedInstanceState)121     public Dialog onCreateDialog(Bundle savedInstanceState) {
122         final Bundle args = getArguments() == null ? Bundle.EMPTY : getArguments();
123         mAlarm = args.getParcelable(ARG_ALARM);
124         mTimerId = args.getInt(ARG_TIMER_ID, -1);
125         mTag = args.getString(ARG_TAG);
126 
127         String label = args.getString(ARG_LABEL);
128         if (savedInstanceState != null) {
129             label = savedInstanceState.getString(ARG_LABEL, label);
130         }
131 
132         final AlertDialog dialog = new AlertDialog.Builder(getActivity())
133                 .setPositiveButton(android.R.string.ok, new OkListener())
134                 .setNegativeButton(android.R.string.cancel, null /* listener */)
135                 .setMessage(R.string.label)
136                 .create();
137         final Context context = dialog.getContext();
138 
139         final int colorControlActivated =
140                 ThemeUtils.resolveColor(context, R.attr.colorControlActivated);
141         final int colorControlNormal =
142                 ThemeUtils.resolveColor(context, R.attr.colorControlNormal);
143 
144         mLabelBox = new AppCompatEditText(context);
145         mLabelBox.setSupportBackgroundTintList(new ColorStateList(
146                 new int[][] { { android.R.attr.state_activated }, {} },
147                 new int[] { colorControlActivated, colorControlNormal }));
148         mLabelBox.setOnEditorActionListener(new ImeDoneListener());
149         mLabelBox.addTextChangedListener(new TextChangeListener());
150         mLabelBox.setSingleLine();
151         mLabelBox.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);
152         mLabelBox.setText(label);
153         mLabelBox.selectAll();
154 
155         // The line at the bottom of EditText is part of its background therefore the padding
156         // must be added to its container.
157         final int padding = context.getResources()
158                 .getDimensionPixelSize(R.dimen.label_edittext_padding);
159         dialog.setView(mLabelBox, padding, 0, padding, 0);
160 
161         final Window alertDialogWindow = dialog.getWindow();
162         if (alertDialogWindow != null) {
163             alertDialogWindow.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE);
164         }
165         return dialog;
166     }
167 
168     @Override
onDestroyView()169     public void onDestroyView() {
170         super.onDestroyView();
171 
172         // Stop callbacks from the IME since there is no view to process them.
173         mLabelBox.setOnEditorActionListener(null);
174     }
175 
176     /**
177      * Sets the new label into the timer or alarm.
178      */
setLabel()179     private void setLabel() {
180         String label = mLabelBox.getText().toString();
181         if (label.trim().isEmpty()) {
182             // Don't allow user to input label with only whitespace.
183             label = "";
184         }
185 
186         if (mAlarm != null) {
187             ((AlarmLabelDialogHandler) getActivity()).onDialogLabelSet(mAlarm, label, mTag);
188         } else if (mTimerId >= 0) {
189             final Timer timer = DataModel.getDataModel().getTimer(mTimerId);
190             if (timer != null) {
191                 DataModel.getDataModel().setTimerLabel(timer, label);
192             }
193         }
194     }
195 
196     public interface AlarmLabelDialogHandler {
onDialogLabelSet(Alarm alarm, String label, String tag)197         void onDialogLabelSet(Alarm alarm, String label, String tag);
198     }
199 
200     /**
201      * Alters the UI to indicate when input is valid or invalid.
202      */
203     private class TextChangeListener implements TextWatcher {
204         @Override
onTextChanged(CharSequence s, int start, int before, int count)205         public void onTextChanged(CharSequence s, int start, int before, int count) {
206             mLabelBox.setActivated(!TextUtils.isEmpty(s));
207         }
208 
209         @Override
beforeTextChanged(CharSequence s, int start, int count, int after)210         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
211         }
212 
213         @Override
afterTextChanged(Editable editable)214         public void afterTextChanged(Editable editable) {
215         }
216     }
217 
218     /**
219      * Handles completing the label edit from the IME keyboard.
220      */
221     private class ImeDoneListener implements TextView.OnEditorActionListener {
222         @Override
onEditorAction(TextView v, int actionId, KeyEvent event)223         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
224             if (actionId == EditorInfo.IME_ACTION_DONE) {
225                 setLabel();
226                 dismissAllowingStateLoss();
227                 return true;
228             }
229             return false;
230         }
231     }
232 
233     /**
234      * Handles completing the label edit from the Ok button of the dialog.
235      */
236     private class OkListener implements DialogInterface.OnClickListener {
237         @Override
onClick(DialogInterface dialog, int which)238         public void onClick(DialogInterface dialog, int which) {
239             setLabel();
240             dismiss();
241         }
242     }
243 }
244