Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2018 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 package com.android.car.dialer.ui;
     17 
     18 import android.app.AlertDialog;
     19 import android.content.Context;
     20 import android.os.Bundle;
     21 import android.support.annotation.NonNull;
     22 import android.support.annotation.Nullable;
     23 import android.support.v4.app.Fragment;
     24 import android.support.v7.widget.RecyclerView;
     25 import android.telecom.CallAudioState;
     26 import android.telecom.CallAudioState.CallAudioRoute;
     27 import android.view.LayoutInflater;
     28 import android.view.View;
     29 import android.view.ViewGroup;
     30 import android.widget.ImageView;
     31 import android.widget.TextView;
     32 
     33 import androidx.car.widget.PagedListView;
     34 
     35 import com.android.car.apps.common.FabDrawable;
     36 import com.android.car.dialer.R;
     37 import com.android.car.dialer.log.L;
     38 import com.android.car.dialer.telecom.UiCall;
     39 import com.android.car.dialer.telecom.UiCallManager;
     40 
     41 import java.util.List;
     42 
     43 /**
     44  * A Fragment of the bar which controls on going call. Its host or parent Fragment is expected to
     45  * implement {@link OnGoingCallControllerBarCallback}.
     46  */
     47 public class OnGoingCallControllerBarFragment extends Fragment {
     48     private static String TAG = "CDialer.OngoingCallCtlFrg";
     49     private AlertDialog mAudioRouteSelectionDialog;
     50     private ImageView mAudioRouteButton;
     51 
     52     public static OnGoingCallControllerBarFragment newInstance() {
     53         return new OnGoingCallControllerBarFragment();
     54     }
     55 
     56     /**
     57      * Callback for control bar buttons.
     58      */
     59     public interface OnGoingCallControllerBarCallback {
     60         void onOpenDialpad();
     61 
     62         void onCloseDialpad();
     63     }
     64 
     65     private OnGoingCallControllerBarCallback mOnGoingCallControllerBarCallback;
     66 
     67     @Override
     68     public void onCreate(@Nullable Bundle savedInstanceState) {
     69         super.onCreate(savedInstanceState);
     70         if (getParentFragment() != null
     71                 && getParentFragment() instanceof OnGoingCallControllerBarCallback) {
     72             mOnGoingCallControllerBarCallback =
     73                     (OnGoingCallControllerBarCallback) getParentFragment();
     74         } else if (getHost() instanceof OnGoingCallControllerBarCallback) {
     75             mOnGoingCallControllerBarCallback = (OnGoingCallControllerBarCallback) getHost();
     76         }
     77 
     78         View dialogView = LayoutInflater.from(getContext()).inflate(
     79                 R.layout.audio_route_switch_dialog, null, false);
     80         PagedListView list = dialogView.findViewById(R.id.list);
     81         List<Integer> availableRoutes = UiCallManager.get().getSupportedAudioRoute();
     82         list.setDividerVisibilityManager(position -> position == (availableRoutes.size() - 1));
     83 
     84         mAudioRouteSelectionDialog = new AlertDialog.Builder(getContext())
     85                 .setView(dialogView)
     86                 .create();
     87         mAudioRouteSelectionDialog.getWindow().setBackgroundDrawableResource(
     88                 android.R.color.transparent);
     89         list.setAdapter(new AudioRouteListAdapter(getContext(), availableRoutes));
     90     }
     91 
     92     @Nullable
     93     @Override
     94     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
     95             @Nullable Bundle savedInstanceState) {
     96         View fragmentView = inflater.inflate(R.layout.on_going_call_controller_bar_fragment,
     97                 container, false);
     98         fragmentView.findViewById(R.id.mute_button).setOnClickListener((v) -> {
     99             if (mOnGoingCallControllerBarCallback == null) {
    100                 return;
    101             }
    102             if (v.isActivated()) {
    103                 v.setActivated(false);
    104                 onMuteMic();
    105             } else {
    106                 v.setActivated(true);
    107                 onUnmuteMic();
    108             }
    109         });
    110 
    111         fragmentView.findViewById(R.id.toggle_dialpad_button).setOnClickListener((v) -> {
    112             if (mOnGoingCallControllerBarCallback == null) {
    113                 return;
    114             }
    115             if (v.isActivated()) {
    116                 v.setActivated(false);
    117                 mOnGoingCallControllerBarCallback.onCloseDialpad();
    118             } else {
    119                 v.setActivated(true);
    120                 mOnGoingCallControllerBarCallback.onOpenDialpad();
    121             }
    122         });
    123 
    124         ImageView endCallButton = fragmentView.findViewById(R.id.end_call_button);
    125         FabDrawable answerCallDrawable = new FabDrawable(getContext());
    126         answerCallDrawable.setFabAndStrokeColor(getContext().getColor(R.color.phone_end_call));
    127         endCallButton.setBackground(answerCallDrawable);
    128         endCallButton.setOnClickListener((v) -> {
    129             if (mOnGoingCallControllerBarCallback == null) {
    130                 return;
    131             }
    132             onEndCall();
    133         });
    134 
    135 
    136         List<Integer> audioRoutes = UiCallManager.get().getSupportedAudioRoute();
    137         mAudioRouteButton = fragmentView.findViewById(R.id.voice_channel_button);
    138         if (audioRoutes.size() > 1) {
    139             fragmentView.findViewById(R.id.voice_channel_chevron).setVisibility(View.VISIBLE);
    140             mAudioRouteButton.setOnClickListener(
    141                     (v) -> mAudioRouteSelectionDialog.show());
    142         } else {
    143             fragmentView.findViewById(R.id.voice_channel_chevron).setVisibility(View.GONE);
    144         }
    145 
    146         fragmentView.findViewById(R.id.pause_button).setOnClickListener((v) -> {
    147             if (mOnGoingCallControllerBarCallback == null) {
    148                 return;
    149             }
    150             if (v.isActivated()) {
    151                 v.setActivated(false);
    152                 onHoldCall();
    153             } else {
    154                 v.setActivated(true);
    155                 onUnholdCall();
    156             }
    157         });
    158         return fragmentView;
    159     }
    160 
    161     @Override
    162     public void onPause() {
    163         super.onPause();
    164         if (mAudioRouteSelectionDialog.isShowing()) {
    165             mAudioRouteSelectionDialog.dismiss();
    166         }
    167     }
    168 
    169     private void onMuteMic() {
    170         UiCallManager.get().setMuted(true);
    171     }
    172 
    173     private void onUnmuteMic() {
    174         UiCallManager.get().setMuted(false);
    175     }
    176 
    177     private void onHoldCall() {
    178         UiCallManager uiCallManager = UiCallManager.get();
    179         UiCall primaryCall = UiCallManager.get().getPrimaryCall();
    180         uiCallManager.holdCall(primaryCall);
    181     }
    182 
    183     private void onUnholdCall() {
    184         UiCallManager uiCallManager = UiCallManager.get();
    185         UiCall primaryCall = UiCallManager.get().getPrimaryCall();
    186         uiCallManager.unholdCall(primaryCall);
    187     }
    188 
    189     private void onVoiceOutputChannelChanged(@CallAudioRoute int audioRoute) {
    190         UiCallManager.get().setAudioRoute(audioRoute);
    191         mAudioRouteSelectionDialog.dismiss();
    192         mAudioRouteButton.setImageResource(getAudioRouteIconRes(audioRoute));
    193     }
    194 
    195     private void onEndCall() {
    196         UiCallManager uiCallManager = UiCallManager.get();
    197         UiCall primaryCall = UiCallManager.get().getPrimaryCall();
    198         uiCallManager.disconnectCall(primaryCall);
    199     }
    200 
    201     private int getAudioRouteIconRes(@CallAudioRoute int audioRoute) {
    202         switch (audioRoute) {
    203             case CallAudioState.ROUTE_WIRED_HEADSET:
    204             case CallAudioState.ROUTE_EARPIECE:
    205                 return R.drawable.ic_smartphone;
    206             case CallAudioState.ROUTE_BLUETOOTH:
    207                 return R.drawable.ic_bluetooth;
    208             case CallAudioState.ROUTE_SPEAKER:
    209                 return R.drawable.ic_speaker_phone;
    210             default:
    211                 L.w(TAG, "Unknown audio route: " + audioRoute);
    212                 return -1;
    213         }
    214     }
    215 
    216     private int getAudioRouteLabelRes(@CallAudioRoute int audioRoute) {
    217         switch (audioRoute) {
    218             case CallAudioState.ROUTE_WIRED_HEADSET:
    219             case CallAudioState.ROUTE_EARPIECE:
    220                 return R.string.audio_route_handset;
    221             case CallAudioState.ROUTE_BLUETOOTH:
    222                 return R.string.audio_route_vehicle;
    223             case CallAudioState.ROUTE_SPEAKER:
    224                 return R.string.audio_route_phone_speaker;
    225             default:
    226                 L.w(TAG, "Unknown audio route: " + audioRoute);
    227                 return -1;
    228         }
    229     }
    230 
    231     private class AudioRouteListAdapter extends
    232             RecyclerView.Adapter<AudioRouteItemViewHolder> {
    233         private List<Integer> mSupportedRoutes;
    234         private Context mContext;
    235 
    236         public AudioRouteListAdapter(Context context, List<Integer> supportedRoutes) {
    237             mSupportedRoutes = supportedRoutes;
    238             mContext = context;
    239             if (mSupportedRoutes.contains(CallAudioState.ROUTE_EARPIECE)
    240                     && mSupportedRoutes.contains(CallAudioState.ROUTE_WIRED_HEADSET)) {
    241                 // Keep either ROUTE_EARPIECE or ROUTE_WIRED_HEADSET, but not both of them.
    242                 mSupportedRoutes.remove(CallAudioState.ROUTE_WIRED_HEADSET);
    243             }
    244         }
    245 
    246         @Override
    247         public AudioRouteItemViewHolder onCreateViewHolder(ViewGroup container, int position) {
    248             View listItemView = LayoutInflater.from(mContext).inflate(
    249                     R.layout.audio_route_list_item, container, false);
    250             return new AudioRouteItemViewHolder(listItemView);
    251         }
    252 
    253         @Override
    254         public void onBindViewHolder(AudioRouteItemViewHolder viewHolder, int position) {
    255             int audioRoute = mSupportedRoutes.get(position);
    256             viewHolder.mBody.setText(mContext.getString(getAudioRouteLabelRes(audioRoute)));
    257             viewHolder.mIcon.setImageResource(getAudioRouteIconRes(audioRoute));
    258             viewHolder.itemView.setOnClickListener((v) -> onVoiceOutputChannelChanged(audioRoute));
    259         }
    260 
    261         @Override
    262         public int getItemCount() {
    263             return mSupportedRoutes.size();
    264         }
    265     }
    266 
    267     private static class AudioRouteItemViewHolder extends RecyclerView.ViewHolder {
    268         public final ImageView mIcon;
    269         public final TextView mBody;
    270 
    271         public AudioRouteItemViewHolder(View itemView) {
    272             super(itemView);
    273             mIcon = itemView.findViewById(R.id.icon);
    274             mBody = itemView.findViewById(R.id.body);
    275         }
    276     }
    277 }