1 /* 2 * Copyright (C) 2017 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.views.grid; 18 19 import android.content.Context; 20 import android.graphics.Canvas; 21 import android.graphics.Path; 22 import android.util.AttributeSet; 23 24 import com.android.systemui.R; 25 import com.android.systemui.recents.views.TaskViewThumbnail; 26 27 public class GridTaskViewThumbnail extends TaskViewThumbnail { 28 29 private final Path mThumbnailOutline = new Path(); 30 private final Path mRestBackgroundOutline = new Path(); 31 // True if either this view's size or thumbnail scale has changed and mThumbnailOutline should 32 // be updated. 33 private boolean mUpdateThumbnailOutline = true; 34 GridTaskViewThumbnail(Context context)35 public GridTaskViewThumbnail(Context context) { 36 this(context, null); 37 } 38 GridTaskViewThumbnail(Context context, AttributeSet attrs)39 public GridTaskViewThumbnail(Context context, AttributeSet attrs) { 40 this(context, attrs, 0); 41 } 42 GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr)43 public GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) { 44 this(context, attrs, defStyleAttr, 0); 45 } 46 GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)47 public GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, 48 int defStyleRes) { 49 super(context, attrs, defStyleAttr, defStyleRes); 50 mCornerRadius = getResources().getDimensionPixelSize( 51 R.dimen.recents_grid_task_view_rounded_corners_radius); 52 } 53 54 /** 55 * Called when the task view frame changes, allowing us to move the contents of the header 56 * to match the frame changes. 57 */ onTaskViewSizeChanged(int width, int height)58 public void onTaskViewSizeChanged(int width, int height) { 59 mUpdateThumbnailOutline = true; 60 super.onTaskViewSizeChanged(width, height); 61 } 62 63 /** 64 * Updates the scale of the bitmap relative to this view. 65 */ updateThumbnailMatrix()66 public void updateThumbnailMatrix() { 67 mUpdateThumbnailOutline = true; 68 super.updateThumbnailMatrix(); 69 } 70 updateThumbnailOutline()71 private void updateThumbnailOutline() { 72 final int titleHeight = getResources().getDimensionPixelSize( 73 R.dimen.recents_grid_task_view_header_height); 74 final int viewWidth = mTaskViewRect.width(); 75 final int viewHeight = mTaskViewRect.height() - titleHeight; 76 final int thumbnailWidth = Math.min(viewWidth, 77 (int) (mThumbnailRect.width() * mThumbnailScale)); 78 final int thumbnailHeight = Math.min(viewHeight, 79 (int) (mThumbnailRect.height() * mThumbnailScale)); 80 81 if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { 82 // Draw the thumbnail, we only round the bottom corners: 83 // 84 // outerLeft outerRight 85 // <-----------------------> mRestBackgroundOutline 86 // _________________________ (thumbnailWidth < viewWidth) 87 // |_______________________| outerTop A ____ B 88 // | | ↑ | | 89 // | | | | | 90 // | | | | | 91 // | | | | | C 92 // \_______________________/ ↓ |__/ 93 // mCornerRadius outerBottom E D 94 // 95 // mRestBackgroundOutline (thumbnailHeight < viewHeight) 96 // A _________________________ B 97 // | | C 98 // F \_______________________/ 99 // E D 100 final int outerLeft = 0; 101 final int outerTop = 0; 102 final int outerRight = outerLeft + thumbnailWidth; 103 final int outerBottom = outerTop + thumbnailHeight; 104 createThumbnailPath(outerLeft, outerTop, outerRight, outerBottom, mThumbnailOutline); 105 106 if (thumbnailWidth < viewWidth) { 107 final int l = Math.max(0, outerRight - mCornerRadius); 108 final int r = outerRight; 109 final int t = outerTop; 110 final int b = outerBottom; 111 mRestBackgroundOutline.reset(); 112 mRestBackgroundOutline.moveTo(l, t); // A 113 mRestBackgroundOutline.lineTo(r, t); // B 114 mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C 115 mRestBackgroundOutline.arcTo(r - 2 * mCornerRadius, b - 2 * mCornerRadius, r, b, 116 0, 90, false); // D 117 mRestBackgroundOutline.lineTo(l, b); // E 118 mRestBackgroundOutline.lineTo(l, t); // A 119 mRestBackgroundOutline.close(); 120 121 } 122 if (thumbnailHeight < viewHeight) { 123 final int l = outerLeft; 124 final int r = outerRight; 125 final int t = Math.max(0, thumbnailHeight - mCornerRadius); 126 final int b = outerBottom; 127 mRestBackgroundOutline.reset(); 128 mRestBackgroundOutline.moveTo(l, t); // A 129 mRestBackgroundOutline.lineTo(r, t); // B 130 mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C 131 mRestBackgroundOutline.arcTo(r - 2 * mCornerRadius, b - 2 * mCornerRadius, r, b, 132 0, 90, false); // D 133 mRestBackgroundOutline.lineTo(l + mCornerRadius, b); // E 134 mRestBackgroundOutline.arcTo(l, b - 2 * mCornerRadius, l + 2 * mCornerRadius, b, 135 90, 90, false); // F 136 mRestBackgroundOutline.lineTo(l, t); // A 137 mRestBackgroundOutline.close(); 138 } 139 } else { 140 createThumbnailPath(0, 0, viewWidth, viewHeight, mThumbnailOutline); 141 } 142 } 143 createThumbnailPath(int outerLeft, int outerTop, int outerRight, int outerBottom, Path outPath)144 private void createThumbnailPath(int outerLeft, int outerTop, int outerRight, int outerBottom, 145 Path outPath) { 146 outPath.reset(); 147 outPath.moveTo(outerLeft, outerTop); 148 outPath.lineTo(outerRight, outerTop); 149 outPath.lineTo(outerRight, outerBottom - mCornerRadius); 150 outPath.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius, outerRight, 151 outerBottom, 0, 90, false); 152 outPath.lineTo(outerLeft + mCornerRadius, outerBottom); 153 outPath.arcTo(outerLeft, outerBottom - 2 * mCornerRadius, outerLeft + 2 * mCornerRadius, 154 outerBottom, 90, 90, false); 155 outPath.lineTo(outerLeft, outerTop); 156 outPath.close(); 157 } 158 159 @Override onDraw(Canvas canvas)160 protected void onDraw(Canvas canvas) { 161 final int titleHeight = getResources().getDimensionPixelSize( 162 R.dimen.recents_grid_task_view_header_height); 163 final int viewWidth = mTaskViewRect.width(); 164 final int viewHeight = mTaskViewRect.height() - titleHeight; 165 final int thumbnailWidth = Math.min(viewWidth, 166 (int) (mThumbnailRect.width() * mThumbnailScale)); 167 final int thumbnailHeight = Math.min(viewHeight, 168 (int) (mThumbnailRect.height() * mThumbnailScale)); 169 170 if (mUpdateThumbnailOutline) { 171 updateThumbnailOutline(); 172 mUpdateThumbnailOutline = false; 173 } 174 175 if (mUserLocked) { 176 canvas.drawPath(mThumbnailOutline, mLockedPaint); 177 } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { 178 // Draw the background, there will be some small overdraw with the thumbnail 179 if (thumbnailWidth < viewWidth) { 180 // Portrait thumbnail on a landscape task view 181 canvas.drawPath(mRestBackgroundOutline, mBgFillPaint); 182 } 183 if (thumbnailHeight < viewHeight) { 184 // Landscape thumbnail on a portrait task view 185 canvas.drawPath(mRestBackgroundOutline, mBgFillPaint); 186 } 187 canvas.drawPath(mThumbnailOutline, getDrawPaint()); 188 } else { 189 canvas.drawPath(mThumbnailOutline, mBgFillPaint); 190 } 191 } 192 } 193