Merge "Fix problem of multiple stacked copies of "Select SIM" dialog" into qt-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 76a8770..a370d35 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2692,10 +2692,12 @@
android:excludeFromRecents="true"
android:exported="false" />
- <activity android:name=".sim.SimDialogActivity"
- android:theme="@style/Theme.AlertDialog"
- android:label="@string/sim_settings_title"
- android:excludeFromRecents="true">
+ <activity
+ android:name=".sim.SimDialogActivity"
+ android:theme="@style/Theme.AlertDialog"
+ android:label="@string/sim_settings_title"
+ android:launchMode="singleTop"
+ android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
diff --git a/src/com/android/settings/sim/CallsSimListDialogFragment.java b/src/com/android/settings/sim/CallsSimListDialogFragment.java
new file mode 100644
index 0000000..bb5a003
--- /dev/null
+++ b/src/com/android/settings/sim/CallsSimListDialogFragment.java
@@ -0,0 +1,59 @@
+/*
+ * 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.settings.sim;
+
+import android.content.Context;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Specialized version of SimListDialogFragment that fetches a list of SIMs which support calls.
+ */
+public class CallsSimListDialogFragment extends SimListDialogFragment {
+ @Override
+ protected List<SubscriptionInfo> getCurrentSubscriptions() {
+ final Context context = getContext();
+ final SubscriptionManager subscriptionManager = context.getSystemService(
+ SubscriptionManager.class);
+ final TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+ final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+ final List<PhoneAccountHandle> phoneAccounts =
+ telecomManager.getCallCapablePhoneAccounts();
+ final List<SubscriptionInfo> result = new ArrayList<>();
+
+ if (phoneAccounts == null) {
+ return result;
+ }
+ for (PhoneAccountHandle handle : phoneAccounts) {
+ final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(handle);
+ final int subId = telephonyManager.getSubIdForPhoneAccount(phoneAccount);
+
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ continue;
+ }
+ result.add(subscriptionManager.getActiveSubscriptionInfo(subId));
+ }
+ return result;
+ }
+}
diff --git a/src/com/android/settings/sim/PreferredSimDialogFragment.java b/src/com/android/settings/sim/PreferredSimDialogFragment.java
new file mode 100644
index 0000000..5b81e62
--- /dev/null
+++ b/src/com/android/settings/sim/PreferredSimDialogFragment.java
@@ -0,0 +1,101 @@
+/*
+ * 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.settings.sim;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+
+/**
+ * Presents a dialog asking the user if they want to update all services to use a given "preferred"
+ * SIM. Typically this would be used in a case where a device goes from having multiple SIMs down to
+ * only one.
+ */
+public class PreferredSimDialogFragment extends SimDialogFragment implements
+ DialogInterface.OnClickListener {
+ private static final String TAG = "PreferredSimDialogFrag";
+
+ public static PreferredSimDialogFragment newInstance() {
+ final PreferredSimDialogFragment fragment = new PreferredSimDialogFragment();
+ final Bundle args = initArguments(SimDialogActivity.PREFERRED_PICK,
+ R.string.sim_preferred_title);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ final AlertDialog dialog = new AlertDialog.Builder(getContext())
+ .setTitle(getTitleResId())
+ .setPositiveButton(R.string.yes, this)
+ .setNegativeButton(R.string.no, null)
+ .create();
+ updateDialog(dialog);
+ return dialog;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int buttonClicked) {
+ if (buttonClicked != DialogInterface.BUTTON_POSITIVE) {
+ return;
+ }
+ final SimDialogActivity activity = (SimDialogActivity) getActivity();
+ final SubscriptionInfo info = getPreferredSubscription();
+ if (info != null) {
+ activity.onSubscriptionSelected(getDialogType(), info.getSubscriptionId());
+ }
+ }
+
+ public SubscriptionInfo getPreferredSubscription() {
+ final Activity activity = getActivity();
+ final int slotId = activity.getIntent().getIntExtra(SimDialogActivity.PREFERRED_SIM,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ return getSubscriptionManager().getActiveSubscriptionInfoForSimSlotIndex(slotId);
+ }
+
+ private void updateDialog(AlertDialog dialog) {
+ final SubscriptionInfo info = getPreferredSubscription();
+ if (info == null) {
+ dismiss();
+ return;
+ }
+ final String message =
+ getContext().getString(R.string.sim_preferred_message, info.getDisplayName());
+ dialog.setMessage(message);
+ }
+
+ @Override
+ public void updateDialog() {
+ updateDialog((AlertDialog) getDialog());
+ }
+
+ @VisibleForTesting
+ protected SubscriptionManager getSubscriptionManager() {
+ return getContext().getSystemService(SubscriptionManager.class);
+ }
+}
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index 487dace..d721efd 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -16,37 +16,30 @@
package com.android.settings.sim;
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
+import android.content.Intent;
import android.os.Bundle;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
-import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
+import android.util.Log;
import android.widget.Toast;
-import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
-import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-public class SimDialogActivity extends Activity {
+/**
+ * This activity provides singleton semantics per dialog type for showing various kinds of
+ * dialogs asking the user to make choices about which SIM to use for various services
+ * (calls, SMS, and data).
+ */
+public class SimDialogActivity extends FragmentActivity {
private static String TAG = "SimDialogActivity";
public static String PREFERRED_SIM = "preferred_sim";
@@ -60,276 +53,118 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final int dialogType = getIntent().getIntExtra(DIALOG_TYPE_KEY, INVALID_PICK);
+ showOrUpdateDialog();
+ }
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ showOrUpdateDialog();
+ }
+
+ private void showOrUpdateDialog() {
+ final int dialogType = getIntent().getIntExtra(DIALOG_TYPE_KEY, INVALID_PICK);
+ final String tag = Integer.toString(dialogType);
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
+
+ if (fragment == null) {
+ fragment = createFragment(dialogType);
+ fragment.show(fragmentManager, tag);
+ } else {
+ fragment.updateDialog();
+ }
+ }
+
+ private SimDialogFragment createFragment(int dialogType) {
+ switch(dialogType) {
+ case DATA_PICK:
+ return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
+ false /* includeAskEveryTime */);
+ case CALLS_PICK:
+ return CallsSimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_calls,
+ true /* includeAskEveryTime */);
+ case SMS_PICK:
+ return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
+ false /* includeAskEveryTime */);
+ case PREFERRED_PICK:
+ if (!getIntent().hasExtra(PREFERRED_SIM)) {
+ throw new IllegalArgumentException("Missing required extra " + PREFERRED_SIM);
+ }
+ return PreferredSimDialogFragment.newInstance();
+ default:
+ throw new IllegalArgumentException( "Invalid dialog type " + dialogType + " sent.");
+ }
+ }
+
+ public void onSubscriptionSelected(int dialogType, int subId) {
+ if (getSupportFragmentManager().findFragmentByTag(Integer.toString(dialogType)) == null) {
+ Log.w(TAG, "onSubscriptionSelected ignored because stored fragment was null");
+ return;
+ }
switch (dialogType) {
case DATA_PICK:
+ setDefaultDataSubId(subId);
+ break;
case CALLS_PICK:
+ setDefaultCallsSubId(subId);
+ break;
case SMS_PICK:
- createDialog(this, dialogType).show();
+ setDefaultSmsSubId(subId);
break;
case PREFERRED_PICK:
- displayPreferredDialog(getIntent().getIntExtra(PREFERRED_SIM, 0));
+ setPreferredSim(subId);
break;
default:
- throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent.");
- }
-
- }
-
- private void displayPreferredDialog(final int slotId) {
- final Resources res = getResources();
- final Context context = getApplicationContext();
- final SubscriptionInfo sir = SubscriptionManager.from(context)
- .getActiveSubscriptionInfoForSimSlotIndex(slotId);
-
- if (sir != null) {
- AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
- alertDialogBuilder.setTitle(R.string.sim_preferred_title);
- alertDialogBuilder.setMessage(res.getString(
- R.string.sim_preferred_message, sir.getDisplayName()));
-
- alertDialogBuilder.setPositiveButton(R.string.yes, new
- DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- final int subId = sir.getSubscriptionId();
- PhoneAccountHandle phoneAccountHandle =
- subscriptionIdToPhoneAccountHandle(subId);
- setDefaultDataSubId(context, subId);
- setDefaultSmsSubId(context, subId);
- setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
- finish();
- }
- });
- alertDialogBuilder.setNegativeButton(R.string.no, new
- DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,int id) {
- finish();
- }
- });
-
- alertDialogBuilder.create().show();
- } else {
- finish();
+ throw new IllegalArgumentException(
+ "Invalid dialog type " + dialogType + " sent.");
}
}
- private static void setDefaultDataSubId(final Context context, final int subId) {
- final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
- final TelephonyManager telephonyManager = TelephonyManager.from(context)
- .createForSubscriptionId(subId);
+ public void onFragmentDismissed(SimDialogFragment simDialogFragment) {
+ final List<Fragment> fragments = getSupportFragmentManager().getFragments();
+ if (fragments.size() == 1 && fragments.get(0) == simDialogFragment) {
+ finishAndRemoveTask();
+ }
+ }
+
+ private void setDefaultDataSubId(final int subId) {
+ final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
+ final TelephonyManager telephonyManager = getSystemService(
+ TelephonyManager.class).createForSubscriptionId(subId);
subscriptionManager.setDefaultDataSubId(subId);
telephonyManager.setDataEnabled(true);
- Toast.makeText(context, R.string.data_switch_started, Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show();
}
- private static void setDefaultSmsSubId(final Context context, final int subId) {
- final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
- subscriptionManager.setDefaultSmsSubId(subId);
- }
-
- private void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle phoneAccount) {
- final TelecomManager telecomManager = TelecomManager.from(this);
+ private void setDefaultCallsSubId(final int subId) {
+ final PhoneAccountHandle phoneAccount = subscriptionIdToPhoneAccountHandle(subId);
+ final TelecomManager telecomManager = getSystemService(TelecomManager.class);
telecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccount);
}
+ private void setDefaultSmsSubId(final int subId) {
+ final SubscriptionManager subscriptionManager = getSystemService(SubscriptionManager.class);
+ subscriptionManager.setDefaultSmsSubId(subId);
+ }
+
+ private void setPreferredSim(final int subId) {
+ setDefaultDataSubId(subId);
+ setDefaultSmsSubId(subId);
+ setDefaultCallsSubId(subId);
+ }
+
private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
- final TelecomManager telecomManager = TelecomManager.from(this);
- final TelephonyManager telephonyManager = TelephonyManager.from(this);
- final Iterator<PhoneAccountHandle> phoneAccounts =
- telecomManager.getCallCapablePhoneAccounts().listIterator();
+ final TelecomManager telecomManager = getSystemService(TelecomManager.class);
+ final TelephonyManager telephonyManager = getSystemService(TelephonyManager.class);
- while (phoneAccounts.hasNext()) {
- final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
- final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
+ for (PhoneAccountHandle handle : telecomManager.getCallCapablePhoneAccounts()) {
+ final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(handle);
if (subId == telephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
- return phoneAccountHandle;
+ return handle;
}
}
-
return null;
}
-
- public Dialog createDialog(final Context context, final int id) {
- final ArrayList<String> list = new ArrayList<String>();
- final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
- final List<SubscriptionInfo> subInfoList =
- subscriptionManager.getActiveSubscriptionInfoList(true);
- final int selectableSubInfoLength = subInfoList == null ? 0 : subInfoList.size();
-
- final DialogInterface.OnClickListener selectionListener =
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int value) {
-
- final SubscriptionInfo sir;
-
- switch (id) {
- case DATA_PICK:
- sir = subInfoList.get(value);
- setDefaultDataSubId(context, sir.getSubscriptionId());
- break;
- case CALLS_PICK:
- final TelecomManager telecomManager =
- TelecomManager.from(context);
- final List<PhoneAccountHandle> phoneAccountsList =
- telecomManager.getCallCapablePhoneAccounts();
- setUserSelectedOutgoingPhoneAccount(
- value < 1 ? null : phoneAccountsList.get(value - 1));
- break;
- case SMS_PICK:
- sir = subInfoList.get(value);
- setDefaultSmsSubId(context, sir.getSubscriptionId());
- break;
- default:
- throw new IllegalArgumentException("Invalid dialog type "
- + id + " in SIM dialog.");
- }
-
- finish();
- }
- };
-
- Dialog.OnKeyListener keyListener = new Dialog.OnKeyListener() {
- @Override
- public boolean onKey(DialogInterface arg0, int keyCode,
- KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- finish();
- }
- return true;
- }
- };
-
- ArrayList<SubscriptionInfo> callsSubInfoList = new ArrayList<SubscriptionInfo>();
- if (id == CALLS_PICK) {
- final TelecomManager telecomManager = TelecomManager.from(context);
- final TelephonyManager telephonyManager = TelephonyManager.from(context);
- final Iterator<PhoneAccountHandle> phoneAccounts =
- telecomManager.getCallCapablePhoneAccounts().listIterator();
-
- list.add(getResources().getString(R.string.sim_calls_ask_first_prefs_title));
- callsSubInfoList.add(null);
- while (phoneAccounts.hasNext()) {
- final PhoneAccount phoneAccount =
- telecomManager.getPhoneAccount(phoneAccounts.next());
- list.add((String)phoneAccount.getLabel());
- int subId = telephonyManager.getSubIdForPhoneAccount(phoneAccount);
- if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- final SubscriptionInfo sir = SubscriptionManager.from(context)
- .getActiveSubscriptionInfo(subId);
- callsSubInfoList.add(sir);
- } else {
- callsSubInfoList.add(null);
- }
- }
- } else {
- for (int i = 0; i < selectableSubInfoLength; ++i) {
- final SubscriptionInfo sir = subInfoList.get(i);
- CharSequence displayName = sir.getDisplayName();
- if (displayName == null) {
- displayName = "";
- }
- list.add(displayName.toString());
- }
- }
-
- String[] arr = list.toArray(new String[0]);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
-
- ListAdapter adapter = new SelectAccountListAdapter(
- id == CALLS_PICK ? callsSubInfoList : subInfoList,
- builder.getContext(),
- R.layout.select_account_list_item,
- arr, id);
-
- switch (id) {
- case DATA_PICK:
- builder.setTitle(R.string.select_sim_for_data);
- break;
- case CALLS_PICK:
- builder.setTitle(R.string.select_sim_for_calls);
- break;
- case SMS_PICK:
- builder.setTitle(R.string.select_sim_for_sms);
- break;
- default:
- throw new IllegalArgumentException("Invalid dialog type "
- + id + " in SIM dialog.");
- }
-
- Dialog dialog = builder.setAdapter(adapter, selectionListener).create();
- dialog.setOnKeyListener(keyListener);
-
- dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- finish();
- }
- });
-
- return dialog;
-
- }
-
- private class SelectAccountListAdapter extends ArrayAdapter<String> {
- private Context mContext;
- private int mResId;
- private int mDialogId;
- private final float OPACITY = 0.54f;
- private List<SubscriptionInfo> mSubInfoList;
-
- public SelectAccountListAdapter(List<SubscriptionInfo> subInfoList,
- Context context, int resource, String[] arr, int dialogId) {
- super(context, resource, arr);
- mContext = context;
- mResId = resource;
- mDialogId = dialogId;
- mSubInfoList = subInfoList;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater)
- mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View rowView;
- final ViewHolder holder;
-
- if (convertView == null) {
- // Cache views for faster scrolling
- rowView = inflater.inflate(mResId, null);
- holder = new ViewHolder();
- holder.title = (TextView) rowView.findViewById(R.id.title);
- holder.summary = (TextView) rowView.findViewById(R.id.summary);
- holder.icon = (ImageView) rowView.findViewById(R.id.icon);
- rowView.setTag(holder);
- } else {
- rowView = convertView;
- holder = (ViewHolder) rowView.getTag();
- }
-
- final SubscriptionInfo sir = mSubInfoList.get(position);
- if (sir == null) {
- holder.title.setText(getItem(position));
- holder.summary.setText("");
- holder.icon.setImageDrawable(getResources()
- .getDrawable(R.drawable.ic_live_help));
- holder.icon.setAlpha(OPACITY);
- } else {
- holder.title.setText(sir.getDisplayName());
- holder.summary.setText(sir.getNumber());
- holder.icon.setImageBitmap(sir.createIconBitmap(mContext));
- }
- return rowView;
- }
-
- private class ViewHolder {
- TextView title;
- TextView summary;
- ImageView icon;
- }
- }
}
diff --git a/src/com/android/settings/sim/SimDialogFragment.java b/src/com/android/settings/sim/SimDialogFragment.java
new file mode 100644
index 0000000..10815fd
--- /dev/null
+++ b/src/com/android/settings/sim/SimDialogFragment.java
@@ -0,0 +1,57 @@
+/*
+ * 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.settings.sim;
+
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+
+/** Common functionality for showing a dialog in SimDialogActivity. */
+public abstract class SimDialogFragment extends DialogFragment {
+ private static final String TAG = "SimDialogFragment";
+
+ private static final String KEY_TITLE_ID = "title_id";
+ private static final String KEY_DIALOG_TYPE = "dialog_type";
+
+ protected static Bundle initArguments(int dialogType, int titleResId) {
+ final Bundle args = new Bundle();
+ args.putInt(KEY_DIALOG_TYPE, dialogType);
+ args.putInt(KEY_TITLE_ID, titleResId);
+ return args;
+ }
+
+ public int getDialogType() {
+ return getArguments().getInt(KEY_DIALOG_TYPE);
+ }
+
+ public int getTitleResId() {
+ return getArguments().getInt(KEY_TITLE_ID);
+ }
+
+ @Override
+ public void onDismiss(@NonNull DialogInterface dialog) {
+ super.onDismiss(dialog);
+ final SimDialogActivity activity = (SimDialogActivity) getActivity();
+ if (activity != null && !activity.isFinishing()) {
+ activity.onFragmentDismissed(this);
+ }
+ }
+
+ public abstract void updateDialog();
+}
diff --git a/src/com/android/settings/sim/SimListDialogFragment.java b/src/com/android/settings/sim/SimListDialogFragment.java
new file mode 100644
index 0000000..f78c4e7
--- /dev/null
+++ b/src/com/android/settings/sim/SimListDialogFragment.java
@@ -0,0 +1,183 @@
+/*
+ * 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.settings.sim;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Shows a dialog consisting of a list of SIMs (aka subscriptions), possibly including an additional
+ * entry indicating "ask me every time".
+ */
+public class SimListDialogFragment extends SimDialogFragment implements
+ DialogInterface.OnClickListener {
+ protected static final String KEY_INCLUDE_ASK_EVERY_TIME = "include_ask_every_time";
+
+ protected SelectSubscriptionAdapter mAdapter;
+ @VisibleForTesting
+ List<SubscriptionInfo> mSubscriptions;
+
+ public static SimListDialogFragment newInstance(int dialogType, int titleResId,
+ boolean includeAskEveryTime) {
+ final SimListDialogFragment fragment = new SimListDialogFragment();
+ final Bundle args = initArguments(dialogType, titleResId);
+ args.putBoolean(KEY_INCLUDE_ASK_EVERY_TIME, includeAskEveryTime);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ mSubscriptions = new ArrayList<>();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(getTitleResId());
+
+ mAdapter = new SelectSubscriptionAdapter(builder.getContext(), mSubscriptions);
+
+ setAdapter(builder);
+ final Dialog dialog = builder.create();
+ updateDialog();
+ return dialog;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int selectionIndex) {
+ if (selectionIndex >= 0 && selectionIndex < mSubscriptions.size()) {
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ final SubscriptionInfo subscription = mSubscriptions.get(selectionIndex);
+ if (subscription != null) {
+ subId = subscription.getSubscriptionId();
+ }
+ final SimDialogActivity activity = (SimDialogActivity) getActivity();
+ activity.onSubscriptionSelected(getDialogType(), subId);
+ }
+ }
+
+ protected List<SubscriptionInfo> getCurrentSubscriptions() {
+ final SubscriptionManager manager = getContext().getSystemService(
+ SubscriptionManager.class);
+ return manager.getActiveSubscriptionInfoList(true);
+ }
+
+ @Override
+ public void updateDialog() {
+ List<SubscriptionInfo> currentSubscriptions = getCurrentSubscriptions();
+ if (currentSubscriptions == null) {
+ dismiss();
+ return;
+ }
+ if (getArguments().getBoolean(KEY_INCLUDE_ASK_EVERY_TIME)) {
+ final List<SubscriptionInfo> tmp = new ArrayList<>(currentSubscriptions.size() + 1);
+ tmp.add(null);
+ tmp.addAll(currentSubscriptions);
+ currentSubscriptions = tmp;
+ }
+ if (currentSubscriptions.equals(mSubscriptions)) {
+ return;
+ }
+ mSubscriptions.clear();
+ mSubscriptions.addAll(currentSubscriptions);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ @VisibleForTesting
+ void setAdapter(AlertDialog.Builder builder) {
+ builder.setAdapter(mAdapter, this);
+ }
+
+ private static class SelectSubscriptionAdapter extends BaseAdapter {
+ private Context mContext;
+ private LayoutInflater mInflater;
+ List<SubscriptionInfo> mSubscriptions;
+
+ public SelectSubscriptionAdapter(Context context, List<SubscriptionInfo> subscriptions) {
+ mSubscriptions = subscriptions;
+ mContext = context;
+ }
+
+ @Override
+ public int getCount() {
+ return mSubscriptions.size();
+ }
+
+ @Override
+ public SubscriptionInfo getItem(int position) {
+ return mSubscriptions.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ final SubscriptionInfo info = mSubscriptions.get(position);
+ if (info == null) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ return info.getSubscriptionId();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ if (mInflater == null) {
+ mInflater = LayoutInflater.from(parent.getContext());
+ }
+ convertView = mInflater.inflate(R.layout.select_account_list_item, parent, false);
+ }
+ final SubscriptionInfo sub = getItem(position);
+
+ final TextView title = convertView.findViewById(R.id.title);
+ final TextView summary = convertView.findViewById(R.id.summary);
+ final ImageView icon = convertView.findViewById(R.id.icon);
+
+ if (sub == null) {
+ title.setText(R.string.sim_calls_ask_first_prefs_title);
+ summary.setText("");
+ icon.setImageDrawable(mContext.getDrawable(R.drawable.ic_help));
+ icon.setImageTintList(
+ Utils.getColorAttr(mContext, android.R.attr.textColorSecondary));
+ } else {
+ title.setText(sub.getDisplayName());
+ summary.setText(sub.getNumber());
+ icon.setImageBitmap(sub.createIconBitmap(mContext));
+
+ }
+ return convertView;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/sim/PreferredSimDialogFragmentTest.java b/tests/robotests/src/com/android/settings/sim/PreferredSimDialogFragmentTest.java
new file mode 100644
index 0000000..0b85c37
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/sim/PreferredSimDialogFragmentTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.settings.sim;
+
+import static com.android.settings.sim.SimDialogActivity.PREFERRED_PICK;
+import static com.android.settings.sim.SimDialogActivity.PREFERRED_SIM;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.DialogInterface;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowAlertDialogCompat.class)
+public class PreferredSimDialogFragmentTest extends
+ SimDialogFragmentTestBase<PreferredSimDialogFragment> {
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ setDialogType(PREFERRED_PICK);
+ mFragment = spy(PreferredSimDialogFragment.newInstance());
+ doReturn(mSubscriptionManager).when(mFragment).getSubscriptionManager();
+ }
+
+ @Test
+ public void onCreateDialog_noSims_dismissed() {
+ when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(anyInt()))
+ .thenReturn(null);
+ mIntent.putExtra(PREFERRED_SIM, 0);
+ startDialog();
+ verify(mFragment).dismiss();
+ }
+
+ @Test
+ public void onCreateDialog_oneSimWrongSlotArgument_dismissed() {
+ when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(1)).thenReturn(null);
+ mIntent.putExtra(PREFERRED_SIM, 1);
+ startDialog();
+ verify(mFragment).dismiss();
+ }
+
+ @Test
+ public void onCreateDialog_twoSimsSelectFirst_correctMessage() {
+ mIntent.putExtra(PREFERRED_SIM, 0);
+
+ final AlertDialog alertDialog = startDialog();
+ final ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
+ final String message = (String) shadowDialog.getMessage();
+ assertThat(message).contains(SIM1_NAME);
+ assertThat(message).doesNotContain(SIM2_NAME);
+ }
+
+ @Test
+ public void onCreateDialog_twoSimsSelectSecond_correctMessage() {
+ mIntent.putExtra(PREFERRED_SIM, 1);
+
+ final AlertDialog alertDialog = startDialog();
+ final ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(alertDialog);
+ final String message = (String) shadowDialog.getMessage();
+ assertThat(message).contains(SIM2_NAME);
+ assertThat(message).doesNotContain(SIM1_NAME);
+ }
+
+ @Test
+ public void onClick_yesClicked_callsOnSubscriptionSelected() {
+ mIntent.putExtra(PREFERRED_SIM, 0);
+
+ final AlertDialog alertDialog = startDialog();
+
+ final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
+ doReturn(activity).when(mFragment).getActivity();
+ doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
+
+ mFragment.onClick(alertDialog, DialogInterface.BUTTON_POSITIVE);
+ verify(activity).onSubscriptionSelected(PREFERRED_PICK, SIM1_ID);
+ }
+
+ @Test
+ public void onClick_noClicked_doesNotCallOnSubscriptionSelected() {
+ mIntent.putExtra(PREFERRED_SIM, 0);
+
+ final AlertDialog alertDialog = startDialog();
+
+ final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
+ doReturn(activity).when(mFragment).getActivity();
+ doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
+
+ mFragment.onClick(alertDialog, DialogInterface.BUTTON_NEGATIVE);
+ verify(activity, never()).onSubscriptionSelected(anyInt(), anyInt());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/sim/SimDialogFragmentTestBase.java b/tests/robotests/src/com/android/settings/sim/SimDialogFragmentTestBase.java
new file mode 100644
index 0000000..904b831
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/sim/SimDialogFragmentTestBase.java
@@ -0,0 +1,76 @@
+/*
+ * 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.settings.sim;
+
+import static com.android.settings.sim.SimDialogActivity.DIALOG_TYPE_KEY;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+public abstract class SimDialogFragmentTestBase<T extends SimDialogFragment> {
+ protected static final int SIM1_ID = 111;
+ protected static final int SIM2_ID = 222;
+ protected static final String SIM1_NAME = "sim111";
+ protected static final String SIM2_NAME = "sim222";
+
+ @Mock
+ protected SubscriptionManager mSubscriptionManager;
+ @Mock
+ protected SubscriptionInfo mSim1;
+ @Mock
+ protected SubscriptionInfo mSim2;
+
+ protected T mFragment;
+ protected Intent mIntent;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mIntent = new Intent();
+
+ when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(0)).thenReturn(mSim1);
+ when(mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(1)).thenReturn(mSim2);
+
+ when(mSim1.getSubscriptionId()).thenReturn(SIM1_ID);
+ when(mSim1.getDisplayName()).thenReturn(SIM1_NAME);
+ when(mSim2.getSubscriptionId()).thenReturn(SIM2_ID);
+ when(mSim2.getDisplayName()).thenReturn(SIM2_NAME);
+ }
+
+ protected void setDialogType(int dialogType) {
+ mIntent.putExtra(DIALOG_TYPE_KEY, dialogType);
+ }
+
+ protected AlertDialog startDialog() {
+ final FragmentController controller = FragmentController.of(mFragment,
+ SimDialogActivity.class, mIntent);
+ controller.create(0 /* containerViewId */, null /* bundle */).start().visible();
+ return ShadowAlertDialogCompat.getLatestAlertDialog();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/sim/SimListDialogFragmentTest.java b/tests/robotests/src/com/android/settings/sim/SimListDialogFragmentTest.java
new file mode 100644
index 0000000..2b33ebe
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/sim/SimListDialogFragmentTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.settings.sim;
+
+import static com.android.settings.sim.SimDialogActivity.DATA_PICK;
+import static com.android.settings.sim.SimDialogActivity.SMS_PICK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.SubscriptionManager;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowAlertDialogCompat.class)
+public class SimListDialogFragmentTest extends SimDialogFragmentTestBase<SimListDialogFragment> {
+
+ @Test
+ public void onCreateDialog_noSubscriptions_dismissed() {
+ final int dialogType = DATA_PICK;
+ setDialogType(dialogType);
+ mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
+ false /* includeAskEveryTime */));
+ doReturn(null).when(mFragment).getCurrentSubscriptions();
+ startDialog();
+ verify(mFragment).dismiss();
+ }
+
+ @Test
+ public void onCreateDialog_twoSubscriptionsNoAskEveryTime_twoSubsForDisplay() {
+ final int dialogType = DATA_PICK;
+ setDialogType(dialogType);
+ mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_data,
+ false /* includeAskEveryTime */));
+ doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
+ // Avoid problems robolectric has with our real adapter.
+ doNothing().when(mFragment).setAdapter(any());
+ final AlertDialog alertDialog = startDialog();
+ assertThat(mFragment.mSubscriptions).hasSize(2);
+
+ final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
+ doReturn(activity).when(mFragment).getActivity();
+ doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
+
+ mFragment.onClick(alertDialog, 1);
+ verify(activity).onSubscriptionSelected(dialogType, SIM2_ID);
+ }
+
+ @Test
+ public void onCreateDialog_twoSubscriptionsAskEveryTime_threeSubsForDisplay() {
+ final int dialogType = SMS_PICK;
+ setDialogType(dialogType);
+ mFragment = spy(SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms,
+ true /* includeAskEveryTime */));
+ doReturn(Arrays.asList(mSim1, mSim2)).when(mFragment).getCurrentSubscriptions();
+ // Avoid problems robolectric has with our real adapter.
+ doNothing().when(mFragment).setAdapter(any());
+ final AlertDialog alertDialog = startDialog();
+ assertThat(mFragment.mSubscriptions).hasSize(3);
+ assertThat(mFragment.mSubscriptions.get(0)).isNull();
+
+ final SimDialogActivity activity = (SimDialogActivity) spy(mFragment.getActivity());
+ doReturn(activity).when(mFragment).getActivity();
+ doNothing().when(activity).onSubscriptionSelected(anyInt(), anyInt());
+
+ mFragment.onClick(alertDialog, 0);
+ verify(activity).onSubscriptionSelected(dialogType,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+}