1/*
2 * Copyright (C) 2014 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.tv.settings.device.privacy;
18
19import com.android.tv.settings.ActionBehavior;
20import com.android.tv.settings.ActionKey;
21import com.android.tv.settings.R;
22import com.android.tv.settings.dialog.old.Action;
23import com.android.tv.settings.dialog.old.ActionAdapter;
24import com.android.tv.settings.dialog.old.ActionFragment;
25import com.android.tv.settings.dialog.old.ContentFragment;
26import com.android.tv.settings.dialog.old.DialogActivity;
27import com.android.tv.settings.util.IntentUtils;
28
29import android.app.ActivityManager;
30import android.app.AlertDialog;
31import android.app.Dialog;
32import android.app.backup.IBackupManager;
33import android.app.Fragment;
34import android.app.admin.DevicePolicyManager;
35import android.content.ComponentName;
36import android.content.Context;
37import android.content.DialogInterface;
38import android.content.Intent;
39import android.content.res.Resources;
40import android.os.Bundle;
41import android.os.RemoteException;
42import android.os.ServiceManager;
43import android.provider.Settings;
44
45import java.util.ArrayList;
46import java.util.Collections;
47import java.util.HashMap;
48import java.util.Map;
49
50/**
51 * Activity that allows enabling and disabling of backup and restore functions.
52 */
53public class PrivacyActivity extends DialogActivity
54        implements ActionAdapter.Listener, DialogInterface.OnClickListener {
55
56    private static final int DIALOG_ERASE_BACKUP = 2;
57
58    /**
59     * Support for shutdown-after-reset. If our launch intent has a true value for
60     * the boolean extra under the following key, then include it in the intent we
61     * use to trigger a factory reset. This will cause us to shut down instead of
62     * restart after the reset.
63     */
64    private static final String SHUTDOWN_INTENT_EXTRA = "shutdown";
65
66    private IBackupManager mBackupManager;
67    private ContentFragment mContentFragment;
68    private ActionFragment mActionFragment;
69    private Dialog mConfirmDialog;
70    private int mDialogType;
71
72    @Override
73    protected void onCreate(Bundle savedInstanceState) {
74        super.onCreate(savedInstanceState);
75
76        mBackupManager = IBackupManager.Stub.asInterface(
77                ServiceManager.getService(Context.BACKUP_SERVICE));
78
79        // TODO implement backup and restore (b/10414565), then:
80        // NOTE1: Don't disable backups in SetupCompletedFragment under
81        // SetupWraith.
82        // NOTE2: Do no call finish() in onFactoryReset() upon CANCEL.
83        // NOTE3: Set breadcrumb to R.string.device_backup_restore for
84        // createFactoryResetContentFragment().
85        // NOTE4: Below call
86        // createMainMenuContentFragment() and getMainActions()
87        // instead of
88        // createFactoryResetContentFragment() and getFactoryResetActions().
89        // NOTE5: Change the backup_restore preference under res/xml/device to
90        // device_backup_restore.
91        mContentFragment = createFactoryResetContentFragment();
92        mActionFragment = ActionFragment.newInstance(getFactoryResetActions());
93        setContentAndActionFragments(mContentFragment, mActionFragment);
94    }
95
96    @Override
97    public void onStop() {
98        if (mConfirmDialog != null && mConfirmDialog.isShowing()) {
99            mConfirmDialog.dismiss();
100        }
101        mConfirmDialog = null;
102        mDialogType = 0;
103        super.onStop();
104    }
105
106    @Override
107    public void onClick(DialogInterface dialog, int which) {
108        if (which == DialogInterface.BUTTON_POSITIVE) {
109            if (mDialogType == DIALOG_ERASE_BACKUP) {
110                try {
111                    mBackupManager.setBackupEnabled(false);
112                } catch (RemoteException e) {
113                    // ignore.
114                }
115                goToMainScreen();
116            }
117        }
118        mDialogType = 0;
119    }
120
121    @Override
122    public void onActionClicked(Action action) {
123        ActionKey<ActionType, ActionBehavior> actionKey = new ActionKey<ActionType, ActionBehavior>(
124                ActionType.class, ActionBehavior.class, action.getKey());
125        ActionType type = actionKey.getType();
126        ActionBehavior behaviour = actionKey.getBehavior();
127        switch (actionKey.getType()) {
128            case BACKUP_DATA:
129                onBackupData(behaviour);
130                break;
131            case BACKUP_ACCOUNT:
132                onBackupAccount(behaviour);
133                break;
134            case AUTOMATIC_RESTORE:
135                onAutomaticRestore(behaviour);
136                break;
137            case FACTORY_RESET:
138                onFactoryReset(behaviour);
139                break;
140            case FACTORY_RESET_CONFIRM:
141                onFactoryResetConfirm(behaviour);
142                break;
143            default:
144                break;
145        }
146    }
147
148    private void onFactoryReset(ActionBehavior behaviour) {
149        switch (behaviour) {
150            case INIT:
151                setContentAndActionFragments(createFactoryResetContentFragment(),
152                        ActionFragment.newInstance(getFactoryResetActions()));
153                break;
154            case CANCEL:
155                // TODO pop from backstack to go back to main menu after backups
156                // are implemented (b/10414565).
157                finish();
158                break;
159            default:
160                break;
161        }
162    }
163
164    private void onFactoryResetConfirm(ActionBehavior behaviour) {
165        switch (behaviour) {
166            case INIT:
167                setContentAndActionFragments(ContentFragment.newInstance(
168                        ActionType.FACTORY_RESET_CONFIRM.getTitle(getResources()),
169                        getString(R.string.device_reset),
170                        getString(R.string.confirm_factory_reset_description),
171                        R.drawable.ic_settings_backuprestore,
172                        getResources().getColor(R.color.icon_background)),
173                        ActionFragment.newInstance(getFactoryResetConfirmActions()));
174                break;
175            case OK:
176                if (!ActivityManager.isUserAMonkey()) {
177                    Intent resetIntent = new Intent("android.intent.action.MASTER_CLEAR");
178                    if (getIntent().getBooleanExtra(SHUTDOWN_INTENT_EXTRA, false)) {
179                        resetIntent.putExtra(SHUTDOWN_INTENT_EXTRA, true);
180                    }
181                    sendBroadcast(resetIntent);
182                }
183                break;
184            case CANCEL:
185                getFragmentManager().popBackStack(null, 0);
186                getFragmentManager().popBackStack(null, 0);
187                break;
188            default:
189                break;
190        }
191    }
192
193    private ArrayList<Action> getFactoryResetActions() {
194        ArrayList<Action> actions = new ArrayList<Action>();
195        actions.add(new Action.Builder()
196                .key(new ActionKey<ActionType, ActionBehavior>(ActionType.FACTORY_RESET_CONFIRM,
197                        ActionBehavior.INIT).getKey())
198                .title(getString(R.string.factory_reset_device))
199                .build());
200        actions.add(new Action.Builder()
201                .key(new ActionKey<ActionType, ActionBehavior>(ActionType.FACTORY_RESET,
202                        ActionBehavior.CANCEL).getKey())
203                .title(getString(R.string.settings_cancel))
204                .build());
205        return actions;
206    }
207
208    private ArrayList<Action> getFactoryResetConfirmActions() {
209        ArrayList<Action> actions = new ArrayList<Action>();
210        actions.add(new Action.Builder()
211                .key(new ActionKey<ActionType, ActionBehavior>(ActionType.FACTORY_RESET_CONFIRM,
212                        ActionBehavior.OK).getKey())
213                .title(getString(R.string.confirm_factory_reset_device))
214                .build());
215        actions.add(new Action.Builder()
216                .key(new ActionKey<ActionType, ActionBehavior>(ActionType.FACTORY_RESET_CONFIRM,
217                        ActionBehavior.CANCEL).getKey())
218                .title(getString(R.string.settings_cancel))
219                .build());
220        return actions;
221    }
222
223    private void onBackupData(ActionBehavior behaviour) {
224        try {
225            boolean isOn = mBackupManager.isBackupEnabled();
226            switch (behaviour) {
227                case INIT:
228                    ArrayList<Action> actions = new ArrayList<Action>();
229                    actions.add(ActionBehavior.ON.toAction(new ActionKey<
230                            ActionType, ActionBehavior>(
231                            ActionType.BACKUP_DATA, ActionBehavior.ON).getKey(),
232                            getResources(), isOn));
233                    actions.add(ActionBehavior.OFF.toAction(new ActionKey<
234                            ActionType, ActionBehavior>(
235                            ActionType.BACKUP_DATA, ActionBehavior.OFF).getKey(),
236                            getResources(), !isOn));
237                    setContentAndActionFragments(
238                            createSubMenuContentFragment(ActionType.BACKUP_DATA),
239                            ActionFragment.newInstance(actions));
240                    break;
241                case ON:
242                    if (!isOn) {
243                        try {
244                            mBackupManager.setBackupEnabled(true);
245                        } catch (RemoteException e) {
246                            // ignore.
247                        }
248                    }
249                    goToMainScreen();
250                    break;
251                case OFF:
252                    if (isOn) {
253                        mDialogType = DIALOG_ERASE_BACKUP;
254                        CharSequence msg = getResources()
255                                .getText(R.string.backup_erase_dialog_message);
256                        // TODO: DialogFragment?
257                        mConfirmDialog = new AlertDialog.Builder(this).setMessage(msg)
258                                .setTitle(R.string.backup_erase_dialog_title)
259                                .setIconAttribute(android.R.attr.alertDialogIcon)
260                                .setPositiveButton(android.R.string.ok, this)
261                                .setNegativeButton(android.R.string.cancel, this).show();
262                    }
263                    goToMainScreen();
264                    break;
265                default:
266                    break;
267            }
268        } catch (RemoteException e) {
269            // ignore.
270        }
271    }
272
273    private void onBackupAccount(ActionBehavior behaviour) {
274        try {
275            String transport = mBackupManager.getCurrentTransport();
276            Intent configIntent = mBackupManager.getConfigurationIntent(transport);
277            if (configIntent != null) {
278                IntentUtils.startActivity(this, configIntent);
279            }
280        } catch (RemoteException e) {
281            // ignore.
282        }
283    }
284
285    private void onAutomaticRestore(ActionBehavior behaviour) {
286        boolean isOn = isAutoRestoreEnabled();
287        switch (behaviour) {
288            case INIT:
289                ArrayList<Action> actions = new ArrayList<Action>();
290                actions.add(ActionBehavior.ON.toAction(new ActionKey<ActionType, ActionBehavior>(
291                        ActionType.AUTOMATIC_RESTORE, ActionBehavior.ON).getKey(), getResources(),
292                        isOn));
293                actions.add(ActionBehavior.OFF.toAction(new ActionKey<ActionType, ActionBehavior>(
294                        ActionType.AUTOMATIC_RESTORE, ActionBehavior.OFF).getKey(), getResources(),
295                        !isOn));
296                setContentAndActionFragments(
297                        createSubMenuContentFragment(ActionType.AUTOMATIC_RESTORE),
298                        ActionFragment.newInstance(actions));
299                break;
300            case ON:
301                if (!isOn) {
302                    try {
303                        mBackupManager.setAutoRestore(true);
304                    } catch (RemoteException e) {
305                        // ignore.
306                    }
307                }
308                goToMainScreen();
309                break;
310            case OFF:
311                if (isOn) {
312                    try {
313                        mBackupManager.setAutoRestore(false);
314                    } catch (RemoteException e) {
315                        // ignore.
316                    }
317                }
318                goToMainScreen();
319                break;
320            default:
321                break;
322        }
323    }
324
325    private boolean isAutoRestoreEnabled() {
326        return Settings.Secure.getInt(getContentResolver(), Settings.Secure.BACKUP_AUTO_RESTORE, 1)
327                == 1;
328    }
329
330    private void goToMainScreen() {
331        updateMainScreen();
332        getFragmentManager().popBackStack(null, 0);
333    }
334
335    private ArrayList<Action> getMainActions() {
336        ArrayList<Action> actions = new ArrayList<Action>();
337
338        try {
339            boolean isBackupEnabled = mBackupManager.isBackupEnabled();
340            String transport = mBackupManager.getCurrentTransport();
341            Intent configIntent = mBackupManager.getConfigurationIntent(transport);
342            String configSummary = mBackupManager.getDestinationString(transport);
343
344            actions.add(ActionType.BACKUP_DATA.toAction(getResources(),
345                    mBackupManager.isBackupEnabled() ? getString(R.string.settings_on)
346                            : getString(R.string.settings_off)));
347            if (isBackupEnabled && configIntent != null) {
348                actions.add(ActionType.BACKUP_ACCOUNT.toAction(
349                        getResources(), configSummary == null ? getString(
350                                R.string.backup_configure_account_default_summary)
351                                : configSummary));
352            }
353            if (isBackupEnabled) {
354                actions.add(ActionType.AUTOMATIC_RESTORE.toAction(
355                        getResources(), isAutoRestoreEnabled() ? getString(R.string.settings_on)
356                                : getString(R.string.settings_off)));
357            }
358
359        } catch (RemoteException re) {
360            // no backup manager means no actions.
361        }
362
363        actions.add(ActionType.FACTORY_RESET.toAction(getResources(), null));
364
365        return actions;
366    }
367
368    private Fragment createSubMenuContentFragment(ActionType type) {
369        return ContentFragment.newInstance(type.getTitle(getResources()),
370                getString(R.string.device_backup_restore), null,
371                R.drawable.ic_settings_backuprestore,
372                getResources().getColor(R.color.icon_background));
373    }
374
375    private ContentFragment createMainMenuContentFragment() {
376        return ContentFragment.newInstance(getString(R.string.device_backup_restore),
377                getString(R.string.header_category_device),
378                null, R.drawable.ic_settings_backuprestore,
379                getResources().getColor(R.color.icon_background));
380    }
381
382    private ContentFragment createFactoryResetContentFragment() {
383        // TODO Set breadcrumb to R.string.device_backup_restore b/10414565
384        return ContentFragment.newInstance(
385                ActionType.FACTORY_RESET.getTitle(getResources()),
386                getString(R.string.header_category_device),
387                getString(R.string.factory_reset_description),
388                R.drawable.ic_settings_backuprestore,
389                getResources().getColor(R.color.icon_background));
390    }
391
392    private void updateMainScreen() {
393        ((ActionAdapter) mActionFragment.getAdapter()).setActions(getMainActions());
394    }
395}
396