1 /* 2 * Copyright (C) 2024 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.wm.shell.draganddrop.anim 18 19 import android.content.res.Resources 20 import android.graphics.Insets 21 import android.graphics.Rect 22 import com.android.wm.shell.R 23 import com.android.wm.shell.common.DisplayLayout 24 import com.android.wm.shell.draganddrop.SplitDragPolicy.Target 25 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0 26 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1 27 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_2 28 import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_3 29 30 /** 31 * Represents Drop Zone targets and animations for when the system is currently in a 2 app 50/50 32 * split. 33 * SnapPosition = 2_50_50 34 * 35 * NOTE: Naming convention for many variables is done as "hXtYZ" 36 * This means that variable is a transformation on the Z property for target index Y while the user 37 * is hovering over target index X 38 * Ex: h1t2scaleX=2 => User is hovering over target index 1, target index 2 should scaleX by 2 39 * 40 * TODO(b/349828130): Everything in this class is temporary, none of this is up to spec. 41 */ 42 class TwoFiftyFiftyTargetAnimator : DropTargetAnimSupplier { 43 /** 44 * TODO: Could we transpose all the horizontal rects by 90 degrees and have that suffice for 45 * top bottom split?? Hmmm... Doubt it. 46 */ getTargetsnull47 override fun getTargets( 48 displayLayout: DisplayLayout, 49 insets: Insets, 50 isLeftRightSplit: Boolean, 51 resources: Resources 52 ): Pair<List<Target>, List<List<HoverAnimProps>>> { 53 val targets : ArrayList<Target> = ArrayList() 54 val w: Int = displayLayout.width() 55 val h: Int = displayLayout.height() 56 val iw = w - insets.left - insets.right 57 val ih = h - insets.top - insets.bottom 58 val l = insets.left 59 val t = insets.top 60 val displayRegion = Rect(l, t, l + iw, t + ih) 61 val fullscreenDrawRegion = Rect(displayRegion) 62 val dividerWidth: Float = resources.getDimensionPixelSize( 63 R.dimen.split_divider_bar_width 64 ).toFloat() 65 66 val farStartBounds = Rect() 67 farStartBounds.set(displayRegion) 68 val startBounds = Rect() 69 startBounds.set(displayRegion) 70 val endBounds = Rect() 71 endBounds.set(displayRegion) 72 val farEndBounds = Rect() 73 farEndBounds.set(displayRegion) 74 val endsPercent = 0.10f 75 val visibleStagePercent = 0.45f 76 val halfDividerWidth = dividerWidth.toInt() / 2 77 val endsWidth = Math.round(displayRegion.width() * endsPercent) 78 val stageWidth = Math.round(displayRegion.width() * visibleStagePercent) 79 80 81 // Place the farStart and farEnds outside of the display, and then 82 // animate them in once the hover starts 83 // | = divider; || = display boundary 84 // farStart || start | end || farEnd 85 farStartBounds.left = -endsWidth 86 farStartBounds.right = 0 87 startBounds.left = farStartBounds.right + dividerWidth.toInt() 88 startBounds.right = startBounds.left + stageWidth 89 endBounds.left = startBounds.right + dividerWidth.toInt() 90 endBounds.right = endBounds.left + stageWidth 91 farEndBounds.left = fullscreenDrawRegion.right 92 farEndBounds.right = farEndBounds.left + endsWidth 93 94 95 // For the hit rect, trim the divider space we've added between the 96 // rects 97 targets.add( 98 Target( 99 Target.TYPE_SPLIT_LEFT, 100 Rect( 101 farStartBounds.left, farStartBounds.top, 102 farStartBounds.right + halfDividerWidth, 103 farStartBounds.bottom 104 ), 105 farStartBounds, SPLIT_INDEX_0 106 ) 107 ) 108 targets.add( 109 Target( 110 Target.TYPE_SPLIT_LEFT, 111 Rect( 112 startBounds.left - halfDividerWidth, 113 startBounds.top, 114 startBounds.right + halfDividerWidth, 115 startBounds.bottom 116 ), 117 startBounds, SPLIT_INDEX_1 118 ) 119 ) 120 targets.add( 121 Target( 122 Target.TYPE_SPLIT_LEFT, 123 Rect( 124 endBounds.left - halfDividerWidth, 125 endBounds.top, endBounds.right, endBounds.bottom 126 ), 127 endBounds, SPLIT_INDEX_2 128 ) 129 ) 130 targets.add( 131 Target( 132 Target.TYPE_SPLIT_LEFT, 133 Rect( 134 farEndBounds.left - halfDividerWidth, 135 farEndBounds.top, farEndBounds.right, farEndBounds.bottom 136 ), 137 farEndBounds, SPLIT_INDEX_3 138 ) 139 ) 140 141 142 // Hovering over target 0, 143 // * increase scaleX of target 0 144 // * decrease scaleX of target 1, 2 145 // * ensure target 3 offscreen 146 147 // bring target 0 in from offscreen and expand 148 val h0t0ScaleX = stageWidth.toFloat() / endsWidth 149 val h0t0TransX: Float = stageWidth / h0t0ScaleX + dividerWidth 150 val h0t0HoverProps = HoverAnimProps( 151 targets.get(0), 152 h0t0TransX, farStartBounds.top.toFloat(), h0t0ScaleX, 1f, 153 Rect( 154 0, 0, (stageWidth + dividerWidth).toInt(), 155 farStartBounds.bottom 156 ) 157 ) 158 159 160 // move target 1 over to the middle/end 161 val h0t1TransX = stageWidth.toFloat() 162 val h0t1ScaleX = 1f 163 val h0t1HoverProps = HoverAnimProps( 164 targets.get(1), 165 h0t1TransX, startBounds.top.toFloat(), h0t1ScaleX, 1f, 166 Rect( 167 stageWidth, 0, (stageWidth + h0t1TransX).toInt(), 168 farStartBounds.bottom 169 ) 170 ) 171 172 173 // move target 2 to the very end 174 val h0t2TransX = endBounds.left + stageWidth / 2f 175 val h0t2ScaleX = endsWidth.toFloat() / stageWidth 176 val h0t2HoverProps = HoverAnimProps( 177 targets.get(2), 178 h0t2TransX, endBounds.top.toFloat(), h0t2ScaleX, 1f, 179 Rect( 180 displayRegion.right as Int - endsWidth, 0, 181 displayRegion.right as Int, 182 farStartBounds.bottom 183 ) 184 ) 185 186 187 // move target 3 off-screen 188 val h0t3TransX = farEndBounds.right.toFloat() 189 val h0t3ScaleX = 1f 190 val h0t3HoverProps = HoverAnimProps( 191 targets.get(3), 192 h0t3TransX, farEndBounds.top.toFloat(), h0t3ScaleX, 1f, 193 null 194 ) 195 val animPropsForHoverTarget0 = 196 listOf(h0t0HoverProps, h0t1HoverProps, h0t2HoverProps, h0t3HoverProps) 197 198 199 // Hovering over target 1, 200 // * Bring in target 0 from offscreen start 201 // * Shift over target 1 202 // * Slightly lower scale of target 2 203 // * Ensure target 4 offscreen 204 // bring target 0 in from offscreen 205 val h1t0TransX = 0f 206 val h1t0ScaleX = 1f 207 val h1t0HoverProps = HoverAnimProps( 208 targets.get(0), 209 h1t0TransX, farStartBounds.top.toFloat(), h1t0ScaleX, 1f, 210 Rect( 211 0, 0, (farStartBounds.width() + dividerWidth).toInt(), 212 farStartBounds.bottom 213 ) 214 ) 215 216 217 // move target 1 over a tiny bit by same amount and make it smaller 218 val h1t1TransX: Float = endsWidth + dividerWidth 219 val h1t1ScaleX = 1f 220 val h1t1HoverProps = HoverAnimProps( 221 targets.get(1), 222 h1t1TransX, startBounds.top.toFloat(), h1t1ScaleX, 1f, 223 Rect( 224 h1t1TransX.toInt(), 0, (h1t1TransX + stageWidth).toInt(), 225 farStartBounds.bottom 226 ) 227 ) 228 229 230 // move target 2 to the very end 231 val h1t2TransX = (endBounds.left + farStartBounds.width()).toFloat() 232 val h1t2ScaleX = h1t1ScaleX 233 val h1t2HoverProps = HoverAnimProps( 234 targets.get(2), 235 h1t2TransX, endBounds.top.toFloat(), h1t2ScaleX, 1f, 236 Rect( 237 endBounds.left + farStartBounds.width(), 238 0, 239 (endBounds.left + farStartBounds.width() + stageWidth), 240 farStartBounds.bottom 241 ) 242 ) 243 244 245 // move target 3 off-screen, default laid out is off-screen 246 val h1t3TransX = farEndBounds.right.toFloat() 247 val h1t3ScaleX = 1f 248 val h1t3HoverProps = HoverAnimProps( 249 targets.get(3), 250 h1t3TransX, farEndBounds.top.toFloat(), h1t3ScaleX, 1f, 251 null 252 ) 253 val animPropsForHoverTarget1 = 254 listOf(h1t0HoverProps, h1t1HoverProps, h1t2HoverProps, h1t3HoverProps) 255 256 257 // Hovering over target 2, 258 // * Ensure Target 0 offscreen 259 // * Ensure target 1 back to start, slightly smaller scale 260 // * Slightly lower scale of target 2 261 // * Bring target 4 on screen 262 // reset target 0 263 val h2t0TransX = farStartBounds.left.toFloat() 264 val h2t0ScaleX = 1f 265 val h2t0HoverProps = HoverAnimProps( 266 targets.get(0), 267 h2t0TransX, farStartBounds.top.toFloat(), h2t0ScaleX, 1f, 268 null 269 ) 270 271 272 // move target 1 over a tiny bit by same amount and make it smaller 273 val h2t1TransX = startBounds.left.toFloat() 274 val h2t1ScaleX = 1f 275 val h2t1HoverProps = HoverAnimProps( 276 targets.get(1), 277 h2t1TransX, startBounds.top.toFloat(), h2t1ScaleX, 1f, 278 Rect( 279 startBounds.left, 0, 280 (startBounds.left + stageWidth), 281 farStartBounds.bottom 282 ) 283 ) 284 285 286 // move target 2 to the very end 287 val h2t2TransX = endBounds.left.toFloat() 288 val h2t2ScaleX = h2t1ScaleX 289 val h2t2HoverProps = HoverAnimProps( 290 targets.get(2), 291 h2t2TransX, endBounds.top.toFloat(), h2t2ScaleX, 1f, 292 Rect( 293 (startBounds.right + dividerWidth).toInt(), 294 0, 295 endBounds.left + stageWidth, 296 farStartBounds.bottom 297 ) 298 ) 299 300 301 // bring target 3 on-screen 302 val h2t3TransX = (farEndBounds.left - farEndBounds.width()).toFloat() 303 val h2t3ScaleX = 1f 304 val h2t3HoverProps = HoverAnimProps( 305 targets.get(3), 306 h2t3TransX, farEndBounds.top.toFloat(), h2t3ScaleX, 1f, 307 Rect( 308 endBounds.right, 309 0, 310 displayRegion.right, 311 farStartBounds.bottom 312 ) 313 ) 314 val animPropsForHoverTarget2 = 315 listOf(h2t0HoverProps, h2t1HoverProps, h2t2HoverProps, h2t3HoverProps) 316 317 318 // Hovering over target 3, 319 // * Ensure Target 0 offscreen 320 // * Ensure target 1 back to start, slightly smaller scale 321 // * Slightly lower scale of target 2 322 // * Bring target 4 on screen and scale up 323 // reset target 0 324 val h3t0TransX = farStartBounds.left.toFloat() 325 val h3t0ScaleX = 1f 326 val h3t0HoverProps = HoverAnimProps( 327 targets.get(0), 328 h3t0TransX, farStartBounds.top.toFloat(), h3t0ScaleX, 1f, 329 null 330 ) 331 332 333 // move target 1 over a tiny bit by same amount and make it smaller 334 val h3t1ScaleX = endsWidth.toFloat() / stageWidth 335 val h3t1TransX = 0 - (stageWidth / (1 / h3t1ScaleX)) 336 val h3t1HoverProps = HoverAnimProps( 337 targets.get(1), 338 h3t1TransX, startBounds.top.toFloat(), h3t1ScaleX, 1f, 339 Rect( 340 0, 0, 341 endsWidth, 342 farStartBounds.bottom 343 ) 344 ) 345 346 347 // move target 2 towards the start 348 val h3t2TransX: Float = endsWidth + dividerWidth 349 val h3t2ScaleX = 1f 350 val h3t2HoverProps = HoverAnimProps( 351 targets.get(2), 352 h3t2TransX, endBounds.top.toFloat(), h3t2ScaleX, 1f, 353 Rect( 354 endsWidth, 0, 355 (endsWidth + stageWidth + dividerWidth).toInt(), 356 farStartBounds.bottom 357 ) 358 ) 359 360 361 // bring target 3 on-screen and expand 362 val h3t3ScaleX = stageWidth.toFloat() / endsWidth 363 val h3t3TransX = endBounds.right - stageWidth / 2f 364 val h3t3HoverProps = HoverAnimProps( 365 targets.get(3), 366 h3t3TransX, farEndBounds.top.toFloat(), h3t3ScaleX, 1f, 367 Rect( 368 displayRegion.right - stageWidth, 0, 369 displayRegion.right, 370 farStartBounds.bottom 371 ) 372 ) 373 val animPropsForHoverTarget3 = 374 listOf(h3t0HoverProps, h3t1HoverProps, h3t2HoverProps, h3t3HoverProps) 375 376 return Pair(targets, listOf(animPropsForHoverTarget0, animPropsForHoverTarget1, 377 animPropsForHoverTarget2, animPropsForHoverTarget3)) 378 379 } 380 }