Home | History | Annotate | Download | only in views
      1 /*
      2  * Copyright (C) 2016 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 package com.android.systemui.recents.tv.views;
     17 
     18 import android.animation.Animator;
     19 import android.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.content.res.Configuration;
     22 import android.content.res.Resources;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Outline;
     25 import android.graphics.Point;
     26 import android.graphics.Rect;
     27 import android.graphics.drawable.Drawable;
     28 import android.util.AttributeSet;
     29 import android.util.Log;
     30 import android.util.TypedValue;
     31 import android.view.Display;
     32 import android.view.KeyEvent;
     33 import android.view.View;
     34 import android.view.ViewOutlineProvider;
     35 import android.view.WindowManager;
     36 import android.widget.ImageView;
     37 import android.widget.LinearLayout;
     38 import android.widget.TextView;
     39 
     40 import com.android.systemui.R;
     41 import com.android.systemui.recents.Recents;
     42 import com.android.systemui.recents.misc.SystemServicesProxy;
     43 import com.android.systemui.recents.model.Task;
     44 import com.android.systemui.recents.tv.RecentsTvActivity;
     45 import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
     46 import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
     47 import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
     48 
     49 public class TaskCardView extends LinearLayout {
     50 
     51     private static final String TAG = "TaskCardView";
     52     private View mThumbnailView;
     53     private View mDismissIconView;
     54     private View mInfoFieldView;
     55     private TextView mTitleTextView;
     56     private ImageView mBadgeView;
     57     private Task mTask;
     58     private boolean mDismissState;
     59     private boolean mTouchExplorationEnabled;
     60     private int mCornerRadius;
     61 
     62     private ViewFocusAnimator mViewFocusAnimator;
     63     private DismissAnimationsHolder mDismissAnimationsHolder;
     64     private RecentsRowFocusAnimationHolder mRecentsRowFocusAnimationHolder;
     65 
     66     public TaskCardView(Context context) {
     67         this(context, null);
     68     }
     69 
     70     public TaskCardView(Context context, AttributeSet attrs) {
     71         this(context, attrs, 0);
     72     }
     73 
     74     public TaskCardView(Context context, AttributeSet attrs, int defStyleAttr) {
     75         super(context, attrs, defStyleAttr);
     76         mDismissState = false;
     77         Configuration config = getResources().getConfiguration();
     78         setLayoutDirection(config.getLayoutDirection());
     79     }
     80 
     81     @Override
     82     protected void onFinishInflate() {
     83         super.onFinishInflate();
     84         mThumbnailView = findViewById(R.id.card_view_thumbnail);
     85         mInfoFieldView = findViewById(R.id.card_info_field);
     86         mTitleTextView = (TextView) findViewById(R.id.card_title_text);
     87         mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
     88         mDismissIconView = findViewById(R.id.dismiss_icon);
     89         mDismissAnimationsHolder = new DismissAnimationsHolder(this);
     90         mCornerRadius = getResources().getDimensionPixelSize(
     91                 R.dimen.recents_task_view_rounded_corners_radius);
     92         mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, mInfoFieldView);
     93         SystemServicesProxy ssp = Recents.getSystemServices();
     94         mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
     95         if (!mTouchExplorationEnabled) {
     96             mDismissIconView.setVisibility(VISIBLE);
     97         } else {
     98             mDismissIconView.setVisibility(GONE);
     99         }
    100         mViewFocusAnimator = new ViewFocusAnimator(this);
    101     }
    102 
    103     public void init(Task task) {
    104         mTask = task;
    105         mTitleTextView.setText(task.title);
    106         mBadgeView.setImageDrawable(task.icon);
    107         setThumbnailView();
    108         setContentDescription(task.titleDescription);
    109         mDismissState = false;
    110         mDismissAnimationsHolder.reset();
    111         mRecentsRowFocusAnimationHolder.reset();
    112     }
    113 
    114     public Task getTask() {
    115         return mTask;
    116     }
    117 
    118     @Override
    119     public void getFocusedRect(Rect r) {
    120         mThumbnailView.getFocusedRect(r);
    121     }
    122 
    123     public Rect getFocusedThumbnailRect() {
    124         Rect r = new Rect();
    125         mThumbnailView.getGlobalVisibleRect(r);
    126         return r;
    127     }
    128 
    129     public static Rect getStartingCardThumbnailRect(
    130             Context context, boolean hasFocus, int numberOfTasks) {
    131         if(numberOfTasks > 1) {
    132             return getStartingCardThumbnailRectForStartPosition(context, hasFocus);
    133         } else {
    134             return getStartingCardThumbnailRectForFocusedPosition(context, hasFocus);
    135         }
    136     }
    137 
    138     private static Rect getStartingCardThumbnailRectForStartPosition(
    139             Context context, boolean hasFocus) {
    140         Resources res = context.getResources();
    141 
    142         int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
    143         int totalSpacing = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_card_spacing) * 2;
    144         if (hasFocus) {
    145             totalSpacing += res.getDimensionPixelOffset(R.dimen.recents_tv_gird_focused_card_delta);
    146         }
    147         int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
    148         int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
    149         int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
    150                 res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
    151 
    152         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    153         Display display = wm.getDefaultDisplay();
    154         Point size = new Point();
    155         display.getSize(size);
    156         int screenWidth = size.x;
    157 
    158         return new Rect(screenWidth / 2 + width / 2 + totalSpacing,
    159                 topMargin + headerHeight,
    160                 screenWidth / 2 + width / 2 + totalSpacing + width,
    161                 topMargin + headerHeight + height);
    162     }
    163 
    164     private static Rect getStartingCardThumbnailRectForFocusedPosition(
    165             Context context, boolean hasFocus) {
    166         Resources res = context.getResources();
    167 
    168         TypedValue out = new TypedValue();
    169         res.getValue(R.integer.selected_scale, out, true);
    170         float scale = hasFocus ? out.getFloat() : 1;
    171 
    172         int width = res.getDimensionPixelOffset(R.dimen.recents_tv_card_width);
    173         int widthDelta = (int) (width * scale - width);
    174         int height = res.getDimensionPixelOffset(R.dimen.recents_tv_screenshot_height);
    175         int heightDelta = (int) (height * scale - height);
    176         int topMargin = res.getDimensionPixelOffset(R.dimen.recents_tv_gird_row_top_margin);
    177 
    178         int headerHeight = res.getDimensionPixelOffset(R.dimen.recents_tv_card_extra_badge_size) +
    179                 res.getDimensionPixelOffset(R.dimen.recents_tv_icon_padding_bottom);
    180         int headerHeightDelta = (int) (headerHeight * scale - headerHeight);
    181 
    182         int dismissAreaHeight =
    183                 res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_top_margin) +
    184                 res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_bottom_margin) +
    185                 res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_icon_size) +
    186                 res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_text_size);
    187 
    188         int dismissAreaHeightDelta = (int) (dismissAreaHeight * scale - dismissAreaHeight);
    189 
    190         int totalHeightDelta = heightDelta + headerHeightDelta + dismissAreaHeightDelta;
    191 
    192         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    193         Display display = wm.getDefaultDisplay();
    194         Point size = new Point();
    195         display.getSize(size);
    196         int screenWidth = size.x;
    197 
    198         return new Rect(screenWidth / 2 - width / 2 - widthDelta / 2,
    199                 topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale),
    200                 screenWidth / 2 + width / 2 + widthDelta / 2,
    201                 topMargin - totalHeightDelta / 2 + (int) (headerHeight * scale) +
    202                         (int) (height * scale));
    203     }
    204 
    205     @Override
    206     public boolean dispatchKeyEvent(KeyEvent event) {
    207         // Override dispatchKeyEvent() instead of onKeyDown() to prevent warning from ViewRootImpl.
    208         switch (event.getKeyCode()) {
    209             case KeyEvent.KEYCODE_DPAD_DOWN : {
    210                 if (!isInDismissState() && event.getAction() == KeyEvent.ACTION_DOWN) {
    211                     setDismissState(true);
    212                     return true;
    213                 }
    214                 break;
    215             }
    216             case KeyEvent.KEYCODE_DPAD_UP : {
    217                 if (event.getAction() == KeyEvent.ACTION_DOWN) {
    218                     if (isInDismissState()) {
    219                         setDismissState(false);
    220                     } else {
    221                         ((RecentsTvActivity) getContext()).requestPipControlsFocus();
    222                     }
    223                 }
    224                 return true;
    225             }
    226 
    227             // Eat right and left key presses when we are in dismiss state
    228             case KeyEvent.KEYCODE_DPAD_LEFT :
    229             case KeyEvent.KEYCODE_DPAD_RIGHT : {
    230                 if (isInDismissState()) {
    231                     return true;
    232                 }
    233                 break;
    234             }
    235         }
    236         return super.dispatchKeyEvent(event);
    237     }
    238 
    239     private void setDismissState(boolean dismissState) {
    240         if (mDismissState != dismissState) {
    241             mDismissState = dismissState;
    242             // Check for touch exploration to ensure dismiss icon/text do not
    243             // get animated. This should be removed based on decision from
    244             // b/29208918
    245             if (!mTouchExplorationEnabled) {
    246                 if (dismissState) {
    247                     mDismissAnimationsHolder.startEnterAnimation();
    248                 } else {
    249                     mDismissAnimationsHolder.startExitAnimation();
    250                 }
    251             }
    252         }
    253     }
    254 
    255     public boolean isInDismissState() {
    256         return mDismissState;
    257     }
    258 
    259     public void startDismissTaskAnimation(Animator.AnimatorListener listener) {
    260         mDismissState = false;
    261         mDismissAnimationsHolder.startDismissAnimation(listener);
    262     }
    263 
    264     public ViewFocusAnimator getViewFocusAnimator() {
    265         return mViewFocusAnimator;
    266     }
    267 
    268     public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() {
    269         return mRecentsRowFocusAnimationHolder;
    270     }
    271 
    272     private void setThumbnailView() {
    273         ImageView screenshotView = (ImageView) findViewById(R.id.card_view_banner_icon);
    274         PackageManager pm = getContext().getPackageManager();
    275         if (mTask.thumbnail != null) {
    276             setAsScreenShotView(mTask.thumbnail, screenshotView);
    277         } else {
    278             try {
    279                 Drawable banner = null;
    280                 if (mTask.key != null) {
    281                     banner = pm.getActivityBanner(mTask.key.baseIntent);
    282                 }
    283                 if (banner != null) {
    284                     setAsBannerView(banner, screenshotView);
    285                 } else {
    286                     setAsIconView(mTask.icon, screenshotView);
    287                 }
    288             } catch (PackageManager.NameNotFoundException e) {
    289                 Log.e(TAG, "Package not found : " + e);
    290                 setAsIconView(mTask.icon, screenshotView);
    291             }
    292         }
    293     }
    294 
    295     private void setAsScreenShotView(Bitmap screenshot, ImageView screenshotView) {
    296         LayoutParams lp = (LayoutParams) screenshotView.getLayoutParams();
    297         lp.width = LayoutParams.MATCH_PARENT;
    298         lp.height = LayoutParams.MATCH_PARENT;
    299 
    300         screenshotView.setLayoutParams(lp);
    301         screenshotView.setClipToOutline(true);
    302         screenshotView.setOutlineProvider(new ViewOutlineProvider() {
    303             @Override
    304             public void getOutline(View view, Outline outline) {
    305                 outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
    306             }
    307         });
    308         screenshotView.setImageBitmap(screenshot);
    309     }
    310 
    311     private void setAsBannerView(Drawable banner, ImageView bannerView) {
    312         LayoutParams lp = (LayoutParams) bannerView.getLayoutParams();
    313         lp.width = getResources()
    314                 .getDimensionPixelSize(R.dimen.recents_tv_banner_width);
    315         lp.height = getResources()
    316                 .getDimensionPixelSize(R.dimen.recents_tv_banner_height);
    317         bannerView.setLayoutParams(lp);
    318         bannerView.setImageDrawable(banner);
    319     }
    320 
    321     private void setAsIconView(Drawable icon, ImageView iconView) {
    322         LayoutParams lp = (LayoutParams) iconView.getLayoutParams();
    323         lp.width = getResources()
    324                 .getDimensionPixelSize(R.dimen.recents_tv_fallback_icon_width);
    325         lp.height = getResources()
    326                 .getDimensionPixelSize(R.dimen.recents_tv_fallback_icon_height);
    327 
    328         iconView.setLayoutParams(lp);
    329         iconView.setImageDrawable(icon);
    330     }
    331 
    332     public View getThumbnailView() {
    333         return mThumbnailView;
    334     }
    335 
    336     public View getInfoFieldView() {
    337         return mInfoFieldView;
    338     }
    339 
    340     public View getDismissIconView() {
    341         return mDismissIconView;
    342     }
    343 
    344     public static int getNumberOfVisibleTasks(Context context) {
    345         Resources res = context.getResources();
    346         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    347         Display display = wm.getDefaultDisplay();
    348         Point size = new Point();
    349         display.getSize(size);
    350         int screenWidth = size.x;
    351         int cardWidth = res.getDimensionPixelSize(R.dimen.recents_tv_card_width);
    352         int spacing = res.getDimensionPixelSize(R.dimen.recents_tv_gird_card_spacing);
    353         return (int) (1.0 + Math.ceil(screenWidth / (cardWidth + spacing * 2.0)));
    354     }
    355 }
    356