1bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov/*
2bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * Copyright (C) 2015 The Android Open Source Project
3bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov *
4bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * Licensed under the Apache License, Version 2.0 (the "License");
5bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * you may not use this file except in compliance with the License.
6bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * You may obtain a copy of the License at
7bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov *
8bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov *      http://www.apache.org/licenses/LICENSE-2.0
9bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov *
10bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * Unless required by applicable law or agreed to in writing, software
11bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * distributed under the License is distributed on an "AS IS" BASIS,
12bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * See the License for the specific language governing permissions and
14bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov * limitations under the License.
15bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov */
16bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
17bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovpackage com.android.packageinstaller.permission.ui;
18bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
19bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.app.Activity;
20bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
21bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.app.DialogFragment;
22bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.app.Fragment;
23bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.content.Intent;
24bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.content.IntentSender;
25bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.content.pm.PackageInfo;
26bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.content.pm.PackageManager;
27bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.graphics.drawable.Drawable;
28bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.os.Bundle;
29bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.os.RemoteCallback;
30bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.preference.Preference;
31bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.preference.PreferenceCategory;
32bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.preference.PreferenceFragment;
33bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.preference.PreferenceGroup;
34bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.preference.PreferenceScreen;
35bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.preference.SwitchPreference;
36bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.preference.TwoStatePreference;
37bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.text.SpannableString;
38bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.text.TextUtils;
39bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.text.style.ForegroundColorSpan;
40bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.util.TypedValue;
41bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.view.View;
42bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.widget.Button;
43bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.widget.ImageView;
44bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport android.widget.TextView;
45bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport com.android.packageinstaller.R;
46bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport com.android.packageinstaller.permission.model.AppPermissionGroup;
47bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport com.android.packageinstaller.permission.model.AppPermissions;
48f9bf4f795615bac03d1b35a0138318473b1ef6bfSvet Ganovimport com.android.packageinstaller.permission.utils.Utils;
49bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment.OnActionConfirmedListener;
50bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
51bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovimport java.util.List;
52bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
53bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganovpublic final class ReviewPermissionsActivity extends Activity
54bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        implements OnActionConfirmedListener {
55bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
56bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    @Override
57bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    protected void onCreate(Bundle savedInstanceState) {
58bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        super.onCreate(savedInstanceState);
59bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
60bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        PackageInfo packageInfo = getTargetPackageInfo();
61bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        if (packageInfo == null) {
62bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            finish();
63bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            return;
64bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
65bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
66bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        setContentView(R.layout.review_permissions);
67bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        if (getFragmentManager().findFragmentById(R.id.preferences_frame) == null) {
68bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            getFragmentManager().beginTransaction().add(R.id.preferences_frame,
69bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    ReviewPermissionsFragment.newInstance(packageInfo)).commit();
70bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
71bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    }
72bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
73bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    @Override
74bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    public void onActionConfirmed(String action) {
75bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        Fragment fragment = getFragmentManager().findFragmentById(R.id.preferences_frame);
76bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        if (fragment instanceof OnActionConfirmedListener) {
77bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            ((OnActionConfirmedListener) fragment).onActionConfirmed(action);
78bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
79bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    }
80bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
81bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    private PackageInfo getTargetPackageInfo() {
82bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
83bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        if (TextUtils.isEmpty(packageName)) {
84bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            return null;
85bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
86bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        try {
87bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            return getPackageManager().getPackageInfo(packageName,
88bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    PackageManager.GET_PERMISSIONS);
89bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        } catch (PackageManager.NameNotFoundException e) {
90bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            return null;
91bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
92bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    }
93bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
94bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    public static final class ReviewPermissionsFragment extends PreferenceFragment
95bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            implements View.OnClickListener, Preference.OnPreferenceChangeListener,
96bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            ConfirmActionDialogFragment.OnActionConfirmedListener {
97bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        public static final String EXTRA_PACKAGE_INFO =
98bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO";
99bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
100bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private AppPermissions mAppPermissions;
101bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
102bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private Button mContinueButton;
103bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private Button mCancelButton;
104bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
105bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private PreferenceCategory mNewPermissionsCategory;
106bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
107bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private boolean mHasConfirmedRevoke;
108bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
109bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) {
110bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            Bundle arguments = new Bundle();
111bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo);
112bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            ReviewPermissionsFragment instance = new ReviewPermissionsFragment();
113bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            instance.setArguments(arguments);
114bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            instance.setRetainInstance(true);
115bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            return instance;
116bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
117bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
118bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        @Override
119f9bf4f795615bac03d1b35a0138318473b1ef6bfSvet Ganov        public void onCreate(Bundle savedInstanceState) {
120bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            super.onCreate(savedInstanceState);
121bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
12215fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            Activity activity = getActivity();
12315fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            if (activity == null) {
12415fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                return;
12515fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            }
12615fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov
127bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO);
128bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (packageInfo == null) {
12915fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                activity.finish();
130bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                return;
131bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
132bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
13315fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            mAppPermissions = new AppPermissions(activity, packageInfo, null, false,
134bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    new Runnable() {
135bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        @Override
136bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        public void run() {
137bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            getActivity().finish();
138bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        }
139bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    });
140bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
141bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (mAppPermissions.getPermissionGroups().isEmpty()) {
14215fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                activity.finish();
143bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                return;
144bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
145bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
146dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            boolean reviewRequired = false;
147bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
148bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (group.isReviewRequired()) {
149bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    reviewRequired = true;
150bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    break;
151bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
152bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
153bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
154bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (!reviewRequired) {
15515fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                activity.finish();
156bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
157bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
158bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
159bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        @Override
160f9bf4f795615bac03d1b35a0138318473b1ef6bfSvet Ganov        public void onViewCreated(View view, Bundle savedInstanceState) {
161bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            super.onViewCreated(view, savedInstanceState);
162bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            bindUi();
163bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
164bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
165bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        @Override
166bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        public void onResume() {
167bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            super.onResume();
168dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            mAppPermissions.refresh();
169bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            loadPreferences();
170bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
171bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
172bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        @Override
173bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        public void onClick(View view) {
17415fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            Activity activity = getActivity();
17515fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            if (activity == null) {
17615fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                return;
17715fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            }
178bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (view == mContinueButton) {
179bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                confirmPermissionsReview();
180bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                executeCallback(true);
181bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            } else if (view == mCancelButton) {
182bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                executeCallback(false);
18315fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                activity.setResult(Activity.RESULT_CANCELED);
184bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
18515fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov            activity.finish();
186bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
187bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
188bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        @Override
189bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        public boolean onPreferenceChange(Preference preference, Object newValue) {
190bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (mHasConfirmedRevoke) {
191bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                return true;
192bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
193bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (preference instanceof SwitchPreference) {
194bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                SwitchPreference switchPreference = (SwitchPreference) preference;
195bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (switchPreference.isChecked()) {
196bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    showWarnRevokeDialog(switchPreference.getKey());
19715fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                } else {
19815fa03702aae07fb325d6be98d8c0cdeb344d20dSvetoslav Ganov                    return true;
199bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
200bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
201bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            return false;
202bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
203bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
204bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        @Override
205bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        public void onActionConfirmed(String action) {
206bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            Preference preference = getPreferenceManager().findPreference(action);
207bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (preference instanceof SwitchPreference) {
208bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                SwitchPreference switchPreference = (SwitchPreference) preference;
209bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                switchPreference.setChecked(false);
210bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                mHasConfirmedRevoke = true;
211bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
212bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
213bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
214bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private void showWarnRevokeDialog(final String groupName) {
215bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            DialogFragment fragment = ConfirmActionDialogFragment.newInstance(
216bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    getString(R.string.old_sdk_deny_warning), groupName);
217bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            fragment.show(getFragmentManager(), fragment.getClass().getName());
218bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
219bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
220bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private void confirmPermissionsReview() {
221bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            PreferenceGroup preferenceGroup = mNewPermissionsCategory != null
222bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                ? mNewPermissionsCategory : getPreferenceScreen();
223bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
224bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            final int preferenceCount = preferenceGroup.getPreferenceCount();
225bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            for (int i = 0; i < preferenceCount; i++) {
226bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                Preference preference = preferenceGroup.getPreference(i);
227bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (preference instanceof TwoStatePreference) {
228bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    TwoStatePreference twoStatePreference = (TwoStatePreference) preference;
229bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    String groupName = preference.getKey();
230bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
231bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    if (twoStatePreference.isChecked()) {
232bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        group.grantRuntimePermissions(false);
233bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    } else {
234bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        group.revokeRuntimePermissions(false);
235bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    }
236bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    group.resetReviewRequired();
237bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
238bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
239bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
240bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
241bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private void bindUi() {
242bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            Activity activity = getActivity();
243dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            if (activity == null) {
244dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov                return;
245dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            }
246bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
247bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            // Set icon
248bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon(
249bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    activity.getPackageManager());
250bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            ImageView iconView = (ImageView) activity.findViewById(R.id.app_icon);
251bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            iconView.setImageDrawable(icon);
252bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
253bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            // Set message
254bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            String appLabel = mAppPermissions.getAppLabel().toString();
255bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            final int labelTemplateResId = isPackageUpdated()
256bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    ?  R.string.permission_review_title_template_update
257bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    :  R.string.permission_review_title_template_install;
258bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            SpannableString message = new SpannableString(getString(labelTemplateResId, appLabel));
259bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            // Set the permission message as the title so it can be announced.
260bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            activity.setTitle(message);
261bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
262bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            // Color the app name.
263bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            final int appLabelStart = message.toString().indexOf(appLabel, 0);
264bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            final int appLabelLength = appLabel.length();
265bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
266bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            TypedValue typedValue = new TypedValue();
267bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            activity.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
268bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            final int color = activity.getColor(typedValue.resourceId);
269bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
270bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            message.setSpan(new ForegroundColorSpan(color), appLabelStart,
271bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    appLabelStart + appLabelLength, 0);
272bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            TextView permissionsMessageView = (TextView) activity.findViewById(
273bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    R.id.permissions_message);
274bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            permissionsMessageView.setText(message);
275bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
276bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
277bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            mContinueButton = (Button) getActivity().findViewById(R.id.continue_button);
278bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            mContinueButton.setOnClickListener(this);
279bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
280bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            mCancelButton = (Button) getActivity().findViewById(R.id.cancel_button);
281bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            mCancelButton.setOnClickListener(this);
282bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
283bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
284bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private void loadPreferences() {
285dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            Activity activity = getActivity();
286dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            if (activity == null) {
287dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov                return;
288dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            }
289dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov
290bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            PreferenceScreen screen = getPreferenceScreen();
291bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (screen == null) {
292bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                screen = getPreferenceManager().createPreferenceScreen(getActivity());
293bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                setPreferenceScreen(screen);
294bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            } else {
295bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                screen.removeAll();
296bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
297bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
298bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            PreferenceGroup currentPermissionsCategory = null;
299bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory;
300bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            mNewPermissionsCategory = null;
301bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
302bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            final boolean isPackageUpdated = isPackageUpdated();
303bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
304bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
305df804e6a76051adb1e626a5713d32a6bf8b3ab9aSvet Ganov                if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)
306df804e6a76051adb1e626a5713d32a6bf8b3ab9aSvet Ganov                        || !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
307bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    continue;
308bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
309bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
310bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                // TODO: Sort permissions - platform first then third-party ones
311bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
312bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                final SwitchPreference preference;
313bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                Preference cachedPreference = oldNewPermissionsCategory != null
314bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        ? oldNewPermissionsCategory.findPreference(group.getName()) : null;
315bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (cachedPreference instanceof SwitchPreference) {
316bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference = (SwitchPreference) cachedPreference;
317bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                } else {
318bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference = new SwitchPreference(getActivity());
319bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
320bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setKey(group.getName());
321dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov                    Drawable icon = Utils.loadDrawable(activity.getPackageManager(),
322bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            group.getIconPkg(), group.getIconResId());
323bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setIcon(Utils.applyTint(getContext(), icon,
324bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            android.R.attr.colorControlNormal));
325bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setTitle(group.getLabel());
326bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setSummary(group.getDescription());
327bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setPersistent(false);
328bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
329bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setOnPreferenceChangeListener(this);
330bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
331bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
332dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov                preference.setChecked(group.areRuntimePermissionsGranted());
333dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov
334bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                // Mutable state
335bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (group.isPolicyFixed()) {
336bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setEnabled(false);
337bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setSummary(getString(
338bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            R.string.permission_summary_enforced_by_policy));
339bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                } else {
340bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    preference.setEnabled(true);
341bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
342bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
343bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (group.isReviewRequired()) {
344bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    if (!isPackageUpdated) {
345bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        screen.addPreference(preference);
346bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    } else {
347bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        if (mNewPermissionsCategory == null) {
348dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov                            mNewPermissionsCategory = new PreferenceCategory(activity);
349bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            mNewPermissionsCategory.setTitle(R.string.new_permissions_category);
350bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            mNewPermissionsCategory.setOrder(1);
351bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            screen.addPreference(mNewPermissionsCategory);
352bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        }
353bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        mNewPermissionsCategory.addPreference(preference);
354bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    }
355bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                } else {
356bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    if (currentPermissionsCategory == null) {
357dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov                        currentPermissionsCategory = new PreferenceCategory(activity);
358bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        currentPermissionsCategory.setTitle(R.string.current_permissions_category);
359bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        currentPermissionsCategory.setOrder(2);
360bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        screen.addPreference(currentPermissionsCategory);
361bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    }
362bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    currentPermissionsCategory.addPreference(preference);
363bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
364bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
365bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
366bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
367bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private boolean isPackageUpdated() {
368bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups();
369bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            final int groupCount = groups.size();
370bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            for (int i = 0; i < groupCount; i++) {
371bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                AppPermissionGroup group = groups.get(i);
372bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (!group.isReviewRequired()) {
373bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    return true;
374bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
375bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
376bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            return false;
377bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
378bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov
379bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        private void executeCallback(boolean success) {
380bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            Activity activity = getActivity();
381dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            if (activity == null) {
382dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov                return;
383dcc39fe43bc27412b7237195392b11299fd56ebfSvetoslav Ganov            }
384bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (success) {
385bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
386bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                if (intent != null) {
387bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    try {
388bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        int flagMask = 0;
389bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        int flagValues = 0;
390bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        if (activity.getIntent().getBooleanExtra(
391bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                                Intent.EXTRA_RESULT_NEEDED, false)) {
392bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
393bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                            flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
394bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        }
395bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        activity.startIntentSenderForResult(intent, -1, null,
396bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                                flagMask, flagValues, 0);
397bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    } catch (IntentSender.SendIntentException e) {
398bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                        /* ignore */
399bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    }
400bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    return;
401bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                }
402bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
403bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            RemoteCallback callback = activity.getIntent().getParcelableExtra(
404bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                    Intent.EXTRA_REMOTE_CALLBACK);
405bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            if (callback != null) {
406bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                Bundle result = new Bundle();
407bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                result.putBoolean(Intent.EXTRA_RETURN_RESULT, success);
408bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov                callback.sendResult(result);
409bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov            }
410bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov        }
411bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov    }
412bf7316751f0c73b435b693fd20b0c9b2fa973e85Svet Ganov}
413