Home | History | Annotate | Download | only in com.example.android.apprestrictionenforcer
      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.Bundle;
     26 import android.support.annotation.Nullable;
     27 import android.support.v4.app.Fragment;
     28 import android.text.Editable;
     29 import android.text.TextUtils;
     30 import android.text.TextWatcher;
     31 import android.view.LayoutInflater;
     32 import android.view.View;
     33 import android.view.ViewGroup;
     34 import android.widget.AdapterView;
     35 import android.widget.ArrayAdapter;
     36 import android.widget.CompoundButton;
     37 import android.widget.EditText;
     38 import android.widget.LinearLayout;
     39 import android.widget.Spinner;
     40 import android.widget.Switch;
     41 import android.widget.Toast;
     42 
     43 import java.util.ArrayList;
     44 import java.util.Arrays;
     45 import java.util.List;
     46 
     47 /**
     48  * This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
     49  * sample.
     50  */
     51 public class AppRestrictionEnforcerFragment extends Fragment implements
     52         CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
     53 
     54     /**
     55      * Key for {@link SharedPreferences}
     56      */
     57     private static final String PREFS_KEY = "AppRestrictionEnforcerFragment";
     58 
     59     /**
     60      * Key for the boolean restriction in AppRestrictionSchema.
     61      */
     62     private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello";
     63 
     64     /**
     65      * Key for the string restriction in AppRestrictionSchema.
     66      */
     67     private static final String RESTRICTION_KEY_MESSAGE = "message";
     68 
     69     /**
     70      * Key for the integer restriction in AppRestrictionSchema.
     71      */
     72     private static final String RESTRICTION_KEY_NUMBER = "number";
     73 
     74     /**
     75      * Key for the choice restriction in AppRestrictionSchema.
     76      */
     77     private static final String RESTRICTION_KEY_RANK = "rank";
     78 
     79     /**
     80      * Key for the multi-select restriction in AppRestrictionSchema.
     81      */
     82     private static final String RESTRICTION_KEY_APPROVALS = "approvals";
     83 
     84     private static final String DELIMETER = ",";
     85 
     86     /**
     87      * Current status of the restrictions.
     88      */
     89     private Bundle mCurrentRestrictions = new Bundle();
     90 
     91     // UI Components
     92     private Switch mSwitchSayHello;
     93     private EditText mEditMessage;
     94     private EditText mEditNumber;
     95     private Spinner mSpinnerRank;
     96     private LinearLayout mLayoutApprovals;
     97 
     98     @Override
     99     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
    100                              @Nullable Bundle savedInstanceState) {
    101         return inflater.inflate(R.layout.fragment_app_restriction_enforcer, container, false);
    102     }
    103 
    104     @Override
    105     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    106         // Retain references for the UI elements
    107         mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello);
    108         mEditMessage = (EditText) view.findViewById(R.id.message);
    109         mEditNumber = (EditText) view.findViewById(R.id.number);
    110         mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
    111         mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
    112     }
    113 
    114     @Override
    115     public void onResume() {
    116         super.onResume();
    117         loadRestrictions(getActivity());
    118     }
    119 
    120     @Override
    121     public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
    122         switch (compoundButton.getId()) {
    123             case R.id.say_hello: {
    124                 saveCanSayHello(getActivity(), checked);
    125                 break;
    126             }
    127             case R.id.approval: {
    128                 if (checked) {
    129                     addApproval(getActivity(), (String) compoundButton.getTag());
    130                 } else {
    131                     removeApproval(getActivity(), (String) compoundButton.getTag());
    132                 }
    133                 break;
    134             }
    135         }
    136     }
    137 
    138     private TextWatcher mWatcherMessage = new EasyTextWatcher() {
    139         @Override
    140         public void afterTextChanged(Editable s) {
    141             saveMessage(getActivity(), s.toString());
    142         }
    143     };
    144 
    145     private TextWatcher mWatcherNumber = new EasyTextWatcher() {
    146         @Override
    147         public void afterTextChanged(Editable s) {
    148             try {
    149                 String string = s.toString();
    150                 if (!TextUtils.isEmpty(string)) {
    151                     saveNumber(getActivity(), Integer.parseInt(string));
    152                 }
    153             } catch (NumberFormatException e) {
    154                 Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
    155             }
    156         }
    157     };
    158 
    159     @Override
    160     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    161         switch (parent.getId()) {
    162             case R.id.rank: {
    163                 saveRank(getActivity(), (String) parent.getAdapter().getItem(position));
    164                 break;
    165             }
    166         }
    167     }
    168 
    169     @Override
    170     public void onNothingSelected(AdapterView<?> parent) {
    171         // Nothing to do
    172     }
    173 
    174     /**
    175      * Loads the restrictions for the AppRestrictionSchema sample. In this implementation, we just
    176      * read the default value for the "can_say_hello" restriction.
    177      *
    178      * @param activity The activity
    179      */
    180     private void loadRestrictions(Activity activity) {
    181         RestrictionsManager manager =
    182                 (RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE);
    183         List<RestrictionEntry> restrictions =
    184                 manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
    185         SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
    186         for (RestrictionEntry restriction : restrictions) {
    187             String key = restriction.getKey();
    188             if (RESTRICTION_KEY_SAY_HELLO.equals(key)) {
    189                 updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO,
    190                         restriction.getSelectedState()));
    191             } else if (RESTRICTION_KEY_MESSAGE.equals(key)) {
    192                 updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE,
    193                         restriction.getSelectedString()));
    194             } else if (RESTRICTION_KEY_NUMBER.equals(key)) {
    195                 updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER,
    196                         restriction.getIntValue()));
    197             } else if (RESTRICTION_KEY_RANK.equals(key)) {
    198                 updateRank(activity, restriction.getChoiceValues(),
    199                         prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString()));
    200             } else if (RESTRICTION_KEY_APPROVALS.equals(key)) {
    201                 updateApprovals(activity, restriction.getChoiceValues(),
    202                         TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS,
    203                                         TextUtils.join(DELIMETER,
    204                                                 restriction.getAllSelectedStrings())),
    205                                 DELIMETER));
    206             }
    207         }
    208     }
    209 
    210     private void updateCanSayHello(boolean canSayHello) {
    211         mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello);
    212         mSwitchSayHello.setOnCheckedChangeListener(null);
    213         mSwitchSayHello.setChecked(canSayHello);
    214         mSwitchSayHello.setOnCheckedChangeListener(this);
    215     }
    216 
    217     private void updateMessage(String message) {
    218         mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
    219         mEditMessage.removeTextChangedListener(mWatcherMessage);
    220         mEditMessage.setText(message);
    221         mEditMessage.addTextChangedListener(mWatcherMessage);
    222     }
    223 
    224     private void updateNumber(int number) {
    225         mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
    226         mEditNumber.removeTextChangedListener(mWatcherNumber);
    227         mEditNumber.setText(String.valueOf(number));
    228         mEditNumber.addTextChangedListener(mWatcherNumber);
    229     }
    230 
    231     private void updateRank(Context context, String[] ranks, String selectedRank) {
    232         mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank);
    233         mSpinnerRank.setAdapter(new ArrayAdapter<>(context,
    234                 android.R.layout.simple_spinner_dropdown_item, ranks));
    235         mSpinnerRank.setSelection(search(ranks, selectedRank));
    236         mSpinnerRank.setOnItemSelectedListener(this);
    237     }
    238 
    239     private void updateApprovals(Context context, String[] approvals,
    240                                  String[] selectedApprovals) {
    241         mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals);
    242         mLayoutApprovals.removeAllViews();
    243         for (String approval : approvals) {
    244             Switch sw = new Switch(context);
    245             sw.setText(approval);
    246             sw.setTag(approval);
    247             sw.setChecked(Arrays.asList(selectedApprovals).contains(approval));
    248             sw.setOnCheckedChangeListener(this);
    249             sw.setId(R.id.approval);
    250             mLayoutApprovals.addView(sw);
    251         }
    252     }
    253 
    254     /**
    255      * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
    256      *
    257      * @param activity The activity
    258      * @param allow    The value to be set for the restriction.
    259      */
    260     private void saveCanSayHello(Activity activity, boolean allow) {
    261         mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
    262         saveRestrictions(activity);
    263         // Note that the owner app needs to remember the restrictions on its own.
    264         editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply();
    265     }
    266 
    267     /**
    268      * Saves the value for the "message" restriction of AppRestrictionSchema.
    269      *
    270      * @param activity The activity
    271      * @param message  The value to be set for the restriction.
    272      */
    273     private void saveMessage(Activity activity, String message) {
    274         mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
    275         saveRestrictions(activity);
    276         editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply();
    277     }
    278 
    279     /**
    280      * Saves the value for the "number" restriction of AppRestrictionSchema.
    281      *
    282      * @param activity The activity
    283      * @param number   The value to be set for the restriction.
    284      */
    285     private void saveNumber(Activity activity, int number) {
    286         mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
    287         saveRestrictions(activity);
    288         editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply();
    289     }
    290 
    291     /**
    292      * Saves the value for the "rank" restriction of AppRestrictionSchema.
    293      *
    294      * @param activity The activity
    295      * @param rank     The value to be set for the restriction.
    296      */
    297     private void saveRank(Activity activity, String rank) {
    298         mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank);
    299         saveRestrictions(activity);
    300         editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply();
    301     }
    302 
    303     private void addApproval(Activity activity, String approval) {
    304         List<String> approvals = new ArrayList<>(Arrays.asList(
    305                 mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
    306         if (approvals.contains(approval)) {
    307             return;
    308         }
    309         approvals.add(approval);
    310         saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
    311     }
    312 
    313     private void removeApproval(Activity activity, String approval) {
    314         List<String> approvals = new ArrayList<>(Arrays.asList(
    315                 mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
    316         if (!approval.contains(approval)) {
    317             return;
    318         }
    319         approvals.remove(approval);
    320         saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
    321     }
    322 
    323     /**
    324      * Saves the value for the "approvals" restriction of AppRestrictionSchema.
    325      *
    326      * @param activity  The activity
    327      * @param approvals The value to be set for the restriction.
    328      */
    329     private void saveApprovals(Activity activity, String[] approvals) {
    330         mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals);
    331         saveRestrictions(activity);
    332         editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS,
    333                 TextUtils.join(DELIMETER, approvals)).apply();
    334     }
    335 
    336     private void saveRestrictions(Activity activity) {
    337         DevicePolicyManager devicePolicyManager
    338                 = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
    339         devicePolicyManager.setApplicationRestrictions(
    340                 EnforcerDeviceAdminReceiver.getComponentName(activity),
    341                 Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, mCurrentRestrictions);
    342     }
    343 
    344     private SharedPreferences.Editor editPreferences(Activity activity) {
    345         return activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE).edit();
    346     }
    347 
    348     /**
    349      * Sequential search
    350      *
    351      * @param array The string array
    352      * @param s     The string to search for
    353      * @return Index if found. -1 if not found.
    354      */
    355     private int search(String[] array, String s) {
    356         for (int i = 0; i < array.length; ++i) {
    357             if (s.equals(array[i])) {
    358                 return i;
    359             }
    360         }
    361         return -1;
    362     }
    363 
    364 }
    365