• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.packageinstaller.permission.ui.handheld;
18 
19 import android.app.Activity;
20 import android.content.Intent;
21 import android.graphics.drawable.Icon;
22 import android.os.Bundle;
23 import android.view.LayoutInflater;
24 import android.view.View;
25 import android.view.View.OnClickListener;
26 import android.view.ViewGroup;
27 import android.view.WindowManager.LayoutParams;
28 import android.view.animation.AnimationUtils;
29 import android.view.animation.Interpolator;
30 import android.widget.Button;
31 import android.widget.CheckBox;
32 import android.widget.ImageView;
33 import android.widget.TextView;
34 
35 import com.android.packageinstaller.R;
36 import com.android.packageinstaller.permission.ui.ButtonBarLayout;
37 import com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler;
38 import com.android.packageinstaller.permission.ui.ManagePermissionsActivity;
39 import com.android.packageinstaller.permission.ui.ManualLayoutFrame;
40 
41 public class GrantPermissionsViewHandlerImpl implements GrantPermissionsViewHandler,
42         OnClickListener {
43 
44     public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
45     public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT";
46     public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX";
47     public static final String ARG_GROUP_ICON = "ARG_GROUP_ICON";
48     public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE";
49     public static final String ARG_GROUP_SHOW_DO_NOT_ASK = "ARG_GROUP_SHOW_DO_NOT_ASK";
50     public static final String ARG_GROUP_DO_NOT_ASK_CHECKED = "ARG_GROUP_DO_NOT_ASK_CHECKED";
51 
52     // Animation parameters.
53     private static final long OUT_DURATION = 200;
54     private static final long IN_DURATION = 300;
55 
56     private final Activity mActivity;
57     private final String mAppPackageName;
58     private final boolean mPermissionReviewRequired;
59 
60     private ResultListener mResultListener;
61 
62     private String mGroupName;
63     private int mGroupCount;
64     private int mGroupIndex;
65     private Icon mGroupIcon;
66     private CharSequence mGroupMessage;
67     private boolean mShowDonNotAsk;
68     private boolean mDoNotAskChecked;
69 
70     private ImageView mIconView;
71     private TextView mCurrentGroupView;
72     private TextView mMessageView;
73     private CheckBox mDoNotAskCheckbox;
74     private Button mAllowButton;
75     private Button mMoreInfoButton;
76 
77     private ManualLayoutFrame mRootView;
78 
79     // Needed for animation
80     private ViewGroup mDescContainer;
81     private ViewGroup mCurrentDesc;
82     private ViewGroup mDialogContainer;
83     private ButtonBarLayout mButtonBar;
84 
GrantPermissionsViewHandlerImpl(Activity activity, String appPackageName)85     public GrantPermissionsViewHandlerImpl(Activity activity, String appPackageName) {
86         mActivity = activity;
87         mAppPackageName = appPackageName;
88         mPermissionReviewRequired = activity.getPackageManager().isPermissionReviewModeEnabled();
89     }
90 
91     @Override
setResultListener(ResultListener listener)92     public GrantPermissionsViewHandlerImpl setResultListener(ResultListener listener) {
93         mResultListener = listener;
94         return this;
95     }
96 
97     @Override
saveInstanceState(Bundle arguments)98     public void saveInstanceState(Bundle arguments) {
99         arguments.putString(ARG_GROUP_NAME, mGroupName);
100         arguments.putInt(ARG_GROUP_COUNT, mGroupCount);
101         arguments.putInt(ARG_GROUP_INDEX, mGroupIndex);
102         arguments.putParcelable(ARG_GROUP_ICON, mGroupIcon);
103         arguments.putCharSequence(ARG_GROUP_MESSAGE, mGroupMessage);
104         arguments.putBoolean(ARG_GROUP_SHOW_DO_NOT_ASK, mShowDonNotAsk);
105         arguments.putBoolean(ARG_GROUP_DO_NOT_ASK_CHECKED, mDoNotAskCheckbox.isChecked());
106     }
107 
108     @Override
loadInstanceState(Bundle savedInstanceState)109     public void loadInstanceState(Bundle savedInstanceState) {
110         mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
111         mGroupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE);
112         mGroupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON);
113         mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT);
114         mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX);
115         mShowDonNotAsk = savedInstanceState.getBoolean(ARG_GROUP_SHOW_DO_NOT_ASK);
116         mDoNotAskChecked = savedInstanceState.getBoolean(ARG_GROUP_DO_NOT_ASK_CHECKED);
117     }
118 
119     @Override
updateUi(String groupName, int groupCount, int groupIndex, Icon icon, CharSequence message, boolean showDonNotAsk)120     public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
121             CharSequence message, boolean showDonNotAsk) {
122         mGroupName = groupName;
123         mGroupCount = groupCount;
124         mGroupIndex = groupIndex;
125         mGroupIcon = icon;
126         mGroupMessage = message;
127         mShowDonNotAsk = showDonNotAsk;
128         mDoNotAskChecked = false;
129         // If this is a second (or later) permission and the views exist, then animate.
130         if (mIconView != null) {
131             if (mGroupIndex > 0) {
132                 // The first message will be announced as the title of the activity, all others
133                 // we need to announce ourselves.
134                 mDescContainer.announceForAccessibility(message);
135                 animateToPermission();
136             } else {
137                 updateDescription();
138                 updateGroup();
139                 updateDoNotAskCheckBox();
140             }
141         }
142     }
143 
onConfigurationChanged()144     public void onConfigurationChanged() {
145         mRootView.onConfigurationChanged();
146     }
147 
animateOldContent(Runnable callback)148     private void animateOldContent(Runnable callback) {
149         // Fade out old description group and scale out the icon for it.
150         Interpolator interpolator = AnimationUtils.loadInterpolator(mActivity,
151                 android.R.interpolator.fast_out_linear_in);
152 
153         // Icon scale to zero
154         mIconView.animate()
155                 .scaleX(0)
156                 .scaleY(0)
157                 .setDuration(OUT_DURATION)
158                 .setInterpolator(interpolator)
159                 .start();
160 
161         // Description fade out
162         mCurrentDesc.animate()
163                 .alpha(0)
164                 .setDuration(OUT_DURATION)
165                 .setInterpolator(interpolator)
166                 .withEndAction(callback)
167                 .start();
168 
169         // Checkbox fade out if needed
170         if (!mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE) {
171             mDoNotAskCheckbox.animate()
172                     .alpha(0)
173                     .setDuration(OUT_DURATION)
174                     .setInterpolator(interpolator)
175                     .start();
176         }
177     }
178 
attachNewContent(final Runnable callback)179     private void attachNewContent(final Runnable callback) {
180         mCurrentDesc = (ViewGroup) LayoutInflater.from(mActivity).inflate(
181                 R.layout.permission_description, mDescContainer, false);
182         mDescContainer.removeAllViews();
183         mDescContainer.addView(mCurrentDesc);
184 
185         mDialogContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
186                 @Override
187                 public void onLayoutChange(View v, int left, int top, int right, int bottom,
188                         int oldLeft, int oldTop, int oldRight, int oldBottom) {
189                     mDialogContainer.removeOnLayoutChangeListener(this);
190 
191                     // Prepare new content to the right to be moved in
192                     final int containerWidth = mDescContainer.getWidth();
193                     mCurrentDesc.setTranslationX(containerWidth);
194 
195                     // How much scale for the dialog to appear the same?
196                     final int oldDynamicHeight = oldBottom - oldTop - mButtonBar.getHeight();
197                     final float scaleY = (float) oldDynamicHeight / mDescContainer.getHeight();
198 
199                     // How much to translate for the dialog to appear the same?
200                     final int translationCompensatingScale = (int) (scaleY
201                             * mDescContainer.getHeight() - mDescContainer.getHeight()) / 2;
202                     final int translationY = (oldTop - top) + translationCompensatingScale;
203 
204                     // Animate to the current layout
205                     mDescContainer.setScaleY(scaleY);
206                     mDescContainer.setTranslationY(translationY);
207                     mDescContainer.animate()
208                             .translationY(0)
209                             .scaleY(1.0f)
210                             .setInterpolator(AnimationUtils.loadInterpolator(mActivity,
211                                     android.R.interpolator.linear_out_slow_in))
212                             .setDuration(IN_DURATION)
213                             .withEndAction(callback)
214                             .start();
215                 }
216             }
217         );
218 
219         mMessageView = (TextView) mCurrentDesc.findViewById(R.id.permission_message);
220         mIconView = (ImageView) mCurrentDesc.findViewById(R.id.permission_icon);
221 
222         final boolean doNotAskWasShown = mDoNotAskCheckbox.getVisibility() == View.VISIBLE;
223 
224         updateDescription();
225         updateGroup();
226         updateDoNotAskCheckBox();
227 
228         if (!doNotAskWasShown && mShowDonNotAsk) {
229             mDoNotAskCheckbox.setAlpha(0);
230         }
231     }
232 
animateNewContent()233     private void animateNewContent() {
234         Interpolator interpolator = AnimationUtils.loadInterpolator(mActivity,
235                 android.R.interpolator.linear_out_slow_in);
236 
237         // Description slide in
238         mCurrentDesc.animate()
239                 .translationX(0)
240                 .setDuration(IN_DURATION)
241                 .setInterpolator(interpolator)
242                 .start();
243 
244         // Checkbox fade in if needed
245         if (mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE
246                 && mDoNotAskCheckbox.getAlpha() < 1.0f) {
247             mDoNotAskCheckbox.setAlpha(0);
248             mDoNotAskCheckbox.animate()
249                     .alpha(1.0f)
250                     .setDuration(IN_DURATION)
251                     .setInterpolator(interpolator)
252                     .start();
253         }
254     }
255 
animateToPermission()256     private void animateToPermission() {
257         // Remove the old content
258         animateOldContent(new Runnable() {
259             @Override
260             public void run() {
261                 // Add the new content
262                 attachNewContent(new Runnable() {
263                     @Override
264                     public void run() {
265                         // Animate the new content
266                         animateNewContent();
267                     }
268                 });
269             }
270         });
271     }
272 
273     @Override
createView()274     public View createView() {
275         mRootView = (ManualLayoutFrame) LayoutInflater.from(mActivity)
276                 .inflate(R.layout.grant_permissions, null);
277         mButtonBar = (ButtonBarLayout) mRootView.findViewById(R.id.button_group);
278         mButtonBar.setAllowStacking(true);
279         mMessageView = (TextView) mRootView.findViewById(R.id.permission_message);
280         mIconView = (ImageView) mRootView.findViewById(R.id.permission_icon);
281         mCurrentGroupView = (TextView) mRootView.findViewById(R.id.current_page_text);
282         mDoNotAskCheckbox = (CheckBox) mRootView.findViewById(R.id.do_not_ask_checkbox);
283 
284         mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button);
285         mAllowButton.setOnClickListener(this);
286 
287         if (mPermissionReviewRequired) {
288             mMoreInfoButton = (Button) mRootView.findViewById(R.id.permission_more_info_button);
289             mMoreInfoButton.setVisibility(View.VISIBLE);
290             mMoreInfoButton.setOnClickListener(this);
291         }
292 
293         mDialogContainer = (ViewGroup) mRootView.findViewById(R.id.dialog_container);
294         mDescContainer = (ViewGroup) mRootView.findViewById(R.id.desc_container);
295         mCurrentDesc = (ViewGroup) mRootView.findViewById(R.id.perm_desc_root);
296 
297         mRootView.findViewById(R.id.permission_deny_button).setOnClickListener(this);
298         mDoNotAskCheckbox.setOnClickListener(this);
299 
300         if (mGroupName != null) {
301             updateDescription();
302             updateGroup();
303             updateDoNotAskCheckBox();
304         }
305 
306         return mRootView;
307     }
308 
309     @Override
updateWindowAttributes(LayoutParams outLayoutParams)310     public void updateWindowAttributes(LayoutParams outLayoutParams) {
311         // No-op
312     }
313 
updateDescription()314     private void updateDescription() {
315         mIconView.setImageDrawable(mGroupIcon.loadDrawable(mActivity));
316         mMessageView.setText(mGroupMessage);
317     }
318 
updateGroup()319     private void updateGroup() {
320         if (mGroupCount > 1) {
321             mCurrentGroupView.setVisibility(View.VISIBLE);
322             mCurrentGroupView.setText(mActivity.getString(R.string.current_permission_template,
323                     mGroupIndex + 1, mGroupCount));
324         } else {
325             mCurrentGroupView.setVisibility(View.GONE);
326         }
327     }
328 
updateDoNotAskCheckBox()329     private void updateDoNotAskCheckBox() {
330         if (mShowDonNotAsk) {
331             mDoNotAskCheckbox.setVisibility(View.VISIBLE);
332             mDoNotAskCheckbox.setOnClickListener(this);
333             mDoNotAskCheckbox.setChecked(mDoNotAskChecked);
334         } else {
335             mDoNotAskCheckbox.setVisibility(View.GONE);
336             mDoNotAskCheckbox.setOnClickListener(null);
337         }
338     }
339 
340     @Override
onClick(View view)341     public void onClick(View view) {
342         switch (view.getId()) {
343             case R.id.permission_allow_button:
344                 if (mResultListener != null) {
345                     view.clearAccessibilityFocus();
346                     mResultListener.onPermissionGrantResult(mGroupName, true, false);
347                 }
348                 break;
349             case R.id.permission_deny_button:
350                 mAllowButton.setEnabled(true);
351                 if (mResultListener != null) {
352                     view.clearAccessibilityFocus();
353                     mResultListener.onPermissionGrantResult(mGroupName, false,
354                             mShowDonNotAsk && mDoNotAskCheckbox.isChecked());
355                 }
356                 break;
357             case R.id.permission_more_info_button:
358                 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
359                 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppPackageName);
360                 intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
361                 mActivity.startActivity(intent);
362                 break;
363             case R.id.do_not_ask_checkbox:
364                 mAllowButton.setEnabled(!mDoNotAskCheckbox.isChecked());
365                 break;
366         }
367     }
368 
369     @Override
onBackPressed()370     public void onBackPressed() {
371         if (mResultListener != null) {
372             final boolean doNotAskAgain = mDoNotAskCheckbox.isChecked();
373             mResultListener.onPermissionGrantResult(mGroupName, false, doNotAskAgain);
374         }
375     }
376 }
377