Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7be879d9 authored by sqian's avatar sqian Committed by Shuo Qian
Browse files

User Interaction for Call Redirection service in Telecom

- Show UX if Telecom gets user-defined service's timeout.
When Telecom does not receive a response from user-defined service, Telecom
will cancel the outgoing call.

- Show UX if Telecom gets confirmFirst request from user-defined service's
timeout.
When Telecom's Call Redirection service receives the redirected call
from user's application, it may show an UX to confirm with users if they
want to process with the call.

Design doc: go/telecom-outflow-ux

Bug: 64959558
Test: Treehugger; Manually Telecom test app and see two types of UX
 sucessfully show up
Change-Id: Ida0d46967bcaab3e4b621a5e635a0ac04c717033
Merged-In: Ida0d46967bcaab3e4b621a5e635a0ac04c717033
(cherry picked from commit e976e681)
parent 6a969368
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -235,6 +235,8 @@
                <action android:name="com.android.server.telecom.ACTION_REJECT_FROM_NOTIFICATION" />
                <action android:name="com.android.server.telecom.PROCEED_WITH_CALL" />
                <action android:name="com.android.server.telecom.CANCEL_CALL" />
                <action android:name="com.android.server.telecom.PROCEED_WITH_REDIRECTED_CALL" />
                <action android:name="com.android.server.telecom.CANCEL_REDIRECTED_CALL" />
            </intent-filter>
        </receiver>

@@ -285,6 +287,22 @@
                android:process=":ui">
        </activity>

        <activity android:name=".ui.CallRedirectionConfirmDialogActivity"
                  android:configChanges="orientation|screenSize|keyboardHidden"
                  android:excludeFromRecents="true"
                  android:launchMode="singleInstance"
                  android:theme="@style/Theme.Telecomm.Transparent"
                  android:process=":ui">
        </activity>

        <activity android:name=".ui.CallRedirectionTimeoutDialogActivity"
                  android:configChanges="orientation|screenSize|keyboardHidden"
                  android:excludeFromRecents="true"
                  android:launchMode="singleInstance"
                  android:theme="@style/Theme.Telecomm.Transparent"
                  android:process=":ui">
        </activity>

        <activity android:name=".components.ChangeDefaultDialerDialog"
                  android:label="@string/change_default_dialer_dialog_title"
                  android:excludeFromRecents="true"
+6 −0
Original line number Diff line number Diff line
@@ -281,6 +281,12 @@
         ongoing call in the app "other_app". -->
    <string name="alert_outgoing_call">Placing this call will end your <xliff:g id="other_app">%1$s</xliff:g> call.</string>

    <!-- Alert dialog content used to ask the user to confirm if they want to place a new outgoing call redirected by the app "other_app". -->
    <string name="alert_redirect_outgoing_call">Allow <xliff:g id="other_app">%1$s</xliff:g> to place call using a different number or account.</string>

    <!-- Alert dialog content used to tell the user the call is canceled because no response from the call redirection app "other_app". -->
    <string name="alert_redirect_outgoing_call_timeout">Call can\'t be placed by <xliff:g id="other_app">%1$s</xliff:g>. Try using a different call redirecting app or contacting the developer for help.</string>

    <!-- The name of a feature available under the Call settings. -->
    <string name="phone_settings_call_blocking_txt">Call Blocking</string>
    <!-- Call type to be blocked. See the explanatory text "phone_settings_number_not_in_contact_summary_txt". -->
+89 −4
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Pair;
import android.widget.Toast;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.AsyncEmergencyContactNotifier;
@@ -81,8 +82,11 @@ import com.android.server.telecom.callfiltering.CallFilteringResult;
import com.android.server.telecom.callfiltering.CallScreeningServiceController;
import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
import com.android.server.telecom.callfiltering.IncomingCallFilter;
import com.android.server.telecom.callredirection.CallRedirectionProcessor;
import com.android.server.telecom.components.ErrorDialogActivity;
import com.android.server.telecom.settings.BlockedNumbersUtil;
import com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity;
import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity;
import com.android.server.telecom.ui.ConfirmCallDialogActivity;
import com.android.server.telecom.ui.IncomingCallNotifier;

