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_gray) 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