blob: 846834f63399b7b4ab5e1b2bf18861a481349e0d [file] [log] [blame]
/*
* Copyright (C) 2016 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.incallui.audioroute;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.graphics.PorterDuff.Mode;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetDialogFragment;
import android.support.v4.os.BuildCompat;
import android.telecom.CallAudioState;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.dialer.common.FragmentUtils;
import com.android.dialer.common.LogUtil;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
import com.android.dialer.theme.base.ThemeComponent;
import com.android.incallui.call.CallList;
import com.android.incallui.call.DialerCall;
import com.android.incallui.call.TelecomAdapter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
/** Shows picker for audio routes */
public class AudioRouteSelectorDialogFragment extends BottomSheetDialogFragment {
public static final String TAG = "AudioRouteSelectorDialogFragment";
private static final String ARG_AUDIO_STATE = "audio_state";
/** Called when an audio route is picked */
public interface AudioRouteSelectorPresenter {
void onAudioRouteSelected(int audioRoute);
void onAudioRouteSelectorDismiss();
}
public static AudioRouteSelectorDialogFragment newInstance(CallAudioState audioState) {
AudioRouteSelectorDialogFragment fragment = new AudioRouteSelectorDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_AUDIO_STATE, audioState);
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
FragmentUtils.checkParent(this, AudioRouteSelectorPresenter.class);
}
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
LogUtil.i("AudioRouteSelectorDialogFragment.onCreateDialog", null);
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (Settings.canDrawOverlays(getContext())) {
dialog
.getWindow()
.setType(
BuildCompat.isAtLeastO()
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
: WindowManager.LayoutParams.TYPE_PHONE);
}
return dialog;
}
@Nullable
@Override
@SuppressLint("NewApi")
public View onCreateView(
LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
View view = layoutInflater.inflate(R.layout.audioroute_selector, viewGroup, false);
CallAudioState audioState = getArguments().getParcelable(ARG_AUDIO_STATE);
if (BuildCompat.isAtLeastP()) {
// Create items for all connected Bluetooth devices
Collection<BluetoothDevice> bluetoothDeviceSet = audioState.getSupportedBluetoothDevices();
for (BluetoothDevice device : bluetoothDeviceSet) {
boolean selected =
(audioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH)
&& (bluetoothDeviceSet.size() == 1
|| device.equals(audioState.getActiveBluetoothDevice()));
TextView textView = createBluetoothItem(device, selected);
((LinearLayout) view).addView(textView, 0);
}
} else {
// Only create Bluetooth audio route
TextView textView =
(TextView) getLayoutInflater().inflate(R.layout.audioroute_item, null, false);
textView.setText(getString(R.string.audioroute_bluetooth));
initItem(
textView,
CallAudioState.ROUTE_BLUETOOTH,
audioState,
DialerImpression.Type.IN_CALL_SWITCH_AUDIO_ROUTE_BLUETOOTH);
((LinearLayout) view).addView(textView, 0);
}
initItem(
(TextView) view.findViewById(R.id.audioroute_speaker),
CallAudioState.ROUTE_SPEAKER,
audioState,
DialerImpression.Type.IN_CALL_SWITCH_AUDIO_ROUTE_SPEAKER);
initItem(
(TextView) view.findViewById(R.id.audioroute_headset),
CallAudioState.ROUTE_WIRED_HEADSET,
audioState,
DialerImpression.Type.IN_CALL_SWITCH_AUDIO_ROUTE_WIRED_HEADSET);
initItem(
(TextView) view.findViewById(R.id.audioroute_earpiece),
CallAudioState.ROUTE_EARPIECE,
audioState,
DialerImpression.Type.IN_CALL_SWITCH_AUDIO_ROUTE_EARPIECE);
// TODO(a bug): set peak height correctly to fully expand it in landscape mode.
return view;
}
@Override
public void onCancel(DialogInterface dialogInterface) {
super.onCancel(dialogInterface);
FragmentUtils.getParentUnsafe(
AudioRouteSelectorDialogFragment.this, AudioRouteSelectorPresenter.class)
.onAudioRouteSelectorDismiss();
}
private void initItem(
TextView item,
final int itemRoute,
CallAudioState audioState,
DialerImpression.Type impressionType) {
int selectedColor = ThemeComponent.get(getContext()).theme().getColorPrimary();
if ((audioState.getSupportedRouteMask() & itemRoute) == 0) {
item.setVisibility(View.GONE);
} else if (audioState.getRoute() == itemRoute) {
item.setSelected(true);
item.setTextColor(selectedColor);
item.setCompoundDrawableTintList(ColorStateList.valueOf(selectedColor));
item.setCompoundDrawableTintMode(Mode.SRC_ATOP);
}
item.setOnClickListener(
(v) -> {
logCallAudioRouteImpression(impressionType);
FragmentUtils.getParentUnsafe(
AudioRouteSelectorDialogFragment.this, AudioRouteSelectorPresenter.class)
.onAudioRouteSelected(itemRoute);
dismiss();
});
}
private TextView createBluetoothItem(BluetoothDevice bluetoothDevice, boolean selected) {
int selectedColor = ThemeComponent.get(getContext()).theme().getColorPrimary();
TextView textView =
(TextView) getLayoutInflater().inflate(R.layout.audioroute_item, null, false);
textView.setText(getAliasName(bluetoothDevice));
if (selected) {
textView.setSelected(true);
textView.setTextColor(selectedColor);
textView.setCompoundDrawableTintList(ColorStateList.valueOf(selectedColor));
textView.setCompoundDrawableTintMode(Mode.SRC_ATOP);
}
textView.setOnClickListener(
(v) -> {
logCallAudioRouteImpression(DialerImpression.Type.IN_CALL_SWITCH_AUDIO_ROUTE_BLUETOOTH);
// Set Bluetooth audio route
FragmentUtils.getParentUnsafe(
AudioRouteSelectorDialogFragment.this, AudioRouteSelectorPresenter.class)
.onAudioRouteSelected(CallAudioState.ROUTE_BLUETOOTH);
// Set active Bluetooth device
TelecomAdapter.getInstance().requestBluetoothAudio(bluetoothDevice);
dismiss();
});
return textView;
}
@SuppressLint("PrivateApi")
private String getAliasName(BluetoothDevice bluetoothDevice) {
try {
Method getActiveDeviceMethod = bluetoothDevice.getClass().getDeclaredMethod("getAliasName");
getActiveDeviceMethod.setAccessible(true);
return (String) getActiveDeviceMethod.invoke(bluetoothDevice);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
return bluetoothDevice.getName();
}
}
private void logCallAudioRouteImpression(DialerImpression.Type impressionType) {
DialerCall dialerCall = CallList.getInstance().getOutgoingCall();
if (dialerCall == null) {
dialerCall = CallList.getInstance().getActiveOrBackgroundCall();
}
if (dialerCall != null) {
Logger.get(getContext())
.logCallImpression(
impressionType, dialerCall.getUniqueCallId(), dialerCall.getTimeAddedMs());
} else {
Logger.get(getContext()).logImpression(impressionType);
}
}
}