AppRestriction: Add examples for M

This adds examples for the following new restriction types introduced in
M.
- Bundle
- Bundle array

Bug: 24185361
Change-Id: I678da65899870612bd2203bc4aa1d850cb38d39e
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java
index 8b0620f..e30a9a4 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java
@@ -23,6 +23,8 @@
 import android.content.RestrictionsManager;
 import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
 import android.text.Editable;
@@ -33,23 +35,28 @@
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
 import android.widget.CompoundButton;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 import android.widget.Spinner;
 import android.widget.Switch;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
  * sample.
  */
 public class AppRestrictionEnforcerFragment extends Fragment implements
-        CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
+        CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener,
+        View.OnClickListener, ItemAddFragment.OnItemAddedListener {
 
     /**
      * Key for {@link SharedPreferences}
@@ -81,7 +88,22 @@
      */
     private static final String RESTRICTION_KEY_APPROVALS = "approvals";
 
+    /**
+     * Key for the bundle restriction in AppRestrictionSchema.
+     */
+    private static final String RESTRICTION_KEY_PROFILE = "profile";
+    private static final String RESTRICTION_KEY_PROFILE_NAME = "name";
+    private static final String RESTRICTION_KEY_PROFILE_AGE = "age";
+
+    /**
+     * Key for the bundle array restriction in AppRestrictionSchema.
+     */
+    private static final String RESTRICTION_KEY_ITEMS = "items";
+    private static final String RESTRICTION_KEY_ITEM_KEY = "key";
+    private static final String RESTRICTION_KEY_ITEM_VALUE = "value";
+
     private static final String DELIMETER = ",";
+    private static final String SEPARATOR = ":";
 
     /**
      * Current status of the restrictions.
@@ -94,6 +116,9 @@
     private EditText mEditNumber;
     private Spinner mSpinnerRank;
     private LinearLayout mLayoutApprovals;
+    private EditText mEditProfileName;
+    private EditText mEditProfileAge;
+    private LinearLayout mLayoutItems;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -109,6 +134,10 @@
         mEditNumber = (EditText) view.findViewById(R.id.number);
         mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
         mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
+        mEditProfileName = (EditText) view.findViewById(R.id.profile_name);
+        mEditProfileAge = (EditText) view.findViewById(R.id.profile_age);
+        mLayoutItems = (LinearLayout) view.findViewById(R.id.items);
+        view.findViewById(R.id.item_add).setOnClickListener(this);
     }
 
     @Override
@@ -156,6 +185,21 @@
         }
     };
 
+    private TextWatcher mWatcherProfile = new EasyTextWatcher() {
+        @Override
+        public void afterTextChanged(Editable s) {
+            try {
+                String name = mEditProfileName.getText().toString();
+                String ageString = mEditProfileAge.getText().toString();
+                if (!TextUtils.isEmpty(ageString)) {
+                    saveProfile(getActivity(), name, Integer.parseInt(ageString));
+                }
+            } catch (NumberFormatException e) {
+                Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
+            }
+        }
+    };
+
     @Override
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         switch (parent.getId()) {
@@ -171,9 +215,42 @@
         // Nothing to do
     }
 
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.item_add:
+                new ItemAddFragment().show(getChildFragmentManager(), "dialog");
+                break;
+            case R.id.item_remove:
+                String key = (String) v.getTag();
+                removeItem(key);
+                mLayoutItems.removeView((View) v.getParent());
+                break;
+        }
+    }
+
+    @Override
+    public void onItemAdded(String key, String value) {
+        key = TextUtils.replace(key,
+                new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString();
+        value = TextUtils.replace(value,
+                new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString();
+        Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS);
+        Map<String, String> items = new HashMap<>();
+        if (parcelables != null) {
+            for (Parcelable parcelable : parcelables) {
+                Bundle bundle = (Bundle) parcelable;
+                items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY),
+                        bundle.getString(RESTRICTION_KEY_ITEM_VALUE));
+            }
+        }
+        items.put(key, value);
+        insertItemRow(LayoutInflater.from(getActivity()), key, value);
+        saveItems(getActivity(), items);
+    }
+
     /**
-     * Loads the restrictions for the AppRestrictionSchema sample. In this implementation, we just
-     * read the default value for the "can_say_hello" restriction.
+     * Loads the restrictions for the AppRestrictionSchema sample.
      *
      * @param activity The activity
      */
@@ -203,6 +280,28 @@
                                         TextUtils.join(DELIMETER,
                                                 restriction.getAllSelectedStrings())),
                                 DELIMETER));
