1 /*
2  * Copyright (C) 2013 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.incallui;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.telephony.PhoneNumberUtils;
22 import android.util.ArrayMap;
23 import android.util.AttributeSet;
24 import android.view.ContextThemeWrapper;
25 import android.view.KeyEvent;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.View.OnClickListener;
29 import android.view.View.OnKeyListener;
30 import android.view.ViewGroup;
31 import android.widget.EditText;
32 import android.widget.LinearLayout;
33 import android.widget.TextView;
34 import com.android.dialer.common.LogUtil;
35 import com.android.dialer.dialpadview.DialpadKeyButton;
36 import com.android.dialer.dialpadview.DialpadKeyButton.OnPressedListener;
37 import com.android.dialer.dialpadview.DialpadView;
38 import com.android.dialer.logging.DialerImpression;
39 import com.android.dialer.logging.Logger;
40 import com.android.incallui.DialpadPresenter.DialpadUi;
41 import com.android.incallui.baseui.BaseFragment;
42 import java.util.Map;
43 
44 /** Fragment for call control buttons */
45 public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
46     implements DialpadUi, OnKeyListener, OnClickListener, OnPressedListener {
47 
48   /** Hash Map to map a view id to a character */
49   private static final Map<Integer, Character> displayMap = new ArrayMap<>();
50 
51   /** Set up the static maps */
52   static {
53     // Map the buttons to the display characters
displayMap.put(R.id.one, '1')54     displayMap.put(R.id.one, '1');
displayMap.put(R.id.two, '2')55     displayMap.put(R.id.two, '2');
displayMap.put(R.id.three, '3')56     displayMap.put(R.id.three, '3');
displayMap.put(R.id.four, '4')57     displayMap.put(R.id.four, '4');
displayMap.put(R.id.five, '5')58     displayMap.put(R.id.five, '5');
displayMap.put(R.id.six, '6')59     displayMap.put(R.id.six, '6');
displayMap.put(R.id.seven, '7')60     displayMap.put(R.id.seven, '7');
displayMap.put(R.id.eight, '8')61     displayMap.put(R.id.eight, '8');
displayMap.put(R.id.nine, '9')62     displayMap.put(R.id.nine, '9');
displayMap.put(R.id.zero, '0')63     displayMap.put(R.id.zero, '0');
displayMap.put(R.id.pound, '#')64     displayMap.put(R.id.pound, '#');
displayMap.put(R.id.star, '*')65     displayMap.put(R.id.star, '*');
66   }
67 
68   private final int[] buttonIds =
69       new int[] {
70         R.id.zero,
71         R.id.one,
72         R.id.two,
73         R.id.three,
74         R.id.four,
75         R.id.five,
76         R.id.six,
77         R.id.seven,
78         R.id.eight,
79         R.id.nine,
80         R.id.star,
81         R.id.pound
82       };
83   private EditText dtmfDialerField;
84   // KeyListener used with the "dialpad digits" EditText widget.
85   private DtmfKeyListener dtmfKeyListener;
86   private DialpadView dialpadView;
87   private int currentTextColor;
88   private View endCallSpace;
89   private boolean shouldShowEndCallSpace = true;
90 
91   @Override
onClick(View v)92   public void onClick(View v) {
93     if (v.getId() == R.id.dialpad_back) {
94       Logger.get(getContext())
95           .logImpression(DialerImpression.Type.IN_CALL_DIALPAD_CLOSE_BUTTON_PRESSED);
96       getActivity().onBackPressed();
97     }
98   }
99 
100   @Override
onKey(View v, int keyCode, KeyEvent event)101   public boolean onKey(View v, int keyCode, KeyEvent event) {
102     Log.d(this, "onKey:  keyCode " + keyCode + ", view " + v);
103 
104     if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
105       int viewId = v.getId();
106       if (displayMap.containsKey(viewId)) {
107         switch (event.getAction()) {
108           case KeyEvent.ACTION_DOWN:
109             if (event.getRepeatCount() == 0) {
110               getPresenter().processDtmf(displayMap.get(viewId));
111             }
112             break;
113           case KeyEvent.ACTION_UP:
114             getPresenter().stopDtmf();
115             break;
116           default: // fall out
117         }
118         // do not return true [handled] here, since we want the
119         // press / click animation to be handled by the framework.
120       }
121     }
122     return false;
123   }
124 
125   @Override
createPresenter()126   public DialpadPresenter createPresenter() {
127     return new DialpadPresenter();
128   }
129 
130   @Override
getUi()131   public DialpadPresenter.DialpadUi getUi() {
132     return this;
133   }
134 
135   // TODO(klp) Adds hardware keyboard listener
136 
137   @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)138   public View onCreateView(
139       LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
140     Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), R.style.Dialer_ThemeBase);
141     LayoutInflater layoutInflater = inflater.cloneInContext(contextThemeWrapper);
142     final View parent = layoutInflater.inflate(R.layout.incall_dialpad_fragment, container, false);
143     dialpadView = (DialpadView) parent.findViewById(R.id.dialpad_view);
144     dialpadView.setCanDigitsBeEdited(false);
145     dialpadView.setBackgroundResource(R.color.incall_dialpad_background);
146     dtmfDialerField = (EditText) parent.findViewById(R.id.digits);
147     if (dtmfDialerField != null) {
148       LogUtil.i("DialpadFragment.onCreateView", "creating dtmfKeyListener");
149       dtmfKeyListener = new DtmfKeyListener(getPresenter());
150       dtmfDialerField.setKeyListener(dtmfKeyListener);
151       // remove the long-press context menus that support
152       // the edit (copy / paste / select) functions.
153       dtmfDialerField.setLongClickable(false);
154       dtmfDialerField.setElegantTextHeight(false);
155       configureKeypadListeners();
156     }
157     View backButton = dialpadView.findViewById(R.id.dialpad_back);
158     backButton.setVisibility(View.VISIBLE);
159     backButton.setOnClickListener(this);
160     endCallSpace = dialpadView.findViewById(R.id.end_call_space);
161 
162     return parent;
163   }
164 
165   @Override
onResume()166   public void onResume() {
167     super.onResume();
168     updateColors();
169     endCallSpace.setVisibility(shouldShowEndCallSpace ? View.VISIBLE : View.GONE);
170   }
171 
updateColors()172   public void updateColors() {
173     int textColor = InCallPresenter.getInstance().getThemeColorManager().getPrimaryColor();
174 
175     if (currentTextColor == textColor) {
176       return;
177     }
178 
179     DialpadKeyButton dialpadKey;
180     for (int i = 0; i < buttonIds.length; i++) {
181       dialpadKey = (DialpadKeyButton) dialpadView.findViewById(buttonIds[i]);
182       ((TextView) dialpadKey.findViewById(R.id.dialpad_key_number)).setTextColor(textColor);
183     }
184 
185     currentTextColor = textColor;
186   }
187 
188   @Override
onDestroyView()189   public void onDestroyView() {
190     dtmfKeyListener = null;
191     super.onDestroyView();
192   }
193 
194   /**
195    * Getter for Dialpad text.
196    *
197    * @return String containing current Dialpad EditText text.
198    */
getDtmfText()199   public String getDtmfText() {
200     return dtmfDialerField.getText().toString();
201   }
202 
203   /**
204    * Sets the Dialpad text field with some text.
205    *
206    * @param text Text to set Dialpad EditText to.
207    */
setDtmfText(String text)208   public void setDtmfText(String text) {
209     dtmfDialerField.setText(PhoneNumberUtils.createTtsSpannable(text));
210   }
211 
212   /** Starts the slide up animation for the Dialpad keys when the Dialpad is revealed. */
animateShowDialpad()213   public void animateShowDialpad() {
214     final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
215     dialpadView.animateShow();
216   }
217 
218   @Override
appendDigitsToField(char digit)219   public void appendDigitsToField(char digit) {
220     if (dtmfDialerField != null) {
221       // TODO: maybe *don't* manually append this digit if
222       // mDialpadDigits is focused and this key came from the HW
223       // keyboard, since in that case the EditText field will
224       // get the key event directly and automatically appends
225       // whetever the user types.
226       // (Or, a cleaner fix would be to just make mDialpadDigits
227       // *not* handle HW key presses.  That seems to be more
228       // complicated than just setting focusable="false" on it,
229       // though.)
230       dtmfDialerField.getText().append(digit);
231     }
232   }
233 
234   /** Called externally (from InCallScreen) to play a DTMF Tone. */
onDialerKeyDown(KeyEvent event)235   /* package */ boolean onDialerKeyDown(KeyEvent event) {
236     Log.d(this, "Notifying dtmf key down.");
237     if (dtmfKeyListener != null) {
238       return dtmfKeyListener.onKeyDown(event);
239     } else {
240       return false;
241     }
242   }
243 
244   /** Called externally (from InCallScreen) to cancel the last DTMF Tone played. */
onDialerKeyUp(KeyEvent event)245   public boolean onDialerKeyUp(KeyEvent event) {
246     Log.d(this, "Notifying dtmf key up.");
247     if (dtmfKeyListener != null) {
248       return dtmfKeyListener.onKeyUp(event);
249     } else {
250       return false;
251     }
252   }
253 
configureKeypadListeners()254   private void configureKeypadListeners() {
255     DialpadKeyButton dialpadKey;
256     for (int i = 0; i < buttonIds.length; i++) {
257       dialpadKey = (DialpadKeyButton) dialpadView.findViewById(buttonIds[i]);
258       dialpadKey.setOnKeyListener(this);
259       dialpadKey.setOnClickListener(this);
260       dialpadKey.setOnPressedListener(this);
261     }
262   }
263 
264   @Override
onPressed(View view, boolean pressed)265   public void onPressed(View view, boolean pressed) {
266     if (pressed && displayMap.containsKey(view.getId())) {
267       Logger.get(getContext())
268           .logImpression(DialerImpression.Type.IN_CALL_DIALPAD_NUMBER_BUTTON_PRESSED);
269       Log.d(this, "onPressed: " + pressed + " " + displayMap.get(view.getId()));
270       getPresenter().processDtmf(displayMap.get(view.getId()));
271     }
272     if (!pressed) {
273       Log.d(this, "onPressed: " + pressed);
274       getPresenter().stopDtmf();
275     }
276   }
277 
setShouldShowEndCallSpace(boolean show)278   public void setShouldShowEndCallSpace(boolean show) {
279     shouldShowEndCallSpace = show;
280   }
281 
282   /**
283    * LinearLayout with getter and setter methods for the translationY property using floats, for
284    * animation purposes.
285    */
286   public static class DialpadSlidingLinearLayout extends LinearLayout {
287 
DialpadSlidingLinearLayout(Context context)288     public DialpadSlidingLinearLayout(Context context) {
289       super(context);
290     }
291 
DialpadSlidingLinearLayout(Context context, AttributeSet attrs)292     public DialpadSlidingLinearLayout(Context context, AttributeSet attrs) {
293       super(context, attrs);
294     }
295 
DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle)296     public DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
297       super(context, attrs, defStyle);
298     }
299 
getYFraction()300     public float getYFraction() {
301       final int height = getHeight();
302       if (height == 0) {
303         return 0;
304       }
305       return getTranslationY() / height;
306     }
307 
setYFraction(float yFraction)308     public void setYFraction(float yFraction) {
309       setTranslationY(yFraction * getHeight());
310     }
311   }
312 }
313