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.Rect; 21 import android.util.AttributeSet; 22 import android.view.View; 23 24 import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; 25 import com.android.systemui.R; 26 import com.android.systemui.recents.model.TaskStack; 27 import com.android.systemui.recents.views.TaskStackView; 28 29 public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener { 30 31 private TaskStackView mSv; 32 private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm; TaskViewFocusFrame(Context context)33 public TaskViewFocusFrame(Context context) { 34 this(context, null); 35 } 36 TaskViewFocusFrame(Context context, AttributeSet attrs)37 public TaskViewFocusFrame(Context context, AttributeSet attrs) { 38 this(context, attrs, 0); 39 } 40 TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr)41 public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) { 42 this(context, attrs, defStyleAttr, 0); 43 } 44 TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)45 public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr, 46 int defStyleRes) { 47 super(context, attrs, defStyleAttr, defStyleRes); 48 setBackground(mContext.getDrawable( 49 R.drawable.recents_grid_task_view_focus_frame_background)); 50 setFocusable(false); 51 hide(); 52 } 53 TaskViewFocusFrame(Context context, TaskStackView stackView, TaskGridLayoutAlgorithm taskGridLayoutAlgorithm)54 public TaskViewFocusFrame(Context context, TaskStackView stackView, 55 TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) { 56 this(context); 57 mSv = stackView; 58 mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm; 59 } 60 61 /** 62 * Measure the width and height of the focus frame according to the current grid task view size. 63 */ measure()64 public void measure() { 65 int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness(); 66 Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect(); 67 measure( 68 MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY), 69 MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY)); 70 } 71 72 /** 73 * Layout the focus frame with its size. 74 */ layout()75 public void layout() { 76 layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); 77 } 78 79 /** 80 * Update the current size of grid task view and the focus frame. 81 */ resize()82 public void resize() { 83 if (mSv.useGridLayout()) { 84 mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount()); 85 measure(); 86 requestLayout(); 87 } 88 } 89 90 /** 91 * Move the task view focus frame to surround the newly focused view. If it's {@code null} or 92 * it's not an instance of GridTaskView, we hide the focus frame. 93 * @param newFocus The newly focused view. 94 */ moveGridTaskViewFocus(View newFocus)95 public void moveGridTaskViewFocus(View newFocus) { 96 if (mSv.useGridLayout()) { 97 // The frame only shows up in the grid layout. It shouldn't show up in the stack 98 // layout including when we're in the split screen. 99 if (newFocus instanceof GridTaskView) { 100 // If the focus goes to a GridTaskView, we show the frame and layout it. 101 int[] location = new int[2]; 102 newFocus.getLocationInWindow(location); 103 int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness(); 104 setTranslationX(location[0] - thickness); 105 setTranslationY(location[1] - thickness); 106 show(); 107 } else { 108 // If focus goes to other views, we hide the frame. 109 hide(); 110 } 111 } 112 } 113 114 @Override onGlobalFocusChanged(View oldFocus, View newFocus)115 public void onGlobalFocusChanged(View oldFocus, View newFocus) { 116 if (!mSv.useGridLayout()) { 117 return; 118 } 119 if (newFocus == null) { 120 // We're going to touch mode, unset the focus. 121 moveGridTaskViewFocus(null); 122 return; 123 } 124 if (oldFocus == null) { 125 // We're returning from touch mode, set the focus to the previously focused task. 126 final TaskStack stack = mSv.getStack(); 127 final int taskCount = stack.getTaskCount(); 128 final int k = stack.indexOfTask(mSv.getFocusedTask()); 129 final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount); 130 mSv.setFocusedTask(taskIndexToFocus, false, true); 131 } 132 } 133 show()134 private void show() { 135 setAlpha(1f); 136 } 137 hide()138 private void hide() { 139 setAlpha(0f); 140 } 141 } 142