+            } else if (RESTRICTION_KEY_PROFILE.equals(key)) {
+                String name = null;
+                int age = 0;
+                for (RestrictionEntry entry : restriction.getRestrictions()) {
+                    String profileKey = entry.getKey();
+                    if (RESTRICTION_KEY_PROFILE_NAME.equals(profileKey)) {
+                        name = entry.getSelectedString();
+                    } else if (RESTRICTION_KEY_PROFILE_AGE.equals(profileKey)) {
+                        age = entry.getIntValue();
+                    }
+                }
+                name = prefs.getString(RESTRICTION_KEY_PROFILE_NAME, name);
+                age = prefs.getInt(RESTRICTION_KEY_PROFILE_AGE, age);
+                updateProfile(name, age);
+            } else if (RESTRICTION_KEY_ITEMS.equals(key)) {
+                String itemsString = prefs.getString(RESTRICTION_KEY_ITEMS, "");
+                HashMap<String, String> items = new HashMap<>();
+                for (String itemString : TextUtils.split(itemsString, DELIMETER)) {
+                    String[] strings = itemString.split(SEPARATOR, 2);
+                    items.put(strings[0], strings[1]);
+                }
+                updateItems(activity, items);
             }
         }
     }
@@ -251,6 +350,66 @@
         }
     }
 
