From fe23fd3894e266095d8c882b8dd66065f7092d5d Mon Sep 17 00:00:00 2001 From: twyen Date: Tue, 7 Nov 2017 13:22:58 -0800 Subject: Make CallIntentBuilder parcelable Multi-SIM and several other new features requires additional user input (ie. select the phone account to call with) or processing (ie. rewrite phone number for assisted dialing) before the call is actually made. The plan is to use a chain of pre-call actions to complete the dialing infomation,with the CallIntentBuilder holding the intermediate steps. For example, a pre-call action can examine the CallIntentBuilder and figure that the PhoneAccountHandle cannot be inferred, and show a dialog for the user the select it. Only after pre-call actions are completed, the intent is built and sent to telecom. Since pre-call actions involves dialogs and the process can be interrupted, the CallIntentBuilder must be persistable so the state can be restored. This CL made it parcelable so it can be stored with onSaveInstanceState(). The AssistedDialingMediator in CallIntentBuilder is temporarily removed as it would be called using a pre-call action in a future CL. Bug: 64216442 Test: CallIntentBuilderTest PiperOrigin-RevId: 174902775 Change-Id: Icf2f8fae620868d1a2f5acf0d08ae0a6bec4fb76 --- java/com/android/dialer/app/DialtactsActivity.java | 8 +- .../android/dialer/app/calllog/IntentProvider.java | 4 +- .../dialer/calldetails/CallDetailsActivity.java | 7 +- .../dialer/callintent/CallIntentBuilder.java | 129 ++++++++++++++------- .../interactions/PhoneNumberInteraction.java | 7 +- .../searchfragment/list/NewSearchFragment.java | 8 +- 6 files changed, 93 insertions(+), 70 deletions(-) (limited to 'java/com/android/dialer') diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java index 12c191e53..089ffd389 100644 --- a/java/com/android/dialer/app/DialtactsActivity.java +++ b/java/com/android/dialer/app/DialtactsActivity.java @@ -44,7 +44,6 @@ import android.support.v4.app.ActivityCompat; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.telecom.PhoneAccount; -import android.telephony.TelephonyManager; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; @@ -90,7 +89,6 @@ import com.android.dialer.app.list.SmartDialSearchFragment; import com.android.dialer.app.settings.DialerSettingsActivity; import com.android.dialer.app.widget.ActionBarController; import com.android.dialer.app.widget.SearchEditTextLayout; -import com.android.dialer.assisteddialing.ConcreteCreator; import com.android.dialer.callcomposer.CallComposerActivity; import com.android.dialer.calldetails.CallDetailsActivity; import com.android.dialer.callintent.CallInitiationType; @@ -1521,11 +1519,7 @@ public class DialtactsActivity extends TransactionSafeActivity Intent intent = new CallIntentBuilder(phoneNumber, callSpecificAppData) .setIsVideoCall(isVideoCall) - .setAllowAssistedDial( - callSpecificAppData.getAllowAssistedDialing(), - ConcreteCreator.createNewAssistedDialingMediator( - getApplication().getSystemService(TelephonyManager.class), - getApplicationContext())) + .setAllowAssistedDial(callSpecificAppData.getAllowAssistedDialing()) .build(); DialerUtils.startActivityWithErrorToast(this, intent); diff --git a/java/com/android/dialer/app/calllog/IntentProvider.java b/java/com/android/dialer/app/calllog/IntentProvider.java index 36e62c379..5180651da 100644 --- a/java/com/android/dialer/app/calllog/IntentProvider.java +++ b/java/com/android/dialer/app/calllog/IntentProvider.java @@ -26,7 +26,6 @@ import android.telecom.PhoneAccountHandle; import android.telephony.TelephonyManager; import com.android.contacts.common.model.Contact; import com.android.contacts.common.model.ContactLoader; -import com.android.dialer.assisteddialing.ConcreteCreator; import com.android.dialer.calldetails.CallDetailsActivity; import com.android.dialer.calldetails.CallDetailsEntries; import com.android.dialer.callintent.CallInitiationType; @@ -67,8 +66,7 @@ public abstract class IntentProvider { @Override public Intent getIntent(Context context) { return new CallIntentBuilder(number, CallInitiationType.Type.CALL_LOG) - .setAllowAssistedDial( - true, ConcreteCreator.createNewAssistedDialingMediator(telephonyManager, context)) + .setAllowAssistedDial(true) .build(); } }; diff --git a/java/com/android/dialer/calldetails/CallDetailsActivity.java b/java/com/android/dialer/calldetails/CallDetailsActivity.java index 3a3c9118a..6ba31e624 100644 --- a/java/com/android/dialer/calldetails/CallDetailsActivity.java +++ b/java/com/android/dialer/calldetails/CallDetailsActivity.java @@ -30,10 +30,8 @@ import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar.OnMenuItemClickListener; -import android.telephony.TelephonyManager; import android.view.MenuItem; import android.widget.Toast; -import com.android.dialer.assisteddialing.ConcreteCreator; import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; @@ -243,10 +241,7 @@ public class CallDetailsActivity extends AppCompatActivity CallIntentBuilder callIntentBuilder = new CallIntentBuilder(phoneNumber + postDialDigits, CallInitiationType.Type.CALL_DETAILS); if (canSupportedAssistedDialing) { - callIntentBuilder.setAllowAssistedDial( - true, - ConcreteCreator.createNewAssistedDialingMediator( - getSystemService(TelephonyManager.class), this)); + callIntentBuilder.setAllowAssistedDial(true); } DialerUtils.startActivityWithErrorToast(this, callIntentBuilder.build()); diff --git a/java/com/android/dialer/callintent/CallIntentBuilder.java b/java/com/android/dialer/callintent/CallIntentBuilder.java index 25a5803dc..aabeabdc8 100644 --- a/java/com/android/dialer/callintent/CallIntentBuilder.java +++ b/java/com/android/dialer/callintent/CallIntentBuilder.java @@ -16,11 +16,11 @@ package com.android.dialer.callintent; -import android.annotation.TargetApi; import android.content.Intent; import android.net.Uri; -import android.os.Build; import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -30,23 +30,22 @@ import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telecom.VideoProfile; import android.text.TextUtils; -import com.android.dialer.assisteddialing.AssistedDialingMediator; -import com.android.dialer.assisteddialing.TransformationInfo; +import com.android.dialer.callintent.CallInitiationType.Type; import com.android.dialer.common.Assert; -import com.android.dialer.compat.telephony.TelephonyManagerCompat; import com.android.dialer.performancereport.PerformanceReport; import com.android.dialer.util.CallUtil; -import java.util.Optional; +import com.google.protobuf.InvalidProtocolBufferException; /** Creates an intent to start a new outgoing call. */ -public class CallIntentBuilder { - private final Uri uri; +public class CallIntentBuilder implements Parcelable { + private Uri uri; private final CallSpecificAppData callSpecificAppData; @Nullable private PhoneAccountHandle phoneAccountHandle; private boolean isVideoCall; private String callSubject; private boolean allowAssistedDial; - private AssistedDialingMediator assistedDialingMediator; + + private final Bundle outgoingCallExtras = new Bundle(); private static int lightbringerButtonAppearInExpandedCallLogItemCount = 0; private static int lightbringerButtonAppearInCollapsedCallLogItemCount = 0; @@ -96,6 +95,23 @@ public class CallIntentBuilder { this(CallUtil.getCallUri(Assert.isNotNull(number)), callInitiationType); } + public CallIntentBuilder(@NonNull Parcel parcel) { + ClassLoader classLoader = CallIntentBuilder.class.getClassLoader(); + uri = parcel.readParcelable(classLoader); + CallSpecificAppData data; + try { + data = CallSpecificAppData.parseFrom(parcel.createByteArray()); + } catch (InvalidProtocolBufferException e) { + data = createCallSpecificAppData(Type.UNKNOWN_INITIATION); + } + callSpecificAppData = data; + phoneAccountHandle = parcel.readParcelable(classLoader); + isVideoCall = parcel.readInt() != 0; + callSubject = parcel.readString(); + allowAssistedDial = parcel.readInt() != 0; + outgoingCallExtras.putAll(parcel.readBundle(classLoader)); + } + public static CallIntentBuilder forVoicemail( @Nullable PhoneAccountHandle phoneAccountHandle, CallInitiationType.Type callInitiationType) { return new CallIntentBuilder( @@ -103,6 +119,14 @@ public class CallIntentBuilder { .setPhoneAccountHandle(phoneAccountHandle); } + public void setUri(@NonNull Uri uri) { + this.uri = Assert.isNotNull(uri); + } + + public Uri getUri() { + return uri; + } + public CallSpecificAppData getCallSpecificAppData() { return callSpecificAppData; } @@ -112,40 +136,54 @@ public class CallIntentBuilder { return this; } + @Nullable + public PhoneAccountHandle getPhoneAccountHandle() { + return phoneAccountHandle; + } + public CallIntentBuilder setIsVideoCall(boolean isVideoCall) { this.isVideoCall = isVideoCall; return this; } - public CallIntentBuilder setAllowAssistedDial( - boolean allowAssistedDial, @NonNull AssistedDialingMediator assistedDialingMediator) { - this.assistedDialingMediator = Assert.isNotNull(assistedDialingMediator); + public boolean isVideoCall() { + return isVideoCall; + } + + public CallIntentBuilder setAllowAssistedDial(boolean allowAssistedDial) { this.allowAssistedDial = allowAssistedDial; return this; } + public boolean isAssistedDialAllowed() { + return allowAssistedDial; + } + public CallIntentBuilder setCallSubject(String callSubject) { this.callSubject = callSubject; return this; } + public String getCallSubject() { + return callSubject; + } + + public Bundle getOutgoingCallExtras() { + return outgoingCallExtras; + } + public Intent build() { Intent intent = new Intent(Intent.ACTION_CALL, uri); - Bundle extras = new Bundle(); - if (allowAssistedDial - && this.assistedDialingMediator != null - && this.assistedDialingMediator.isPlatformEligible()) { - intent = buildAssistedDialingParameters(intent, extras); - } intent.putExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, isVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY); - extras.putLong(Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime()); - CallIntentParser.putCallSpecificAppData(extras, callSpecificAppData); + outgoingCallExtras.putLong( + Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime()); + CallIntentParser.putCallSpecificAppData(outgoingCallExtras, callSpecificAppData); - intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); + intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, outgoingCallExtras); if (phoneAccountHandle != null) { intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); @@ -158,26 +196,6 @@ public class CallIntentBuilder { return intent; } - @SuppressWarnings("AndroidApiChecker") // Use of optional - @TargetApi(Build.VERSION_CODES.N) - private Intent buildAssistedDialingParameters(Intent intent, Bundle extras) { - extras.putBoolean(TelephonyManagerCompat.ALLOW_ASSISTED_DIAL, true); - String phoneNumber = - uri.getScheme().equals(PhoneAccount.SCHEME_TEL) ? uri.getSchemeSpecificPart() : ""; - Optional transformedNumber = - assistedDialingMediator.attemptAssistedDial(phoneNumber); - if (transformedNumber.isPresent()) { - Bundle assistedDialingExtras = transformedNumber.get().toBundle(); - extras.putBoolean(TelephonyManagerCompat.IS_ASSISTED_DIALED, true); - extras.putBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS, assistedDialingExtras); - intent = - new Intent( - Intent.ACTION_CALL, - CallUtil.getCallUri(Assert.isNotNull(transformedNumber.get().transformedNumber()))); - } - return intent; - } - private static @NonNull CallSpecificAppData createCallSpecificAppData( CallInitiationType.Type callInitiationType) { CallSpecificAppData callSpecificAppData = @@ -218,4 +236,33 @@ public class CallIntentBuilder { lightbringerButtonAppearInExpandedCallLogItemCount = 0; lightbringerButtonAppearInSearchCount = 0; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(uri, flags); + dest.writeByteArray(callSpecificAppData.toByteArray()); + dest.writeParcelable(phoneAccountHandle, flags); + dest.writeInt(isVideoCall ? 1 : 0); + dest.writeString(callSubject); + dest.writeInt(allowAssistedDial ? 1 : 0); + dest.writeBundle(outgoingCallExtras); + } + + public static final Creator CREATOR = + new Creator() { + @Override + public CallIntentBuilder createFromParcel(Parcel source) { + return new CallIntentBuilder(source); + } + + @Override + public CallIntentBuilder[] newArray(int size) { + return new CallIntentBuilder[0]; + } + }; } diff --git a/java/com/android/dialer/interactions/PhoneNumberInteraction.java b/java/com/android/dialer/interactions/PhoneNumberInteraction.java index ac744cce7..255f6c380 100644 --- a/java/com/android/dialer/interactions/PhoneNumberInteraction.java +++ b/java/com/android/dialer/interactions/PhoneNumberInteraction.java @@ -41,7 +41,6 @@ import android.provider.ContactsContract.RawContacts; import android.support.annotation.IntDef; import android.support.annotation.VisibleForTesting; import android.support.v4.app.ActivityCompat; -import android.telephony.TelephonyManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -53,7 +52,6 @@ import com.android.contacts.common.Collapser; import com.android.contacts.common.Collapser.Collapsible; import com.android.contacts.common.MoreContactUtils; import com.android.contacts.common.util.ContactDisplayUtils; -import com.android.dialer.assisteddialing.ConcreteCreator; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.callintent.CallIntentParser; @@ -187,10 +185,7 @@ public class PhoneNumberInteraction implements OnLoadCompleteListener { intent = new CallIntentBuilder(phoneNumber, callSpecificAppData) .setIsVideoCall(isVideoCall) - .setAllowAssistedDial( - callSpecificAppData.getAllowAssistedDialing(), - ConcreteCreator.createNewAssistedDialingMediator( - context.getSystemService(TelephonyManager.class), context)) + .setAllowAssistedDial(callSpecificAppData.getAllowAssistedDialing()) .build(); break; } diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java index cd42a7476..c0a6700eb 100644 --- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java +++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java @@ -32,7 +32,6 @@ import android.support.annotation.VisibleForTesting; import android.support.v13.app.FragmentCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -44,7 +43,6 @@ import android.widget.FrameLayout; import android.widget.FrameLayout.LayoutParams; import com.android.contacts.common.extensions.PhoneDirectoryExtenderAccessor; import com.android.dialer.animation.AnimUtils; -import com.android.dialer.assisteddialing.ConcreteCreator; import com.android.dialer.callcomposer.CallComposerActivity; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; @@ -488,11 +486,7 @@ public final class NewSearchFragment extends Fragment Intent intent = new CallIntentBuilder(phoneNumber, callSpecificAppData) .setIsVideoCall(isVideoCall) - .setAllowAssistedDial( - allowAssistedDial, - ConcreteCreator.createNewAssistedDialingMediator( - getContext().getSystemService(TelephonyManager.class), - getContext().getApplicationContext())) + .setAllowAssistedDial(allowAssistedDial) .build(); DialerUtils.startActivityWithErrorToast(getActivity(), intent); FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class).onCallPlaced(); -- cgit v1.2.3 From b0ed171b1c9d5b5d5e686b31be623c99ced15cf0 Mon Sep 17 00:00:00 2001 From: twyen Date: Tue, 7 Nov 2017 14:19:23 -0800 Subject: Implement PreCallCoordinator When the user makes a call, the PreCallCoordinator will get a list of actions to perform from PreCallActionProviderComponent and apply them to the pending CallIntentBuilder, before sending the built intent to telecom Bug: 64216442 Test: PreCallExecutorImplTest PiperOrigin-RevId: 174911537 Change-Id: Ide5ec431d4e6e7879fcdfdbbbd7ea5eed9963b2c --- .../binary/aosp/AospDialerRootComponent.java | 2 + .../basecomponent/BaseDialerRootComponent.java | 2 + .../google/GoogleStubDialerRootComponent.java | 2 + java/com/android/dialer/precall/PreCall.java | 44 ++++++ java/com/android/dialer/precall/PreCallAction.java | 46 +++++++ .../android/dialer/precall/PreCallComponent.java | 37 +++++ .../android/dialer/precall/PreCallCoordinator.java | 68 +++++++++ .../dialer/precall/impl/AndroidManifest.xml | 29 ++++ .../dialer/precall/impl/PreCallActivity.java | 58 ++++++++ .../precall/impl/PreCallCoordinatorImpl.java | 153 +++++++++++++++++++++ .../android/dialer/precall/impl/PreCallImpl.java | 46 +++++++ .../android/dialer/precall/impl/PreCallModule.java | 31 +++++ .../dialer/precall/impl/res/values/styles.xml | 29 ++++ .../dialer/precall/testing/TestPreCallModule.java | 59 ++++++++ packages.mk | 1 + 15 files changed, 607 insertions(+) create mode 100644 java/com/android/dialer/precall/PreCall.java create mode 100644 java/com/android/dialer/precall/PreCallAction.java create mode 100644 java/com/android/dialer/precall/PreCallComponent.java create mode 100644 java/com/android/dialer/precall/PreCallCoordinator.java create mode 100644 java/com/android/dialer/precall/impl/AndroidManifest.xml create mode 100644 java/com/android/dialer/precall/impl/PreCallActivity.java create mode 100644 java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java create mode 100644 java/com/android/dialer/precall/impl/PreCallImpl.java create mode 100644 java/com/android/dialer/precall/impl/PreCallModule.java create mode 100644 java/com/android/dialer/precall/impl/res/values/styles.xml create mode 100644 java/com/android/dialer/precall/testing/TestPreCallModule.java (limited to 'java/com/android/dialer') diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java index bd275df55..a02727ea8 100644 --- a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java +++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java @@ -25,6 +25,7 @@ import com.android.dialer.enrichedcall.stub.StubEnrichedCallModule; import com.android.dialer.inject.ContextModule; import com.android.dialer.phonelookup.PhoneLookupModule; import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule; +import com.android.dialer.precall.impl.PreCallModule; import com.android.dialer.simulator.impl.SimulatorModule; import com.android.dialer.storage.StorageModule; import com.android.dialer.strictmode.impl.SystemStrictModeModule; @@ -43,6 +44,7 @@ import javax.inject.Singleton; DialerExecutorModule.class, PhoneLookupModule.class, PhoneNumberGeoUtilModule.class, + PreCallModule.class, SharedPrefConfigProviderModule.class, SimulatorModule.class, StorageModule.class, diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java index bc5036ff1..387fca530 100644 --- a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java +++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java @@ -25,6 +25,7 @@ import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.main.MainComponent; import com.android.dialer.phonelookup.PhoneLookupComponent; import com.android.dialer.phonenumbergeoutil.PhoneNumberGeoUtilComponent; +import com.android.dialer.precall.PreCallComponent; import com.android.dialer.simulator.SimulatorComponent; import com.android.dialer.storage.StorageComponent; import com.android.dialer.strictmode.StrictModeComponent; @@ -48,6 +49,7 @@ public interface BaseDialerRootComponent MapsComponent.HasComponent, PhoneLookupComponent.HasComponent, PhoneNumberGeoUtilComponent.HasComponent, + PreCallComponent.HasComponent, SimulatorComponent.HasComponent, StorageComponent.HasComponent, StrictModeComponent.HasComponent, diff --git a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java index c47a69bbd..273d1e4fc 100644 --- a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java +++ b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java @@ -25,6 +25,7 @@ import com.android.dialer.enrichedcall.stub.StubEnrichedCallModule; import com.android.dialer.inject.ContextModule; import com.android.dialer.phonelookup.PhoneLookupModule; import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule; +import com.android.dialer.precall.impl.PreCallModule; import com.android.dialer.simulator.impl.SimulatorModule; import com.android.dialer.storage.StorageModule; import com.android.dialer.strictmode.impl.SystemStrictModeModule; @@ -47,6 +48,7 @@ import javax.inject.Singleton; DialerExecutorModule.class, PhoneLookupModule.class, // TODO(zachh): Module which uses APDL? PhoneNumberGeoUtilModule.class, + PreCallModule.class, SharedPrefConfigProviderModule.class, SimulatorModule.class, StorageModule.class, diff --git a/java/com/android/dialer/precall/PreCall.java b/java/com/android/dialer/precall/PreCall.java new file mode 100644 index 000000000..93fd8e9b6 --- /dev/null +++ b/java/com/android/dialer/precall/PreCall.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.MainThread; +import android.support.annotation.NonNull; +import com.android.dialer.callintent.CallIntentBuilder; +import com.google.common.collect.ImmutableList; + +/** Interface to prepare a {@link CallIntentBuilder} before placing the call with telecom. */ +public interface PreCall { + + /** + * @return a list of {@link PreCallAction} in execution order for the {@link PreCallCoordinator} + * to run. + */ + @NonNull + ImmutableList getActions(); + + /** + * @return a intent when started as activity, will perform the pre-call actions and then place a + * call. TODO(twyen): if all actions do not require an UI, return a intent that will place the + * call directly instead. + */ + @NonNull + @MainThread + Intent buildIntent(Context context, CallIntentBuilder builder); +} diff --git a/java/com/android/dialer/precall/PreCallAction.java b/java/com/android/dialer/precall/PreCallAction.java new file mode 100644 index 000000000..9434694a4 --- /dev/null +++ b/java/com/android/dialer/precall/PreCallAction.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall; + +import android.support.annotation.MainThread; +import com.android.dialer.callintent.CallIntentBuilder; + +/** + * An action to perform before the call is made. The action should inspect and modify the {@link + * CallIntentBuilder} to generate full information for the call. For example, showing a dialog to + * select the phone account on a multi-SIM device, ask if RTT should be enabled, or rewrite the + * number for roaming calls. + */ +public interface PreCallAction { + + /** + * Runs the action. Should block on the main thread until the action is finished. If the action is + * not instantaneous, {@link PreCallCoordinator#startPendingAction()} should be called to release + * the thread and continue later. + */ + @MainThread + void run(PreCallCoordinator coordinator); + + /** + * Called when the UI is being paused when a {@link PreCallCoordinator.PendingAction} is started, + * and the action is going to be discarded. If the action is showing a dialog the dialog should be + * dismissed. The action should not retain state, a new instance of the action will be re-run when + * the UI is resumed. + */ + @MainThread + void onDiscard(); +} diff --git a/java/com/android/dialer/precall/PreCallComponent.java b/java/com/android/dialer/precall/PreCallComponent.java new file mode 100644 index 000000000..00adde56d --- /dev/null +++ b/java/com/android/dialer/precall/PreCallComponent.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall; + +import android.content.Context; +import com.android.dialer.inject.HasRootComponent; +import dagger.Subcomponent; + +/** Daggaer component for {@link PreCall} */ +@Subcomponent +public abstract class PreCallComponent { + public abstract PreCall getPreCall(); + + public static PreCallComponent get(Context context) { + return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) + .preCallActionsComponent(); + } + + /** Used to refer to the root application component. */ + public interface HasComponent { + PreCallComponent preCallActionsComponent(); + } +} diff --git a/java/com/android/dialer/precall/PreCallCoordinator.java b/java/com/android/dialer/precall/PreCallCoordinator.java new file mode 100644 index 000000000..b5e9e8594 --- /dev/null +++ b/java/com/android/dialer/precall/PreCallCoordinator.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall; + +import android.app.Activity; +import android.support.annotation.MainThread; +import android.support.annotation.NonNull; +import com.android.dialer.callintent.CallIntentBuilder; + +/** + * Runs {@link PreCallAction} one by one to prepare a {@link + * com.android.dialer.callintent.CallIntentBuilder} for a call. + */ +public interface PreCallCoordinator { + + @NonNull + CallIntentBuilder getBuilder(); + + /** + * @return the activity to attach the UI to. Returns {@link null} if the coordinator is running on + * headless mode. TODO(twyen): implement headless mode. + */ + @NonNull + Activity getActivity(); + + /** + * Called by a {@link PreCallAction} to abort the call. For example, the user has dismissed the + * dialog and must start over. + */ + void abortCall(); + + /** + * Callback from a {@link PreCallAction} to signal the action started by {@link + * PreCallCoordinator#startPendingAction()} has finished. + */ + interface PendingAction { + + @MainThread + void finish(); + } + + /** + * Called by the current running {@link PreCallAction} to release the main thread and resume + * pre-call later. + * + * @return a {@link PendingAction} which {@link PendingAction#finish(boolean)} should be called to + * resume pre-call. For example the action shows a dialog to the user, startPendingAction() + * should be called as the action will not be finished immediately. When the dialog is + * completed, {@code finish()} is then called to continue the next step. + */ + @MainThread + @NonNull + PendingAction startPendingAction(); +} diff --git a/java/com/android/dialer/precall/impl/AndroidManifest.xml b/java/com/android/dialer/precall/impl/AndroidManifest.xml new file mode 100644 index 000000000..d3d0f538d --- /dev/null +++ b/java/com/android/dialer/precall/impl/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/java/com/android/dialer/precall/impl/PreCallActivity.java b/java/com/android/dialer/precall/impl/PreCallActivity.java new file mode 100644 index 000000000..938d31d5f --- /dev/null +++ b/java/com/android/dialer/precall/impl/PreCallActivity.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall.impl; + +import android.app.Activity; +import android.os.Bundle; +import android.support.annotation.Nullable; + +/** A transparent activity to host dialogs for {@link PreCallCoordinatorImpl} */ +public class PreCallActivity extends Activity { + + private PreCallCoordinatorImpl preCallCoordinator; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + preCallCoordinator = new PreCallCoordinatorImpl(this); + preCallCoordinator.onCreate(getIntent(), savedInstanceState); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + preCallCoordinator.onRestoreInstanceState(savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + preCallCoordinator.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + preCallCoordinator.onPause(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + preCallCoordinator.onSaveInstanceState(outState); + } +} diff --git a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java new file mode 100644 index 000000000..25083ef0b --- /dev/null +++ b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall.impl; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.dialer.precall.PreCallAction; +import com.android.dialer.precall.PreCallComponent; +import com.android.dialer.precall.PreCallCoordinator; +import com.android.dialer.telecom.TelecomUtil; +import com.google.common.collect.ImmutableList; + +/** + * Implements {@link PreCallCoordinator}. Listens to the life cycle of {@link PreCallActivity} to + * save/restore states. + */ +public class PreCallCoordinatorImpl implements PreCallCoordinator { + + private static final String SAVED_STATE_CURRENT_ACTION = "current_action"; + + static final String EXTRA_CALL_INTENT_BUILDER = "extra_call_intent_builder"; + + @NonNull private final Activity activity; + + private CallIntentBuilder builder; + private ImmutableList actions; + private int currentActionIndex = 0; + private PreCallAction currentAction; + private PendingAction pendingAction; + private boolean aborted = false; + + PreCallCoordinatorImpl(@NonNull Activity activity) { + this.activity = Assert.isNotNull(activity); + } + + void onCreate(Intent intent, @Nullable Bundle savedInstanceState) { + LogUtil.enterBlock("PreCallCoordinatorImpl.onCreate"); + if (savedInstanceState != null) { + currentActionIndex = savedInstanceState.getInt(SAVED_STATE_CURRENT_ACTION); + builder = Assert.isNotNull(savedInstanceState.getParcelable(EXTRA_CALL_INTENT_BUILDER)); + } else { + builder = Assert.isNotNull(intent.getParcelableExtra(EXTRA_CALL_INTENT_BUILDER)); + } + actions = PreCallComponent.get(activity).getPreCall().getActions(); + } + + void onRestoreInstanceState(Bundle savedInstanceState) { + currentActionIndex = savedInstanceState.getInt(SAVED_STATE_CURRENT_ACTION); + builder = savedInstanceState.getParcelable(EXTRA_CALL_INTENT_BUILDER); + } + + void onResume() { + runNextAction(); + } + + void onPause() { + if (currentAction != null) { + currentAction.onDiscard(); + } + currentAction = null; + } + + void onSaveInstanceState(Bundle outState) { + outState.putInt(SAVED_STATE_CURRENT_ACTION, currentActionIndex); + outState.putParcelable(EXTRA_CALL_INTENT_BUILDER, builder); + } + + private void runNextAction() { + LogUtil.enterBlock("PreCallCoordinatorImpl.runNextAction"); + Assert.checkArgument(currentAction == null); + if (currentActionIndex >= actions.size()) { + TelecomUtil.placeCall(activity, builder.build()); + activity.finish(); + return; + } + LogUtil.i("PreCallCoordinatorImpl.runNextAction", "running " + actions.get(currentActionIndex)); + currentAction = actions.get(currentActionIndex); + actions.get(currentActionIndex).run(this); + if (pendingAction == null) { + onActionFinished(); + } + } + + private void onActionFinished() { + LogUtil.enterBlock("PreCallCoordinatorImpl.onActionFinished"); + Assert.isNotNull(currentAction); + currentAction = null; + currentActionIndex++; + if (!aborted) { + runNextAction(); + } else { + activity.finish(); + } + } + + @Override + public void abortCall() { + Assert.checkState(currentAction != null); + aborted = true; + } + + @NonNull + @Override + public CallIntentBuilder getBuilder() { + return builder; + } + + @NonNull + @Override + public Activity getActivity() { + return activity; + } + + @Override + @NonNull + public PendingAction startPendingAction() { + Assert.isMainThread(); + Assert.isNotNull(currentAction); + Assert.checkArgument(pendingAction == null); + pendingAction = new PendingActionImpl(); + return pendingAction; + } + + private class PendingActionImpl implements PendingAction { + + @Override + public void finish() { + Assert.checkArgument(pendingAction == this); + pendingAction = null; + onActionFinished(); + } + } +} diff --git a/java/com/android/dialer/precall/impl/PreCallImpl.java b/java/com/android/dialer/precall/impl/PreCallImpl.java new file mode 100644 index 000000000..ac9750ef2 --- /dev/null +++ b/java/com/android/dialer/precall/impl/PreCallImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall.impl; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.precall.PreCall; +import com.android.dialer.precall.PreCallAction; +import com.google.common.collect.ImmutableList; +import javax.inject.Inject; + +/** Implementation of {@link PreCall} */ +public class PreCallImpl implements PreCall { + + @Inject + PreCallImpl() {} + + @Override + public ImmutableList getActions() { + return ImmutableList.of(); + } + + @NonNull + @Override + public Intent buildIntent(Context context, CallIntentBuilder builder) { + Intent intent = new Intent(context, PreCallActivity.class); + intent.putExtra(PreCallCoordinatorImpl.EXTRA_CALL_INTENT_BUILDER, builder); + return intent; + } +} diff --git a/java/com/android/dialer/precall/impl/PreCallModule.java b/java/com/android/dialer/precall/impl/PreCallModule.java new file mode 100644 index 000000000..608cd5a8f --- /dev/null +++ b/java/com/android/dialer/precall/impl/PreCallModule.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall.impl; + +import com.android.dialer.precall.PreCall; +import dagger.Binds; +import dagger.Module; +import javax.inject.Singleton; + +/** Dagger module for {@link PreCall}. */ +@Module +public abstract class PreCallModule { + + @Binds + @Singleton + public abstract PreCall bindPreCall(PreCallImpl simulator); +} diff --git a/java/com/android/dialer/precall/impl/res/values/styles.xml b/java/com/android/dialer/precall/impl/res/values/styles.xml new file mode 100644 index 000000000..dd41265a4 --- /dev/null +++ b/java/com/android/dialer/precall/impl/res/values/styles.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/java/com/android/dialer/precall/testing/TestPreCallModule.java b/java/com/android/dialer/precall/testing/TestPreCallModule.java new file mode 100644 index 000000000..b777de890 --- /dev/null +++ b/java/com/android/dialer/precall/testing/TestPreCallModule.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 The Android Open Source 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.precall.testing; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.precall.PreCall; +import com.android.dialer.precall.PreCallAction; +import com.google.common.collect.ImmutableList; +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; + +/** Provides test implementation of {@link PreCall} */ +@Module +public class TestPreCallModule { + private static PreCall preCall = new StubPreCall(); + + public static void setPreCall(PreCall preCall) { + TestPreCallModule.preCall = preCall; + } + + @Provides + @Singleton + public static PreCall providePreCall() { + return preCall; + } + + private static class StubPreCall implements PreCall { + + @NonNull + @Override + public ImmutableList getActions() { + return ImmutableList.of(); + } + + @NonNull + @Override + public Intent buildIntent(Context context, CallIntentBuilder builder) { + return builder.build(); + } + } +} diff --git a/packages.mk b/packages.mk index 3b9c2e254..06f8dae4e 100644 --- a/packages.mk +++ b/packages.mk @@ -39,6 +39,7 @@ LOCAL_AAPT_FLAGS := \ com.android.dialer.oem \ com.android.dialer.phonenumberutil \ com.android.dialer.postcall \ + com.android.dialer.precall.impl \ com.android.dialer.searchfragment.common \ com.android.dialer.searchfragment.cp2 \ com.android.dialer.searchfragment.list \ -- cgit v1.2.3