Skip to content

Most visited

Recently visited

navigation
AppRestrictionEnforcer / src / com.example.android.apprestrictionenforcer /

AppRestrictionEnforcerFragment.java

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
 
17
package com.example.android.apprestrictionenforcer;
18
 
19
import android.app.Activity;
20
import android.app.admin.DevicePolicyManager;
21
import android.content.Context;
22
import android.content.RestrictionEntry;
23
import android.content.RestrictionsManager;
24
import android.content.SharedPreferences;
25
import android.os.Build;
26
import android.os.Bundle;
27
import android.os.Parcelable;
28
import android.support.annotation.NonNull;
29
import android.support.annotation.Nullable;
30
import android.support.v4.app.Fragment;
31
import android.text.Editable;
32
import android.text.TextUtils;
33
import android.text.TextWatcher;
34
import android.view.LayoutInflater;
35
import android.view.View;
36
import android.view.ViewGroup;
37
import android.widget.AdapterView;
38
import android.widget.ArrayAdapter;
39
import android.widget.Button;
40
import android.widget.CompoundButton;
41
import android.widget.EditText;
42
import android.widget.LinearLayout;
43
import android.widget.Spinner;
44
import android.widget.Switch;
45
import android.widget.TextView;
46
import android.widget.Toast;
47
 
48
import java.util.ArrayList;
49
import java.util.Arrays;
50
import java.util.HashMap;
51
import java.util.List;
52
import java.util.Map;
53
 
54
/**
55
 * This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
56
 * sample.
57
 */
58
public class AppRestrictionEnforcerFragment extends Fragment implements
59
        CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener,
60
        View.OnClickListener, ItemAddFragment.OnItemAddedListener {
61
 
62
    /**
63
     * Key for {@link SharedPreferences}
64
     */
65
    private static final String PREFS_KEY = "AppRestrictionEnforcerFragment";
66
 
67
    /**
68
     * Key for the boolean restriction in AppRestrictionSchema.
69
     */
70
    private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello";
71
 
72
    /**
73
     * Key for the string restriction in AppRestrictionSchema.
74
     */
75
    private static final String RESTRICTION_KEY_MESSAGE = "message";
76
 
77
    /**
78
     * Key for the integer restriction in AppRestrictionSchema.
79
     */
80
    private static final String RESTRICTION_KEY_NUMBER = "number";
81
 
82
    /**
83
     * Key for the choice restriction in AppRestrictionSchema.
84
     */
85
    private static final String RESTRICTION_KEY_RANK = "rank";
86
 
87
    /**
88
     * Key for the multi-select restriction in AppRestrictionSchema.
89
     */
90
    private static final String RESTRICTION_KEY_APPROVALS = "approvals";
91
 
92
    /**
93
     * Key for the bundle array restriction in AppRestrictionSchema.
94
     */
95
    private static final String RESTRICTION_KEY_ITEMS = "items";
96
    private static final String RESTRICTION_KEY_ITEM_KEY = "key";
97
    private static final String RESTRICTION_KEY_ITEM_VALUE = "value";
98
 
99
    private static final String DELIMETER = ",";
100
    private static final String SEPARATOR = ":";
101
 
102
    private static final boolean BUNDLE_SUPPORTED = Build.VERSION.SDK_INT >= 23;
103
 
104
    /**
105
     * Current status of the restrictions.
106
     */
107
    private Bundle mCurrentRestrictions = new Bundle();
108
 
109
    // UI Components
110
    private Switch mSwitchSayHello;
111
    private EditText mEditMessage;
112
    private EditText mEditNumber;
113
    private Spinner mSpinnerRank;
114
    private LinearLayout mLayoutApprovals;
115
    private LinearLayout mLayoutItems;
116
 
117
    @Override
118
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
119
                             @Nullable Bundle savedInstanceState) {
120
        return inflater.inflate(R.layout.fragment_app_restriction_enforcer, container, false);
121
    }
122
 
123
    @Override
124
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
125
        // Retain references for the UI elements
126
        mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello);
