Home | History | Annotate | Download | only in recents
      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 
     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 
     67     public void clearPrompt() {
     68         if (mRequestWindow != null) {
     69             mWindowManager.removeView(mRequestWindow);
     70             mRequestWindow = null;
     71         }
     72     }
     73 
     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 
     93     public void onConfigurationChanged() {
     94         if (mRequestWindow != null) {
     95             mRequestWindow.onConfigurationChanged();
     96         }
     97     }
     98 
     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
    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 
    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 
    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
    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 
    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 
    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             ((TextView) mLayout.findViewById(R.id.screen_pinning_description))
    234                     .setText(R.string.screen_pinning_description);
    235             final int backBgVisibility =
    236                     mAccessibilityService.isEnabled() ? View.INVISIBLE : View.VISIBLE;
    237             mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
    238             mLayout.findViewById(R.id.screen_pinning_back_bg_light).setVisibility(backBgVisibility);
    239 
    240             addView(mLayout, getRequestLayoutParams(isLandscape));
    241         }
    242 
    243         private void swapChildrenIfRtlAndVertical(View group) {
    244             if (mContext.getResources().getConfiguration().getLayoutDirection()
    245                     != View.LAYOUT_DIRECTION_RTL) {
    246                 return;
    247             }
    248             LinearLayout linearLayout = (LinearLayout) group;
    249             if (linearLayout.getOrientation() == LinearLayout.VERTICAL) {
    250                 int childCount = linearLayout.getChildCount();
    251                 ArrayList<View> childList = new ArrayList<>(childCount);
    252                 for (int i = 0; i < childCount; i++) {
    253                     childList.add(linearLayout.getChildAt(i));
    254                 }
    255                 linearLayout.removeAllViews();
    256                 for (int i = childCount - 1; i >= 0; i--) {
    257                     linearLayout.addView(childList.get(i));
    258                 }
    259             }
    260         }
    261 
    262         @Override
    263         public void onDetachedFromWindow() {
    264             mContext.unregisterReceiver(mReceiver);
    265         }
    266 
    267         protected void onConfigurationChanged() {
    268             removeAllViews();
    269             inflateView(isLandscapePhone(mContext));
    270         }
    271 
    272         private final Runnable mUpdateLayoutRunnable = new Runnable() {
    273             @Override
    274             public void run() {
    275                 if (mLayout != null && mLayout.getParent() != null) {
    276                     mLayout.setLayoutParams(getRequestLayoutParams(isLandscapePhone(mContext)));
    277                 }
    278             }
    279         };
    280 
    281         private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    282             @Override
    283             public void onReceive(Context context, Intent intent) {
    284                 if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
    285                     post(mUpdateLayoutRunnable);
    286                 } else if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)
    287                         || intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
    288                     clearPrompt();
    289                 }
    290             }
    291         };
    292     }
    293 
    294 }
    295