+    private void updateProfile(String name, int age) {
+        Bundle profile = new Bundle();
+        profile.putString(RESTRICTION_KEY_PROFILE_NAME, name);
+        profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age);
+        mCurrentRestrictions.putBundle(RESTRICTION_KEY_PROFILE, profile);
+        mEditProfileName.removeTextChangedListener(mWatcherProfile);
+        mEditProfileName.setText(name);
+        mEditProfileName.addTextChangedListener(mWatcherProfile);
+        mEditProfileAge.removeTextChangedListener(mWatcherProfile);
+        mEditProfileAge.setText(String.valueOf(age));
+        mEditProfileAge.addTextChangedListener((mWatcherProfile));
+    }
+
+    private void updateItems(Context context, Map<String, String> items) {
+        mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items));
+        LayoutInflater inflater = LayoutInflater.from(context);
+        mLayoutItems.removeAllViews();
+        for (String key : items.keySet()) {
+            insertItemRow(inflater, key, items.get(key));
+        }
+    }
+
+    private void insertItemRow(LayoutInflater inflater, String key, String value) {
+        View view = inflater.inflate(R.layout.item, mLayoutItems, false);
+        TextView textView = (TextView) view.findViewById(R.id.item_text);
+        textView.setText(getString(R.string.item, key, value));
+        Button remove = (Button) view.findViewById(R.id.item_remove);
+        remove.setTag(key);
+        remove.setOnClickListener(this);
+        mLayoutItems.addView(view);
+    }
+
+    @NonNull
+    private Bundle[] convertToBundles(Map<String, String> items) {
+        Bundle[] bundles = new Bundle[items.size()];
+        int i = 0;
+        for (String key : items.keySet()) {
+            Bundle bundle = new Bundle();
+            bundle.putString(RESTRICTION_KEY_ITEM_KEY, key);
+            bundle.putString(RESTRICTION_KEY_ITEM_VALUE, items.get(key));
+            bundles[i++] = bundle;
+        }
+        return bundles;
+    }
+
+    private void removeItem(String key) {
+        Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS);
+        if (parcelables != null) {
+            Map<String, String> items = new HashMap<>();
+            for (Parcelable parcelable : parcelables) {
+                Bundle bundle = (Bundle) parcelable;
+                if (!key.equals(bundle.getString(RESTRICTION_KEY_ITEM_KEY))) {
+                    items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY),
+                            bundle.getString(RESTRICTION_KEY_ITEM_VALUE));
+                }
+            }
+            saveItems(getActivity(), items);
+        }
+    }
+
     /**
      * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
      *
@@ -333,6 +492,51 @@
                 TextUtils.join(DELIMETER, approvals)).apply();
     }
 
+    /**
+     * Saves the value for the "profile" restriction of AppRestrictionSchema.
+     *
+     * @param activity The activity
+     * @param name     The value to be set for the "name" field.
+     * @param age      The value to be set for the "age" field.
+     */
+    private void saveProfile(Activity activity, String name, int age) {
+        Bundle profile = new Bundle();
+        profile.putString(RESTRICTION_KEY_PROFILE_NAME, name);
+        profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age);
+        mCurrentRestrictions.putBundle(RESTRICTION_KEY_PROFILE, profile);
+        saveRestrictions(activity);
+        editPreferences(activity).putString(RESTRICTION_KEY_PROFILE_NAME, name).apply();
+    }
+
+    /**
+     * Saves the value for the "items" restriction of AppRestrictionSchema.
+     *
+     * @param activity The activity.
+     * @param items    The values.
+     */
+    private void saveItems(Activity activity, Map<String, String> items) {
+        mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items));
+        saveRestrictions(activity);
+        StringBuilder builder = new StringBuilder();
+        boolean first = true;
+        for (String key : items.keySet()) {
+            if (first) {
+                first = false;
+            } else {
+                builder.append(DELIMETER);
+            }
+            builder.append(key);
+            builder.append(SEPARATOR);
+            builder.append(items.get(key));
+        }
+        editPreferences(activity).putString(RESTRICTION_KEY_ITEMS, builder.toString()).apply();
+    }
+
+    /**
+     * Saves all the restrictions.
+     *
+     * @param activity The activity.
+     */
     private void saveRestrictions(Activity activity) {
         DevicePolicyManager devicePolicyManager
                 = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/ItemAddFragment.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/ItemAddFragment.java
new file mode 100644
index 0000000..cda2726
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/ItemAddFragment.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apprestrictionenforcer;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.Toast;
+
+/**
+ * Provides a dialog to create a new restriction item for the sample bundle array.
+ */
+public class ItemAddFragment extends DialogFragment implements View.OnClickListener {
+
+    public interface OnItemAddedListener {
+        void onItemAdded(String key, String value);
+    }
+
+    private OnItemAddedListener mListener;
+    private EditText mEditKey;
+    private EditText mEditValue;
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        Fragment parentFragment = getParentFragment();
+        mListener = (OnItemAddedListener) (parentFragment == null ? activity : parentFragment);
+    }
+
+    @Override
+    public void onDetach() {
+        mListener = null;
+        super.onDetach();
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        getDialog().setTitle(R.string.add_item);
+        return inflater.inflate(R.layout.fragment_item_add, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        mEditKey = (EditText) view.findViewById(R.id.key);
+        mEditValue = (EditText) view.findViewById(R.id.value);
+        view.findViewById(R.id.ok).setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.ok:
+                if (addItem()) {
+                    dismiss();
+                }
+                break;
+        }
+    }
+
+    private boolean addItem() {
+        String key = mEditKey.getText().toString();
+        if (TextUtils.isEmpty(key)) {
+            Toast.makeText(getActivity(), "Input the key.", Toast.LENGTH_SHORT).show();
+            return false;
+        }
+        String value = mEditValue.getText().toString();
+        if (TextUtils.isEmpty(value)) {
+            Toast.makeText(getActivity(), "Input the value.", Toast.LENGTH_SHORT).show();
+            return false;
+        }
+        if (mListener != null) {
+            mListener.onItemAdded(key, value);
+        }
+        return true;
+    }
+
+}
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml
index 0118191..56c9133 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml
@@ -119,6 +119,65 @@
 
         </LinearLayout>
 
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/profile"/>
+
+            <EditText
+                android:id="@+id/profile_name"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.75"
+                android:hint="@string/name"/>
+
+            <EditText
+                android:id="@+id/profile_age"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.25"
+                android:hint="@string/age"
+                android:inputType="number"/>
+
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:id="@+id/items_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:text="@string/items"/>
+
+            <LinearLayout
+                android:id="@+id/items"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentTop="true"
+                android:layout_toEndOf="@id/items_label"
+                android:orientation="vertical"
+                android:paddingEnd="16dp"
+                android:paddingStart="16dp"/>
+
+            <Button
+                android:id="@+id/item_add"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/items"
+                android:layout_toEndOf="@id/items_label"
+                android:text="@string/add"/>
+
+        </RelativeLayout>
+
     </LinearLayout>
 
 </ScrollView>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_item_add.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_item_add.xml
