1 /*
2  * Copyright (C) 2015 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.messaging.ui.appsettings;
18 
19 import android.app.Activity;
20 import android.app.Dialog;
21 import android.app.ProgressDialog;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.database.Cursor;
25 import android.database.sqlite.SQLiteDatabase;
26 import android.os.AsyncTask;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.HandlerThread;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.UserManager;
33 import android.preference.Preference;
34 import android.preference.PreferenceFragment;
35 import android.preference.PreferenceGroup;
36 import android.preference.PreferenceScreen;
37 import android.provider.Telephony;
38 import androidx.core.app.NavUtils;
39 import android.view.Menu;
40 import android.view.MenuInflater;
41 import android.view.MenuItem;
42 import android.widget.ListView;
43 import android.widget.TextView;
44 import android.widget.Toast;
45 
46 import com.android.messaging.R;
47 import com.android.messaging.datamodel.data.ParticipantData;
48 import com.android.messaging.sms.ApnDatabase;
49 import com.android.messaging.sms.BugleApnSettingsLoader;
50 import com.android.messaging.ui.BugleActionBarActivity;
51 import com.android.messaging.ui.UIIntents;
52 import com.android.messaging.util.OsUtil;
53 import com.android.messaging.util.PhoneUtils;
54 
55 public class ApnSettingsActivity extends BugleActionBarActivity {
56     private static final int DIALOG_RESTORE_DEFAULTAPN = 1001;
57 
58     @Override
onCreate(Bundle savedInstanceState)59     protected void onCreate(Bundle savedInstanceState) {
60         super.onCreate(savedInstanceState);
61 
62         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
63 
64         // Display the fragment as the main content.
65         final ApnSettingsFragment fragment = new ApnSettingsFragment();
66         fragment.setSubId(getIntent().getIntExtra(UIIntents.UI_INTENT_EXTRA_SUB_ID,
67                 ParticipantData.DEFAULT_SELF_SUB_ID));
68         getFragmentManager().beginTransaction()
69                 .replace(android.R.id.content, fragment)
70                 .commit();
71     }
72 
73     @Override
onOptionsItemSelected(final MenuItem item)74     public boolean onOptionsItemSelected(final MenuItem item) {
75         switch (item.getItemId()) {
76         case android.R.id.home:
77             NavUtils.navigateUpFromSameTask(this);
78             return true;
79         }
80         return super.onOptionsItemSelected(item);
81     }
82 
83     @Override
onCreateDialog(int id)84     protected Dialog onCreateDialog(int id) {
85         if (id == DIALOG_RESTORE_DEFAULTAPN) {
86             ProgressDialog dialog = new ProgressDialog(this);
87             dialog.setMessage(getResources().getString(R.string.restore_default_apn));
88             dialog.setCancelable(false);
89             return dialog;
90         }
91         return null;
92     }
93 
94     public static class ApnSettingsFragment extends PreferenceFragment implements
95             Preference.OnPreferenceChangeListener {
96         public static final String EXTRA_POSITION = "position";
97 
98         public static final String APN_ID = "apn_id";
99 
100         private static final String[] APN_PROJECTION = {
101             Telephony.Carriers._ID,         // 0
102             Telephony.Carriers.NAME,        // 1
103             Telephony.Carriers.APN,         // 2
104             Telephony.Carriers.TYPE         // 3
105         };
106         private static final int ID_INDEX    = 0;
107         private static final int NAME_INDEX  = 1;
108         private static final int APN_INDEX   = 2;
109         private static final int TYPES_INDEX = 3;
110 
111         private static final int MENU_NEW = Menu.FIRST;
112         private static final int MENU_RESTORE = Menu.FIRST + 1;
113 
114         private static final int EVENT_RESTORE_DEFAULTAPN_START = 1;
115         private static final int EVENT_RESTORE_DEFAULTAPN_COMPLETE = 2;
116 
117         private static boolean mRestoreDefaultApnMode;
118 
119         private RestoreApnUiHandler mRestoreApnUiHandler;
120         private RestoreApnProcessHandler mRestoreApnProcessHandler;
121         private HandlerThread mRestoreDefaultApnThread;
122 
123         private String mSelectedKey;
124 
125         private static final ContentValues sCurrentNullMap;
126         private static final ContentValues sCurrentSetMap;
127 
128         private UserManager mUm;
129 
130         private boolean mUnavailable;
131         private int mSubId;
132 
133         static {
134             sCurrentNullMap = new ContentValues(1);
135             sCurrentNullMap.putNull(Telephony.Carriers.CURRENT);
136 
137             sCurrentSetMap = new ContentValues(1);
sCurrentSetMap.put(Telephony.Carriers.CURRENT, "2")138             sCurrentSetMap.put(Telephony.Carriers.CURRENT, "2");    // 2 for user-selected APN,
139             // 1 for Bugle-selected APN
140         }
141 
142         private SQLiteDatabase mDatabase;
143 
setSubId(final int subId)144         public void setSubId(final int subId) {
145             mSubId = subId;
146         }
147 
148         @Override
onCreate(Bundle icicle)149         public void onCreate(Bundle icicle) {
150             super.onCreate(icicle);
151 
152             mDatabase = ApnDatabase.getApnDatabase().getWritableDatabase();
153 
154             if (OsUtil.isAtLeastL()) {
155                 mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
156                 if (!mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
157                     setHasOptionsMenu(true);
158                 }
159             } else {
160                 setHasOptionsMenu(true);
161             }
162         }
163 
164         @Override
onActivityCreated(Bundle savedInstanceState)165         public void onActivityCreated(Bundle savedInstanceState) {
166             super.onActivityCreated(savedInstanceState);
167 
168             final ListView lv = (ListView) getView().findViewById(android.R.id.list);
169             TextView empty = (TextView) getView().findViewById(android.R.id.empty);
170             if (empty != null) {
171                 empty.setText(R.string.apn_settings_not_available);
172                 lv.setEmptyView(empty);
173             }
174 
175             if (OsUtil.isAtLeastL() &&
176                     mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
177                 mUnavailable = true;
178                 setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getActivity()));
179                 return;
180             }
181 
182             addPreferencesFromResource(R.xml.apn_settings);
183 
184             lv.setItemsCanFocus(true);
185         }
186 
187         @Override
onResume()188         public void onResume() {
189             super.onResume();
190 
191             if (mUnavailable) {
192                 return;
193             }
194 
195             if (!mRestoreDefaultApnMode) {
196                 fillList();
197             }
198         }
199 
200         @Override
onPause()201         public void onPause() {
202             super.onPause();
203 
204             if (mUnavailable) {
205                 return;
206             }
207         }
208 
209         @Override
onDestroy()210         public void onDestroy() {
211             super.onDestroy();
212 
213             if (mRestoreDefaultApnThread != null) {
214                 mRestoreDefaultApnThread.quit();
215             }
216         }
217 
fillList()218         private void fillList() {
219             final String mccMnc = PhoneUtils.getMccMncString(PhoneUtils.get(mSubId).getMccMnc());
220 
221             new AsyncTask<Void, Void, Cursor>() {
222                 @Override
223                 protected Cursor doInBackground(Void... params) {
224                     String selection = Telephony.Carriers.NUMERIC + " =?";
225                     String[] selectionArgs = new String[]{ mccMnc };
226                     final Cursor cursor = mDatabase.query(ApnDatabase.APN_TABLE, APN_PROJECTION,
227                             selection, selectionArgs, null, null, null, null);
228                     return cursor;
229                 }
230 
231                 @Override
232                 protected void onPostExecute(Cursor cursor) {
233                     if (cursor != null) {
234                         try {
235                             PreferenceGroup apnList = (PreferenceGroup)
236                                     findPreference(getString(R.string.apn_list_pref_key));
237                             apnList.removeAll();
238 
239                             mSelectedKey = BugleApnSettingsLoader.getFirstTryApn(mDatabase, mccMnc);
240                             while (cursor.moveToNext()) {
241                                 String name = cursor.getString(NAME_INDEX);
242                                 String apn = cursor.getString(APN_INDEX);
243                                 String key = cursor.getString(ID_INDEX);
244                                 String type = cursor.getString(TYPES_INDEX);
245 
246                                 if (BugleApnSettingsLoader.isValidApnType(type,
247                                         BugleApnSettingsLoader.APN_TYPE_MMS)) {
248                                     ApnPreference pref = new ApnPreference(getActivity());
249                                     pref.setKey(key);
250                                     pref.setTitle(name);
251                                     pref.setSummary(apn);
252                                     pref.setPersistent(false);
253                                     pref.setOnPreferenceChangeListener(ApnSettingsFragment.this);
254                                     pref.setSelectable(true);
255 
256                                     // Turn on the radio button for the currently selected APN. If
257                                     // there is no selected APN, don't select an APN.
258                                     if ((mSelectedKey != null && mSelectedKey.equals(key))) {
259                                         pref.setChecked();
260                                     }
261                                     apnList.addPreference(pref);
262                                 }
263                             }
264                         } finally {
265                             cursor.close();
266                         }
267                     }
268                 }
269             }.execute((Void) null);
270         }
271 
272         @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)273         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
274             if (!mUnavailable) {
275                 menu.add(0, MENU_NEW, 0,
276                         getResources().getString(R.string.menu_new_apn))
277                         .setIcon(R.drawable.ic_add_white)
278                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
279                 menu.add(0, MENU_RESTORE, 0,
280                         getResources().getString(R.string.menu_restore_default_apn))
281                         .setIcon(android.R.drawable.ic_menu_upload);
282             }
283 
284             super.onCreateOptionsMenu(menu, inflater);
285         }
286 
287         @Override
onOptionsItemSelected(MenuItem item)288         public boolean onOptionsItemSelected(MenuItem item) {
289             switch (item.getItemId()) {
290                 case MENU_NEW:
291                     addNewApn();
292                     return true;
293 
294                 case MENU_RESTORE:
295                     restoreDefaultApn();
296                     return true;
297             }
298             return super.onOptionsItemSelected(item);
299         }
300 
addNewApn()301         private void addNewApn() {
302             startActivity(UIIntents.get().getApnEditorIntent(getActivity(), null, mSubId));
303         }
304 
305         @Override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)306         public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
307                 Preference preference) {
308             startActivity(
309                     UIIntents.get().getApnEditorIntent(getActivity(), preference.getKey(), mSubId));
310             return true;
311         }
312 
313         @Override
onPreferenceChange(Preference preference, Object newValue)314         public boolean onPreferenceChange(Preference preference, Object newValue) {
315             if (newValue instanceof String) {
316                 setSelectedApnKey((String) newValue);
317             }
318 
319             return true;
320         }
321 
322         // current=2 means user selected APN
323         private static final String UPDATE_SELECTION = Telephony.Carriers.CURRENT + " =?";
324         private static final String[] UPDATE_SELECTION_ARGS = new String[] { "2" };
setSelectedApnKey(final String key)325         private void setSelectedApnKey(final String key) {
326             mSelectedKey = key;
327 
328             // Make database changes not on the UI thread
329             new AsyncTask<Void, Void, Void>() {
330                 @Override
331                 protected Void doInBackground(Void... params) {
332                     // null out the previous "current=2" APN
333                     mDatabase.update(ApnDatabase.APN_TABLE, sCurrentNullMap,
334                             UPDATE_SELECTION, UPDATE_SELECTION_ARGS);
335 
336                     // set the new "current" APN (2)
337                     String selection = Telephony.Carriers._ID + " =?";
338                     String[] selectionArgs = new String[]{ key };
339 
340                     mDatabase.update(ApnDatabase.APN_TABLE, sCurrentSetMap,
341                             selection, selectionArgs);
342                     return null;
343                 }
344             }.execute((Void) null);
345         }
346 
restoreDefaultApn()347         private boolean restoreDefaultApn() {
348             getActivity().showDialog(DIALOG_RESTORE_DEFAULTAPN);
349             mRestoreDefaultApnMode = true;
350 
351             if (mRestoreApnUiHandler == null) {
352                 mRestoreApnUiHandler = new RestoreApnUiHandler();
353             }
354 
355             if (mRestoreApnProcessHandler == null ||
356                     mRestoreDefaultApnThread == null) {
357                 mRestoreDefaultApnThread = new HandlerThread(
358                         "Restore default APN Handler: Process Thread");
359                 mRestoreDefaultApnThread.start();
360                 mRestoreApnProcessHandler = new RestoreApnProcessHandler(
361                         mRestoreDefaultApnThread.getLooper(), mRestoreApnUiHandler);
362             }
363 
364             mRestoreApnProcessHandler.sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START);
365             return true;
366         }
367 
368         private class RestoreApnUiHandler extends Handler {
369             @Override
handleMessage(Message msg)370             public void handleMessage(Message msg) {
371                 switch (msg.what) {
372                     case EVENT_RESTORE_DEFAULTAPN_COMPLETE:
373                         fillList();
374                         getPreferenceScreen().setEnabled(true);
375                         mRestoreDefaultApnMode = false;
376                         final Activity activity = getActivity();
377                         activity.dismissDialog(DIALOG_RESTORE_DEFAULTAPN);
378                         Toast.makeText(activity, getResources().getString(
379                                         R.string.restore_default_apn_completed), Toast.LENGTH_LONG)
380                                             .show();
381                         break;
382                 }
383             }
384         }
385 
386         private class RestoreApnProcessHandler extends Handler {
387             private Handler mCachedRestoreApnUiHandler;
388 
RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler)389             public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
390                 super(looper);
391                 this.mCachedRestoreApnUiHandler = restoreApnUiHandler;
392             }
393 
394             @Override
handleMessage(Message msg)395             public void handleMessage(Message msg) {
396                 switch (msg.what) {
397                     case EVENT_RESTORE_DEFAULTAPN_START:
398                         ApnDatabase.forceBuildAndLoadApnTables();
399                         mCachedRestoreApnUiHandler.sendEmptyMessage(
400                                 EVENT_RESTORE_DEFAULTAPN_COMPLETE);
401                         break;
402                 }
403             }
404         }
405     }
406 }
407