1/*
2 * Copyright (C) 2017 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
17package com.android.incallui;
18
19import android.app.PendingIntent;
20import android.content.Context;
21import android.content.Intent;
22import android.graphics.drawable.Icon;
23import android.support.annotation.NonNull;
24import android.support.annotation.VisibleForTesting;
25import android.telecom.CallAudioState;
26import com.android.dialer.common.LogUtil;
27import com.android.dialer.configprovider.ConfigProviderBindings;
28import com.android.dialer.telecom.TelecomUtil;
29import com.android.dialershared.bubble.Bubble;
30import com.android.dialershared.bubble.BubbleInfo;
31import com.android.dialershared.bubble.BubbleInfo.Action;
32import com.android.incallui.InCallPresenter.InCallUiListener;
33import com.android.incallui.audiomode.AudioModeProvider;
34import com.android.incallui.audiomode.AudioModeProvider.AudioModeListener;
35import com.android.incallui.call.CallList;
36import com.android.incallui.call.CallList.Listener;
37import com.android.incallui.call.DialerCall;
38import com.android.incallui.speakerbuttonlogic.SpeakerButtonInfo;
39import com.android.incallui.speakerbuttonlogic.SpeakerButtonInfo.IconSize;
40import java.util.ArrayList;
41import java.util.List;
42
43/**
44 * Listens for events relevant to the return-to-call bubble and updates the bubble's state as
45 * necessary
46 */
47public class ReturnToCallController implements InCallUiListener, Listener, AudioModeListener {
48
49  private final Context context;
50
51  @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
52  Bubble bubble;
53
54  private CallAudioState audioState;
55
56  private final PendingIntent toggleSpeaker;
57  private final PendingIntent showSpeakerSelect;
58  private final PendingIntent toggleMute;
59  private final PendingIntent endCall;
60
61  public static boolean isEnabled(Context context) {
62    return ConfigProviderBindings.get(context).getBoolean("enable_return_to_call_bubble", false);
63  }
64
65  public ReturnToCallController(Context context) {
66    this.context = context;
67
68    toggleSpeaker = createActionIntent(ReturnToCallActionReceiver.ACTION_TOGGLE_SPEAKER);
69    showSpeakerSelect =
70        createActionIntent(ReturnToCallActionReceiver.ACTION_SHOW_AUDIO_ROUTE_SELECTOR);
71    toggleMute = createActionIntent(ReturnToCallActionReceiver.ACTION_TOGGLE_MUTE);
72    endCall = createActionIntent(ReturnToCallActionReceiver.ACTION_END_CALL);
73
74    InCallPresenter.getInstance().addInCallUiListener(this);
75    CallList.getInstance().addListener(this);
76    AudioModeProvider.getInstance().addListener(this);
77    audioState = AudioModeProvider.getInstance().getAudioState();
78  }
79
80  public void tearDown() {
81    InCallPresenter.getInstance().removeInCallUiListener(this);
82    CallList.getInstance().removeListener(this);
83    AudioModeProvider.getInstance().removeListener(this);
84  }
85
86  @Override
87  public void onUiShowing(boolean showing) {
88    if (showing) {
89      hide();
90    } else {
91      if (TelecomUtil.isInCall(context)) {
92        show();
93      }
94    }
95  }
96
97  private void hide() {
98    if (bubble != null) {
99      bubble.hide();
100    } else {
101      LogUtil.i("ReturnToCallController.hide", "hide() called without calling show()");
102    }
103  }
104
105  private void show() {
106    if (bubble == null) {
107      bubble = startNewBubble();
108    } else {
109      bubble.show();
110    }
111  }
112
113  private Bubble startNewBubble() {
114    if (!Bubble.canShowBubbles(context)) {
115      LogUtil.i("ReturnToCallController.startNewBubble", "can't show bubble, no permission");
116      return null;
117    }
118    Bubble returnToCallBubble = Bubble.createBubble(context, generateBubbleInfo());
119    returnToCallBubble.show();
120    return returnToCallBubble;
121  }
122
123  @Override
124  public void onIncomingCall(DialerCall call) {}
125
126  @Override
127  public void onUpgradeToVideo(DialerCall call) {}
128
129  @Override
130  public void onSessionModificationStateChange(DialerCall call) {}
131
132  @Override
133  public void onCallListChange(CallList callList) {}
134
135  @Override
136  public void onDisconnect(DialerCall call) {
137    if (bubble != null && bubble.isVisible()) {
138      bubble.showText(context.getText(R.string.incall_call_ended));
139    }
140
141    if (!TelecomUtil.isInCall(context)) {
142      hide();
143    }
144  }
145
146  @Override
147  public void onWiFiToLteHandover(DialerCall call) {}
148
149  @Override
150  public void onHandoverToWifiFailed(DialerCall call) {}
151
152  @Override
153  public void onInternationalCallOnWifi(@NonNull DialerCall call) {}
154
155  @Override
156  public void onAudioStateChanged(CallAudioState audioState) {
157    this.audioState = audioState;
158    if (bubble != null) {
159      bubble.updateActions(generateActions());
160    }
161  }
162
163  private BubbleInfo generateBubbleInfo() {
164    Intent activityIntent = InCallActivity.getIntent(context, false, false, false);
165    activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
166    return BubbleInfo.builder()
167        .setPrimaryColor(context.getResources().getColor(R.color.dialer_theme_color, null))
168        .setPrimaryIcon(Icon.createWithResource(context, R.drawable.on_going_call))
169        .setStartingYPosition(
170            context.getResources().getDimensionPixelOffset(R.dimen.return_to_call_initial_offset_y))
171        .setPrimaryIntent(PendingIntent.getActivity(context, 0, activityIntent, 0))
172        .setActions(generateActions())
173        .build();
174  }
175
176  @NonNull
177  private List<Action> generateActions() {
178    List<Action> actions = new ArrayList<>();
179    SpeakerButtonInfo speakerButtonInfo = new SpeakerButtonInfo(audioState, IconSize.SIZE_24_DP);
180
181    actions.add(
182        Action.builder()
183            .setIcon(Icon.createWithResource(context, speakerButtonInfo.icon))
184            .setName(context.getText(speakerButtonInfo.label))
185            .setChecked(speakerButtonInfo.isChecked)
186            .setIntent(speakerButtonInfo.checkable ? toggleSpeaker : showSpeakerSelect)
187            .build());
188
189    actions.add(
190        Action.builder()
191            .setIcon(Icon.createWithResource(context, R.drawable.quantum_ic_mic_off_white_24))
192            .setName(context.getText(R.string.incall_label_mute))
193            .setChecked(audioState.isMuted())
194            .setIntent(toggleMute)
195            .build());
196    actions.add(
197        Action.builder()
198            .setIcon(Icon.createWithResource(context, R.drawable.quantum_ic_call_end_white_24))
199            .setName(context.getText(R.string.incall_label_end_call))
200            .setIntent(endCall)
201            .build());
202    return actions;
203  }
204
205  @NonNull
206  private PendingIntent createActionIntent(String action) {
207    Intent toggleSpeaker = new Intent(context, ReturnToCallActionReceiver.class);
208    toggleSpeaker.setAction(action);
209    return PendingIntent.getBroadcast(context, 0, toggleSpeaker, 0);
210  }
211}
212