blob: e84f18914e06ef3b2198764f64819298e7b7fdaf [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.dialer;
import android.content.Context;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.car.apps.common.FabDrawable;
import com.android.car.dialer.telecom.TelecomUtils;
import com.android.car.dialer.telecom.UiCallManager;
import com.android.car.dialer.telecom.UiCallManager.CallListener;
/**
* Fragment that controls the dialpad.
*/
public class DialerFragment extends Fragment {
private static final String TAG = "Em.DialerFragment";
private static final String INPUT_ACTIVE_KEY = "INPUT_ACTIVE_KEY";
private static final int TONE_RELATIVE_VOLUME = 80;
private static final int ABANDON_AUDIO_FOCUS_DELAY_MS = 250;
private static final int MAX_DIAL_NUMBER = 20;
private static final int NO_TONE = -1;
private static final ArrayMap<Integer, Integer> mToneMap = new ArrayMap<>();
static {
mToneMap.put(KeyEvent.KEYCODE_1, ToneGenerator.TONE_DTMF_1);
mToneMap.put(KeyEvent.KEYCODE_2, ToneGenerator.TONE_DTMF_2);
mToneMap.put(KeyEvent.KEYCODE_3, ToneGenerator.TONE_DTMF_3);
mToneMap.put(KeyEvent.KEYCODE_4, ToneGenerator.TONE_DTMF_4);
mToneMap.put(KeyEvent.KEYCODE_5, ToneGenerator.TONE_DTMF_5);
mToneMap.put(KeyEvent.KEYCODE_6, ToneGenerator.TONE_DTMF_6);
mToneMap.put(KeyEvent.KEYCODE_7, ToneGenerator.TONE_DTMF_7);
mToneMap.put(KeyEvent.KEYCODE_8, ToneGenerator.TONE_DTMF_8);
mToneMap.put(KeyEvent.KEYCODE_9, ToneGenerator.TONE_DTMF_9);
mToneMap.put(KeyEvent.KEYCODE_0, ToneGenerator.TONE_DTMF_0);
mToneMap.put(KeyEvent.KEYCODE_STAR, ToneGenerator.TONE_DTMF_S);
mToneMap.put(KeyEvent.KEYCODE_POUND, ToneGenerator.TONE_DTMF_P);
}
private Context mContext;
private UiCallManager mUiCallManager;
private final StringBuffer mNumber = new StringBuffer(MAX_DIAL_NUMBER);
private AudioManager mAudioManager;
private ToneGenerator mToneGenerator;
private final Handler mHandler = new Handler();
private final Object mToneGeneratorLock = new Object();
private TextView mNumberView;
private boolean mShowInput = true;
private Runnable mPendingRunnable;
private DialerBackButtonListener mBackListener;
/**
* Interface for a class that will be notified when the back button of the dialer has been
* clicked.
*/
public interface DialerBackButtonListener {
/**
* Called when the back button has been clicked on the dialer. This action should dismiss
* the dialer fragment.
*/
void onDialerBackClick();
}
public static DialerFragment newInstance(UiCallManager callManager) {
DialerFragment fragment = new DialerFragment();
fragment.mUiCallManager = callManager;
return fragment;
}
/**
* Sets the given {@link DialerBackButtonListener} to be notified whenever the back button
* on the dialer has been clicked. Passing {@code null} to this method will clear all listeners.
*/
public void setDialerBackButtonListener(DialerBackButtonListener listener) {
mBackListener = listener;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAudioManager =
(AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
if (savedInstanceState != null && savedInstanceState.containsKey(INPUT_ACTIVE_KEY)) {
mShowInput = savedInstanceState.getBoolean(INPUT_ACTIVE_KEY);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onCreateView");
}
mContext = getContext();
View view = inflater.inflate(R.layout.dialer_fragment, container, false);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "onCreateView: inflated successfully");
}
view.findViewById(R.id.exit_dialer_button).setOnClickListener((unusedView) -> {
if (mBackListener != null) {
mBackListener.onDialerBackClick();
}
});
mNumberView = (TextView) view.findViewById(R.id.number);
final boolean hasTouch = getResources().getBoolean(R.bool.has_touch);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "hasTouch: " + hasTouch + ", mShowInput: " + mShowInput);
}
if (hasTouch) {
// Only show the delete and call buttons in touch mode.
// Buttons are in rotary input itself.
View callButton = view.findViewById(R.id.call);
FabDrawable answerCallDrawable = new FabDrawable(mContext);
answerCallDrawable.setFabAndStrokeColor(getResources().getColor(R.color.phone_call));
callButton.setBackground(answerCallDrawable);
callButton.setVisibility(View.VISIBLE);
callButton.setOnClickListener((unusedView) -> {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Call button clicked, placing a call: " + mNumber.toString());
}
if (!TextUtils.isEmpty(mNumber.toString())) {
mUiCallManager.safePlaceCall(mNumber.toString(), false);
}
});
View deleteButton = view.findViewById(R.id.delete);
deleteButton.setVisibility(View.VISIBLE);
deleteButton.setOnClickListener((unusedView) -> {
if (mNumber.length() != 0) {
mNumber.deleteCharAt(mNumber.length() - 1);
mNumberView.setText(getFormattedNumber(mNumber.toString()));
}
});
}
setupKeypad(view);
return view;
}
/**
* The default click listener for all dialpad buttons. This click listener will append its
* associated value to {@link #mNumber}.
*/
private class DialpadClickListener implements View.OnClickListener {
private String mValue;
public DialpadClickListener(String value) {
mValue = value;
}
@Override
public void onClick(View v) {
mNumber.append(mValue);
mNumberView.setText(getFormattedNumber(mNumber.toString()));
}
};
/**
* Sets up the click listeners for all the dialpad buttons.
*/
private void setupKeypad(View parent) {
parent.findViewById(R.id.zero).setOnClickListener(new DialpadClickListener("0"));
parent.findViewById(R.id.one).setOnClickListener(new DialpadClickListener("1"));
parent.findViewById(R.id.two).setOnClickListener(new DialpadClickListener("2"));
parent.findViewById(R.id.three).setOnClickListener(new DialpadClickListener("3"));
parent.findViewById(R.id.four).setOnClickListener(new DialpadClickListener("4"));
parent.findViewById(R.id.five).setOnClickListener(new DialpadClickListener("5"));
parent.findViewById(R.id.six).setOnClickListener(new DialpadClickListener("6"));
parent.findViewById(R.id.seven).setOnClickListener(new DialpadClickListener("7"));
parent.findViewById(R.id.eight).setOnClickListener(new DialpadClickListener("8"));
parent.findViewById(R.id.nine).setOnClickListener(new DialpadClickListener("9"));
parent.findViewById(R.id.star).setOnClickListener(new DialpadClickListener("*"));
parent.findViewById(R.id.pound).setOnClickListener(new DialpadClickListener("#"));
}
@Override
public void onResume() {
super.onResume();
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, TONE_RELATIVE_VOLUME);
}
}
mUiCallManager.addListener(mCallListener);
if (mPendingRunnable != null) {
mPendingRunnable.run();
mPendingRunnable = null;
}
}
@Override
public void onPause() {
super.onPause();
mUiCallManager.removeListener(mCallListener);
stopTone();
synchronized (mToneGeneratorLock) {
if (mToneGenerator != null) {
mToneGenerator.release();
mToneGenerator = null;
}
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mContext = null;
mNumberView = null;
}
public void setDialNumber(final String number) {
if (TextUtils.isEmpty(number)) {
return;
}
if (mContext != null && mNumberView != null) {
setDialNumberInternal(number);
} else {
mPendingRunnable = () -> setDialNumberInternal(number);
}
}
private void setDialNumberInternal(final String number) {
// Clear existing content in mNumber.
mNumber.setLength(0);
mNumber.append(number);
mNumberView.setText(getFormattedNumber(mNumber.toString()));
}
private void stopTone() {
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
Log.w(TAG, "stopTone: mToneGenerator == null");
return;
}
mToneGenerator.stopTone();
mHandler.postDelayed(mDelayedAbandonAudioFocusRunnable, ABANDON_AUDIO_FOCUS_DELAY_MS);
}
}
private final Runnable mDelayedAbandonAudioFocusRunnable = new Runnable() {
@Override
public void run() {
mAudioManager.abandonAudioFocus(null);
}
};
private String getFormattedNumber(String number) {
return TelecomUtils.getFormattedNumber(mContext, number);
}
private final CallListener mCallListener = new CallListener() {
@Override
public void dispatchPhoneKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_CALL &&
event.getAction() == KeyEvent.ACTION_UP &&
!TextUtils.isEmpty(mNumber.toString())) {
mUiCallManager.safePlaceCall(mNumber.toString(), false);
}
}
};
}