• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
newInstance()52     public static OnGoingCallControllerBarFragment newInstance() {
53         return new OnGoingCallControllerBarFragment();
54     }
55 
56     /**
57      * Callback for control bar buttons.
58      */
59     public interface OnGoingCallControllerBarCallback {
onOpenDialpad()60         void onOpenDialpad();
61 
onCloseDialpad()62         void onCloseDialpad();
63     }
64 
65     private OnGoingCallControllerBarCallback mOnGoingCallControllerBarCallback;
66 
67     @Override
onCreate(@ullable Bundle savedInstanceState)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.transpare‌​nt);
89         list.setAdapter(new AudioRouteListAdapter(getContext(), availableRoutes));
90     }
91 
92     @Nullable
93     @Override
onCreateView(@onNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)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
onPause()162     public void onPause() {
163         super.onPause();
164         if (mAudioRouteSelectionDialog.isShowing()) {
165             mAudioRouteSelectionDialog.dismiss();
166         }
167     }
168 
onMuteMic()169     private void onMuteMic() {
170         UiCallManager.get().setMuted(true);
171     }
172 
onUnmuteMic()173     private void onUnmuteMic() {
174         UiCallManager.get().setMuted(false);
175     }
176 
onHoldCall()177     private void onHoldCall() {
178         UiCallManager uiCallManager = UiCallManager.get();
179         UiCall primaryCall = UiCallManager.get().getPrimaryCall();
180         uiCallManager.holdCall(primaryCall);
181     }
182 
onUnholdCall()183     private void onUnholdCall() {
184         UiCallManager uiCallManager = UiCallManager.get();
185         UiCall primaryCall = UiCallManager.get().getPrimaryCall();
186         uiCallManager.unholdCall(primaryCall);
187     }
188 
onVoiceOutputChannelChanged(@allAudioRoute int audioRoute)189     private void onVoiceOutputChannelChanged(@CallAudioRoute int audioRoute) {
190         UiCallManager.get().setAudioRoute(audioRoute);
191         mAudioRouteSelectionDialog.dismiss();
192         mAudioRouteButton.setImageResource(getAudioRouteIconRes(audioRoute));
193     }
194 
onEndCall()195     private void onEndCall() {
196         UiCallManager uiCallManager = UiCallManager.get();
197         UiCall primaryCall = UiCallManager.get().getPrimaryCall();
198         uiCallManager.disconnectCall(primaryCall);
199     }
200 
getAudioRouteIconRes(@allAudioRoute int audioRoute)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 
getAudioRouteLabelRes(@allAudioRoute int audioRoute)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 
AudioRouteListAdapter(Context context, List<Integer> supportedRoutes)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
onCreateViewHolder(ViewGroup container, int position)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
onBindViewHolder(AudioRouteItemViewHolder viewHolder, int position)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
getItemCount()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 
AudioRouteItemViewHolder(View itemView)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 }