@@ -242,6 +246,18 @@ public class CallsManager extends Call.ListenerBase
     * Used by {@link #startCallConfirmation}.
     */
    private Call mPendingCall;
    /**
     * Cached latest pending redirected call which requires user-intervention in order to be placed.
     * Used by {@link #onCallRedirectionComplete}.
     */
    private Call mPendingRedirectedOutgoingCall;
    /**
     * Cached latest pending redirected call information which require user-intervention in order
     * to be placed. Used by {@link #onCallRedirectionComplete}.
     */
    private final Map<String, Runnable> mPendingRedirectionOutgoingCallInfo =
            new ConcurrentHashMap<>();

    private CompletableFuture<Call> mPendingCallConfirm;
    private CompletableFuture<Pair<Call, PhoneAccountHandle>> mPendingAccountSelection;

@@ -1698,11 +1714,24 @@ public class CallsManager extends Call.ListenerBase
        boolean endEarly = false;
        String disconnectReason = "";

        String callRedirectionApp = mRoleManagerAdapter.getDefaultCallRedirectionApp();

        if (shouldCancelCall) {
            Log.w(this, "onCallRedirectionComplete: call is canceled");
            endEarly = true;
            disconnectReason = "Canceled from Call Redirection Service";
            // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_TIMEOUT
            // Show UX when user-defined call redirection service does not response; the UX
            // is not needed to show if the call is disconnected (e.g. by the user)
            if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_TIMEOUT)
                    && !call.isDisconnected()) {
                Intent timeoutIntent = new Intent(mContext,
                        CallRedirectionTimeoutDialogActivity.class);
                timeoutIntent.putExtra(
                        CallRedirectionTimeoutDialogActivity.EXTRA_REDIRECTION_APP_NAME,
                        mRoleManagerAdapter.getApplicationLabelForPackageName(callRedirectionApp));
                timeoutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mContext.startActivityAsUser(timeoutIntent, UserHandle.CURRENT);
            }
        } else if (handle == null) {
            Log.w(this, "onCallRedirectionComplete: handle is null");
            endEarly = true;
@@ -1732,11 +1761,67 @@ public class CallsManager extends Call.ListenerBase
            return;
        }

        // TODO show UI uiAction is CallRedirectionProcessor#UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM
        if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM)) {
            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMATION);
            mPendingRedirectedOutgoingCall = call;

            mPendingRedirectionOutgoingCallInfo.put(call.getId(),
                    new Runnable("CM.oCRC", mLock) {
                        @Override
                        public void loggedRun() {
                            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMED);
                            call.setTargetPhoneAccount(phoneAccountHandle);
                            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn,
                                    videoState);
                        }
                    });

            Log.i(this, "onCallRedirectionComplete: UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM "
                    + "callId=%s, callRedirectionAppName=%s",
                    call.getId(), callRedirectionApp);

            Intent confirmIntent = new Intent(mContext,
                    CallRedirectionConfirmDialogActivity.class);
            confirmIntent.putExtra(
                    CallRedirectionConfirmDialogActivity.EXTRA_REDIRECTION_OUTGOING_CALL_ID,
                    call.getId());
            confirmIntent.putExtra(CallRedirectionConfirmDialogActivity.EXTRA_REDIRECTION_APP_NAME,
                    mRoleManagerAdapter.getApplicationLabelForPackageName(callRedirectionApp));
            confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT);
        } else {
            call.setTargetPhoneAccount(phoneAccountHandle);
            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
        }
    }

    public void placeRedirectedOutgoingCallAfterUserInteraction(String callId) {
        Log.i(this, "placeRedirectedOutgoingCallAfterUserInteraction for Call ID %s", callId);
        if (mPendingRedirectedOutgoingCall != null && mPendingRedirectedOutgoingCall.getId()
                .equals(callId)) {
            mHandler.post(mPendingRedirectionOutgoingCallInfo.get(callId).prepare());
            mPendingRedirectedOutgoingCall = null;
            mPendingRedirectionOutgoingCallInfo.remove(callId);
        } else {
            Log.w(this, "placeRedirectedOutgoingCallAfterUserInteraction for non-matched Call ID "
                    + " %s with handle %s and phoneAccountHandle %s", callId);
        }
    }

    public void cancelRedirectedOutgoingCallAfterUserInteraction(String callId) {
        Log.i(this, "cancelRedirectedOutgoingCallAfterUserInteraction for Call ID %s", callId);
        if (mPendingRedirectedOutgoingCall != null && mPendingRedirectedOutgoingCall.getId()
                .equals(callId)) {
            Log.addEvent(mPendingRedirectedOutgoingCall,
                    LogUtils.Events.REDIRECTION_USER_CANCELLED);
            mPendingRedirectedOutgoingCall.disconnect("User canceled the redirected call.");
            mPendingRedirectedOutgoingCall = null;
            mPendingRedirectionOutgoingCallInfo.remove(callId);
        } else {
            Log.w(this, "cancelRedirectedOutgoingCallAfterUserInteraction for non-matched Call"
                    + " ID ", callId);
        }
    }

    /**
     * Attempts to issue/connect the specified call.
+3 −0
Original line number Diff line number Diff line
@@ -149,6 +149,9 @@ public class LogUtils {
        public static final String REDIRECTION_COMPLETED_CARRIER = "REDIRECTION_COMPLETED_CARRIER";
        public static final String REDIRECTION_TIMED_OUT_USER = "REDIRECTION_TIMED_OUT_USER";
        public static final String REDIRECTION_TIMED_OUT_CARRIER = "REDIRECTION_TIMED_OUT_CARRIER";
        public static final String REDIRECTION_USER_CONFIRMATION = "REDIRECTION_USER_CONFIRMATION";
        public static final String REDIRECTION_USER_CONFIRMED = "REDIRECTION_USER_CONFIRMED";
        public static final String REDIRECTION_USER_CANCELLED = "REDIRECTION_USER_CANCELLED";

        public static class Timings {
            public static final String ACCEPT_TIMING = "accept";
+7 −0
Original line number Diff line number Diff line
@@ -87,4 +87,11 @@ public interface RoleManagerAdapter {
     * @param currentUserHandle The new user handle.
     */
    void setCurrentUserHandle(UserHandle currentUserHandle);

    /**
     * Returns the application label that corresponds to the given package name.
     * @param packageName A valid package name.
     * @return Application label for the given package name, or null if not found.
     */
    String getApplicationLabelForPackageName(String packageName);
}
Loading