1 /*
2  * Copyright (C) 2016 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 
17 package com.android.dialer.callintent;
18 
19 import android.content.Intent;
20 import android.net.Uri;
21 import android.os.Bundle;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.SystemClock;
25 import android.support.annotation.NonNull;
26 import android.support.annotation.Nullable;
27 import android.support.annotation.VisibleForTesting;
28 import android.telecom.Call.Details;
29 import android.telecom.PhoneAccount;
30 import android.telecom.PhoneAccountHandle;
31 import android.telecom.TelecomManager;
32 import android.telecom.VideoProfile;
33 import android.text.TextUtils;
34 import com.android.dialer.callintent.CallInitiationType.Type;
35 import com.android.dialer.common.Assert;
36 import com.android.dialer.performancereport.PerformanceReport;
37 import com.android.dialer.util.CallUtil;
38 import com.google.protobuf.InvalidProtocolBufferException;
39 
40 /** Creates an intent to start a new outgoing call. */
41 public class CallIntentBuilder implements Parcelable {
42   private Uri uri;
43   private final CallSpecificAppData callSpecificAppData;
44   @Nullable private PhoneAccountHandle phoneAccountHandle;
45   private boolean isVideoCall;
46   private boolean isDuoCall;
47   private String callSubject;
48   private boolean allowAssistedDial;
49 
50   private final Bundle inCallUiIntentExtras = new Bundle();
51   private final Bundle placeCallExtras = new Bundle();
52 
53   private static int lightbringerButtonAppearInExpandedCallLogItemCount = 0;
54   private static int lightbringerButtonAppearInCollapsedCallLogItemCount = 0;
55   private static int lightbringerButtonAppearInSearchCount = 0;
56 
CallIntentBuilder(@onNull Uri uri, @NonNull CallSpecificAppData callSpecificAppData)57   public CallIntentBuilder(@NonNull Uri uri, @NonNull CallSpecificAppData callSpecificAppData) {
58     this.uri = Assert.isNotNull(uri);
59     Assert.isNotNull(callSpecificAppData);
60     Assert.checkArgument(
61         callSpecificAppData.getCallInitiationType() != CallInitiationType.Type.UNKNOWN_INITIATION);
62 
63     CallSpecificAppData.Builder builder =
64         CallSpecificAppData.newBuilder(callSpecificAppData)
65             .setLightbringerButtonAppearInExpandedCallLogItemCount(
66                 lightbringerButtonAppearInExpandedCallLogItemCount)
67             .setLightbringerButtonAppearInCollapsedCallLogItemCount(
68                 lightbringerButtonAppearInCollapsedCallLogItemCount)
69             .setLightbringerButtonAppearInSearchCount(lightbringerButtonAppearInSearchCount);
70     lightbringerButtonAppearInExpandedCallLogItemCount = 0;
71     lightbringerButtonAppearInCollapsedCallLogItemCount = 0;
72     lightbringerButtonAppearInSearchCount = 0;
73 
74     if (PerformanceReport.isRecording()) {
75       builder
76           .setTimeSinceAppLaunch(PerformanceReport.getTimeSinceAppLaunch())
77           .setTimeSinceFirstClick(PerformanceReport.getTimeSinceFirstClick())
78           .addAllUiActionsSinceAppLaunch(PerformanceReport.getActions())
79           .addAllUiActionTimestampsSinceAppLaunch(PerformanceReport.getActionTimestamps())
80           .setStartingTabIndex(PerformanceReport.getStartingTabIndex());
81       PerformanceReport.stopRecording();
82     }
83 
84     this.callSpecificAppData = builder.build();
85   }
86 
CallIntentBuilder(@onNull Uri uri, CallInitiationType.Type callInitiationType)87   public CallIntentBuilder(@NonNull Uri uri, CallInitiationType.Type callInitiationType) {
88     this(uri, createCallSpecificAppData(callInitiationType));
89   }
90 
CallIntentBuilder( @onNull String number, @NonNull CallSpecificAppData callSpecificAppData)91   public CallIntentBuilder(
92       @NonNull String number, @NonNull CallSpecificAppData callSpecificAppData) {
93     this(CallUtil.getCallUri(Assert.isNotNull(number)), callSpecificAppData);
94   }
95 
CallIntentBuilder(@onNull String number, CallInitiationType.Type callInitiationType)96   public CallIntentBuilder(@NonNull String number, CallInitiationType.Type callInitiationType) {
97     this(CallUtil.getCallUri(Assert.isNotNull(number)), callInitiationType);
98   }
99 
CallIntentBuilder(@onNull Parcel parcel)100   public CallIntentBuilder(@NonNull Parcel parcel) {
101     ClassLoader classLoader = CallIntentBuilder.class.getClassLoader();
102     uri = parcel.readParcelable(classLoader);
103     CallSpecificAppData data;
104     try {
105       data = CallSpecificAppData.parseFrom(parcel.createByteArray());
106     } catch (InvalidProtocolBufferException e) {
107       data = createCallSpecificAppData(Type.UNKNOWN_INITIATION);
108     }
109     callSpecificAppData = data;
110     phoneAccountHandle = parcel.readParcelable(classLoader);
111     isVideoCall = parcel.readInt() != 0;
112     isDuoCall = parcel.readInt() != 0;
113     callSubject = parcel.readString();
114     allowAssistedDial = parcel.readInt() != 0;
115     inCallUiIntentExtras.putAll(parcel.readBundle(classLoader));
116   }
117 
forVoicemail( CallInitiationType.Type callInitiationType)118   public static CallIntentBuilder forVoicemail(
119       CallInitiationType.Type callInitiationType) {
120     return new CallIntentBuilder(
121             Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null), callInitiationType)
122         .setPhoneAccountHandle(null);
123   }
124 
setUri(@onNull Uri uri)125   public void setUri(@NonNull Uri uri) {
126     this.uri = Assert.isNotNull(uri);
127   }
128 
getUri()129   public Uri getUri() {
130     return uri;
131   }
132 
getCallSpecificAppData()133   public CallSpecificAppData getCallSpecificAppData() {
134     return callSpecificAppData;
135   }
136 
setPhoneAccountHandle(@ullable PhoneAccountHandle accountHandle)137   public CallIntentBuilder setPhoneAccountHandle(@Nullable PhoneAccountHandle accountHandle) {
138     this.phoneAccountHandle = accountHandle;
139     return this;
140   }
141 
142   @Nullable
getPhoneAccountHandle()143   public PhoneAccountHandle getPhoneAccountHandle() {
144     return phoneAccountHandle;
145   }
146 
setIsVideoCall(boolean isVideoCall)147   public CallIntentBuilder setIsVideoCall(boolean isVideoCall) {
148     this.isVideoCall = isVideoCall;
149     return this;
150   }
151 
isVideoCall()152   public boolean isVideoCall() {
153     return isVideoCall;
154   }
155 
setIsDuoCall(boolean isDuoCall)156   public CallIntentBuilder setIsDuoCall(boolean isDuoCall) {
157     this.isDuoCall = isDuoCall;
158     return this;
159   }
160 
isDuoCall()161   public boolean isDuoCall() {
162     return isDuoCall;
163   }
164 
165   /** Default false. Should only be set to true if the number has a lookup URI. */
setAllowAssistedDial(boolean allowAssistedDial)166   public CallIntentBuilder setAllowAssistedDial(boolean allowAssistedDial) {
167     this.allowAssistedDial = allowAssistedDial;
168     return this;
169   }
170 
isAssistedDialAllowed()171   public boolean isAssistedDialAllowed() {
172     return allowAssistedDial;
173   }
174 
setCallSubject(String callSubject)175   public CallIntentBuilder setCallSubject(String callSubject) {
176     this.callSubject = callSubject;
177     return this;
178   }
179 
getCallSubject()180   public String getCallSubject() {
181     return callSubject;
182   }
183 
184   /** Additional data the in call UI can read with {@link Details#getIntentExtras()} */
getInCallUiIntentExtras()185   public Bundle getInCallUiIntentExtras() {
186     return inCallUiIntentExtras;
187   }
188 
189   /**
190    * Other extras that should be used with {@link TelecomManager#placeCall(Uri, Bundle)}. This will
191    * override everything set by the CallIntentBuilder
192    */
getPlaceCallExtras()193   public Bundle getPlaceCallExtras() {
194     return placeCallExtras;
195   }
196 
197   /**
198    * @deprecated Use {@link com.android.dialer.precall.PreCall#getIntent(android.content.Context,
199    *     CallIntentBuilder)} instead.
200    */
201   @Deprecated
build()202   public Intent build() {
203     Intent intent = new Intent(Intent.ACTION_CALL, uri);
204 
205     intent.putExtra(
206         TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
207         isVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);
208 
209     inCallUiIntentExtras.putLong(
210         Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
211     CallIntentParser.putCallSpecificAppData(inCallUiIntentExtras, callSpecificAppData);
212 
213     intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, inCallUiIntentExtras);
214 
215     if (phoneAccountHandle != null) {
216       intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
217     }
218 
219     if (!TextUtils.isEmpty(callSubject)) {
220       intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject);
221     }
222 
223     intent.putExtras(placeCallExtras);
224 
225     return intent;
226   }
227 
createCallSpecificAppData( CallInitiationType.Type callInitiationType)228   private static @NonNull CallSpecificAppData createCallSpecificAppData(
229       CallInitiationType.Type callInitiationType) {
230     CallSpecificAppData callSpecificAppData =
231         CallSpecificAppData.newBuilder().setCallInitiationType(callInitiationType).build();
232     return callSpecificAppData;
233   }
234 
increaseLightbringerCallButtonAppearInExpandedCallLogItemCount()235   public static void increaseLightbringerCallButtonAppearInExpandedCallLogItemCount() {
236     CallIntentBuilder.lightbringerButtonAppearInExpandedCallLogItemCount++;
237   }
238 
increaseLightbringerCallButtonAppearInCollapsedCallLogItemCount()239   public static void increaseLightbringerCallButtonAppearInCollapsedCallLogItemCount() {
240     CallIntentBuilder.lightbringerButtonAppearInCollapsedCallLogItemCount++;
241   }
242 
increaseLightbringerCallButtonAppearInSearchCount()243   public static void increaseLightbringerCallButtonAppearInSearchCount() {
244     CallIntentBuilder.lightbringerButtonAppearInSearchCount++;
245   }
246 
247   @VisibleForTesting
getLightbringerButtonAppearInExpandedCallLogItemCount()248   public static int getLightbringerButtonAppearInExpandedCallLogItemCount() {
249     return lightbringerButtonAppearInExpandedCallLogItemCount;
250   }
251 
252   @VisibleForTesting
getLightbringerButtonAppearInCollapsedCallLogItemCount()253   public static int getLightbringerButtonAppearInCollapsedCallLogItemCount() {
254     return lightbringerButtonAppearInCollapsedCallLogItemCount;
255   }
256 
257   @VisibleForTesting
getLightbringerButtonAppearInSearchCount()258   public static int getLightbringerButtonAppearInSearchCount() {
259     return lightbringerButtonAppearInSearchCount;
260   }
261 
262   @VisibleForTesting(otherwise = VisibleForTesting.NONE)
clearLightbringerCounts()263   public static void clearLightbringerCounts() {
264     lightbringerButtonAppearInCollapsedCallLogItemCount = 0;
265     lightbringerButtonAppearInExpandedCallLogItemCount = 0;
266     lightbringerButtonAppearInSearchCount = 0;
267   }
268 
269   @Override
describeContents()270   public int describeContents() {
271     return 0;
272   }
273 
274   @Override
writeToParcel(Parcel dest, int flags)275   public void writeToParcel(Parcel dest, int flags) {
276     dest.writeParcelable(uri, flags);
277     dest.writeByteArray(callSpecificAppData.toByteArray());
278     dest.writeParcelable(phoneAccountHandle, flags);
279     dest.writeInt(isVideoCall ? 1 : 0);
280     dest.writeInt(isDuoCall ? 1 : 0);
281     dest.writeString(callSubject);
282     dest.writeInt(allowAssistedDial ? 1 : 0);
283     dest.writeBundle(inCallUiIntentExtras);
284   }
285 
286   public static final Creator<CallIntentBuilder> CREATOR =
287       new Creator<CallIntentBuilder>() {
288         @Override
289         public CallIntentBuilder createFromParcel(Parcel source) {
290           return new CallIntentBuilder(source);
291         }
292 
293         @Override
294         public CallIntentBuilder[] newArray(int size) {
295           return new CallIntentBuilder[0];
296         }
297       };
298 }
299