blob: dc875cc75df69eb8227d18dfc162927ebeb36256 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
* Copyright (C) 2023 The LineageOS 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.dialer.callintent;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.dialer.common.Assert;
import com.android.dialer.util.CallUtil;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.InvalidProtocolBufferException;
/** Creates an intent to start a new outgoing call. */
@AutoValue
public abstract class CallIntent implements Parcelable {
abstract Uri number();
abstract CallSpecificAppData callSpecificAppData();
@Nullable
abstract PhoneAccountHandle phoneAccountHandle();
abstract boolean isVideoCall();
@Nullable
abstract String callSubject();
abstract boolean allowAssistedDial();
abstract ImmutableMap<String, String> stringInCallUiIntentExtras();
abstract ImmutableMap<String, Long> longInCallUiIntentExtras();
abstract ImmutableMap<String, String> stringPlaceCallExtras();
abstract ImmutableMap<String, Long> longPlaceCallExtras();
public static Builder builder() {
return new AutoValue_CallIntent.Builder().setIsVideoCall(false).setAllowAssistedDial(false);
}
public abstract Builder toBuilder();
/** Builder class for CallIntent info. */
@AutoValue.Builder
public abstract static class Builder {
public Builder setTelNumber(String number) {
return setNumber(CallUtil.getCallUri(Assert.isNotNull(number)));
}
public Builder setVoicemailNumber(@Nullable PhoneAccountHandle phoneAccountHandle) {
return setNumber(Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null))
.setPhoneAccountHandle(phoneAccountHandle);
}
public abstract Builder setNumber(@NonNull Uri number);
public Builder setCallInitiationType(CallInitiationType.Type callInitiationType) {
return setCallSpecificAppData(
CallSpecificAppData.newBuilder().setCallInitiationType(callInitiationType).build());
}
abstract CallSpecificAppData callSpecificAppData();
public abstract Builder setCallSpecificAppData(
@NonNull CallSpecificAppData callSpecificAppData);
public abstract Builder setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle);
public abstract Builder setIsVideoCall(boolean isVideoCall);
public abstract Builder setCallSubject(String callSubject);
public abstract Builder setAllowAssistedDial(boolean allowAssistedDial);
abstract ImmutableMap.Builder<String, String> stringInCallUiIntentExtrasBuilder();
abstract ImmutableMap.Builder<String, Long> longInCallUiIntentExtrasBuilder();
public Builder addInCallUiIntentExtra(String key, String value) {
stringInCallUiIntentExtrasBuilder().put(key, value);
return this;
}
public Builder addInCallUiIntentExtra(String key, Long value) {
longInCallUiIntentExtrasBuilder().put(key, value);
return this;
}
abstract ImmutableMap.Builder<String, String> stringPlaceCallExtrasBuilder();
abstract ImmutableMap.Builder<String, Long> longPlaceCallExtrasBuilder();
abstract CallIntent autoBuild();
public Intent build() {
CallSpecificAppData.Builder builder = CallSpecificAppData.newBuilder(callSpecificAppData());
setCallSpecificAppData(builder.build());
// Validate CallIntent.
CallIntent callIntent = autoBuild();
Assert.isNotNull(callIntent.number());
Assert.isNotNull(callIntent.callSpecificAppData());
Assert.checkArgument(
callIntent.callSpecificAppData().getCallInitiationType()
!= CallInitiationType.Type.UNKNOWN_INITIATION);
return autoBuild().newIntent();
}
}
// Creates the intent which can start a call
private Intent newIntent() {
Intent intent = new Intent(Intent.ACTION_CALL, number());
intent.putExtra(
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
isVideoCall() ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);
Bundle inCallUiIntentExtras = createInCallUiIntentExtras();
inCallUiIntentExtras.putLong(
Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, inCallUiIntentExtras);
if (phoneAccountHandle() != null) {
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle());
}
if (!TextUtils.isEmpty(callSubject())) {
intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject());
}
intent.putExtras(createPlaceCallExtras());
return intent;
}
private Bundle createInCallUiIntentExtras() {
Bundle bundle = new Bundle();
stringInCallUiIntentExtras().forEach(bundle::putString);
longInCallUiIntentExtras().forEach(bundle::putLong);
CallIntentParser.putCallSpecificAppData(bundle, callSpecificAppData());
return bundle;
}
private Bundle createPlaceCallExtras() {
Bundle bundle = new Bundle();
stringPlaceCallExtras().forEach(bundle::putString);
longPlaceCallExtras().forEach(bundle::putLong);
CallIntentParser.putCallSpecificAppData(bundle, callSpecificAppData());
return bundle;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(number(), flags);
dest.writeByteArray(callSpecificAppData().toByteArray());
dest.writeParcelable(phoneAccountHandle(), flags);
dest.writeInt(isVideoCall() ? 1 : 0);
dest.writeString(callSubject());
dest.writeInt(allowAssistedDial() ? 1 : 0);
Bundle stringInCallUiIntentExtrasBundle = new Bundle();
stringInCallUiIntentExtras().forEach(stringInCallUiIntentExtrasBundle::putString);
dest.writeBundle(stringInCallUiIntentExtrasBundle);
Bundle longInCallUiIntentExtrasBundle = new Bundle();
longInCallUiIntentExtras().forEach(longInCallUiIntentExtrasBundle::putLong);
dest.writeBundle(longInCallUiIntentExtrasBundle);
}
// @TODO(justinmcclain): Investigate deleting the parcelable logic and instead switching
// to using an internal proto for serialization.
public static final Creator<CallIntent> CREATOR =
new Creator<CallIntent>() {
@Override
public CallIntent createFromParcel(Parcel source) {
CallIntent.Builder callIntentBuilder = builder();
ClassLoader classLoader = CallIntent.class.getClassLoader();
callIntentBuilder.setNumber(source.readParcelable(classLoader, Uri.class));
CallSpecificAppData data;
try {
data = CallSpecificAppData.parseFrom(source.createByteArray());
} catch (InvalidProtocolBufferException e) {
data = CallSpecificAppData.getDefaultInstance();
}
callIntentBuilder
.setCallSpecificAppData(data)
.setPhoneAccountHandle(source.readParcelable(classLoader, PhoneAccountHandle.class))
.setIsVideoCall(source.readInt() != 0)
.setCallSubject(source.readString())
.setAllowAssistedDial(source.readInt() != 0);
Bundle stringInCallUiIntentExtrasBundle = source.readBundle(classLoader);
for (String key : stringInCallUiIntentExtrasBundle.keySet()) {
callIntentBuilder.addInCallUiIntentExtra(
key, stringInCallUiIntentExtrasBundle.getString(key));
}
Bundle longInCallUiIntentExtrasBundle = source.readBundle(classLoader);
for (String key : longInCallUiIntentExtrasBundle.keySet()) {
callIntentBuilder.addInCallUiIntentExtra(
key, longInCallUiIntentExtrasBundle.getLong(key));
}
return callIntentBuilder.autoBuild();
}
@Override
public CallIntent[] newArray(int size) {
return new CallIntent[0];
}
};
}