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