| /* |
| * Copyright (C) 2019 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.phone.settings; |
| |
| import android.app.Activity; |
| import android.content.ComponentName; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.os.RemoteException; |
| import android.telephony.SubscriptionManager; |
| import android.view.WindowManager; |
| import android.util.Log; |
| |
| import com.android.internal.telephony.IIntegerConsumer; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Trampolines a request to Settings to get the SMS subscription associated with an SmsManager |
| * operation. |
| * |
| * Since a Service can not start an Activity with |
| * {@link Activity#startActivityForResult(Intent, int)} and get a response (only Activities can |
| * handle the results), we have to "Trampoline" this operation by creating an empty Activity whose |
| * only job is to call startActivityForResult with the correct Intent and handle the result. |
| */ |
| // TODO: SmsManager should be constructed with an activity context so it can start as part of its |
| // task and fall back to PickSmsSubscriptionActivity being called in PhoneInterfaceManager if not |
| // called from an activity context. |
| public class PickSmsSubscriptionActivity extends Activity { |
| |
| private static final String LOG_TAG = "PickSmsSubActivity"; |
| |
| // Defined in Settings SimDialogActivity |
| private static final String RESULT_SUB_ID = "result_sub_id"; |
| public static final String DIALOG_TYPE_KEY = "dialog_type"; |
| public static final int SMS_PICK_FOR_MESSAGE = 4; |
| |
| private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName( |
| "com.android.settings", "com.android.settings.sim.SimDialogActivity"); |
| |
| private static final List<IIntegerConsumer> sSmsPickPendingList = new ArrayList<>(); |
| |
| private static final int REQUEST_GET_SMS_SUB_ID = 1; |
| |
| /** |
| * Adds a consumer to the list of pending results that will be accepted once the activity |
| * completes. |
| */ |
| public static void addPendingResult(IIntegerConsumer consumer) { |
| synchronized (sSmsPickPendingList) { |
| sSmsPickPendingList.add(consumer); |
| } |
| Log.i(LOG_TAG, "queue pending result, token: " + consumer); |
| } |
| |
| private static void sendResultAndClear(int resultId) { |
| // If the calling process died, just ignore callback. |
| synchronized (sSmsPickPendingList) { |
| for (IIntegerConsumer c : sSmsPickPendingList) { |
| try { |
| c.accept(resultId); |
| Log.i(LOG_TAG, "Result received, token: " + c + ", result: " + resultId); |
| } catch (RemoteException e) { |
| // The calling process died, skip this one. |
| } |
| } |
| sSmsPickPendingList.clear(); |
| } |
| } |
| |
| // Keep track if this activity has been stopped (i.e. user navigated away, power screen off,...) |
| // if so, treat it as the user navigating away and end the task if it is restarted without an |
| // onCreate/onNewIntent. |
| private boolean mPreviouslyStopped = false; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| getWindow().addSystemFlags( |
| android.view.WindowManager.LayoutParams |
| .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); |
| mPreviouslyStopped = false; |
| } |
| |
| @Override |
| protected void onNewIntent(Intent intent) { |
| mPreviouslyStopped = false; |
| } |
| |
| @Override |
| protected void onResume() { |
| super.onResume(); |
| // This is cause a little jank with the recents display, but there is no other way to handle |
| // the case where activity has stopped and we want to dismiss the dialog. We use the |
| // tag "excludeFromRecents", but in the cases where it is still shown, kill it in onResume. |
| if (mPreviouslyStopped) { |
| finishAndRemoveTask(); |
| } else { |
| launchSmsPicker(new Intent(getIntent())); |
| } |
| } |
| |
| @Override |
| protected void onStop() { |
| super.onStop(); |
| // User navigated away from dialog, send invalid sub id result. |
| mPreviouslyStopped = true; |
| sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID); |
| // triggers cancelled result for onActivityResult |
| finishActivity(REQUEST_GET_SMS_SUB_ID); |
| } |
| |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (requestCode == REQUEST_GET_SMS_SUB_ID) { |
| int result = data == null ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : |
| data.getIntExtra(RESULT_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); |
| if (resultCode == Activity.RESULT_OK) { |
| sendResultAndClear(result); |
| } else { |
| sendResultAndClear(SubscriptionManager.INVALID_SUBSCRIPTION_ID); |
| } |
| } |
| // This will be handled in onResume - we do not want to call this all the time here because |
| // we need to be able to restart if stopped and a new intent comes in via onNewIntent. |
| if (!mPreviouslyStopped) { |
| finishAndRemoveTask(); |
| } |
| } |
| |
| private void launchSmsPicker(Intent trampolineIntent) { |
| trampolineIntent.setComponent(SETTINGS_SUB_PICK_ACTIVITY); |
| // Remove this flag if it exists, we want the settings activity to be part of this task. |
| trampolineIntent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| startActivityForResult(trampolineIntent, REQUEST_GET_SMS_SUB_ID); |
| } |
| } |