1 /* 2 * Copyright (C) 2008 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.timer; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.graphics.Color; 24 import android.text.format.DateUtils; 25 import android.util.AttributeSet; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.widget.Button; 29 import android.widget.ImageButton; 30 import android.widget.ImageView; 31 import android.widget.LinearLayout; 32 33 import com.android.deskclock.AnimatorUtils; 34 import com.android.deskclock.R; 35 import com.android.deskclock.Utils; 36 37 import java.io.Serializable; 38 import java.util.Arrays; 39 40 public class TimerSetupView extends LinearLayout implements Button.OnClickListener, 41 Button.OnLongClickListener { 42 43 private final Button[] mNumbers = new Button[10]; 44 private final int[] mInput = new int[6]; 45 private int mInputPointer = -1; 46 private ImageView mCreate; 47 private ImageButton mDelete; 48 private TimerView mEnteredTime; 49 private View mDivider; 50 51 private final int mColorAccent; 52 private final int mColorHairline; 53 54 private final AnimatorListenerAdapter mHideCreateListener = new AnimatorListenerAdapter() { 55 @Override 56 public void onAnimationEnd(Animator animation) { 57 mCreate.setScaleX(1); 58 mCreate.setScaleY(1); 59 mCreate.setVisibility(INVISIBLE); 60 } 61 }; 62 63 private final AnimatorListenerAdapter mShowCreateListener = new AnimatorListenerAdapter() { 64 @Override 65 public void onAnimationStart(Animator animation) { 66 mCreate.setVisibility(VISIBLE); 67 } 68 }; 69 TimerSetupView(Context context)70 public TimerSetupView(Context context) { 71 this(context, null /* attrs */); 72 } 73 TimerSetupView(Context context, AttributeSet attrs)74 public TimerSetupView(Context context, AttributeSet attrs) { 75 super(context, attrs); 76 77 mColorAccent = Utils.obtainStyledColor(context, R.attr.colorAccent, Color.RED); 78 mColorHairline = context.getResources().getColor(R.color.hairline); 79 80 LayoutInflater.from(context).inflate(R.layout.time_setup_view, this); 81 } 82 83 @Override onFinishInflate()84 protected void onFinishInflate() { 85 super.onFinishInflate(); 86 87 final View v1 = findViewById(R.id.first); 88 final View v2 = findViewById(R.id.second); 89 final View v3 = findViewById(R.id.third); 90 final View v4 = findViewById(R.id.fourth); 91 92 mDivider = findViewById(R.id.divider); 93 mCreate = (ImageView) findViewById(R.id.timer_create); 94 mDelete = (ImageButton) findViewById(R.id.delete); 95 mDelete.setOnClickListener(this); 96 mDelete.setOnLongClickListener(this); 97 mEnteredTime = (TimerView) findViewById(R.id.timer_time_text); 98 99 mNumbers[1] = (Button) v1.findViewById(R.id.key_left); 100 mNumbers[2] = (Button) v1.findViewById(R.id.key_middle); 101 mNumbers[3] = (Button) v1.findViewById(R.id.key_right); 102 103 mNumbers[4] = (Button) v2.findViewById(R.id.key_left); 104 mNumbers[5] = (Button) v2.findViewById(R.id.key_middle); 105 mNumbers[6] = (Button) v2.findViewById(R.id.key_right); 106 107 mNumbers[7] = (Button) v3.findViewById(R.id.key_left); 108 mNumbers[8] = (Button) v3.findViewById(R.id.key_middle); 109 mNumbers[9] = (Button) v3.findViewById(R.id.key_right); 110 111 mNumbers[0] = (Button) v4.findViewById(R.id.key_middle); 112 v4.findViewById(R.id.key_left).setVisibility(INVISIBLE); 113 v4.findViewById(R.id.key_right).setVisibility(INVISIBLE); 114 115 for (int i = 0; i < mNumbers.length; i++) { 116 mNumbers[i].setOnClickListener(this); 117 mNumbers[i].setText(String.valueOf(i)); 118 mNumbers[i].setTextColor(Color.WHITE); 119 mNumbers[i].setTag(R.id.numbers_key, i); 120 } 121 122 reset(); 123 } 124 125 @Override onClick(View v)126 public void onClick(View v) { 127 final Integer n = (Integer) v.getTag(R.id.numbers_key); 128 // A number was pressed 129 if (n != null) { 130 // pressing "0" as the first digit does nothing 131 if (mInputPointer == -1 && n == 0) { 132 return; 133 } 134 135 // No space for more digits, so ignore input. 136 if (mInputPointer == mInput.length - 1) { 137 return; 138 } 139 140 // Append the new digit. 141 System.arraycopy(mInput, 0, mInput, 1, mInputPointer + 1); 142 mInput[0] = n; 143 mInputPointer++; 144 updateTime(); 145 146 // Update talkback to read the number being deleted 147 final Resources resources = getResources(); 148 final String cd = resources.getString(R.string.timer_descriptive_delete, n.toString()); 149 mDelete.setContentDescription(cd); 150 } 151 152 // other keys 153 if (v == mDelete) { 154 if (mInputPointer < 0) { 155 // Nothing exists to delete so return. 156 return; 157 } 158 159 System.arraycopy(mInput, 1, mInput, 0, mInputPointer); 160 mInput[mInputPointer] = 0; 161 mInputPointer--; 162 updateTime(); 163 164 // Update talkback to read the number being deleted or its original description. 165 final String number = mInputPointer < 0 ? "" : Integer.toString(mInput[mInputPointer]); 166 final String cd = getResources().getString(R.string.timer_descriptive_delete, number); 167 mDelete.setContentDescription(cd); 168 } 169 170 updateStartButton(); 171 updateDeleteButtonAndDivider(); 172 } 173 174 @Override 175 public boolean onLongClick(View v) { 176 if (v == mDelete) { 177 reset(); 178 updateStartButton(); 179 return true; 180 } 181 return false; 182 } 183 184 public void reset() { 185 for (int i = 0; i < mInput.length; i ++) { 186 mInput[i] = 0; 187 } 188 mInputPointer = -1; 189 updateTime(); 190 updateDeleteButtonAndDivider(); 191 mCreate.setVisibility(INVISIBLE); 192 } 193 194 public long getTimeInMillis() { 195 final int hoursInSeconds = mInput[5] * 36000 + mInput[4] * 3600; 196 final int minutesInSeconds = mInput[3] * 600 + mInput[2] * 60; 197 final int seconds = mInput[1] * 10 + mInput[0]; 198 final int totalSeconds = hoursInSeconds + minutesInSeconds + seconds; 199 200 return totalSeconds * DateUtils.SECOND_IN_MILLIS; 201 } 202 203 /** 204 * @return an opaque representation of the state of timer setup 205 */ 206 public Serializable getState() { 207 return Arrays.copyOf(mInput, mInput.length); 208 } 209 210 /** 211 * @param state an opaque state of this view previously produced by {@link #getState()} 212 */ 213 public void setState(Serializable state) { 214 final int[] input = (int[]) state; 215 if (input != null && mInput.length == input.length) { 216 for (int i = 0; i < mInput.length; i++) { 217 mInput[i] = input[i]; 218 if (mInput[i] != 0) { 219 mInputPointer = i; 220 } 221 } 222 updateTime(); 223 updateDeleteButtonAndDivider(); 224 mCreate.setVisibility(getInputExists() ? VISIBLE : INVISIBLE); 225 } 226 } 227 228 private void updateTime() { 229 final int seconds = mInput[1] * 10 + mInput[0]; 230 mEnteredTime.setTime(mInput[5], mInput[4], mInput[3], mInput[2], seconds); 231 } 232 233 private void updateStartButton() { 234 final boolean show = getInputExists(); 235 final int finalVisibility = show ? VISIBLE : INVISIBLE; 236 if (mCreate.getVisibility() == finalVisibility) { 237 // Fab is not initialized yet or already shown/hidden 238 return; 239 } 240 241 final int from = show ? 0 : 1; 242 final int to = show ? 1 : 0; 243 final Animator scaleAnimator = AnimatorUtils.getScaleAnimator(mCreate, from, to); 244 scaleAnimator.setDuration(AnimatorUtils.ANIM_DURATION_SHORT); 245 scaleAnimator.addListener(show ? mShowCreateListener : mHideCreateListener); 246 scaleAnimator.start(); 247 } 248 249 private void updateDeleteButtonAndDivider() { 250 final boolean enabled = getInputExists(); 251 mDelete.setEnabled(enabled); 252 mDivider.setBackgroundColor(enabled ? mColorAccent : mColorHairline); 253 } 254 255 private boolean getInputExists() { 256 return mInputPointer != -1; 257 } 258 }