Home | History | Annotate | Download | only in deskclock
      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 android.support.annotation.NonNull;
     30 import android.support.v7.app.AlertDialog;
     31 import android.support.v7.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 
     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 
     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      */
     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
    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
    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
    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      */
    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 {
    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
    205         public void onTextChanged(CharSequence s, int start, int before, int count) {
    206             mLabelBox.setActivated(!TextUtils.isEmpty(s));
    207         }
    208 
    209         @Override
    210         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    211         }
    212 
    213         @Override
    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
    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
    238         public void onClick(DialogInterface dialog, int which) {
    239             setLabel();
    240             dismiss();
    241         }
    242     }
    243 }
    244