• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.dialer.ui.dialpad;
18 
19 import android.animation.AnimatorInflater;
20 import android.animation.ValueAnimator;
21 import android.os.Bundle;
22 import android.provider.Settings;
23 import android.text.SpannableString;
24 import android.text.Spanned;
25 import android.text.TextUtils;
26 import android.util.SparseArray;
27 import android.view.KeyEvent;
28 import android.view.View;
29 import android.widget.TextView;
30 
31 import androidx.annotation.CallSuper;
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 
35 import com.android.car.dialer.R;
36 import com.android.car.dialer.log.L;
37 import com.android.car.dialer.ui.common.DialerBaseFragment;
38 import com.android.car.dialer.ui.view.ScaleSpan;
39 
40 /** Fragment that controls the dialpad. */
41 public abstract class AbstractDialpadFragment extends DialerBaseFragment implements
42         KeypadFragment.KeypadCallback {
43     private static final String TAG = "CD.AbsDialpadFragment";
44     private static final String DIAL_NUMBER_KEY = "DIAL_NUMBER_KEY";
45     private static final int PLAY_DTMF_TONE = 1;
46 
47     static final SparseArray<Character> sDialValueMap = new SparseArray<>();
48 
49     static {
sDialValueMap.put(KeyEvent.KEYCODE_1, '1')50         sDialValueMap.put(KeyEvent.KEYCODE_1, '1');
sDialValueMap.put(KeyEvent.KEYCODE_2, '2')51         sDialValueMap.put(KeyEvent.KEYCODE_2, '2');
sDialValueMap.put(KeyEvent.KEYCODE_3, '3')52         sDialValueMap.put(KeyEvent.KEYCODE_3, '3');
sDialValueMap.put(KeyEvent.KEYCODE_4, '4')53         sDialValueMap.put(KeyEvent.KEYCODE_4, '4');
sDialValueMap.put(KeyEvent.KEYCODE_5, '5')54         sDialValueMap.put(KeyEvent.KEYCODE_5, '5');
sDialValueMap.put(KeyEvent.KEYCODE_6, '6')55         sDialValueMap.put(KeyEvent.KEYCODE_6, '6');
sDialValueMap.put(KeyEvent.KEYCODE_7, '7')56         sDialValueMap.put(KeyEvent.KEYCODE_7, '7');
sDialValueMap.put(KeyEvent.KEYCODE_8, '8')57         sDialValueMap.put(KeyEvent.KEYCODE_8, '8');
sDialValueMap.put(KeyEvent.KEYCODE_9, '9')58         sDialValueMap.put(KeyEvent.KEYCODE_9, '9');
sDialValueMap.put(KeyEvent.KEYCODE_0, '0')59         sDialValueMap.put(KeyEvent.KEYCODE_0, '0');
sDialValueMap.put(KeyEvent.KEYCODE_STAR, '*')60         sDialValueMap.put(KeyEvent.KEYCODE_STAR, '*');
sDialValueMap.put(KeyEvent.KEYCODE_POUND, '#')61         sDialValueMap.put(KeyEvent.KEYCODE_POUND, '#');
62     }
63 
64     private boolean mDTMFToneEnabled;
65     private final StringBuffer mNumber = new StringBuffer();
66     private ValueAnimator mInputMotionAnimator;
67     private ScaleSpan mScaleSpan;
68     private TextView mTitleView;
69     private int mCurrentlyPlayingTone = KeyEvent.KEYCODE_UNKNOWN;
70 
71     /** Defines how the dialed number should be presented. */
presentDialedNumber(@onNull StringBuffer number)72     abstract void presentDialedNumber(@NonNull StringBuffer number);
73 
74     /** Plays the tone for the pressed keycode when "play DTMF tone" is enabled in settings. */
playTone(int keycode)75     abstract void playTone(int keycode);
76 
77     /** Stops playing all tones when "play DTMF tone" is enabled in settings. */
stopAllTones()78     abstract void stopAllTones();
79 
80     @Override
onCreate(Bundle savedInstanceState)81     public void onCreate(Bundle savedInstanceState) {
82         super.onCreate(savedInstanceState);
83         if (savedInstanceState != null) {
84             mNumber.append(savedInstanceState.getCharSequence(DIAL_NUMBER_KEY));
85         }
86         L.d(TAG, "onCreate, number: %s", mNumber);
87     }
88 
89     @CallSuper
90     @Override
onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)91     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
92         mTitleView = view.findViewById(R.id.title);
93         if (mTitleView != null && getResources().getBoolean(R.bool.config_enable_dial_motion)) {
94             mInputMotionAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(getContext(),
95                     R.animator.scale_down);
96             float startTextSize = mTitleView.getTextSize() * getResources().getFloat(
97                     R.integer.config_dial_motion_scale_start);
98             mScaleSpan = new ScaleSpan(startTextSize);
99         }
100     }
101 
102     @Override
onResume()103     public void onResume() {
104         super.onResume();
105         mDTMFToneEnabled = Settings.System.getInt(getContext().getContentResolver(),
106                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == PLAY_DTMF_TONE;
107         L.d(TAG, "DTMF tone enabled = %s", String.valueOf(mDTMFToneEnabled));
108 
109         presentDialedNumber();
110     }
111 
112     @Override
onPause()113     public void onPause() {
114         super.onPause();
115         stopAllTones();
116     }
117 
118     @Override
onSaveInstanceState(Bundle outState)119     public void onSaveInstanceState(Bundle outState) {
120         super.onSaveInstanceState(outState);
121         outState.putCharSequence(DIAL_NUMBER_KEY, mNumber);
122     }
123 
124     @Override
onKeypadKeyDown(@eypadFragment.DialKeyCode int keycode)125     public void onKeypadKeyDown(@KeypadFragment.DialKeyCode int keycode) {
126         String digit = sDialValueMap.get(keycode).toString();
127         appendDialedNumber(digit);
128 
129         if (mDTMFToneEnabled) {
130             mCurrentlyPlayingTone = keycode;
131             playTone(keycode);
132         }
133     }
134 
135     @Override
onKeypadKeyUp(@eypadFragment.DialKeyCode int keycode)136     public void onKeypadKeyUp(@KeypadFragment.DialKeyCode int keycode) {
137         if (mDTMFToneEnabled && keycode == mCurrentlyPlayingTone) {
138             mCurrentlyPlayingTone = KeyEvent.KEYCODE_UNKNOWN;
139             stopAllTones();
140         }
141     }
142 
143     /** Set the dialed number to the given number. Must be called after the fragment is added. */
setDialedNumber(String number)144     public void setDialedNumber(String number) {
145         mNumber.setLength(0);
146         if (!TextUtils.isEmpty(number)) {
147             mNumber.append(number);
148         }
149         presentDialedNumber();
150     }
151 
clearDialedNumber()152     void clearDialedNumber() {
153         mNumber.setLength(0);
154         presentDialedNumber();
155     }
156 
removeLastDigit()157     void removeLastDigit() {
158         if (mNumber.length() != 0) {
159             mNumber.deleteCharAt(mNumber.length() - 1);
160         }
161         presentDialedNumber();
162     }
163 
appendDialedNumber(String number)164     void appendDialedNumber(String number) {
165         mNumber.append(number);
166         presentDialedNumber();
167 
168         if (TextUtils.isEmpty(number)) {
169             return;
170         }
171 
172         if (mInputMotionAnimator != null) {
173             final String currentText = mTitleView.getText().toString();
174             final SpannableString spannableString = new SpannableString(currentText);
175             mInputMotionAnimator.addUpdateListener(valueAnimator -> {
176                 float textSize =
177                         (float) valueAnimator.getAnimatedValue() * mTitleView.getTextSize();
178                 mScaleSpan.setTextSize(textSize);
179                 spannableString.setSpan(mScaleSpan, currentText.length() - number.length(),
180                         currentText.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
181                 mTitleView.setText(spannableString, TextView.BufferType.SPANNABLE);
182             });
183             mInputMotionAnimator.start();
184         }
185     }
186 
presentDialedNumber()187     private void presentDialedNumber() {
188         if (mInputMotionAnimator != null) {
189             mInputMotionAnimator.cancel();
190             mInputMotionAnimator.removeAllUpdateListeners();
191         }
192 
193         presentDialedNumber(mNumber);
194     }
195 
196     @NonNull
getNumber()197     StringBuffer getNumber() {
198         return mNumber;
199     }
200 }
201