1 /*
2  * Copyright (C) 2009 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;
18 
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.app.backup.IBackupManager;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.os.Bundle;
27 import android.os.Process;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.os.UserHandle;
31 import android.os.UserManager;
32 import android.preference.Preference;
33 import android.preference.PreferenceCategory;
34 import android.preference.Preference.OnPreferenceChangeListener;
35 import android.preference.PreferenceScreen;
36 import android.preference.SwitchPreference;
37 import android.provider.SearchIndexableResource;
38 import android.provider.Settings;
39 import android.util.Log;
40 
41 import com.android.settings.search.BaseSearchIndexProvider;
42 import com.android.settings.search.Indexable;
43 import com.android.settings.search.Indexable.SearchIndexProvider;
44 
45 import java.util.ArrayList;
46 import java.util.List;
47 
48 /**
49  * Gesture lock pattern settings.
50  */
51 public class PrivacySettings extends SettingsPreferenceFragment implements
52         DialogInterface.OnClickListener, Indexable {
53 
54     // Vendor specific
55     private static final String GSETTINGS_PROVIDER = "com.google.settings";
56     private static final String BACKUP_CATEGORY = "backup_category";
57     private static final String BACKUP_DATA = "backup_data";
58     private static final String AUTO_RESTORE = "auto_restore";
59     private static final String CONFIGURE_ACCOUNT = "configure_account";
60     private static final String BACKUP_INACTIVE = "backup_inactive";
61     private static final String PERSONAL_DATA_CATEGORY = "personal_data_category";
62     private static final String TAG = "PrivacySettings";
63     private IBackupManager mBackupManager;
64     private SwitchPreference mBackup;
65     private SwitchPreference mAutoRestore;
66     private Dialog mConfirmDialog;
67     private PreferenceScreen mConfigure;
68     private boolean mEnabled;
69 
70     private static final int DIALOG_ERASE_BACKUP = 2;
71     private int mDialogType;
72 
73     @Override
onCreate(Bundle savedInstanceState)74     public void onCreate(Bundle savedInstanceState) {
75         super.onCreate(savedInstanceState);
76         // Don't allow any access if this is a secondary user
77         mEnabled = Process.myUserHandle().isOwner();
78         if (!mEnabled) {
79             return;
80         }
81 
82         addPreferencesFromResource(R.xml.privacy_settings);
83         final PreferenceScreen screen = getPreferenceScreen();
84         mBackupManager = IBackupManager.Stub.asInterface(
85                 ServiceManager.getService(Context.BACKUP_SERVICE));
86 
87         mBackup = (SwitchPreference) screen.findPreference(BACKUP_DATA);
88         mBackup.setOnPreferenceChangeListener(preferenceChangeListener);
89 
90         mAutoRestore = (SwitchPreference) screen.findPreference(AUTO_RESTORE);
91         mAutoRestore.setOnPreferenceChangeListener(preferenceChangeListener);
92 
93         mConfigure = (PreferenceScreen) screen.findPreference(CONFIGURE_ACCOUNT);
94 
95         ArrayList<String> keysToRemove = getNonVisibleKeys(getActivity());
96         final int screenPreferenceCount = screen.getPreferenceCount();
97         for (int i = screenPreferenceCount - 1; i >= 0; --i) {
98             Preference preference = screen.getPreference(i);
99             if (keysToRemove.contains(preference.getKey())) {
100                 screen.removePreference(preference);
101             }
102         }
103         PreferenceCategory backupCategory = (PreferenceCategory) findPreference(BACKUP_CATEGORY);
104         if (backupCategory != null) {
105             final int backupCategoryPreferenceCount = backupCategory.getPreferenceCount();
106             for (int i = backupCategoryPreferenceCount - 1; i >= 0; --i) {
107                 Preference preference = backupCategory.getPreference(i);
108                 if (keysToRemove.contains(preference.getKey())) {
109                     backupCategory.removePreference(preference);
110                 }
111             }
112         }
113         updateToggles();
114     }
115 
116     @Override
onResume()117     public void onResume() {
118         super.onResume();
119 
120         // Refresh UI
121         if (mEnabled) {
122             updateToggles();
123         }
124     }
125 
126     @Override
onStop()127     public void onStop() {
128         if (mConfirmDialog != null && mConfirmDialog.isShowing()) {
129             mConfirmDialog.dismiss();
130         }
131         mConfirmDialog = null;
132         mDialogType = 0;
133         super.onStop();
134     }
135 
136     private OnPreferenceChangeListener preferenceChangeListener = new OnPreferenceChangeListener() {
137         @Override
138         public boolean onPreferenceChange(Preference preference, Object newValue) {
139             if (!(preference instanceof SwitchPreference)) {
140                 return true;
141             }
142             boolean nextValue = (Boolean) newValue;
143             boolean result = false;
144             if (preference == mBackup) {
145                 if (nextValue == false) {
146                     // Don't change Switch status until user makes choice in dialog
147                     // so return false here.
148                     showEraseBackupDialog();
149                 } else {
150                     setBackupEnabled(true);
151                     result = true;
152                 }
153             } else if (preference == mAutoRestore) {
154                 try {
155                     mBackupManager.setAutoRestore(nextValue);
156                     result = true;
157                 } catch (RemoteException e) {
158                     mAutoRestore.setChecked(!nextValue);
159                 }
160             }
161             return result;
162         }
163     };
164 
showEraseBackupDialog()165     private void showEraseBackupDialog() {
166         mDialogType = DIALOG_ERASE_BACKUP;
167         CharSequence msg = getResources().getText(R.string.backup_erase_dialog_message);
168         // TODO: DialogFragment?
169         mConfirmDialog = new AlertDialog.Builder(getActivity()).setMessage(msg)
170                 .setTitle(R.string.backup_erase_dialog_title)
171                 .setPositiveButton(android.R.string.ok, this)
172                 .setNegativeButton(android.R.string.cancel, this)
173                 .show();
174     }
175 
176     /*
177      * Creates toggles for each available location provider
178      */
updateToggles()179     private void updateToggles() {
180         ContentResolver res = getContentResolver();
181 
182         boolean backupEnabled = false;
183         Intent configIntent = null;
184         String configSummary = null;
185         try {
186             backupEnabled = mBackupManager.isBackupEnabled();
187             String transport = mBackupManager.getCurrentTransport();
188             configIntent = mBackupManager.getConfigurationIntent(transport);
189             configSummary = mBackupManager.getDestinationString(transport);
190         } catch (RemoteException e) {
191             // leave it 'false' and disable the UI; there's no backup manager
192             mBackup.setEnabled(false);
193         }
194         mBackup.setChecked(backupEnabled);
195 
196         mAutoRestore.setChecked(Settings.Secure.getInt(res,
197                 Settings.Secure.BACKUP_AUTO_RESTORE, 1) == 1);
198         mAutoRestore.setEnabled(backupEnabled);
199 
200         final boolean configureEnabled = (configIntent != null) && backupEnabled;
201         mConfigure.setEnabled(configureEnabled);
202         mConfigure.setIntent(configIntent);
203         setConfigureSummary(configSummary);
204     }
205 
setConfigureSummary(String summary)206     private void setConfigureSummary(String summary) {
207         if (summary != null) {
208             mConfigure.setSummary(summary);
209         } else {
210             mConfigure.setSummary(R.string.backup_configure_account_default_summary);
211         }
212     }
213 
updateConfigureSummary()214     private void updateConfigureSummary() {
215         try {
216             String transport = mBackupManager.getCurrentTransport();
217             String summary = mBackupManager.getDestinationString(transport);
218             setConfigureSummary(summary);
219         } catch (RemoteException e) {
220             // Not much we can do here
221         }
222     }
223 
224     @Override
onClick(DialogInterface dialog, int which)225     public void onClick(DialogInterface dialog, int which) {
226         // Dialog is triggered before Switch status change, that means marking the Switch to
227         // true in showEraseBackupDialog() method will be override by following status change.
228         // So we do manual switching here due to users' response.
229         if (mDialogType == DIALOG_ERASE_BACKUP) {
230             // Accept turning off backup
231             if (which == DialogInterface.BUTTON_POSITIVE) {
232                 setBackupEnabled(false);
233             } else if (which == DialogInterface.BUTTON_NEGATIVE) {
234                 // Reject turning off backup
235                 setBackupEnabled(true);
236             }
237             updateConfigureSummary();
238         }
239         mDialogType = 0;
240     }
241 
242     /**
243      * Informs the BackupManager of a change in backup state - if backup is disabled,
244      * the data on the server will be erased.
245      * @param enable whether to enable backup
246      */
setBackupEnabled(boolean enable)247     private void setBackupEnabled(boolean enable) {
248         if (mBackupManager != null) {
249             try {
250                 mBackupManager.setBackupEnabled(enable);
251             } catch (RemoteException e) {
252                 mBackup.setChecked(!enable);
253                 mAutoRestore.setEnabled(!enable);
254                 return;
255             }
256         }
257         mBackup.setChecked(enable);
258         mAutoRestore.setEnabled(enable);
259         mConfigure.setEnabled(enable);
260     }
261 
262     @Override
getHelpResource()263     protected int getHelpResource() {
264         return R.string.help_url_backup_reset;
265     }
266 
267     /**
268      * For Search.
269      */
270     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
271             new PrivacySearchIndexProvider();
272 
273     private static class PrivacySearchIndexProvider extends BaseSearchIndexProvider {
274 
275         boolean mIsPrimary;
276 
PrivacySearchIndexProvider()277         public PrivacySearchIndexProvider() {
278             super();
279 
280             mIsPrimary = UserHandle.myUserId() == UserHandle.USER_OWNER;
281         }
282 
283         @Override
getXmlResourcesToIndex( Context context, boolean enabled)284         public List<SearchIndexableResource> getXmlResourcesToIndex(
285                 Context context, boolean enabled) {
286 
287             List<SearchIndexableResource> result = new ArrayList<SearchIndexableResource>();
288 
289             // For non-primary user, no backup or reset is available
290             if (!mIsPrimary) {
291                 return result;
292             }
293 
294             SearchIndexableResource sir = new SearchIndexableResource(context);
295             sir.xmlResId = R.xml.privacy_settings;
296             result.add(sir);
297 
298             return result;
299         }
300 
301         @Override
getNonIndexableKeys(Context context)302         public List<String> getNonIndexableKeys(Context context) {
303             return getNonVisibleKeys(context);
304         }
305     }
306 
getNonVisibleKeys(Context context)307     private static ArrayList<String> getNonVisibleKeys(Context context) {
308         final ArrayList<String> nonVisibleKeys = new ArrayList<String>();
309         final IBackupManager backupManager = IBackupManager.Stub.asInterface(
310                 ServiceManager.getService(Context.BACKUP_SERVICE));
311         boolean isServiceActive = false;
312         try {
313             isServiceActive = backupManager.isBackupServiceActive(UserHandle.myUserId());
314         } catch (RemoteException e) {
315             Log.w(TAG, "Failed querying backup manager service activity status. " +
316                     "Assuming it is inactive.");
317         }
318         if (isServiceActive) {
319             nonVisibleKeys.add(BACKUP_INACTIVE);
320         } else {
321             nonVisibleKeys.add(AUTO_RESTORE);
322             nonVisibleKeys.add(CONFIGURE_ACCOUNT);
323             nonVisibleKeys.add(BACKUP_DATA);
324         }
325         if (UserManager.get(context).hasUserRestriction(
326                 UserManager.DISALLOW_FACTORY_RESET)) {
327             nonVisibleKeys.add(PERSONAL_DATA_CATEGORY);
328         }
329         // Vendor specific
330         if (context.getPackageManager().
331                 resolveContentProvider(GSETTINGS_PROVIDER, 0) == null) {
332             nonVisibleKeys.add(BACKUP_CATEGORY);
333         }
334         return nonVisibleKeys;
335     }
336 }
337