127
        mEditMessage = (EditText) view.findViewById(R.id.message);
128
        mEditNumber = (EditText) view.findViewById(R.id.number);
129
        mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
130
        mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
131
        mLayoutItems = (LinearLayout) view.findViewById(R.id.items);
132
        view.findViewById(R.id.item_add).setOnClickListener(this);
133
        View bundleArrayLayout = view.findViewById(R.id.bundle_array_layout);
134
        if (BUNDLE_SUPPORTED) {
135
            bundleArrayLayout.setVisibility(View.VISIBLE);
136
        } else {
137
            bundleArrayLayout.setVisibility(View.GONE);
138
        }
139
    }
140
 
141
    @Override
142
    public void onResume() {
143
        super.onResume();
144
        loadRestrictions(getActivity());
145
    }
146
 
147
    @Override
148
    public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
149
        switch (compoundButton.getId()) {
150
            case R.id.say_hello: {
151
                saveCanSayHello(getActivity(), checked);
152
                break;
153
            }
154
            case R.id.approval: {
155
                if (checked) {
156
                    addApproval(getActivity(), (String) compoundButton.getTag());
157
                } else {
158
                    removeApproval(getActivity(), (String) compoundButton.getTag());
159
                }
160
                break;
161
            }
162
        }
163
    }
164
 
165
    private TextWatcher mWatcherMessage = new EasyTextWatcher() {
166
        @Override
167
        public void afterTextChanged(Editable s) {
168
            saveMessage(getActivity(), s.toString());
169
        }
170
    };
171
 
172
    private TextWatcher mWatcherNumber = new EasyTextWatcher() {
173
        @Override
174
        public void afterTextChanged(Editable s) {
175
            try {
176
                String string = s.toString();
177
                if (!TextUtils.isEmpty(string)) {
178
                    saveNumber(getActivity(), Integer.parseInt(string));
179
                }
180
            } catch (NumberFormatException e) {
181
                Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
182
            }
183
        }
184
    };
185
 
186
    @Override
187
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
188
        switch (parent.getId()) {
189
            case R.id.rank: {
190
                saveRank(getActivity(), (String) parent.getAdapter().getItem(position));
191
                break;
192
            }
193
        }
194
    }
195
 
196
    @Override
197
    public void onNothingSelected(AdapterView<?> parent) {
198
        // Nothing to do
199
    }
200
 
201
    @Override
202
    public void onClick(View v) {
203
        switch (v.getId()) {
204
            case R.id.item_add:
205
                new ItemAddFragment().show(getChildFragmentManager(), "dialog");
206
                break;
207
            case R.id.item_remove:
208
                String key = (String) v.getTag();
209
                removeItem(key);
210
                mLayoutItems.removeView((View) v.getParent());
211
                break;
212
        }
213
    }
214
 
215
    @Override
216
    public void onItemAdded(String key, String value) {
217
        key = TextUtils.replace(key,
218
                new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString();
219
        value = TextUtils.replace(value,
220
                new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString();
221
        Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS);
222
        Map<String, String> items = new HashMap<>();
223
        if (parcelables != null) {
224
            for (Parcelable parcelable : parcelables) {
225
                Bundle bundle = (Bundle) parcelable;
226
                items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY),
227
                        bundle.getString(RESTRICTION_KEY_ITEM_VALUE));
228
            }
229
        }
230
        items.put(key, value);
231
        insertItemRow(LayoutInflater.from(getActivity()), key, value);
232
        saveItems(getActivity(), items);
233
    }
234
 
235
    /**
236
     * Loads the restrictions for the AppRestrictionSchema sample.
237
     *
238
     * @param activity The activity
239
     */
240
    private void loadRestrictions(Activity activity) {
241
        RestrictionsManager manager =
242
                (RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE);
243
        List<RestrictionEntry> restrictions =
244
                manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
245
        SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
246
        for (RestrictionEntry restriction : restrictions) {
247
            String key = restriction.getKey();
248
            if (RESTRICTION_KEY_SAY_HELLO.equals(key)) {
249
                updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO,
250
                        restriction.getSelectedState()));
251
            } else if (RESTRICTION_KEY_MESSAGE.equals(key)) {
252
                updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE,
253
                        restriction.getSelectedString()));
