22
22
import android .content .Context ;
23
23
import android .content .Intent ;
24
24
import android .os .Bundle ;
25
- import android .text .Editable ;
26
- import android .text .TextUtils ;
27
- import android .text .TextWatcher ;
28
- import android .view .LayoutInflater ;
29
- import android .view .View ;
30
- import android .view .ViewGroup ;
31
- import android .widget .Button ;
32
- import android .widget .EditText ;
33
- import android .widget .RadioButton ;
34
- import android .widget .RadioGroup ;
25
+ import android .preference .EditTextPreference ;
26
+ import android .preference .ListPreference ;
27
+ import android .preference .Preference ;
28
+ import android .preference .PreferenceFragment ;
35
29
import android .widget .Toast ;
36
30
37
31
import com .afwsamples .testdpc .DeviceAdminReceiver ;
38
32
import com .afwsamples .testdpc .R ;
39
33
40
- import java .util .LinkedHashMap ;
41
- import java .util .Map ;
34
+ import java .util .ArrayList ;
35
+ import java .util .List ;
36
+ import java .util .TreeMap ;
42
37
43
38
/**
44
39
* This fragment provides functionalities to set password constraint policies as a profile
45
- * or device owner.
40
+ * or device owner. In the former case, it is also possible to set password constraints on
41
+ * the parent profile.
46
42
*
47
43
* <p>These include:
48
44
* <ul>
45
+ * <li>{@link DevicePolicyManager#setPasswordQuality(ComponentName, int)}</li>
49
46
* <li>{@link DevicePolicyManager#setPasswordMinimumLength(ComponentName, String)}</li>
50
47
* <li>{@link DevicePolicyManager#setPasswordMinimumLetters(ComponentName, String)}</li>
51
48
* <li>{@link DevicePolicyManager#setPasswordMinimumNumeric(ComponentName, String)}</li>
55
52
* <li>{@link DevicePolicyManager#setPasswordMinimumNonLetter(ComponentName, String)}</li>
56
53
* </ul>
57
54
*/
58
- public final class PasswordConstraintsFragment extends Fragment implements
59
- RadioGroup . OnCheckedChangeListener , TextWatcher {
55
+ public final class PasswordConstraintsFragment extends PreferenceFragment implements
56
+ Preference . OnPreferenceChangeListener {
60
57
61
- private static final Map <Integer , Integer > PASSWORD_QUALITIES = new LinkedHashMap <>(7 );
58
+ private DevicePolicyManager mDevicePolicyManager ;
59
+ private ComponentName mAdminComponent ;
60
+
61
+ private DevicePolicyManager getDpm () {
62
+ return mDevicePolicyManager ;
63
+ }
64
+
65
+ private ComponentName getAdmin () {
66
+ return mAdminComponent ;
67
+ }
68
+
69
+ abstract static class Keys {
70
+ final static String QUALITY = "minimum_password_quality" ;
71
+
72
+ final static String MIN_LENGTH = "password_min_length" ;
73
+ final static String MIN_LETTERS = "password_min_letters" ;
74
+ final static String MIN_NUMERIC = "password_min_numeric" ;
75
+ final static String MIN_LOWERCASE = "password_min_lowercase" ;
76
+ final static String MIN_UPPERCASE = "password_min_uppercase" ;
77
+ final static String MIN_SYMBOLS = "password_min_symbols" ;
78
+ final static String MIN_NONLETTER = "password_min_nonletter" ;
79
+ }
80
+
81
+ private static final TreeMap <Integer , Integer > PASSWORD_QUALITIES = new TreeMap <>();
62
82
static {
63
83
// IDs of settings for {@link DevicePolicyManager#setPasswordQuality(ComponentName, int)}.
64
84
final int [] policyIds = new int [] {
@@ -88,128 +108,121 @@ public final class PasswordConstraintsFragment extends Fragment implements
88
108
}
89
109
};
90
110
91
- // Radio list of all complexity settings, as defined above.
92
- private RadioGroup mQualityGroup ;
93
-
94
- // Individual minimum password attribute requirements.
95
- private EditText mMinLength ;
96
- private EditText mMinLetters ;
97
- private EditText mMinNumeric ;
98
- private EditText mMinLowerCase ;
99
- private EditText mMinUpperCase ;
100
- private EditText mMinSymbols ;
101
- private EditText mMinNonLetter ;
102
-
103
- private DevicePolicyManager mDpm ;
104
- private ComponentName mAdminComponent ;
105
-
106
111
@ Override
107
112
public void onCreate (Bundle savedInstanceState ) {
108
113
super .onCreate (savedInstanceState );
109
114
getActivity ().getActionBar ().setTitle (R .string .password_constraints );
110
115
111
- mDpm = (DevicePolicyManager ) getActivity ().getSystemService (Context .DEVICE_POLICY_SERVICE );
116
+ mDevicePolicyManager = (DevicePolicyManager )
117
+ getActivity ().getSystemService (Context .DEVICE_POLICY_SERVICE );
112
118
mAdminComponent = DeviceAdminReceiver .getComponentName (getActivity ());
113
- }
114
-
115
- @ Override
116
- public View onCreateView (LayoutInflater layoutInflater , ViewGroup container ,
117
- Bundle savedInstanceState ) {
118
- final View root = layoutInflater .inflate (R .layout .password_quality , null );
119
-
120
- // Create numeric text fields
121
- mMinLength = findAndPrepareField (root , R .id .password_min_length );
122
- mMinLetters = findAndPrepareField (root , R .id .password_min_letters );
123
- mMinNumeric = findAndPrepareField (root , R .id .password_min_numeric );
124
- mMinLowerCase = findAndPrepareField (root , R .id .password_min_lowercase );
125
- mMinUpperCase = findAndPrepareField (root , R .id .password_min_uppercase );
126
- mMinSymbols = findAndPrepareField (root , R .id .password_min_symbols );
127
- mMinNonLetter = findAndPrepareField (root , R .id .password_min_nonletter );
128
-
129
- // Create radio group for password quality
130
- mQualityGroup = (RadioGroup ) root .findViewById (R .id .password_quality );
131
- for (Map .Entry <Integer , Integer > entry : PASSWORD_QUALITIES .entrySet ()) {
132
- final RadioButton choice = new RadioButton (getContext ());
133
- choice .setId (entry .getKey ());
134
- choice .setText (entry .getValue ());
135
- mQualityGroup .addView (choice );
136
- }
137
- mQualityGroup .setOnCheckedChangeListener (this );
138
-
139
- return root ;
140
- }
141
119
142
- @ Override
143
- public void onResume () {
144
- super .onResume ();
145
-
146
- // Set the password quality radio group to show the requirement, if there is one.
147
- mQualityGroup .check (mDpm .getPasswordQuality (mAdminComponent ));
148
-
149
- // Update all of our minimum requirement fields via getPasswordMinimum(.*)
150
- mMinLength .setText (Integer .toString (mDpm .getPasswordMinimumLength (mAdminComponent )));
151
- mMinLetters .setText (Integer .toString (mDpm .getPasswordMinimumLetters (mAdminComponent )));
152
- mMinNumeric .setText (Integer .toString (mDpm .getPasswordMinimumNumeric (mAdminComponent )));
153
- mMinLowerCase .setText (Integer .toString (mDpm .getPasswordMinimumLowerCase (mAdminComponent )));
154
- mMinUpperCase .setText (Integer .toString (mDpm .getPasswordMinimumUpperCase (mAdminComponent )));
155
- mMinSymbols .setText (Integer .toString (mDpm .getPasswordMinimumSymbols (mAdminComponent )));
156
- mMinNonLetter .setText (Integer .toString (mDpm .getPasswordMinimumNonLetter (mAdminComponent )));
157
-
158
- sendPasswordChangedBroadcast ();
159
- }
120
+ addPreferencesFromResource (R .xml .password_constraint_preferences );
160
121
161
- @ Override
162
- public void onCheckedChanged (RadioGroup view , int checkedId ) {
163
- if (view == mQualityGroup ) {
164
- mDpm .setPasswordQuality (mAdminComponent , checkedId );
122
+ // Populate password quality settings - messy because the only API for this requires two
123
+ // separate String[]s.
124
+ List <CharSequence > entries = new ArrayList <>();
125
+ List <CharSequence > values = new ArrayList <>();
126
+ for (TreeMap .Entry <Integer , Integer > entry : PASSWORD_QUALITIES .entrySet ()) {
127
+ values .add (Integer .toString (entry .getKey ()));
128
+ entries .add (getString (entry .getValue ()));
165
129
}
166
- sendPasswordChangedBroadcast ();
130
+ ListPreference quality = (ListPreference ) findPreference (Keys .QUALITY );
131
+ quality .setEntries (entries .toArray (new CharSequence [0 ]));
132
+ quality .setEntryValues (values .toArray (new CharSequence [0 ]));
133
+
134
+ // Minimum quality requirement.
135
+ setup (Keys .QUALITY , PASSWORD_QUALITIES .floorKey (getDpm ().getPasswordQuality (getAdmin ())));
136
+
137
+ // Minimum length requirements.
138
+ setup (Keys .MIN_LENGTH , getDpm ().getPasswordMinimumLength (getAdmin ()));
139
+ setup (Keys .MIN_LETTERS , getDpm ().getPasswordMinimumLetters (getAdmin ()));
140
+ setup (Keys .MIN_NUMERIC , getDpm ().getPasswordMinimumNumeric (getAdmin ()));
141
+ setup (Keys .MIN_LOWERCASE , getDpm ().getPasswordMinimumLowerCase (getAdmin ()));
142
+ setup (Keys .MIN_UPPERCASE , getDpm ().getPasswordMinimumUpperCase (getAdmin ()));
143
+ setup (Keys .MIN_SYMBOLS , getDpm ().getPasswordMinimumSymbols (getAdmin ()));
144
+ setup (Keys .MIN_NONLETTER , getDpm ().getPasswordMinimumNonLetter (getAdmin ()));
167
145
}
168
146
169
147
@ Override
170
- public void afterTextChanged (Editable editable ) {
171
- if (TextUtils .isEmpty (editable .toString ())) {
172
- return ;
173
- }
174
-
148
+ public boolean onPreferenceChange (Preference preference , Object newValue ) {
175
149
final int value ;
176
- try {
177
- value = Integer .parseInt (editable .toString ());
178
- } catch (NumberFormatException e ) {
179
- Toast .makeText (getActivity (), R .string .not_valid_input , Toast .LENGTH_SHORT ).show ();
180
- return ;
150
+ if (newValue instanceof String && ((String ) newValue ).length () != 0 ) {
151
+ try {
152
+ value = Integer .parseInt ((String ) newValue );
153
+ } catch (NumberFormatException e ) {
154
+ Toast .makeText (getActivity (), R .string .not_valid_input , Toast .LENGTH_SHORT ).show ();
155
+ return false ;
156
+ }
157
+ } else {
158
+ value = 0 ;
181
159
}
182
160
183
- if (editable == mMinLength .getEditableText ()) {
184
- mDpm .setPasswordMinimumLength (mAdminComponent , value );
185
- } else if (editable == mMinLetters .getEditableText ()) {
186
- mDpm .setPasswordMinimumLetters (mAdminComponent , value );
187
- } else if (editable == mMinNumeric .getEditableText ()) {
188
- mDpm .setPasswordMinimumNumeric (mAdminComponent , value );
189
- } else if (editable == mMinLowerCase .getEditableText ()) {
190
- mDpm .setPasswordMinimumLowerCase (mAdminComponent , value );
191
- } else if (editable == mMinUpperCase .getEditableText ()) {
192
- mDpm .setPasswordMinimumUpperCase (mAdminComponent , value );
193
- } else if (editable == mMinSymbols .getEditableText ()) {
194
- mDpm .setPasswordMinimumSymbols (mAdminComponent , value );
195
- } else if (editable == mMinNonLetter .getEditableText ()) {
196
- mDpm .setPasswordMinimumNonLetter (mAdminComponent , value );
161
+ // By default, show the new value as a summary.
162
+ CharSequence summary = newValue .toString ();
163
+
164
+ switch (preference .getKey ()) {
165
+ case Keys .QUALITY : {
166
+ final ListPreference list = (ListPreference ) preference ;
167
+ // Store newValue now so getEntry() can return the new setting
168
+ list .setValue ((String ) newValue );
169
+ summary = list .getEntry ();
170
+ getDpm ().setPasswordQuality (getAdmin (), value );
171
+ break ;
172
+ }
173
+ case Keys .MIN_LENGTH :
174
+ getDpm ().setPasswordMinimumLength (getAdmin (), value );
175
+ break ;
176
+ case Keys .MIN_LETTERS :
177
+ getDpm ().setPasswordMinimumLetters (getAdmin (), value );
178
+ break ;
179
+ case Keys .MIN_NUMERIC :
180
+ getDpm ().setPasswordMinimumNumeric (getAdmin (), value );
181
+ break ;
182
+ case Keys .MIN_LOWERCASE :
183
+ getDpm ().setPasswordMinimumLowerCase (getAdmin (), value );
184
+ break ;
185
+ case Keys .MIN_UPPERCASE :
186
+ getDpm ().setPasswordMinimumUpperCase (getAdmin (), value );
187
+ break ;
188
+ case Keys .MIN_SYMBOLS :
189
+ getDpm ().setPasswordMinimumSymbols (getAdmin (), value );
190
+ break ;
191
+ case Keys .MIN_NONLETTER :
192
+ getDpm ().setPasswordMinimumNonLetter (getAdmin (), value );
193
+ break ;
194
+ default :
195
+ return false ;
197
196
}
198
- sendPasswordChangedBroadcast ();
199
- }
200
197
201
- @ Override
202
- public void beforeTextChanged (CharSequence s , int start , int count , int after ) {
198
+ preference .setSummary (summary );
199
+ sendPasswordRequirementsChanged ();
200
+ return true ;
203
201
}
204
202
205
- @ Override
206
- public void onTextChanged (CharSequence s , int start , int count , int after ) {
207
- }
203
+ /**
204
+ * Set an initial value. Updates the summary to match.
205
+ */
206
+ private void setup (String key , Object adminSetting ) {
207
+ Preference field = findPreference (key );
208
+ field .setOnPreferenceChangeListener (this );
208
209
209
- private EditText findAndPrepareField (View root , final int id ) {
210
- EditText field = (EditText ) root .findViewById (id );
211
- field .addTextChangedListener (this );
212
- return field ;
210
+ if (adminSetting == null ) {
211
+ return ;
212
+ }
213
+
214
+ final String stringSetting = adminSetting .toString ();
215
+ CharSequence summary = stringSetting ;
216
+
217
+ if (field instanceof EditTextPreference ) {
218
+ EditTextPreference p = (EditTextPreference ) field ;
219
+ p .setText (stringSetting );
220
+ } else if (field instanceof ListPreference ) {
221
+ ListPreference p = (ListPreference ) field ;
222
+ p .setValue (stringSetting );
223
+ summary = p .getEntry ();
224
+ }
225
+ field .setSummary (summary );
213
226
}
214
227
215
228
/**
@@ -221,10 +234,10 @@ private EditText findAndPrepareField(View root, final int id) {
221
234
*
222
235
* <p>May trigger a show/hide of the notification warning to change the password through
223
236
* Settings.
224
- ** /
225
- private void sendPasswordChangedBroadcast () {
237
+ */
238
+ private void sendPasswordRequirementsChanged () {
226
239
Intent changedIntent = new Intent (DeviceAdminReceiver .ACTION_PASSWORD_REQUIREMENTS_CHANGED );
227
- changedIntent .setComponent (mAdminComponent );
240
+ changedIntent .setComponent (getAdmin () );
228
241
getContext ().sendBroadcast (changedIntent );
229
242
}
230
243
}
0 commit comments