Fix problem of multiple stacked copies of "Select SIM" dialog

The SimDialogActivity is used to ask the user questions about which SIM
card to use for various services like calls, SMS, and data. In some
cases of SIM changes (eg when a SIM is added or removed), the telephony
stack sends a broadcast that SimSelectNotification listens for so it can
pop up a general "SIM cards changed" notification, and we additionally
want to bring up an interruptive dialog to ask the user a specific
question. This might happen for instance when we want to ask the user's
permission to turn on data on a SIM.

Recent DSDS changes in the telephony stack have meant that we
accidentally create several stacked copies of this dialog, because they
send several broadcast updates as information about SIMs asynchronously
changes. For instance, we might initially detect a SIM with a generic
name of "CARD 1", and shortly after discover the actual carrier name. So
what we really want is to put up the dialog, and update it as
information changes.

This CL makes SimDialogActivity use launchMode="singleTop" so that
additional copies of the activity won't be launched. Then it internally
enforces only showing one dialog per type of request (calls, SMS, data,
or preferred sim). If we get a request for a dialog that already exists,
we just update it instead of creating a new one for that type. So there
can still be a stack of more than one dialog, but each one will be
asking a different question.

This also refactors the monolithic, somewhat confusing code for showing
the various types of dialogs into a more clearly separated class
hierarchy, and switches to using DialogFragment for the dialog.

Fixes: 126596081
Test: manual (start with device in DSDS mode with 2 subs, remove SIM
card and re-insert it)

Change-Id: I0dbc41dc3b15015389823a24df10bbff08ec6615
This commit is contained in:
Antony Sargent
2019-04-12 15:08:16 -07:00
parent f078971771
commit a7dc277459
9 changed files with 805 additions and 269 deletions

View File

@@ -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;
}
}
}