1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.settings.sim;
18 
19 import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
20 
21 import android.app.Dialog;
22 import android.app.settings.SettingsEnums;
23 import android.content.Context;
24 import android.os.Bundle;
25 import android.telephony.SubscriptionInfo;
26 import android.telephony.SubscriptionManager;
27 import android.text.TextUtils;
28 import android.util.Log;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.widget.AdapterView;
33 import android.widget.BaseAdapter;
34 import android.widget.ListView;
35 import android.widget.TextView;
36 
37 import androidx.annotation.NonNull;
38 import androidx.annotation.Nullable;
39 import androidx.annotation.VisibleForTesting;
40 import androidx.appcompat.app.AlertDialog;
41 
42 import com.android.internal.telephony.flags.Flags;
43 import com.android.settings.R;
44 import com.android.settings.network.SubscriptionUtil;
45 
46 import java.util.ArrayList;
47 import java.util.List;
48 
49 /**
50  * Shows a dialog consisting of a list of SIMs (aka subscriptions), possibly including an additional
51  * entry indicating "ask me every time".
52  */
53 public class SimListDialogFragment extends SimDialogFragment {
54     private static final String TAG = "SimListDialogFragment";
55     protected static final String KEY_INCLUDE_ASK_EVERY_TIME = "include_ask_every_time";
56     protected static final String KEY_SHOW_CANCEL_ITEM = "show_cancel_item";
57 
58     protected SelectSubscriptionAdapter mAdapter;
59     @VisibleForTesting
60     @NonNull
61     List<SubscriptionInfo> mSubscriptions = new ArrayList<>();
62 
newInstance(int dialogType, int titleResId, boolean includeAskEveryTime, boolean isCancelItemShowed)63     public static SimListDialogFragment newInstance(int dialogType, int titleResId,
64             boolean includeAskEveryTime, boolean isCancelItemShowed) {
65         final SimListDialogFragment fragment = new SimListDialogFragment();
66         final Bundle args = initArguments(dialogType, titleResId);
67         args.putBoolean(KEY_INCLUDE_ASK_EVERY_TIME, includeAskEveryTime);
68         args.putBoolean(KEY_SHOW_CANCEL_ITEM, isCancelItemShowed);
69         fragment.setArguments(args);
70         return fragment;
71     }
72 
73     @NonNull
74     @Override
onCreateDialog(@ullable Bundle savedInstanceState)75     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
76         final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
77         View titleView = LayoutInflater.from(getContext()).inflate(
78                 R.layout.sim_confirm_dialog_title_multiple_enabled_profiles_supported, null);
79         TextView titleTextView = titleView.findViewById(R.id.title);
80         titleTextView.setText(getContext().getString(getTitleResId()));
81         builder.setCustomTitle(titleTextView);
82         mAdapter = new SelectSubscriptionAdapter(builder.getContext(), mSubscriptions);
83 
84         final AlertDialog dialog = builder.create();
85 
86         View content = LayoutInflater.from(getContext()).inflate(
87                 R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
88 
89         final ListView lvItems = content != null ? content.findViewById(R.id.carrier_list) : null;
90         if (lvItems != null) {
91             setAdapter(lvItems);
92             lvItems.setVisibility(View.VISIBLE);
93             lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
94                 @Override
95                 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
96                     onClick(position);
97                 }
98             });
99         }
100 
101         dialog.setView(content);
102         updateDialog();
103 
104         return dialog;
105     }
106 
107     /**
108      * If the user click the item at the list, then it sends the callback.
109      * @param selectionIndex the index of item in the list.
110      */
onClick(int selectionIndex)111     public void onClick(int selectionIndex) {
112         final SimDialogActivity activity = (SimDialogActivity) getActivity();
113         if (selectionIndex >= 0 && selectionIndex < mSubscriptions.size()) {
114             int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
115             final SubscriptionInfo subscription = mSubscriptions.get(selectionIndex);
116             if (subscription != null) {
117                 subId = subscription.getSubscriptionId();
118             }
119             activity.onSubscriptionSelected(getDialogType(), subId);
120         }
121         Log.d(TAG, "Start showing auto data switch dialog");
122         activity.showEnableAutoDataSwitchDialog();
123         if (getDialog() != null) getDialog().dismiss();
124     }
125 
getCurrentSubscriptions()126     protected List<SubscriptionInfo> getCurrentSubscriptions() {
127         final SubscriptionManager manager = getContext().getSystemService(
128                 SubscriptionManager.class).createForAllUserProfiles();
129         return manager.getActiveSubscriptionInfoList();
130     }
131 
132     @Override
updateDialog()133     public void updateDialog() {
134         Log.d(TAG, "Dialog updated, dismiss status: " + mWasDismissed);
135         if (mWasDismissed) {
136             return;
137         }
138 
139         List<SubscriptionInfo> currentSubscriptions = getCurrentSubscriptions();
140         if (currentSubscriptions == null) {
141             dismiss();
142             return;
143         }
144 
145         // Remove the provisioning or satellite eSIM from the subscription list.
146         currentSubscriptions.removeIf(info -> info.isEmbedded()
147             && (info.getProfileClass() == PROFILE_CLASS_PROVISIONING
148                 || (Flags.oemEnabledSatelliteFlag() && info.isOnlyNonTerrestrialNetwork())));
149 
150         boolean includeAskEveryTime = getArguments().getBoolean(KEY_INCLUDE_ASK_EVERY_TIME);
151         boolean isCancelItemShowed = getArguments().getBoolean(KEY_SHOW_CANCEL_ITEM);
152         if (includeAskEveryTime || isCancelItemShowed) {
153             int arraySize = currentSubscriptions.size()
154                     + (includeAskEveryTime ? 1 : 0)
155                     + (isCancelItemShowed ? 1 : 0);
156             final List<SubscriptionInfo> tmp = new ArrayList<>(arraySize);
157             if (includeAskEveryTime) {
158                 // add the value of 'AskEveryTime' item
159                 tmp.add(null);
160             }
161             tmp.addAll(currentSubscriptions);
162             if (isCancelItemShowed) {
163                 // add the value of 'Cancel' item
164                 tmp.add(null);
165             }
166             currentSubscriptions = tmp;
167         }
168         if (currentSubscriptions.equals(mSubscriptions)) {
169             return;
170         }
171 
172         mSubscriptions.clear();
173         mSubscriptions.addAll(currentSubscriptions);
174         mAdapter.notifyDataSetChanged();
175     }
176 
177     @VisibleForTesting
setAdapter(ListView lvItems)178     void setAdapter(ListView lvItems) {
179         lvItems.setAdapter(mAdapter);
180     }
181 
182     @Override
getMetricsCategory()183     public int getMetricsCategory() {
184         return SettingsEnums.DIALOG_SIM_LIST;
185     }
186 
187     private static class SelectSubscriptionAdapter extends BaseAdapter {
188         private Context mContext;
189         private LayoutInflater mInflater;
190         List<SubscriptionInfo> mSubscriptions;
191 
SelectSubscriptionAdapter(Context context, List<SubscriptionInfo> subscriptions)192         public SelectSubscriptionAdapter(Context context, List<SubscriptionInfo> subscriptions) {
193             mSubscriptions = subscriptions;
194             mContext = context;
195         }
196 
197         @Override
getCount()198         public int getCount() {
199             return mSubscriptions.size();
200         }
201 
202         @Override
getItem(int position)203         public SubscriptionInfo getItem(int position) {
204             return mSubscriptions.get(position);
205         }
206 
207         @Override
getItemId(int position)208         public long getItemId(int position) {
209             final SubscriptionInfo info = mSubscriptions.get(position);
210             if (info == null) {
211                 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
212             }
213             return info.getSubscriptionId();
214         }
215 
216         @Override
getView(int position, View convertView, ViewGroup parent)217         public View getView(int position, View convertView, ViewGroup parent) {
218             if (convertView == null) {
219                 if (mInflater == null) {
220                     mInflater = LayoutInflater.from(parent.getContext());
221                 }
222                 convertView = mInflater.inflate(R.layout.select_account_list_item, parent, false);
223             }
224             final SubscriptionInfo sub = getItem(position);
225 
226             final TextView title = convertView.findViewById(R.id.title);
227             final TextView summary = convertView.findViewById(R.id.summary);
228 
229             if (sub == null) {
230                 if (position == 0) {
231                     title.setText(R.string.sim_calls_ask_first_prefs_title);
232                 } else {
233                     title.setText(R.string.sim_action_cancel);
234                 }
235                 summary.setVisibility(View.GONE);
236             } else {
237                 title.setText(SubscriptionUtil.getUniqueSubscriptionDisplayName(sub, mContext));
238                 String phoneNumber = isMdnProvisioned(sub.getNumber()) ? sub.getNumber() : "";
239                 if (!TextUtils.isEmpty(phoneNumber)) {
240                     summary.setVisibility(View.VISIBLE);
241                     summary.setText(phoneNumber);
242                 } else {
243                     summary.setVisibility(View.GONE);
244                 }
245             }
246             return convertView;
247         }
248 
249         // An MDN is considered not provisioned if it's empty or all 0's
isMdnProvisioned(String mdn)250         private boolean isMdnProvisioned(String mdn) {
251             return !(TextUtils.isEmpty(mdn) || mdn.matches("[\\D0]+"));
252         }
253     }
254 }
255