new file mode 100644
index 0000000..f60bb15
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_item_add.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <EditText
+        android:id="@+id/key"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:hint="@string/key"/>
+
+    <EditText
+        android:id="@+id/value"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_marginStart="16dp"
+        android:hint="@string/value"/>
+
+    <Button
+        android:id="@+id/ok"
+        style="?android:attr/buttonBarButtonStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@android:string/ok"/>
+
+</LinearLayout>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/item.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/item.xml
new file mode 100644
index 0000000..66e6b3d
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/item.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/item_text"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        tools:text="key:value"/>
+
+    <Button
+        android:id="@+id/item_remove"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/remove"/>
+
+</LinearLayout>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
index e35daee..ead4152 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
@@ -29,4 +29,14 @@
     <string name="number">Number: </string>
     <string name="rank">Rank: </string>
     <string name="approvals">Approvals: </string>
+    <string name="profile">Profile: </string>
+    <string name="name">Name</string>
+    <string name="age">Age</string>
+    <string name="items">Items: </string>
+    <string name="add">Add</string>
+    <string name="key">Key</string>
+    <string name="value">Value</string>
+    <string name="remove">Remove</string>
+    <string name="item">%1$s: %2$s</string>
+    <string name="add_item">Add a new item</string>
 </resources>
diff --git a/admin/AppRestrictionEnforcer/template-params.xml b/admin/AppRestrictionEnforcer/template-params.xml
index ff1a7a0..3fe913d 100644
--- a/admin/AppRestrictionEnforcer/template-params.xml
+++ b/admin/AppRestrictionEnforcer/template-params.xml
@@ -22,8 +22,7 @@
     <group>Admin</group>
     <package>com.example.android.apprestrictionenforcer</package>
 
-    <minSdk>21</minSdk>
-    <compileSdkVersion>21</compileSdkVersion>
+    <minSdk>23</minSdk>
 
     <strings>
         <intro>
diff --git a/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java b/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java
index 7b8dba8..ea1aad8 100644
--- a/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java
+++ b/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java
@@ -20,6 +20,7 @@
 import android.content.RestrictionEntry;
 import android.content.RestrictionsManager;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
 import android.text.TextUtils;
@@ -49,6 +50,12 @@
     private static final String KEY_NUMBER = "number";
     private static final String KEY_RANK = "rank";
     private static final String KEY_APPROVALS = "approvals";
+    private static final String KEY_PROFILE = "profile";
+    private static final String KEY_PROFILE_NAME = "name";
+    private static final String KEY_PROFILE_AGE = "age";
+    private static final String KEY_ITEMS = "items";
+    private static final String KEY_ITEM_KEY = "key";
+    private static final String KEY_ITEM_VALUE = "value";
 
     // Message to show when the button is clicked (String restriction)
     private String mMessage;
@@ -59,6 +66,8 @@
     private TextView mTextNumber;
     private TextView mTextRank;
     private TextView mTextApprovals;
+    private TextView mTextProfile;
+    private TextView mTextItems;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -73,6 +82,8 @@
         mTextNumber = (TextView) view.findViewById(R.id.your_number);
         mTextRank = (TextView) view.findViewById(R.id.your_rank);
         mTextApprovals = (TextView) view.findViewById(R.id.approvals_you_have);
+        mTextProfile = (TextView) view.findViewById(R.id.your_profile);
+        mTextItems = (TextView) view.findViewById(R.id.your_items);
         mButtonSayHello.setOnClickListener(this);
     }
 
@@ -86,7 +97,8 @@
         RestrictionsManager manager =
                 (RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
         Bundle restrictions = manager.getApplicationRestrictions();
-        List<RestrictionEntry> entries = manager.getManifestRestrictions(getActivity().getApplicationContext().getPackageName());
+        List<RestrictionEntry> entries = manager.getManifestRestrictions(
+                getActivity().getApplicationContext().getPackageName());
         for (RestrictionEntry entry : entries) {
             String key = entry.getKey();
             Log.d(TAG, "key: " + key);
@@ -100,6 +112,10 @@
                 updateRank(entry, restrictions);
             } else if (key.equals(KEY_APPROVALS)) {
                 updateApprovals(entry, restrictions);
+            } else if (key.equals(KEY_PROFILE)) {
+                updateProfile(entry, restrictions);
+            } else if (key.equals(KEY_ITEMS)) {
+                updateItems(entry, restrictions);
             }
         }
     }
