AppRestrictionsFragment.java revision 5ee175051951d3a0bc1533aece69af520c295d37
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.users;
18
19import android.app.Activity;
20import android.app.AppGlobals;
21import android.appwidget.AppWidgetManager;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.RestrictionEntry;
26import android.content.pm.ApplicationInfo;
27import android.content.pm.IPackageManager;
28import android.content.pm.PackageInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageManager.NameNotFoundException;
31import android.content.pm.ResolveInfo;
32import android.content.pm.UserInfo;
33import android.graphics.Bitmap;
34import android.graphics.Color;
35import android.graphics.ColorFilter;
36import android.graphics.ColorMatrix;
37import android.graphics.ColorMatrixColorFilter;
38import android.graphics.drawable.BitmapDrawable;
39import android.graphics.drawable.ColorDrawable;
40import android.graphics.drawable.Drawable;
41import android.os.Bundle;
42import android.os.Parcelable;
43import android.os.RemoteException;
44import android.os.ServiceManager;
45import android.os.UserHandle;
46import android.os.UserManager;
47import android.preference.CheckBoxPreference;
48import android.preference.EditTextPreference;
49import android.preference.ListPreference;
50import android.preference.MultiSelectListPreference;
51import android.preference.Preference;
52import android.preference.PreferenceActivity;
53import android.preference.PreferenceCategory;
54import android.preference.Preference.OnPreferenceChangeListener;
55import android.preference.Preference.OnPreferenceClickListener;
56import android.preference.PreferenceGroup;
57import android.preference.SwitchPreference;
58import android.text.InputType;
59import android.text.TextUtils;
60import android.util.Log;
61import android.view.View;
62import android.view.View.OnClickListener;
63import android.view.ViewGroup;
64import android.widget.CompoundButton;
65import android.widget.CompoundButton.OnCheckedChangeListener;
66import android.widget.Switch;
67
68import com.android.settings.R;
69import com.android.settings.SelectableEditTextPreference;
70import com.android.settings.SettingsPreferenceFragment;
71
72import java.util.ArrayList;
73import java.util.Collection;
74import java.util.Collections;
75import java.util.Comparator;
76import java.util.HashMap;
77import java.util.HashSet;
78import java.util.List;
79import java.util.Map;
80import java.util.Set;
81import java.util.StringTokenizer;
82
83import libcore.util.CollectionUtils;
84
85public class AppRestrictionsFragment extends SettingsPreferenceFragment implements
86        OnPreferenceChangeListener, OnClickListener, OnPreferenceClickListener {
87
88    private static final String TAG = AppRestrictionsFragment.class.getSimpleName();
89
90    private static final boolean DEBUG = false;
91
92    private static final String PKG_PREFIX = "pkg_";
93    private static final String KEY_USER_INFO = "user_info";
94
95    private UserManager mUserManager;
96    private UserHandle mUser;
97
98    private SelectableEditTextPreference mUserPreference;
99    private PreferenceGroup mAppList;
100
101    private static final int MAX_APP_RESTRICTIONS = 100;
102
103    private static final String DELIMITER = ";";
104
105    /** Key for extra passed in from calling fragment for the userId of the user being edited */
106    public static final String EXTRA_USER_ID = "user_id";
107
108    /** Key for extra passed in from calling fragment to indicate if this is a newly created user */
109    public static final String EXTRA_NEW_USER = "new_user";
110
111    HashMap<String,Boolean> mSelectedPackages = new HashMap<String,Boolean>();
112    private boolean mFirstTime = true;
113    private boolean mNewUser;
114    private boolean mAppListChanged;
115
116    private int mCustomRequestCode;
117    private HashMap<Integer, AppRestrictionsPreference> mCustomRequestMap =
118            new HashMap<Integer,AppRestrictionsPreference>();
119
120    static class SelectableAppInfo {
121        String packageName;
122        CharSequence appName;
123        CharSequence activityName;
124        Drawable icon;
125        SelectableAppInfo masterEntry;
126
127        @Override
128        public String toString() {
129            return packageName + ": appName=" + appName + "; activityName=" + activityName
130                    + "; icon=" + icon + "; masterEntry=" + masterEntry;
131        }
132    }
133
134    static class AppRestrictionsPreference extends SwitchPreference {
135        private boolean hasSettings;
136        private OnClickListener listener;
137        private ArrayList<RestrictionEntry> restrictions;
138        boolean panelOpen;
139        private boolean immutable;
140        List<Preference> childPreferences = new ArrayList<Preference>();
141        private SelectableAppInfo appInfo;
142        private final ColorFilter grayscaleFilter;
143
144        AppRestrictionsPreference(Context context, OnClickListener listener) {
145            super(context);
146            setLayoutResource(R.layout.preference_app_restrictions);
147            this.listener = listener;
148
149            ColorMatrix colorMatrix = new ColorMatrix();
150            colorMatrix.setSaturation(0f);
151            float[] matrix = colorMatrix.getArray();
152            matrix[18] = 0.5f;
153            grayscaleFilter = new ColorMatrixColorFilter(colorMatrix);
154        }
155
156        private void setSettingsEnabled(boolean enable) {
157            hasSettings = enable;
158        }
159
160        @Override
161        public void setChecked(boolean checked) {
162            if (checked) {
163                getIcon().setColorFilter(null);
164            } else {
165                getIcon().setColorFilter(grayscaleFilter);
166            }
167            super.setChecked(checked);
168        }
169
170        void setRestrictions(ArrayList<RestrictionEntry> restrictions) {
171            this.restrictions = restrictions;
172        }
173
174        void setImmutable(boolean immutable) {
175            this.immutable = immutable;
176        }
177
178        boolean isImmutable() {
179            return immutable;
180        }
181
182        void setSelectableAppInfo(SelectableAppInfo appInfo) {
183            this.appInfo = appInfo;
184        }
185
186        RestrictionEntry getRestriction(String key) {
187            if (restrictions == null) return null;
188            for (RestrictionEntry entry : restrictions) {
189                if (entry.getKey().equals(key)) {
190                    return entry;
191                }
192            }
193            return null;
194        }
195
196        ArrayList<RestrictionEntry> getRestrictions() {
197            return restrictions;
198        }
199
200        @Override
201        protected void onBindView(View view) {
202            super.onBindView(view);
203
204            View appRestrictionsSettings = view.findViewById(R.id.app_restrictions_settings);
205            appRestrictionsSettings.setVisibility(hasSettings ? View.VISIBLE : View.GONE);
206            view.findViewById(R.id.settings_divider).setVisibility(
207                    hasSettings ? View.VISIBLE : View.GONE);
208            appRestrictionsSettings.setOnClickListener(listener);
209            appRestrictionsSettings.setTag(this);
210
211            View appRestrictionsPref = view.findViewById(R.id.app_restrictions_pref);
212            appRestrictionsPref.setOnClickListener(listener);
213            appRestrictionsPref.setTag(this);
214
215            ViewGroup widget = (ViewGroup) view.findViewById(android.R.id.widget_frame);
216            widget.setEnabled(!isImmutable());
217            if (widget.getChildCount() > 0) {
218                final Switch switchView = (Switch) widget.getChildAt(0);
219                switchView.setEnabled(!isImmutable());
220                switchView.setTag(this);
221                switchView.setOnCheckedChangeListener(new OnCheckedChangeListener() {
222                    @Override
223                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
224                        listener.onClick(switchView);
225                    }
226                });
227            }
228        }
229    }
230
231    @Override
232    public void onCreate(Bundle icicle) {
233        super.onCreate(icicle);
234
235        if (icicle != null) {
236            mNewUser = icicle.getBoolean(EXTRA_NEW_USER, false);
237            mUser = new UserHandle(icicle.getInt(EXTRA_USER_ID));
238        } else {
239            Bundle args = getArguments();
240
241            if (args.containsKey(EXTRA_USER_ID)) {
242                mUser = new UserHandle(args.getInt(EXTRA_USER_ID));
243            }
244            mNewUser = args.getBoolean(EXTRA_NEW_USER, false);
245        }
246        mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
247        addPreferencesFromResource(R.xml.app_restrictions);
248        mAppList = getPreferenceScreen();
249        mUserPreference = (SelectableEditTextPreference) findPreference(KEY_USER_INFO);
250        mUserPreference.setOnPreferenceChangeListener(this);
251        mUserPreference.getEditText().setInputType(
252                InputType.TYPE_TEXT_VARIATION_NORMAL | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
253        mUserPreference.setInitialSelectionMode(
254                SelectableEditTextPreference.SELECTION_SELECT_ALL);
255        setHasOptionsMenu(true);
256    }
257
258    @Override
259    public void onSaveInstanceState(Bundle outState) {
260        super.onSaveInstanceState(outState);
261        outState.putBoolean(EXTRA_NEW_USER, mNewUser);
262        outState.putInt(EXTRA_USER_ID, mUser.getIdentifier());
263    }
264
265    public void onResume() {
266        super.onResume();
267        mAppListChanged = false;
268        if (mFirstTime) {
269            mFirstTime = false;
270            populateApps();
271        }
272        UserInfo info = mUserManager.getUserInfo(mUser.getIdentifier());
273        mUserPreference.setTitle(info.name);
274        Bitmap userIcon = mUserManager.getUserIcon(mUser.getIdentifier());
275        CircleFramedDrawable circularIcon =
276                CircleFramedDrawable.getInstance(this.getActivity(), userIcon);
277        mUserPreference.setIcon(circularIcon);
278        mUserPreference.setText(info.name);
279    }
280
281    public void onPause() {
282        super.onPause();
283        if (mAppListChanged) {
284            new Thread() {
285                public void run() {
286                    updateUserAppList();
287                }
288            }.start();
289        }
290    }
291
292    private void updateUserAppList() {
293        IPackageManager ipm = IPackageManager.Stub.asInterface(
294                ServiceManager.getService("package"));
295        for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) {
296            if (entry.getValue()) {
297                // Enable selected apps
298                try {
299                    ipm.installExistingPackageAsUser(entry.getKey(), mUser.getIdentifier());
300                } catch (RemoteException re) {
301                }
302            } else {
303                // Blacklist all other apps, system or downloaded
304                try {
305                    ipm.deletePackageAsUser(entry.getKey(), null, mUser.getIdentifier(),
306                            PackageManager.DELETE_SYSTEM_APP);
307                } catch (RemoteException re) {
308                }
309            }
310        }
311    }
312
313    private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent) {
314        final PackageManager pm = getActivity().getPackageManager();
315        List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent, 0);
316        for (ResolveInfo app : launchableApps) {
317            if (app.activityInfo != null && app.activityInfo.applicationInfo != null) {
318                int flags = app.activityInfo.applicationInfo.flags;
319                if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
320                        || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
321                    // System app
322                    SelectableAppInfo info = new SelectableAppInfo();
323                    info.packageName = app.activityInfo.packageName;
324                    info.appName = app.activityInfo.applicationInfo.loadLabel(pm);
325                    info.icon = app.activityInfo.loadIcon(pm);
326                    info.activityName = app.activityInfo.loadLabel(pm);
327                    if (info.activityName == null) info.activityName = info.appName;
328                    visibleApps.add(info);
329                }
330            }
331        }
332    }
333
334    private void populateApps() {
335        mAppList.setOrderingAsAdded(false);
336        List<SelectableAppInfo> visibleApps = new ArrayList<SelectableAppInfo>();
337        // TODO: Do this asynchronously since it can be a long operation
338        final Context context = getActivity();
339        PackageManager pm = context.getPackageManager();
340        IPackageManager ipm = AppGlobals.getPackageManager();
341
342        // Add launchers
343        Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
344        launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
345        addSystemApps(visibleApps, launcherIntent);
346
347        // Add widgets
348        Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
349        addSystemApps(visibleApps, widgetIntent);
350
351        List<ApplicationInfo> installedApps = pm.getInstalledApplications(0);
352        for (ApplicationInfo app : installedApps) {
353            if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
354                    && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
355                // Downloaded app
356                SelectableAppInfo info = new SelectableAppInfo();
357                info.packageName = app.packageName;
358                info.appName = app.loadLabel(pm);
359                info.activityName = info.appName;
360                info.icon = app.loadIcon(pm);
361                visibleApps.add(info);
362            }
363        }
364
365        // Now check apps that are installed on target user
366        List<ApplicationInfo> userApps = null;
367        try {
368            userApps = ipm.getInstalledApplications(
369                    0, mUser.getIdentifier()).getList();
370        } catch (RemoteException re) {
371        }
372
373        if (userApps != null) {
374            for (ApplicationInfo app : userApps) {
375                if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
376                        && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
377                    // Downloaded app
378                    SelectableAppInfo info = new SelectableAppInfo();
379                    info.packageName = app.packageName;
380                    info.appName = app.loadLabel(pm);
381                    info.activityName = info.appName;
382                    info.icon = app.loadIcon(pm);
383                    visibleApps.add(info);
384                }
385            }
386        }
387        Collections.sort(visibleApps, new AppLabelComparator());
388
389        // Remove dupes
390        for (int i = visibleApps.size() - 1; i > 1; i--) {
391            SelectableAppInfo info = visibleApps.get(i);
392            if (DEBUG) Log.i(TAG, info.toString());
393            if (info.packageName.equals(visibleApps.get(i-1).packageName)
394                    && info.activityName.equals(visibleApps.get(i-1).activityName)) {
395                visibleApps.remove(i);
396            }
397        }
398
399        // Establish master/slave relationship for entries that share a package name
400        HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
401        for (SelectableAppInfo info : visibleApps) {
402            if (packageMap.containsKey(info.packageName)) {
403                info.masterEntry = packageMap.get(info.packageName);
404            } else {
405                packageMap.put(info.packageName, info);
406            }
407        }
408
409        Intent restrictionsIntent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES);
410        final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(restrictionsIntent, 0);
411        int i = 0;
412        if (visibleApps.size() > 0) {
413            for (SelectableAppInfo app : visibleApps) {
414                String packageName = app.packageName;
415                if (packageName == null) continue;
416                final boolean isSettingsApp = packageName.equals(getActivity().getPackageName());
417                AppRestrictionsPreference p = new AppRestrictionsPreference(context, this);
418                final boolean hasSettings = resolveInfoListHasPackage(receivers, packageName);
419                p.setIcon(app.icon);
420                p.setChecked(false);
421                p.setTitle(app.activityName);
422                if (app.masterEntry != null) {
423                    p.setSummary(getActivity().getString(R.string.user_restrictions_controlled_by,
424                            app.masterEntry.activityName));
425                }
426                p.setKey(PKG_PREFIX + packageName);
427                p.setSettingsEnabled(hasSettings || isSettingsApp);
428                p.setPersistent(false);
429                p.setOnPreferenceChangeListener(this);
430                p.setOnPreferenceClickListener(this);
431                PackageInfo pi = null;
432                try {
433                    pi = pm.getPackageInfo(packageName, 0);
434                } catch (NameNotFoundException re) {
435                    try {
436                        pi = ipm.getPackageInfo(packageName, 0, mUser.getIdentifier());
437                    } catch (RemoteException e) {
438                    }
439                }
440                if (pi != null && pi.requiredForAllUsers) {
441                    p.setChecked(true);
442                    p.setImmutable(true);
443                    // If the app is required and has no restrictions, skip showing it
444                    if (!hasSettings && !isSettingsApp) continue;
445                } else if (!mNewUser && appInfoListHasPackage(userApps, packageName)) {
446                    p.setChecked(true);
447                }
448                if (pi.requiredAccountType != null && pi.restrictedAccountType == null) {
449                    p.setChecked(false);
450                    p.setImmutable(true);
451                    p.setSummary(R.string.app_not_supported_in_limited);
452                }
453                if (app.masterEntry != null) {
454                    p.setImmutable(true);
455                    p.setChecked(mSelectedPackages.get(packageName));
456                }
457                mAppList.addPreference(p);
458                if (isSettingsApp) {
459                    p.setOrder(MAX_APP_RESTRICTIONS * 1);
460                } else {
461                    p.setOrder(MAX_APP_RESTRICTIONS * (i + 2));
462                }
463                p.setSelectableAppInfo(app);
464                mSelectedPackages.put(packageName, p.isChecked());
465                mAppListChanged = true;
466                i++;
467            }
468        }
469    }
470
471    private class AppLabelComparator implements Comparator<SelectableAppInfo> {
472
473        @Override
474        public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) {
475            String lhsLabel = lhs.activityName.toString();
476            String rhsLabel = rhs.activityName.toString();
477            return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase());
478        }
479    }
480
481    private boolean resolveInfoListHasPackage(List<ResolveInfo> receivers, String packageName) {
482        for (ResolveInfo info : receivers) {
483            if (info.activityInfo.packageName.equals(packageName)) {
484                return true;
485            }
486        }
487        return false;
488    }
489
490    private boolean appInfoListHasPackage(List<ApplicationInfo> apps, String packageName) {
491        for (ApplicationInfo info : apps) {
492            if (info.packageName.equals(packageName)) {
493                return true;
494            }
495        }
496        return false;
497    }
498
499    private void updateAllEntries(String prefKey, boolean checked) {
500        for (int i = 0; i < mAppList.getPreferenceCount(); i++) {
501            Preference pref = mAppList.getPreference(i);
502            if (pref instanceof AppRestrictionsPreference) {
503                if (prefKey.equals(pref.getKey())) {
504                    ((AppRestrictionsPreference) pref).setChecked(checked);
505                }
506            }
507        }
508    }
509
510    @Override
511    public void onClick(View v) {
512        if (v.getTag() instanceof AppRestrictionsPreference) {
513            AppRestrictionsPreference pref = (AppRestrictionsPreference) v.getTag();
514            if (v.getId() == R.id.app_restrictions_settings) {
515                toggleAppPanel(pref);
516            } else if (!pref.isImmutable()) {
517                pref.setChecked(!pref.isChecked());
518                mSelectedPackages.put(pref.getKey().substring(PKG_PREFIX.length()),
519                        pref.isChecked());
520                mAppListChanged = true;
521                updateAllEntries(pref.getKey(), pref.isChecked());
522            }
523        }
524    }
525
526    @Override
527    public boolean onPreferenceChange(Preference preference, Object newValue) {
528        String key = preference.getKey();
529        if (key != null && key.contains(DELIMITER)) {
530            StringTokenizer st = new StringTokenizer(key, DELIMITER);
531            final String packageName = st.nextToken();
532            final String restrictionKey = st.nextToken();
533            AppRestrictionsPreference appPref = (AppRestrictionsPreference)
534                    mAppList.findPreference(PKG_PREFIX+packageName);
535            ArrayList<RestrictionEntry> restrictions = appPref.getRestrictions();
536            if (restrictions != null) {
537                for (RestrictionEntry entry : restrictions) {
538                    if (entry.getKey().equals(restrictionKey)) {
539                        switch (entry.getType()) {
540                        case RestrictionEntry.TYPE_BOOLEAN:
541                            entry.setSelectedState((Boolean) newValue);
542                            break;
543                        case RestrictionEntry.TYPE_CHOICE:
544                        case RestrictionEntry.TYPE_CHOICE_LEVEL:
545                            ListPreference listPref = (ListPreference) preference;
546                            entry.setSelectedString((String) newValue);
547                            String readable = findInArray(entry.getChoiceEntries(),
548                                    entry.getChoiceValues(), (String) newValue);
549                            listPref.setSummary(readable);
550                            break;
551                        case RestrictionEntry.TYPE_MULTI_SELECT:
552                            MultiSelectListPreference msListPref =
553                                    (MultiSelectListPreference) preference;
554                            Set<String> set = (Set<String>) newValue;
555                            String [] selectedValues = new String[set.size()];
556                            set.toArray(selectedValues);
557                            entry.setAllSelectedStrings(selectedValues);
558                            break;
559                        default:
560                            continue;
561                        }
562                        if (packageName.equals(getActivity().getPackageName())) {
563                            RestrictionUtils.setRestrictions(getActivity(), restrictions, mUser);
564                        } else {
565                            mUserManager.setApplicationRestrictions(packageName,
566                                    RestrictionUtils.restrictionsToBundle(restrictions),
567                                    mUser);
568                        }
569                        break;
570                    }
571                }
572            }
573        } else if (preference == mUserPreference) {
574            String userName = ((CharSequence) newValue).toString();
575            if (!TextUtils.isEmpty(userName)) {
576                mUserManager.setUserName(mUser.getIdentifier(), userName);
577                mUserPreference.setTitle(userName);
578            }
579        }
580        return true;
581    }
582
583    private void toggleAppPanel(AppRestrictionsPreference preference) {
584        if (preference.getKey().startsWith(PKG_PREFIX)) {
585            if (preference.panelOpen) {
586                for (Preference p : preference.childPreferences) {
587                    mAppList.removePreference(p);
588                }
589                preference.childPreferences.clear();
590            } else {
591                String packageName = preference.getKey().substring(PKG_PREFIX.length());
592                if (packageName.equals(getActivity().getPackageName())) {
593                    // Settings, fake it by using user restrictions
594                    ArrayList<RestrictionEntry> restrictions = RestrictionUtils.getRestrictions(
595                            getActivity(), mUser);
596                    onRestrictionsReceived(preference, packageName, restrictions);
597                } else {
598                    Bundle oldEntries =
599                            mUserManager.getApplicationRestrictions(packageName, mUser);
600                    Intent intent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES);
601                    intent.setPackage(packageName);
602                    intent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, oldEntries);
603                    intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
604                    getActivity().sendOrderedBroadcast(intent, null,
605                            new RestrictionsResultReceiver(packageName, preference),
606                            null, Activity.RESULT_OK, null, null);
607                }
608            }
609            preference.panelOpen = !preference.panelOpen;
610        }
611    }
612
613    class RestrictionsResultReceiver extends BroadcastReceiver {
614
615        private static final String CUSTOM_RESTRICTIONS_INTENT = Intent.EXTRA_RESTRICTIONS_INTENT;
616        String packageName;
617        AppRestrictionsPreference preference;
618
619        RestrictionsResultReceiver(String packageName, AppRestrictionsPreference preference) {
620            super();
621            this.packageName = packageName;
622            this.preference = preference;
623        }
624
625        @Override
626        public void onReceive(Context context, Intent intent) {
627            Bundle results = getResultExtras(true);
628            final ArrayList<RestrictionEntry> restrictions = results.getParcelableArrayList(
629                    Intent.EXTRA_RESTRICTIONS_LIST);
630            Intent restrictionsIntent = (Intent) results.getParcelable(CUSTOM_RESTRICTIONS_INTENT);
631            if (restrictions != null && restrictionsIntent == null) {
632                onRestrictionsReceived(preference, packageName, restrictions);
633                mUserManager.setApplicationRestrictions(packageName,
634                        RestrictionUtils.restrictionsToBundle(restrictions), mUser);
635            } else if (restrictionsIntent != null) {
636                final Intent customIntent = restrictionsIntent;
637                customIntent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE,
638                        RestrictionUtils.restrictionsToBundle(restrictions));
639                Preference p = new Preference(context);
640                p.setTitle(R.string.app_restrictions_custom_label);
641                p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
642                    @Override
643                    public boolean onPreferenceClick(Preference preference) {
644                        int requestCode = generateCustomActivityRequestCode(
645                                RestrictionsResultReceiver.this.preference);
646                        AppRestrictionsFragment.this.startActivityForResult(
647                                customIntent, requestCode);
648                        return false;
649                    }
650                });
651                p.setPersistent(false);
652                p.setOrder(preference.getOrder() + 1);
653                preference.childPreferences.add(p);
654                mAppList.addPreference(p);
655                preference.setRestrictions(restrictions);
656            }
657        }
658    }
659
660    private void onRestrictionsReceived(AppRestrictionsPreference preference, String packageName,
661            ArrayList<RestrictionEntry> restrictions) {
662        // Non-custom-activity case - expand the restrictions in-place
663        final Context context = preference.getContext();
664        int count = 1;
665        for (RestrictionEntry entry : restrictions) {
666            Preference p = null;
667            switch (entry.getType()) {
668            case RestrictionEntry.TYPE_BOOLEAN:
669                p = new CheckBoxPreference(context);
670                p.setTitle(entry.getTitle());
671                p.setSummary(entry.getDescription());
672                ((CheckBoxPreference)p).setChecked(entry.getSelectedState());
673                break;
674            case RestrictionEntry.TYPE_CHOICE:
675            case RestrictionEntry.TYPE_CHOICE_LEVEL:
676                p = new ListPreference(context);
677                p.setTitle(entry.getTitle());
678                String value = entry.getSelectedString();
679                if (value == null) {
680                    value = entry.getDescription();
681                }
682                p.setSummary(findInArray(entry.getChoiceEntries(), entry.getChoiceValues(),
683                        value));
684                ((ListPreference)p).setEntryValues(entry.getChoiceValues());
685                ((ListPreference)p).setEntries(entry.getChoiceEntries());
686                ((ListPreference)p).setValue(value);
687                ((ListPreference)p).setDialogTitle(entry.getTitle());
688                break;
689            case RestrictionEntry.TYPE_MULTI_SELECT:
690                p = new MultiSelectListPreference(context);
691                p.setTitle(entry.getTitle());
692                ((MultiSelectListPreference)p).setEntryValues(entry.getChoiceValues());
693                ((MultiSelectListPreference)p).setEntries(entry.getChoiceEntries());
694                HashSet<String> set = new HashSet<String>();
695                for (String s : entry.getAllSelectedStrings()) {
696                    set.add(s);
697                }
698                ((MultiSelectListPreference)p).setValues(set);
699                ((MultiSelectListPreference)p).setDialogTitle(entry.getTitle());
700                break;
701            case RestrictionEntry.TYPE_NULL:
702            default:
703            }
704            if (p != null) {
705                p.setPersistent(false);
706                p.setOrder(preference.getOrder() + count);
707                // Store the restrictions key string as a key for the preference
708                p.setKey(preference.getKey().substring(PKG_PREFIX.length()) + DELIMITER
709                        + entry.getKey());
710                mAppList.addPreference(p);
711                p.setOnPreferenceChangeListener(AppRestrictionsFragment.this);
712                preference.childPreferences.add(p);
713                count++;
714            }
715        }
716        preference.setRestrictions(restrictions);
717    }
718
719    /**
720     * Generates a request code that is stored in a map to retrieve the associated
721     * AppRestrictionsPreference.
722     * @param preference
723     * @return
724     */
725    private int generateCustomActivityRequestCode(AppRestrictionsPreference preference) {
726        mCustomRequestCode++;
727        mCustomRequestMap.put(mCustomRequestCode, preference);
728        return mCustomRequestCode;
729    }
730
731    @Override
732    public void onActivityResult(int requestCode, int resultCode, Intent data) {
733        super.onActivityResult(requestCode, resultCode, data);
734
735        Log.i(TAG, "Got activity resultCode=" + resultCode + ", requestCode="
736                + requestCode + ", data=" + data);
737
738        AppRestrictionsPreference pref = mCustomRequestMap.get(requestCode);
739        if (pref == null) {
740            Log.w(TAG, "Unknown requestCode " + requestCode);
741            return;
742        }
743
744        if (resultCode == Activity.RESULT_OK) {
745            ArrayList<RestrictionEntry> list =
746                    data.getParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST);
747            if (list != null) {
748                // If there's a valid result, persist it to the user manager.
749                String packageName = pref.getKey().substring(PKG_PREFIX.length());
750                pref.setRestrictions(list);
751                mUserManager.setApplicationRestrictions(packageName,
752                        RestrictionUtils.restrictionsToBundle(list), mUser);
753            }
754            toggleAppPanel(pref);
755        }
756        // Remove request from the map
757        mCustomRequestMap.remove(requestCode);
758    }
759
760    private String findInArray(String[] choiceEntries, String[] choiceValues,
761            String selectedString) {
762        for (int i = 0; i < choiceValues.length; i++) {
763            if (choiceValues[i].equals(selectedString)) {
764                return choiceEntries[i];
765            }
766        }
767        return selectedString;
768    }
769
770    @Override
771    public boolean onPreferenceClick(Preference preference) {
772        if (preference.getKey().startsWith(PKG_PREFIX)) {
773            AppRestrictionsPreference arp = (AppRestrictionsPreference) preference;
774            if (!arp.isImmutable()) {
775                arp.setChecked(!arp.isChecked());
776                mSelectedPackages.put(arp.getKey().substring(PKG_PREFIX.length()), arp.isChecked());
777                updateAllEntries(arp.getKey(), arp.isChecked());
778                mAppListChanged = true;
779            }
780            return true;
781        }
782        return false;
783    }
784}
785