• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.recents;
18 
19 import android.animation.ArgbEvaluator;
20 import android.animation.ValueAnimator;
21 import android.app.ActivityManager;
22 import android.app.ActivityManagerNative;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.res.Configuration;
28 import android.graphics.PixelFormat;
29 import android.graphics.Rect;
30 import android.graphics.drawable.ColorDrawable;
31 import android.os.RemoteException;
32 import android.util.DisplayMetrics;
33 import android.view.Gravity;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.view.WindowManager;
37 import android.view.accessibility.AccessibilityManager;
38 import android.view.animation.DecelerateInterpolator;
39 import android.widget.Button;
40 import android.widget.FrameLayout;
41 import android.widget.LinearLayout;
42 import android.widget.TextView;
43 
44 import com.android.systemui.R;
45 
46 import java.util.ArrayList;
47 
48 public class ScreenPinningRequest implements View.OnClickListener {
49     private final Context mContext;
50 
51     private final AccessibilityManager mAccessibilityService;
52     private final WindowManager mWindowManager;
53 
54     private RequestWindowView mRequestWindow;
55 
56     // Id of task to be pinned or locked.
57     private int taskId;
58 
ScreenPinningRequest(Context context)59     public ScreenPinningRequest(Context context) {
60         mContext = context;
61         mAccessibilityService = (AccessibilityManager)
62                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
63         mWindowManager = (WindowManager)
64                 mContext.getSystemService(Context.WINDOW_SERVICE);
65     }
66 
clearPrompt()67     public void clearPrompt() {
68         if (mRequestWindow != null) {
69             mWindowManager.removeView(mRequestWindow);
70             mRequestWindow = null;
71         }
72     }
73 
showPrompt(int taskId, boolean allowCancel)74     public void showPrompt(int taskId, boolean allowCancel) {
75         try {
76             clearPrompt();
77         } catch (IllegalArgumentException e) {
78             // If the call to show the prompt fails due to the request window not already being
79             // attached, then just ignore the error since we will be re-adding it below.
80         }
81 
82         this.taskId = taskId;
83 
84         mRequestWindow = new RequestWindowView(mContext, allowCancel);
85 
86         mRequestWindow.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
87 
88         // show the confirmation
89         WindowManager.LayoutParams lp = getWindowLayoutParams();
90         mWindowManager.addView(mRequestWindow, lp);
91     }
92 
onConfigurationChanged()93     public void onConfigurationChanged() {
94         if (mRequestWindow != null) {
95             mRequestWindow.onConfigurationChanged();
96         }
97     }
98 
getWindowLayoutParams()99     private WindowManager.LayoutParams getWindowLayoutParams() {
100         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
101                 ViewGroup.LayoutParams.MATCH_PARENT,
102                 ViewGroup.LayoutParams.MATCH_PARENT,
103                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
104                 0
105                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
106                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
107                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
108                 ,
109                 PixelFormat.TRANSLUCENT);
110         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
111         lp.setTitle("ScreenPinningConfirmation");
112         lp.gravity = Gravity.FILL;
113         return lp;
114     }
115 
116     @Override
onClick(View v)117     public void onClick(View v) {
118         if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) {
119             try {
120                 ActivityManagerNative.getDefault().startSystemLockTaskMode(taskId);
121             } catch (RemoteException e) {}
122         }
123         clearPrompt();
124     }
125 
getRequestLayoutParams(boolean isLandscape)126     public FrameLayout.LayoutParams getRequestLayoutParams(boolean isLandscape) {
127         return new FrameLayout.LayoutParams(
128                 ViewGroup.LayoutParams.WRAP_CONTENT,
129                 ViewGroup.LayoutParams.WRAP_CONTENT,
130                 isLandscape ? (Gravity.CENTER_VERTICAL | Gravity.RIGHT)
131                             : (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
132     }
133 
134     private class RequestWindowView extends FrameLayout {
135         private static final int OFFSET_DP = 96;
136 
137         private final ColorDrawable mColor = new ColorDrawable(0);
138         private ValueAnimator mColorAnim;
139         private ViewGroup mLayout;
140         private boolean mShowCancel;
141 
RequestWindowView(Context context, boolean showCancel)142         public RequestWindowView(Context context, boolean showCancel) {
143             super(context);
144             setClickable(true);
145             setOnClickListener(ScreenPinningRequest.this);
146             setBackground(mColor);
147             mShowCancel = showCancel;
148         }
149 
150         @Override
onAttachedToWindow()151         public void onAttachedToWindow() {
152             DisplayMetrics metrics = new DisplayMetrics();
153             mWindowManager.getDefaultDisplay().getMetrics(metrics);
154             float density = metrics.density;
155             boolean isLandscape = isLandscapePhone(mContext);
156 
157             inflateView(isLandscape);
158             int bgColor = mContext.getColor(
159                     R.color.screen_pinning_request_window_bg);
160             if (ActivityManager.isHighEndGfx()) {
161                 mLayout.setAlpha(0f);
162                 if (isLandscape) {
163                     mLayout.setTranslationX(OFFSET_DP * density);
164                 } else {
165                     mLayout.setTranslationY(OFFSET_DP * density);
166                 }
167                 mLayout.animate()
168                         .alpha(1f)
169                         .translationX(0)
170                         .translationY(0)
171                         .setDuration(300)
172                         .setInterpolator(new DecelerateInterpolator())
173                         .start();
174 
175                 mColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), 0, bgColor);
176                 mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
177                     @Override
178                     public void onAnimationUpdate(ValueAnimator animation) {
179                         final int c = (Integer) animation.getAnimatedValue();
180                         mColor.setColor(c);
181                     }
182                 });
183                 mColorAnim.setDuration(1000);
184                 mColorAnim.start();
185             } else {
186                 mColor.setColor(bgColor);
187             }
188 
189             IntentFilter filter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
190             filter.addAction(Intent.ACTION_USER_SWITCHED);
191             filter.addAction(Intent.ACTION_SCREEN_OFF);
192             mContext.registerReceiver(mReceiver, filter);
193         }
194 
isLandscapePhone(Context context)195         private boolean isLandscapePhone(Context context) {
196             Configuration config = mContext.getResources().getConfiguration();
197             return config.orientation == Configuration.ORIENTATION_LANDSCAPE
198                     && config.smallestScreenWidthDp < 600;
199         }
200 
inflateView(boolean isLandscape)201         private void inflateView(boolean isLandscape) {
202             // We only want this landscape orientation on <600dp, so rather than handle
203             // resource overlay for -land and -sw600dp-land, just inflate this
204             // other view for this single case.
205             mLayout = (ViewGroup) View.inflate(getContext(), isLandscape
206                     ? R.layout.screen_pinning_request_land_phone : R.layout.screen_pinning_request,
207                     null);
208             // Catch touches so they don't trigger cancel/activate, like outside does.
209             mLayout.setClickable(true);
210             // Status bar is always on the right.
211             mLayout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
212             // Buttons and text do switch sides though.
213             mLayout.findViewById(R.id.screen_pinning_text_area)
214                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
215             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
216             if (Recents.getSystemServices().hasSoftNavigationBar()) {
217                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
218                 swapChildrenIfRtlAndVertical(buttons);
219             } else {
220                 buttons.setVisibility(View.GONE);
221             }
222 
223             ((Button) mLayout.findViewById(R.id.screen_pinning_ok_button))
224                     .setOnClickListener(ScreenPinningRequest.this);
225             if (mShowCancel) {
226                 ((Button) mLayout.findViewById(R.id.screen_pinning_cancel_button))
227                         .setOnClickListener(ScreenPinningRequest.this);
228             } else {
229                 ((Button) mLayout.findViewById(R.id.screen_pinning_cancel_button))
230                         .setVisibility(View.INVISIBLE);
231             }
232 
233             boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
234             ((TextView) mLayout.findViewById(R.id.screen_pinning_description))
235                     .setText(touchExplorationEnabled
236                             ? R.string.screen_pinning_description_accessible
237                             : R.string.screen_pinning_description);
238             final int backBgVisibility = touchExplorationEnabled ? View.INVISIBLE : View.VISIBLE;
239             mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
240             mLayout.findViewById(R.id.screen_pinning_back_bg_light).setVisibility(backBgVisibility);
241 
242             addView(mLayout, getRequestLayoutParams(isLandscape));
243         }
244 
swapChildrenIfRtlAndVertical(View group)245         private void swapChildrenIfRtlAndVertical(View group) {
246             if (mContext.getResources().getConfiguration().getLayoutDirection()
247                     != View.LAYOUT_DIRECTION_RTL) {
248                 return;
249             }
250             LinearLayout linearLayout = (LinearLayout) group;
251             if (linearLayout.getOrientation() == LinearLayout.VERTICAL) {
252                 int childCount = linearLayout.getChildCount();
253                 ArrayList<View> childList = new ArrayList<>(childCount);
254                 for (int i = 0; i < childCount; i++) {
255                     childList.add(linearLayout.getChildAt(i));
256                 }
257                 linearLayout.removeAllViews();
258                 for (int i = childCount - 1; i >= 0; i--) {
259                     linearLayout.addView(childList.get(i));
260                 }
261             }
262         }
263 
264         @Override
onDetachedFromWindow()265         public void onDetachedFromWindow() {
266             mContext.unregisterReceiver(mReceiver);
267         }
268 
onConfigurationChanged()269         protected void onConfigurationChanged() {
270             removeAllViews();
271             inflateView(isLandscapePhone(mContext));
272         }
273 
274         private final Runnable mUpdateLayoutRunnable = new Runnable() {
275             @Override
276             public void run() {
277                 if (mLayout != null && mLayout.getParent() != null) {
278                     mLayout.setLayoutParams(getRequestLayoutParams(isLandscapePhone(mContext)));
279                 }
280             }
281         };
282 
283         private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
284             @Override
285             public void onReceive(Context context, Intent intent) {
286                 if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
287                     post(mUpdateLayoutRunnable);
288                 } else if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)
289                         || intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
290                     clearPrompt();
291                 }
292             }
293         };
294     }
295 
296 }
297