@@ -161,6 +177,61 @@
         mTextApprovals.setText(getString(R.string.approvals_you_have, text));
     }
 
+    private void updateProfile(RestrictionEntry entry, Bundle restrictions) {
+        String name = null;
+        int age = 0;
+        if (restrictions == null || !restrictions.containsKey(KEY_PROFILE)) {
+            RestrictionEntry[] entries = entry.getRestrictions();
+            for (RestrictionEntry profileEntry : entries) {
+                String key = profileEntry.getKey();
+                if (key.equals(KEY_PROFILE_NAME)) {
+                    name = profileEntry.getSelectedString();
+                } else if (key.equals(KEY_PROFILE_AGE)) {
+                    age = profileEntry.getIntValue();
+                }
+            }
+        } else {
+            Bundle profile = restrictions.getBundle(KEY_PROFILE);
+            if (profile != null) {
+                name = profile.getString(KEY_PROFILE_NAME);
+                age = profile.getInt(KEY_PROFILE_AGE);
+            }
+        }
+        mTextProfile.setText(getString(R.string.your_profile, name, age));
+    }
+
+    private void updateItems(RestrictionEntry entry, Bundle restrictions) {
+        StringBuilder builder = new StringBuilder();
+        if (restrictions != null) {
+            Parcelable[] parcelables = restrictions.getParcelableArray(KEY_ITEMS);
+            if (parcelables != null && parcelables.length > 0) {
+                Bundle[] items = new Bundle[parcelables.length];
+                for (int i = 0; i < parcelables.length; i++) {
+                    items[i] = (Bundle) parcelables[i];
+                }
+                boolean first = true;
+                for (Bundle item : items) {
+                    if (!item.containsKey(KEY_ITEM_KEY) || !item.containsKey(KEY_ITEM_VALUE)) {
+                        continue;
+                    }
+                    if (first) {
+                        first = false;
+                    } else {
+                        builder.append(", ");
+                    }
+                    builder.append(item.getString(KEY_ITEM_KEY));
+                    builder.append(":");
+                    builder.append(item.getString(KEY_ITEM_VALUE));
+                }
+            } else {
+                builder.append(getString(R.string.none));
+            }
+        } else {
+            builder.append(getString(R.string.none));
+        }
+        mTextItems.setText(getString(R.string.your_items, builder));
+    }
+
     @Override
     public void onClick(View view) {
         switch (view.getId()) {
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml b/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml
index 18ca0a4..8570869 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml
@@ -68,6 +68,24 @@
             android:textAppearance="?android:attr/textAppearanceMedium"
             tools:text="@string/approvals_you_have"/>
 
+        <include layout="@layout/separator"/>
+
+        <TextView
+            android:id="@+id/your_profile"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/your_profile"/>
+
+        <include layout="@layout/separator"/>
+
+        <TextView
+            android:id="@+id/your_items"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/your_items"/>
+
     </LinearLayout>
 
 </ScrollView>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml b/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
index 558d097..53be746 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
@@ -74,4 +74,23 @@
     <string name="description_secret_code">This restriction is hidden and will not be shown to the administrator.</string>
     <string name="default_secret_code">(Hidden restriction must have some default value)</string>
 
+    <!-- Bundle restriction -->
+    <string name="description_profile">Sample profile</string>
+    <string name="title_profile">Profile</string>
+
+    <string name="default_profile_name">John</string>
+    <string name="description_profile_name">The name of this person</string>
+    <string name="title_profile_name">Name</string>
+
+    <integer name="default_profile_age">25</integer>
+    <string name="description_profile_age">The age of this person</string>
+    <string name="title_profile_age">Age</string>
+
+    <!-- Bundle array restriction -->
+    <string name="description_items">Sample items</string>
+    <string name="title_items">Items</string>
+    <string name="title_item">Item</string>
+    <string name="title_key">Key</string>
+    <string name="title_value">Value</string>
+
 </resources>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml b/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
index 6dce123..1ec68d5 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
@@ -25,5 +25,7 @@
     <string name="your_rank">Your rank: %s</string>
     <string name="approvals_you_have">Approvals you have: %s</string>
     <string name="none">none</string>
+    <string name="your_profile">Your profile: %1$s (%2$d)</string>
+    <string name="your_items">Your items: %s</string>
 
 </resources>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml b/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
index 9e47f45..1e2ea45 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 Copyright 2014 The Android Open Source Project
 
 Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +20,7 @@
     https://developer.android.com/reference/android/content/RestrictionsManager.html
     -->
 
+    <!-- Boolean restriction -->
     <restriction
         android:defaultValue="@bool/default_can_say_hello"
         android:description="@string/description_can_say_hello"
@@ -28,6 +28,7 @@
         android:restrictionType="bool"
         android:title="@string/title_can_say_hello"/>
 
+    <!-- String restriction -->
     <restriction
         android:defaultValue="@string/default_message"
         android:description="@string/description_message"
@@ -35,6 +36,7 @@
         android:restrictionType="string"
         android:title="@string/title_message"/>
 
+    <!-- Integer restriction -->
     <restriction
         android:defaultValue="@integer/default_number"
         android:description="@string/description_number"
@@ -42,6 +44,7 @@
         android:restrictionType="integer"
         android:title="@string/title_number"/>
 
+    <!-- Choice restriction -->
     <restriction
         android:defaultValue="@string/default_rank"
         android:description="@string/description_rank"
@@ -51,6 +54,7 @@
         android:restrictionType="choice"
         android:title="@string/title_rank"/>
 
+    <!-- Multi-select restriction -->
     <restriction
         android:defaultValue="@array/default_approvals"
         android:description="@string/description_approvals"
@@ -60,6 +64,7 @@
         android:restrictionType="multi-select"
         android:title="@string/title_approvals"/>
 
+    <!-- Hidden restriction -->
     <restriction
         android:defaultValue="@string/default_secret_code"
         android:description="@string/description_secret_code"
@@ -67,4 +72,46 @@
         android:restrictionType="hidden"
         android:title="@string/title_secret_code"/>
 
+    <!-- Bundle restriction; useful for grouping restrictions -->
+    <restriction
+        android:description="@string/description_profile"
+        android:key="profile"
+        android:restrictionType="bundle"
+        android:title="@string/title_profile">
+        <restriction
+            android:defaultValue="@string/default_profile_name"
+            android:description="@string/description_profile_name"
+            android:key="name"
+            android:restrictionType="string"
+            android:title="@string/title_profile_name"/>
+        <restriction
+            android:defaultValue="@integer/default_profile_age"
+            android:description="@string/description_profile_age"
+            android:key="age"
+            android:restrictionType="integer"
+            android:title="@string/title_profile_age"/>
+    </restriction>
+
+    <!-- Bundle array restriction -->
+    <restriction
+        android:description="@string/description_items"
+        android:key="items"
+        android:restrictionType="bundle_array"
+        android:title="@string/title_items">
+        <!-- Bundle array must have one bundle restriction -->
+        <restriction
+            android:key="item"
+            android:restrictionType="bundle"
+            android:title="@string/title_item">
+            <restriction
+                android:key="key"
+                android:restrictionType="string"
+                android:title="@string/title_key"/>
+            <restriction
+                android:key="value"
+                android:restrictionType="string"
+                android:title="@string/title_value"/>
+        </restriction>
+    </restriction>
+
 </restrictions>
diff --git a/admin/AppRestrictionSchema/template-params.xml b/admin/AppRestrictionSchema/template-params.xml
index 6603034..3e7a202 100644
--- a/admin/AppRestrictionSchema/template-params.xml
+++ b/admin/AppRestrictionSchema/template-params.xml
@@ -20,8 +20,7 @@
     <group>Admin</group>
     <package>com.example.android.apprestrictionschema</package>
 
-    <minSdk>21</minSdk>
-    <compileSdkVersion>21</compileSdkVersion>
+    <minSdk>23</minSdk>
 
     <strings>
         <intro>