254
            } else if (RESTRICTION_KEY_NUMBER.equals(key)) {
255
                updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER,
256
                        restriction.getIntValue()));
257
            } else if (RESTRICTION_KEY_RANK.equals(key)) {
258
                updateRank(activity, restriction.getChoiceValues(),
259
                        prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString()));
260
            } else if (RESTRICTION_KEY_APPROVALS.equals(key)) {
261
                updateApprovals(activity, restriction.getChoiceValues(),
262
                        TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS,
263
                                        TextUtils.join(DELIMETER,
264
                                                restriction.getAllSelectedStrings())),
265
                                DELIMETER));
266
            } else if (BUNDLE_SUPPORTED && RESTRICTION_KEY_ITEMS.equals(key)) {
267
                String itemsString = prefs.getString(RESTRICTION_KEY_ITEMS, "");
268
                HashMap<String, String> items = new HashMap<>();
269
                for (String itemString : TextUtils.split(itemsString, DELIMETER)) {
270
                    String[] strings = itemString.split(SEPARATOR, 2);
271
                    items.put(strings[0], strings[1]);
272
                }
273
                updateItems(activity, items);
274
            }
275
        }
276
    }
277
 
278
    private void updateCanSayHello(boolean canSayHello) {
279
        mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello);
280
        mSwitchSayHello.setOnCheckedChangeListener(null);
281
        mSwitchSayHello.setChecked(canSayHello);
282
        mSwitchSayHello.setOnCheckedChangeListener(this);
283
    }
284
 
285
    private void updateMessage(String message) {
286
        mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
287
        mEditMessage.removeTextChangedListener(mWatcherMessage);
288
        mEditMessage.setText(message);
289
        mEditMessage.addTextChangedListener(mWatcherMessage);
290
    }
291
 
292
    private void updateNumber(int number) {
293
        mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
294
        mEditNumber.removeTextChangedListener(mWatcherNumber);
295
        mEditNumber.setText(String.valueOf(number));
296
        mEditNumber.addTextChangedListener(mWatcherNumber);
297
    }
298
 
299
    private void updateRank(Context context, String[] ranks, String selectedRank) {
300
        mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank);
301
        mSpinnerRank.setAdapter(new ArrayAdapter<>(context,
302
                android.R.layout.simple_spinner_dropdown_item, ranks));
303
        mSpinnerRank.setSelection(search(ranks, selectedRank));
304
        mSpinnerRank.setOnItemSelectedListener(this);
305
    }
306
 
307
    private void updateApprovals(Context context, String[] approvals,
308
                                 String[] selectedApprovals) {
309
        mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals);
310
        mLayoutApprovals.removeAllViews();
311
        for (String approval : approvals) {
312
            Switch sw = new Switch(context);
313
            sw.setText(approval);
314
            sw.setTag(approval);
315
            sw.setChecked(Arrays.asList(selectedApprovals).contains(approval));
316
            sw.setOnCheckedChangeListener(this);
317
            sw.setId(R.id.approval);
318
            mLayoutApprovals.addView(sw);
319
        }
320
    }
321
 
322
    private void updateItems(Context context, Map<String, String> items) {
323
        if (!BUNDLE_SUPPORTED) {
324
            return;
325
        }
326
        mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items));
327
        LayoutInflater inflater = LayoutInflater.from(context);
328
        mLayoutItems.removeAllViews();
329
        for (String key : items.keySet()) {
330
            insertItemRow(inflater, key, items.get(key));
331
        }
332
    }
333
 
334
    private void insertItemRow(LayoutInflater inflater, String key, String value) {
335
        View view = inflater.inflate(R.layout.item, mLayoutItems, false);
336
        TextView textView = (TextView) view.findViewById(R.id.item_text);
337
        textView.setText(getString(R.string.item, key, value));
338
        Button remove = (Button) view.findViewById(R.id.item_remove);
339
        remove.setTag(key);
340
        remove.setOnClickListener(this);
341
        mLayoutItems.addView(view);
342
    }
