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
17package com.android.messaging.ui.appsettings;
18
19import android.app.Activity;
20import android.app.Dialog;
21import android.app.ProgressDialog;
22import android.content.ContentValues;
23import android.content.Context;
24import android.database.Cursor;
25import android.database.sqlite.SQLiteDatabase;
26import android.os.AsyncTask;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.HandlerThread;
30import android.os.Looper;
31import android.os.Message;
32import android.os.UserManager;
33import android.preference.Preference;
34import android.preference.PreferenceFragment;
35import android.preference.PreferenceGroup;
36import android.preference.PreferenceScreen;
37import android.provider.Telephony;
38import android.support.v4.app.NavUtils;
39import android.view.Menu;
40import android.view.MenuInflater;
41import android.view.MenuItem;
42import android.widget.ListView;
43import android.widget.TextView;
44import android.widget.Toast;
45
46import com.android.messaging.R;
47import com.android.messaging.datamodel.data.ParticipantData;
48import com.android.messaging.sms.ApnDatabase;
49import com.android.messaging.sms.BugleApnSettingsLoader;
50import com.android.messaging.ui.BugleActionBarActivity;
51import com.android.messaging.ui.UIIntents;
52import com.android.messaging.util.OsUtil;
53import com.android.messaging.util.PhoneUtils;
54
55public class ApnSettingsActivity extends BugleActionBarActivity {
56    private static final int DIALOG_RESTORE_DEFAULTAPN = 1001;
57
58    @Override
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
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
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);
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
144        public void setSubId(final int subId) {
145            mSubId = subId;
146        }
147
148        @Override
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
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
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
201        public void onPause() {
202            super.onPause();
203
204            if (mUnavailable) {
205                return;
206            }
207        }
208
209        @Override
210        public void onDestroy() {
211            super.onDestroy();
212
213            if (mRestoreDefaultApnThread != null) {
214                mRestoreDefaultApnThread.quit();
215            }
216        }
217
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
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
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
301        private void addNewApn() {
302            startActivity(UIIntents.get().getApnEditorIntent(getActivity(), null, mSubId));
303        }
304
305        @Override
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
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" };
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
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
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
389            public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
390                super(looper);
391                this.mCachedRestoreApnUiHandler = restoreApnUiHandler;
392            }
393
394            @Override
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