RestrictedSettingsFragment.java revision 4453496448feaf9152aca7ebffd952ad05a97fdd
1/*
2 * Copyright (C) 2013 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.settings;
18
19import java.util.HashSet;
20
21import android.app.Activity;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.os.Bundle;
27import android.os.UserManager;
28import android.preference.CheckBoxPreference;
29import android.preference.Preference;
30
31/**
32 * Base class for settings activities that should be pin protected when in restricted mode.
33 * The constructor for this class will take the restriction key that this screen should be
34 * locked by.  If {@link UserManager.hasRestrictionsPin()} and
35 * {@link UserManager.hasUserRestriction(String)} returns true for the restriction key, then
36 * the user will have to enter the restrictions pin before seeing the Settings screen.
37 *
38 * If this settings screen should be pin protected whenever
39 * {@link UserManager.hasUserRestriction(String)} returns true, pass in
40 * {@link RESTRICTIONS_PIN_SET} to the constructor instead of a restrictions key.
41 */
42public class RestrictedSettingsFragment extends SettingsPreferenceFragment {
43
44    protected static final String RESTRICTIONS_PIN_SET = "restrictions_pin_set";
45
46    private static final String EXTRA_PREFERENCE = "pref";
47    private static final String EXTRA_CHECKBOX_STATE = "isChecked";
48    // Should be unique across all settings screens that use this.
49    private static final int REQUEST_PIN_CHALLENGE = 12309;
50
51    private static final String KEY_CHALLENGE_SUCCEEDED = "chsc";
52    private static final String KEY_CHALLENGE_REQUESTED = "chrq";
53    private static final String KEY_RESUME_ACTION_BUNDLE = "rsmb";
54
55    // If the restriction PIN is entered correctly.
56    private boolean mChallengeSucceeded;
57    private boolean mChallengeRequested;
58    private Bundle mResumeActionBundle;
59
60    private UserManager mUserManager;
61
62    private final String mRestrictionKey;
63
64    private final HashSet<Preference> mProtectedByRestictionsPrefs = new HashSet<Preference>();
65
66    // Receiver to clear pin status when the screen is turned off.
67    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
68        @Override
69        public void onReceive(Context context, Intent intent) {
70            mChallengeSucceeded = false;
71            if (shouldBePinProtected(mRestrictionKey)) {
72                ensurePin(null);
73            }
74        }
75    };
76
77    /**
78     * @param restrictionKey The restriction key to check before pin protecting
79     *            this settings page. Pass in {@link RESTRICTIONS_PIN_SET} if it should
80     *            be PIN protected whenever a restrictions pin is set. Pass in
81     *            null if it should never be PIN protected.
82     */
83    public RestrictedSettingsFragment(String restrictionKey) {
84        mRestrictionKey = restrictionKey;
85    }
86
87    @Override
88    public void onCreate(Bundle icicle) {
89        super.onCreate(icicle);
90
91        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
92
93        if (icicle != null) {
94            mChallengeSucceeded = icicle.getBoolean(KEY_CHALLENGE_SUCCEEDED, false);
95            mChallengeRequested = icicle.getBoolean(KEY_CHALLENGE_REQUESTED, false);
96            mResumeActionBundle = icicle.getBundle(KEY_RESUME_ACTION_BUNDLE);
97        }
98    }
99
100    @Override
101    public void onSaveInstanceState(Bundle outState) {
102        super.onSaveInstanceState(outState);
103
104        outState.putBoolean(KEY_CHALLENGE_REQUESTED, mChallengeRequested);
105        if (mResumeActionBundle != null) {
106            outState.putBundle(KEY_RESUME_ACTION_BUNDLE, mResumeActionBundle);
107        }
108        if (getActivity().isChangingConfigurations()) {
109            outState.putBoolean(KEY_CHALLENGE_SUCCEEDED, mChallengeSucceeded);
110        }
111    }
112
113    @Override
114    public void onResume() {
115        super.onResume();
116        if (shouldBePinProtected(mRestrictionKey)) {
117            ensurePin(null);
118        } else {
119            // If the whole screen is not pin protected, reset mChallengeSucceeded so next
120            // time user uses a protected preference, they are prompted for pin again.
121            mChallengeSucceeded = false;
122        }
123        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
124        filter.addAction(Intent.ACTION_USER_PRESENT);
125        getActivity().registerReceiver(mScreenOffReceiver, filter);
126    }
127
128    @Override
129    public void onPause() {
130        super.onPause();
131        getActivity().unregisterReceiver(mScreenOffReceiver);
132    }
133
134    @Override
135    public void onActivityResult(int requestCode, int resultCode, Intent data) {
136        if (requestCode == REQUEST_PIN_CHALLENGE) {
137            Bundle resumeActionBundle = mResumeActionBundle;
138            mResumeActionBundle = null;
139            mChallengeRequested = false;
140            if (resultCode == Activity.RESULT_OK) {
141                mChallengeSucceeded = true;
142                String prefKey = resumeActionBundle == null ?
143                        null : resumeActionBundle.getString(EXTRA_PREFERENCE);
144                if (prefKey != null) {
145                    Preference pref = findPreference(prefKey);
146                    if (pref != null) {
147                        // Make sure the checkbox state is the same as it was when we launched the
148                        // pin challenge.
149                        if (pref instanceof CheckBoxPreference
150                                && resumeActionBundle.containsKey(EXTRA_CHECKBOX_STATE)) {
151                            boolean isChecked =
152                                    resumeActionBundle.getBoolean(EXTRA_CHECKBOX_STATE, false);
153                            ((CheckBoxPreference)pref).setChecked(isChecked);
154                        }
155                        if (!onPreferenceTreeClick(getPreferenceScreen(), pref)) {
156                            Intent prefIntent = pref.getIntent();
157                            if (prefIntent != null) {
158                                pref.getContext().startActivity(prefIntent);
159                            }
160                        }
161                    }
162                }
163            } else if (!isDetached()) {
164                finishFragment();
165            }
166            return;
167        }
168
169        super.onActivityResult(requestCode, resultCode, data);
170    }
171
172    private void ensurePin(Preference preference) {
173        if (!mChallengeSucceeded) {
174            final UserManager um = UserManager.get(getActivity());
175            if (!mChallengeRequested) {
176                if (um.hasRestrictionsPin()) {
177                    mResumeActionBundle = new Bundle();
178                    if (preference != null) {
179                        mResumeActionBundle.putString(EXTRA_PREFERENCE, preference.getKey());
180                        if (preference instanceof CheckBoxPreference) {
181                            mResumeActionBundle.putBoolean(EXTRA_CHECKBOX_STATE,
182                                    ((CheckBoxPreference)preference).isChecked());
183                        }
184                    }
185                    Intent requestPin = new Intent(Intent.ACTION_RESTRICTIONS_PIN_CHALLENGE);
186                    startActivityForResult(requestPin, REQUEST_PIN_CHALLENGE);
187                    mChallengeRequested = true;
188                }
189            }
190        }
191        mChallengeSucceeded = false;
192    }
193
194    /**
195     * Returns true if this activity is restricted, but no restriction pin has been set.
196     * Used to determine if the settings UI should disable UI.
197     */
198    protected boolean isRestrictedAndNotPinProtected() {
199        if (mRestrictionKey == null || RESTRICTIONS_PIN_SET.equals(mRestrictionKey)) {
200            return false;
201        }
202        return mUserManager.hasUserRestriction(mRestrictionKey)
203                && !mUserManager.hasRestrictionsPin();
204    }
205
206    /**
207     * Called to trigger the pin entry if the given restriction key is locked down.
208     * @param restrictionsKey The restriction key or {@link RESTRICTIONS_PIN_SET} if
209     *          pin entry should get triggered if there is a pin set.
210     */
211   protected boolean restrictionsPinCheck(String restrictionsKey, Preference preference) {
212       if (shouldBePinProtected(restrictionsKey) && !mChallengeSucceeded) {
213           ensurePin(preference);
214           return false;
215       } else {
216           return true;
217       }
218   }
219
220   protected boolean hasChallengeSucceeded() {
221       return mChallengeSucceeded;
222   }
223
224   /**
225    * Returns true if this restrictions key is locked down.
226    */
227   protected boolean shouldBePinProtected(String restrictionKey) {
228       if (restrictionKey == null) {
229           return false;
230       }
231       boolean restricted = RESTRICTIONS_PIN_SET.equals(restrictionKey)
232               || mUserManager.hasUserRestriction(restrictionKey);
233       return restricted && mUserManager.hasRestrictionsPin();
234   }
235
236   /**
237    * If the preference is one that was added by protectByRestrictions(), then it will
238    * prompt the user for the restrictions pin if they haven't entered it already.
239    * Intended to be called at the top of onPreferenceTreeClick.  If this function returns
240    * true, then onPreferenceTreeClick should return true.
241    */
242   boolean ensurePinRestrictedPreference(Preference preference) {
243       return mProtectedByRestictionsPrefs.contains(preference)
244               && !restrictionsPinCheck(RESTRICTIONS_PIN_SET, preference);
245   }
246
247    /**
248     * Call this with any preferences that should require the PIN to be entered
249     * before they are accessible.
250     */
251   protected void protectByRestrictions(Preference pref) {
252       if (pref != null) {
253           mProtectedByRestictionsPrefs.add(pref);
254       }
255   }
256
257   protected void protectByRestrictions(String key) {
258       Preference pref = findPreference(key);
259       protectByRestrictions(pref);
260   }
261}
262