343
 
344
    @NonNull
345
    private Bundle[] convertToBundles(Map<String, String> items) {
346
        Bundle[] bundles = new Bundle[items.size()];
347
        int i = 0;
348
        for (String key : items.keySet()) {
349
            Bundle bundle = new Bundle();
350
            bundle.putString(RESTRICTION_KEY_ITEM_KEY, key);
351
            bundle.putString(RESTRICTION_KEY_ITEM_VALUE, items.get(key));
352
            bundles[i++] = bundle;
353
        }
354
        return bundles;
355
    }
356
 
357
    private void removeItem(String key) {
358
        Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS);
359
        if (parcelables != null) {
360
            Map<String, String> items = new HashMap<>();
361
            for (Parcelable parcelable : parcelables) {
362
                Bundle bundle = (Bundle) parcelable;
363
                if (!key.equals(bundle.getString(RESTRICTION_KEY_ITEM_KEY))) {
364
                    items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY),
365
                            bundle.getString(RESTRICTION_KEY_ITEM_VALUE));
366
                }
367
            }
368
            saveItems(getActivity(), items);
369
        }
370
    }
371
 
372
    /**
373
     * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
374
     *
375
     * @param activity The activity
376
     * @param allow    The value to be set for the restriction.
377
     */
378
    private void saveCanSayHello(Activity activity, boolean allow) {
379
        mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
380
        saveRestrictions(activity);
381
        // Note that the owner app needs to remember the restrictions on its own.
382
        editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply();
383
    }
384
 
385
    /**
386
     * Saves the value for the "message" restriction of AppRestrictionSchema.
387
     *
388
     * @param activity The activity
389
     * @param message  The value to be set for the restriction.
390
     */
391
    private void saveMessage(Activity activity, String message) {
392
        mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
393
        saveRestrictions(activity);
394
        editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply();
395
    }
396
 
397
    /**
398
     * Saves the value for the "number" restriction of AppRestrictionSchema.
399
     *
400
     * @param activity The activity
401
     * @param number   The value to be set for the restriction.
402
     */
403
    private void saveNumber(Activity activity, int number) {
404
        mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
405
        saveRestrictions(activity);
406
        editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply();
407
    }
408
 
409
    /**
410
     * Saves the value for the "rank" restriction of AppRestrictionSchema.
411
     *
412
     * @param activity The activity
413
     * @param rank     The value to be set for the restriction.
414
     */
415
    private void saveRank(Activity activity, String rank) {
416
        mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank);
417
        saveRestrictions(activity);
418
        editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply();
419
    }
420
 
421
    private void addApproval(Activity activity, String approval) {
422
        List<String> approvals = new ArrayList<>(Arrays.asList(
423
                mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
424
        if (approvals.contains(approval)) {
425
            return;
426
        }
427
        approvals.add(approval);
428
        saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
429
    }
430
 
431
    private void removeApproval(Activity activity, String approval) {
432
        List<String> approvals = new ArrayList<>(Arrays.asList(
433
                mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
434
        if (!approval.contains(approval)) {
435
            return;
436
        }
437
        approvals.remove(approval);
438
        saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
439
    }
440
 
441
    /**
442
     * Saves the value for the "approvals" restriction of AppRestrictionSchema.
443
     *
444
     * @param activity  The activity
445
     * @param approvals The value to be set for the restriction.
446
     */
447
    private void saveApprovals(Activity activity, String[] approvals) {
448
        mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals);
449
        saveRestrictions(activity);
450
        editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS,
451
                TextUtils.join(DELIMETER, approvals)).apply();
452
    }
453
 
454
    /**
455
     * Saves the value for the "items" restriction of AppRestrictionSchema.
456
     *
457
     * @param activity The activity.
458
     * @param items    The values.
459
     */
460
    private void saveItems(Activity activity, Map<String, String> items) {
461
        if (!BUNDLE_SUPPORTED) {
462
            ret