blob: 4438980934690129985ff1fec2e48bc1ac8f66c8 [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.ui.dialpad;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.provider.CallLog;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.car.apps.common.util.ViewUtils;
import com.android.car.dialer.R;
import com.android.car.dialer.log.L;
import com.android.car.dialer.telecom.UiCallManager;
import com.android.car.dialer.ui.view.ContactAvatarOutputlineProvider;
import com.android.car.telephony.common.Contact;
import com.android.car.telephony.common.InMemoryPhoneBook;
import com.android.car.telephony.common.PhoneNumber;
import com.android.car.telephony.common.TelecomUtils;
import com.android.car.ui.toolbar.Toolbar;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
/**
* Fragment that controls the dialpad.
*/
public class DialpadFragment extends AbstractDialpadFragment {
private static final String TAG = "CD.DialpadFragment";
private static final String DIALPAD_MODE_KEY = "DIALPAD_MODE_KEY";
private static final int MODE_DIAL = 1;
private static final int MODE_EMERGENCY = 2;
@VisibleForTesting
static final int MAX_DIAL_NUMBER = 20;
private static final int TONE_RELATIVE_VOLUME = 80;
private static final int TONE_LENGTH_INFINITE = -1;
private final ImmutableMap<Integer, Integer> mToneMap =
ImmutableMap.<Integer, Integer>builder()
.put(KeyEvent.KEYCODE_1, ToneGenerator.TONE_DTMF_1)
.put(KeyEvent.KEYCODE_2, ToneGenerator.TONE_DTMF_2)
.put(KeyEvent.KEYCODE_3, ToneGenerator.TONE_DTMF_3)
.put(KeyEvent.KEYCODE_4, ToneGenerator.TONE_DTMF_4)
.put(KeyEvent.KEYCODE_5, ToneGenerator.TONE_DTMF_5)
.put(KeyEvent.KEYCODE_6, ToneGenerator.TONE_DTMF_6)
.put(KeyEvent.KEYCODE_7, ToneGenerator.TONE_DTMF_7)
.put(KeyEvent.KEYCODE_8, ToneGenerator.TONE_DTMF_8)
.put(KeyEvent.KEYCODE_9, ToneGenerator.TONE_DTMF_9)
.put(KeyEvent.KEYCODE_0, ToneGenerator.TONE_DTMF_0)
.put(KeyEvent.KEYCODE_STAR, ToneGenerator.TONE_DTMF_S)
.put(KeyEvent.KEYCODE_POUND, ToneGenerator.TONE_DTMF_P)
.build();
private TextView mTitleView;
@Nullable
private TextView mDisplayName;
@Nullable
private TextView mLabel;
@Nullable
private ImageView mAvatar;
private ImageButton mDeleteButton;
private int mMode;
private ToneGenerator mToneGenerator;
/**
* Creates a new instance of the {@link DialpadFragment} which is used for dialing a number.
*/
public static DialpadFragment newPlaceCallDialpad() {
DialpadFragment fragment = newDialpad(MODE_DIAL);
return fragment;
}
/**
* Creates a new instance used for emergency dialing.
*/
public static DialpadFragment newEmergencyDialpad() {
return newDialpad(MODE_EMERGENCY);
}
private static DialpadFragment newDialpad(int mode) {
DialpadFragment fragment = new DialpadFragment();
Bundle args = new Bundle();
args.putInt(DIALPAD_MODE_KEY, mode);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMode = getArguments().getInt(DIALPAD_MODE_KEY);
L.d(TAG, "onCreate mode: %s", mMode);
mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, TONE_RELATIVE_VOLUME);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.dialpad_fragment, container, false);
mTitleView = rootView.findViewById(R.id.title);
mTitleView.setTextAppearance(
mMode == MODE_EMERGENCY ? R.style.TextAppearance_EmergencyDialNumber
: R.style.TextAppearance_DialNumber);
mDisplayName = rootView.findViewById(R.id.display_name);
mLabel = rootView.findViewById(R.id.label);
mAvatar = rootView.findViewById(R.id.dialpad_contact_avatar);
if (mAvatar != null) {
mAvatar.setOutlineProvider(ContactAvatarOutputlineProvider.get());
}
View callButton = rootView.findViewById(R.id.call_button);
callButton.setOnClickListener(v -> {
if (!TextUtils.isEmpty(getNumber().toString())) {
UiCallManager.get().placeCall(getNumber().toString());
// Update dialed number UI later in onResume() when in call intent is handled.
getNumber().setLength(0);
} else {
setDialedNumber(CallLog.Calls.getLastOutgoingCall(getContext()));
}
});
callButton.addOnUnhandledKeyEventListener((v, event) -> {
if (event.getKeyCode() == KeyEvent.KEYCODE_CALL) {
// Use onKeyDown/Up instead of performClick() because it animates the ripple
if (event.getAction() == KeyEvent.ACTION_DOWN) {
callButton.onKeyDown(KeyEvent.KEYCODE_ENTER, event);
} else if (event.getAction() == KeyEvent.ACTION_UP) {
callButton.onKeyUp(KeyEvent.KEYCODE_ENTER, event);
}
return true;
} else {
return false;
}
});
mDeleteButton = rootView.findViewById(R.id.delete_button);
mDeleteButton.setOnClickListener(v -> removeLastDigit());
mDeleteButton.setOnLongClickListener(v -> {
clearDialedNumber();
return true;
});
return rootView;
}
@Override
public void setupToolbar(Toolbar toolbar) {
// Only setup the actionbar if we're in dial mode.
// In all the other modes, there will be another fragment in the activity
// at the same time, and we don't want to mess up it's action bar.
if (mMode == MODE_DIAL) {
super.setupToolbar(toolbar);
}
}
@Override
public void onKeypadKeyLongPressed(@KeypadFragment.DialKeyCode int keycode) {
switch (keycode) {
case KeyEvent.KEYCODE_0:
removeLastDigit();
appendDialedNumber("+");
break;
case KeyEvent.KEYCODE_STAR:
removeLastDigit();
appendDialedNumber(",");
break;
case KeyEvent.KEYCODE_1:
UiCallManager.get().callVoicemail();
break;
default:
break;
}
}
@Override
void playTone(int keycode) {
L.d(TAG, "start key pressed tone for %s", keycode);
mToneGenerator.startTone(mToneMap.get(keycode), TONE_LENGTH_INFINITE);
}
@Override
void stopAllTones() {
L.d(TAG, "stop key pressed tone");
mToneGenerator.stopTone();
}
@Override
void presentDialedNumber(@NonNull StringBuffer number) {
if (getView() == null) {
return;
}
if (number.length() == 0) {
mTitleView.setGravity(Gravity.CENTER);
mTitleView.setText(
mMode == MODE_DIAL ? R.string.dial_a_number
: R.string.emergency_call_description);
ViewUtils.setVisible(mDeleteButton, false);
} else {
mTitleView.setGravity(
getResources().getInteger(R.integer.config_dialed_number_gravity));
if (number.length() <= MAX_DIAL_NUMBER) {
mTitleView.setText(
TelecomUtils.getFormattedNumber(getContext(), number.toString()));
} else {
mTitleView.setText(number.substring(number.length() - MAX_DIAL_NUMBER));
}
ViewUtils.setVisible(mDeleteButton, true);
}
presentContactInfo(number.toString());
}
@Override
public void onToolbarHeightChange(int toolbarHeight) {
// Offset the dialpad to under the tabs in normal dial mode.
getView().setPadding(0, mMode == MODE_DIAL ? toolbarHeight : 0, 0, 0);
}
private void presentContactInfo(@NonNull String number) {
Contact contact = InMemoryPhoneBook.get().lookupContactEntry(number);
ViewUtils.setText(mDisplayName, contact == null ? "" : contact.getDisplayName());
if (contact != null && getResources().getBoolean(
R.bool.config_show_detailed_user_profile_on_dialpad)) {
presentContactDetail(contact, number);
} else {
resetContactInfo();
}
}
private void presentContactDetail(@Nullable Contact contact, @NonNull String number) {
PhoneNumber phoneNumber = contact.getPhoneNumber(getContext(), number);
CharSequence readableLabel = phoneNumber.getReadableLabel(
getContext().getResources());
ViewUtils.setText(mLabel, phoneNumber.isPrimary() ? getContext().getString(
R.string.primary_number_description, readableLabel) : readableLabel);
ViewUtils.setVisible(mLabel, true);
TelecomUtils.setContactBitmapAsync(getContext(), mAvatar, contact);
ViewUtils.setVisible(mAvatar, true);
}
private void resetContactInfo() {
ViewUtils.setVisible(mLabel, false);
ViewUtils.setVisible(mAvatar, false);
}
}