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