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