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