• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.car.dialer.ui;
17 
18 import android.media.AudioManager;
19 import android.media.ToneGenerator;
20 import android.os.Bundle;
21 import android.support.annotation.NonNull;
22 import android.support.annotation.Nullable;
23 import android.support.v4.app.Fragment;
24 import android.util.SparseArray;
25 import android.util.SparseIntArray;
26 import android.view.KeyEvent;
27 import android.view.LayoutInflater;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.view.ViewGroup;
31 
32 import com.android.car.dialer.R;
33 import com.android.car.dialer.telecom.UiCallManager;
34 
35 /**
36  * Dialpad Fragment which displays a dialpad.
37  */
38 public class DialpadFragment extends Fragment {
39     private static final SparseIntArray sToneMap = new SparseIntArray();
40     private static final SparseArray<String> sDialValueMap = new SparseArray<>();
41     private static final SparseArray<Integer> sRIdMap = new SparseArray<>();
42 
43     private static final int TONE_LENGTH_INFINITE = -1;
44     private static final int TONE_RELATIVE_VOLUME = 80;
45 
46     static {
sToneMap.put(KeyEvent.KEYCODE_1, ToneGenerator.TONE_DTMF_1)47         sToneMap.put(KeyEvent.KEYCODE_1, ToneGenerator.TONE_DTMF_1);
sToneMap.put(KeyEvent.KEYCODE_2, ToneGenerator.TONE_DTMF_2)48         sToneMap.put(KeyEvent.KEYCODE_2, ToneGenerator.TONE_DTMF_2);
sToneMap.put(KeyEvent.KEYCODE_3, ToneGenerator.TONE_DTMF_3)49         sToneMap.put(KeyEvent.KEYCODE_3, ToneGenerator.TONE_DTMF_3);
sToneMap.put(KeyEvent.KEYCODE_4, ToneGenerator.TONE_DTMF_4)50         sToneMap.put(KeyEvent.KEYCODE_4, ToneGenerator.TONE_DTMF_4);
sToneMap.put(KeyEvent.KEYCODE_5, ToneGenerator.TONE_DTMF_5)51         sToneMap.put(KeyEvent.KEYCODE_5, ToneGenerator.TONE_DTMF_5);
sToneMap.put(KeyEvent.KEYCODE_6, ToneGenerator.TONE_DTMF_6)52         sToneMap.put(KeyEvent.KEYCODE_6, ToneGenerator.TONE_DTMF_6);
sToneMap.put(KeyEvent.KEYCODE_7, ToneGenerator.TONE_DTMF_7)53         sToneMap.put(KeyEvent.KEYCODE_7, ToneGenerator.TONE_DTMF_7);
sToneMap.put(KeyEvent.KEYCODE_8, ToneGenerator.TONE_DTMF_8)54         sToneMap.put(KeyEvent.KEYCODE_8, ToneGenerator.TONE_DTMF_8);
sToneMap.put(KeyEvent.KEYCODE_9, ToneGenerator.TONE_DTMF_9)55         sToneMap.put(KeyEvent.KEYCODE_9, ToneGenerator.TONE_DTMF_9);
sToneMap.put(KeyEvent.KEYCODE_0, ToneGenerator.TONE_DTMF_0)56         sToneMap.put(KeyEvent.KEYCODE_0, ToneGenerator.TONE_DTMF_0);
sToneMap.put(KeyEvent.KEYCODE_STAR, ToneGenerator.TONE_DTMF_S)57         sToneMap.put(KeyEvent.KEYCODE_STAR, ToneGenerator.TONE_DTMF_S);
sToneMap.put(KeyEvent.KEYCODE_POUND, ToneGenerator.TONE_DTMF_P)58         sToneMap.put(KeyEvent.KEYCODE_POUND, ToneGenerator.TONE_DTMF_P);
59 
sDialValueMap.put(KeyEvent.KEYCODE_1, "1")60         sDialValueMap.put(KeyEvent.KEYCODE_1, "1");
sDialValueMap.put(KeyEvent.KEYCODE_2, "2")61         sDialValueMap.put(KeyEvent.KEYCODE_2, "2");
sDialValueMap.put(KeyEvent.KEYCODE_3, "3")62         sDialValueMap.put(KeyEvent.KEYCODE_3, "3");
sDialValueMap.put(KeyEvent.KEYCODE_4, "4")63         sDialValueMap.put(KeyEvent.KEYCODE_4, "4");
sDialValueMap.put(KeyEvent.KEYCODE_5, "5")64         sDialValueMap.put(KeyEvent.KEYCODE_5, "5");
sDialValueMap.put(KeyEvent.KEYCODE_6, "6")65         sDialValueMap.put(KeyEvent.KEYCODE_6, "6");
sDialValueMap.put(KeyEvent.KEYCODE_7, "7")66         sDialValueMap.put(KeyEvent.KEYCODE_7, "7");
sDialValueMap.put(KeyEvent.KEYCODE_8, "8")67         sDialValueMap.put(KeyEvent.KEYCODE_8, "8");
sDialValueMap.put(KeyEvent.KEYCODE_9, "9")68         sDialValueMap.put(KeyEvent.KEYCODE_9, "9");
sDialValueMap.put(KeyEvent.KEYCODE_0, "0")69         sDialValueMap.put(KeyEvent.KEYCODE_0, "0");
sDialValueMap.put(KeyEvent.KEYCODE_STAR, "*")70         sDialValueMap.put(KeyEvent.KEYCODE_STAR, "*");
sDialValueMap.put(KeyEvent.KEYCODE_POUND, "#")71         sDialValueMap.put(KeyEvent.KEYCODE_POUND, "#");
72 
sRIdMap.put(KeyEvent.KEYCODE_1, R.id.one)73         sRIdMap.put(KeyEvent.KEYCODE_1, R.id.one);
sRIdMap.put(KeyEvent.KEYCODE_2, R.id.two)74         sRIdMap.put(KeyEvent.KEYCODE_2, R.id.two);
sRIdMap.put(KeyEvent.KEYCODE_3, R.id.three)75         sRIdMap.put(KeyEvent.KEYCODE_3, R.id.three);
sRIdMap.put(KeyEvent.KEYCODE_4, R.id.four)76         sRIdMap.put(KeyEvent.KEYCODE_4, R.id.four);
sRIdMap.put(KeyEvent.KEYCODE_5, R.id.five)77         sRIdMap.put(KeyEvent.KEYCODE_5, R.id.five);
sRIdMap.put(KeyEvent.KEYCODE_6, R.id.six)78         sRIdMap.put(KeyEvent.KEYCODE_6, R.id.six);
sRIdMap.put(KeyEvent.KEYCODE_7, R.id.seven)79         sRIdMap.put(KeyEvent.KEYCODE_7, R.id.seven);
sRIdMap.put(KeyEvent.KEYCODE_8, R.id.eight)80         sRIdMap.put(KeyEvent.KEYCODE_8, R.id.eight);
sRIdMap.put(KeyEvent.KEYCODE_9, R.id.nine)81         sRIdMap.put(KeyEvent.KEYCODE_9, R.id.nine);
sRIdMap.put(KeyEvent.KEYCODE_0, R.id.zero)82         sRIdMap.put(KeyEvent.KEYCODE_0, R.id.zero);
sRIdMap.put(KeyEvent.KEYCODE_STAR, R.id.star)83         sRIdMap.put(KeyEvent.KEYCODE_STAR, R.id.star);
sRIdMap.put(KeyEvent.KEYCODE_POUND, R.id.pound)84         sRIdMap.put(KeyEvent.KEYCODE_POUND, R.id.pound);
85     }
86 
newInstance()87     public static DialpadFragment newInstance() {
88         return new DialpadFragment();
89     }
90 
91     /**
92      * Callback for dialpad to interact with its host.
93      */
94     public interface DialpadCallback {
95         /**
96          * Called when voice mail should be dialed.
97          */
onDialVoiceMail()98         void onDialVoiceMail();
99 
100         /**
101          * Called when a digit should be append.
102          */
onAppendDigit(String digit)103         void onAppendDigit(String digit);
104     }
105 
106     private ToneGenerator mToneGenerator;
107     private DialpadCallback mDialpadCallback;
108 
109     @Override
onCreate(@ullable Bundle savedInstanceState)110     public void onCreate(@Nullable Bundle savedInstanceState) {
111         super.onCreate(savedInstanceState);
112         mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, TONE_RELATIVE_VOLUME);
113     }
114 
115     @Override
onCreateView(@onNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)116     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
117             @Nullable Bundle savedInstanceState) {
118         if (getParentFragment() != null && getParentFragment() instanceof DialpadCallback) {
119             mDialpadCallback = (DialpadCallback) getParentFragment();
120         } else if (getHost() instanceof DialpadCallback) {
121             mDialpadCallback = (DialpadCallback) getHost();
122         }
123 
124         View dialpadView = inflater.inflate(R.layout.dialpad, container, false);
125         setupKeypadClickListeners(dialpadView);
126         return dialpadView;
127     }
128 
129     @Override
onPause()130     public void onPause() {
131         super.onPause();
132         stopTone();
133     }
134 
135     /**
136      * The click listener for all dialpad buttons.  Reacts to touch-down and touch-up events, as
137      * well as long-press for certain keys.  Mimics the behavior of the phone dialer app.
138      */
139     private class DialpadClickListener implements View.OnTouchListener,
140             View.OnLongClickListener {
141         private final int mTone;
142         private final String mValue;
143 
DialpadClickListener(int keyCode)144         DialpadClickListener(int keyCode) {
145             mTone = sToneMap.get(keyCode);
146             mValue = sDialValueMap.get(keyCode);
147         }
148 
149         @Override
onLongClick(View v)150         public boolean onLongClick(View v) {
151             switch (mValue) {
152                 case "0":
153                     if (mDialpadCallback != null) {
154                         mDialpadCallback.onAppendDigit("+");
155                     }
156                     stopTone();
157                     return true;
158                 case "1":
159                     // TODO: this currently does not work (at least over bluetooth HFP), because
160                     // the framework is unable to get the voicemail number. Revisit later...
161                     if (mDialpadCallback != null) {
162                         mDialpadCallback.onDialVoiceMail();
163                     }
164                     return true;
165                 default:
166                     return false;
167             }
168         }
169 
170         @Override
onTouch(View v, MotionEvent event)171         public boolean onTouch(View v, MotionEvent event) {
172             UiCallManager uiCallmanager = UiCallManager.get();
173             boolean hasActiveCall = uiCallmanager.getPrimaryCall() != null;
174             if (event.getAction() == MotionEvent.ACTION_DOWN) {
175                 if (mDialpadCallback != null) {
176                     mDialpadCallback.onAppendDigit(mValue);
177                 }
178                 if (hasActiveCall) {
179                     uiCallmanager.playDtmfTone(uiCallmanager.getPrimaryCall(), mValue.charAt(0));
180                 } else {
181                     playTone(mTone);
182                 }
183             } else if (event.getAction() == MotionEvent.ACTION_UP) {
184                 if (hasActiveCall) {
185                     uiCallmanager.stopDtmfTone(uiCallmanager.getPrimaryCall());
186                 } else {
187                     stopTone();
188                 }
189             }
190 
191             // Continue propagating the touch event
192             return false;
193         }
194     }
195 
playTone(int tone)196     private void playTone(int tone) {
197         if (mToneGenerator == null) {
198             return;
199         }
200 
201         // Start the new tone
202         mToneGenerator.startTone(tone, TONE_LENGTH_INFINITE);
203     }
204 
stopTone()205     private void stopTone() {
206         if (mToneGenerator == null) {
207             return;
208         }
209 
210         mToneGenerator.stopTone();
211     }
212 
setupKeypadClickListeners(View parent)213     private void setupKeypadClickListeners(View parent) {
214         for (int i = 0; i < sRIdMap.size(); i++) {
215             int key = sRIdMap.keyAt(i);
216             DialpadClickListener clickListener = new DialpadClickListener(key);
217             View v = parent.findViewById(sRIdMap.get(key));
218             v.setOnTouchListener(clickListener);
219             v.setOnLongClickListener(clickListener);
220         }
221     }
222 }
223