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