/* * 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 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); } } }