1 /* 2 * Copyright (C) 2023 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.car.portraitlauncher.panel; 18 19 import static android.provider.Settings.Global.ANIMATOR_DURATION_SCALE; 20 import static android.provider.Settings.Global.getFloat; 21 22 import static com.android.car.portraitlauncher.panel.TaskViewPanelStateChangeReason.ON_GRIP_BAR_CLICKED; 23 import static com.android.car.portraitlauncher.panel.TaskViewPanelStateChangeReason.ON_GRIP_BAR_DRAG; 24 import static com.android.car.portraitlauncher.panel.TaskViewPanelStateChangeReason.ON_PANEL_READY; 25 import static com.android.car.portraitlauncher.panel.TaskViewPanelStateChangeReason.createReason; 26 27 import android.annotation.SuppressLint; 28 import android.app.TaskInfo; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.res.Resources; 32 import android.graphics.Insets; 33 import android.graphics.Point; 34 import android.graphics.Rect; 35 import android.os.Build; 36 import android.util.AttributeSet; 37 import android.util.Log; 38 import android.view.SurfaceView; 39 import android.view.View; 40 import android.view.ViewGroup; 41 import android.widget.FrameLayout; 42 import android.widget.RelativeLayout; 43 44 import androidx.annotation.NonNull; 45 import androidx.annotation.Nullable; 46 47 import com.android.car.portraitlauncher.R; 48 import com.android.car.portraitlauncher.panel.animation.ClosePanelAnimator; 49 import com.android.car.portraitlauncher.panel.animation.ExpandPanelAnimator; 50 import com.android.car.portraitlauncher.panel.animation.FadeInPanelAnimator; 51 import com.android.car.portraitlauncher.panel.animation.FadeOutPanelAnimator; 52 import com.android.car.portraitlauncher.panel.animation.FullScreenPanelAnimator; 53 import com.android.car.portraitlauncher.panel.animation.OpenPanelAnimator; 54 import com.android.car.portraitlauncher.panel.animation.OpenPanelWithIconAnimator; 55 import com.android.car.portraitlauncher.panel.animation.PanelAnimator; 56 57 /** 58 * A view container used to display CarTaskViews. 59 * 60 * This panel can transition between various states, e.g. open, close and full screen. 61 * When panel is in open state it shows a grab bar to the users which can be dragged to transition 62 * to the other states. 63 */ 64 public class TaskViewPanel extends RelativeLayout { 65 66 private static final String TAG = TaskViewPanel.class.getSimpleName(); 67 private static final boolean DBG = Build.IS_DEBUGGABLE; 68 69 /** The properties of each valid state of the panel. */ 70 public static class State { 71 /** The bounds used for the panel when put in this state. */ 72 Rect mBounds = new Rect(); 73 /** The insets used for the panel. */ 74 Insets mInsets = Insets.NONE; 75 /** Whether or not the panel should display the grip bar. */ 76 private final boolean mHasGripBar; 77 /** Whether the panel is visible when put in this state. */ 78 private final boolean mIsVisible; 79 /** Whether the panel is considered full screen when put in this state. */ 80 private final boolean mIsFullScreen; 81 /** Whether the panel should display the toolbar. */ 82 private boolean mHasToolBar; 83 State(boolean hasGripBar, boolean isVisible, boolean isFullScreen, boolean hasToolBar)84 public State(boolean hasGripBar, boolean isVisible, boolean isFullScreen, 85 boolean hasToolBar) { 86 mHasGripBar = hasGripBar; 87 mIsVisible = isVisible; 88 mIsFullScreen = isFullScreen; 89 mHasToolBar = hasToolBar; 90 } 91 hasGripBar()92 boolean hasGripBar() { 93 return mHasGripBar; 94 } 95 hasToolBar()96 boolean hasToolBar() { 97 return mHasToolBar; 98 } 99 100 /** Whether the panel in this state has any visible parts. */ isVisible()101 public boolean isVisible() { 102 return mIsVisible; 103 } 104 105 /** Is this state considered full screen or not. */ isFullScreen()106 public boolean isFullScreen() { 107 return mIsFullScreen; 108 } 109 110 /** The string representation of the state. Used for debugging */ toString()111 public String toString() { 112 return "(visible: " + isVisible() + ", fullscreen: " + isFullScreen() + ", bounds: " 113 + mBounds + ")"; 114 } 115 } 116 117 /** Notifies the listener when the panel state changes. */ 118 public interface OnStateChangeListener { 119 /** 120 * Called right before the panel state changes. 121 * 122 * @param oldState The state from which the transition is about to start. 123 * @param newState The final state of the panel after the transition. 124 * @param animated If the transition is animated. 125 * @param reason The reason for the state change. 126 */ onStateChangeStart(State oldState, State newState, boolean animated, TaskViewPanelStateChangeReason reason)127 void onStateChangeStart(State oldState, State newState, boolean animated, 128 TaskViewPanelStateChangeReason reason); 129 130 /** 131 * Called right after the panel state changes. 132 * 133 * If the transition is animated, this method would be called after the animation. 134 * 135 * @param oldState The state from which the transition started. 136 * @param newState The final state of the panel after the transition. 137 * @param animated If the transition is animated. 138 * @param reason The reason for the state change. 139 */ onStateChangeEnd(State oldState, State newState, boolean animated, TaskViewPanelStateChangeReason reason)140 void onStateChangeEnd(State oldState, State newState, boolean animated, 141 TaskViewPanelStateChangeReason reason); 142 } 143 logIfDebuggable(String message)144 private static void logIfDebuggable(String message) { 145 if (DBG) { 146 Log.d(TAG, message); 147 } 148 } 149 150 /** The properties of the panel when in {@code open} state. */ 151 private final State mOpenState; 152 /** The properties of the panel when in {@code close} state. */ 153 private final State mCloseState; 154 /** The properties of the panel when in {@code full screen} state. */ 155 private final State mFullScreenState; 156 157 /** 158 * The current state of the panel. 159 * 160 * When transitioning from an state to another, this value will show the final state of the 161 * animation. 162 */ 163 private State mActiveState; 164 165 /** 166 * The current animator if there is an on-going animation. 167 */ 168 private PanelAnimator mActiveAnimator; 169 170 /** An optional listener to observe when the panel state changes. */ 171 private OnStateChangeListener mOnStateChangeListener; 172 173 /** The drag threshold after which the panel transitions to the close mode. */ 174 private final int mDragThreshold; 175 176 /** The top margin for task view panel. */ 177 private final int mPanelTopMargin; 178 179 /** The grip bar used to drag the panel. */ 180 private GripBarView mGripBar; 181 182 /** The toolbar on top of the panel. */ 183 private ToolBarView mToolBarView; 184 185 /** Internal container of the {@code CarTaskView}. */ 186 private ViewGroup mTaskViewContainer; 187 188 /** A view that is shown on top of the task view and used to improve visual effects. */ 189 private TaskViewPanelOverlay mTaskViewOverlay; 190 191 /** 192 * The {@code View} embedded in this panel, used to show application. This is the main 193 * content of the panel, should be instance of CarTaskView or RemoteCarTaskView. 194 */ 195 private View mTaskView; 196 197 /** The last reported window bounds of the task view. */ 198 private Rect mTaskViewWindowBounds; 199 200 /** The surface view showed on the back of the panel. */ 201 private BackgroundSurfaceView mBackgroundSurfaceView; 202 203 /** The flag indicating if the task view on the panel is ready. */ 204 private boolean mIsReady; 205 206 /** The current task running inside panel. */ 207 private TaskInfo mCurrentTask; 208 TaskViewPanel(Context context)209 public TaskViewPanel(Context context) { 210 this(context, null); 211 } 212 TaskViewPanel(Context context, @Nullable AttributeSet attrs)213 public TaskViewPanel(Context context, 214 @Nullable AttributeSet attrs) { 215 this(context, attrs, 0); 216 } 217 TaskViewPanel(Context context, @Nullable AttributeSet attrs, int defStyleAttr)218 public TaskViewPanel(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 219 this(context, attrs, defStyleAttr, 0); 220 } 221 TaskViewPanel(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)222 public TaskViewPanel(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 223 super(context, attrs, defStyleAttr, defStyleRes); 224 225 mDragThreshold = (int) getResources().getDimension(R.dimen.panel_drag_threshold); 226 mPanelTopMargin = (int) getResources().getDimension(R.dimen.panel_default_top_margin); 227 228 mOpenState = new State(/* hasGripBar = */ true, /* isVisible = */ true, 229 /* isFullScreen */false, /* hasToolBar = */ false); 230 mCloseState = new State(/* hasGripBar = */ true, /* isVisible = */ false, 231 /* isFullScreen */false, /* hasToolBar = */ false); 232 mFullScreenState = new State(/* hasGripBar = */ false, /* isVisible = */ true, 233 /* isFullScreen */true, /* hasToolBar = */ true); 234 235 mCurrentTask = null; 236 } 237 238 @Override onFinishInflate()239 protected void onFinishInflate() { 240 super.onFinishInflate(); 241 242 mGripBar = findViewById(R.id.grip_bar); 243 mToolBarView = findViewById(R.id.toolbar); 244 mTaskViewContainer = findViewById(R.id.task_view_container); 245 mTaskViewOverlay = findViewById(R.id.task_view_overlay); 246 mBackgroundSurfaceView = findViewById(R.id.surface_view); 247 mActiveState = mCloseState; 248 249 setupGripBar(); 250 } 251 252 /** Whether the panel is in the open state. */ isOpen()253 public boolean isOpen() { 254 return mActiveState == mOpenState; 255 } 256 257 /** Whether the panel is in the full screen state. */ isFullScreen()258 public boolean isFullScreen() { 259 return mActiveState.isFullScreen(); 260 } 261 262 /** Whether the panel is visible */ isVisible()263 public boolean isVisible() { 264 return mActiveState.isVisible(); 265 } 266 267 /** Whether the panel is actively animating. */ isAnimating()268 public boolean isAnimating() { 269 return mActiveAnimator != null; 270 } 271 272 /** Transitions the panel into the open state. */ openPanel(TaskViewPanelStateChangeReason reason)273 public void openPanel(TaskViewPanelStateChangeReason reason) { 274 openPanel(/* animated= */ true, reason); 275 } 276 277 /** Transitions the panel into the open state. */ openPanel(boolean animated, TaskViewPanelStateChangeReason reason)278 public void openPanel(boolean animated, TaskViewPanelStateChangeReason reason) { 279 PanelAnimator animator = 280 animated ? new OpenPanelAnimator(this, mOpenState.mBounds, 281 getAnimationScale(getContext())) : null; 282 setActiveState(mOpenState, animator, reason); 283 } 284 285 /** Transitions the panel into the open state with overlay and centered icon. */ openPanelWithIcon(TaskViewPanelStateChangeReason reason)286 public void openPanelWithIcon(TaskViewPanelStateChangeReason reason) { 287 PanelAnimator animator = new OpenPanelWithIconAnimator(this, mOpenState.mBounds, 288 mTaskViewOverlay, getAnimationScale(getContext())); 289 setActiveState(mOpenState, animator, reason); 290 } 291 292 /** Transitions the panel into the close state. */ closePanel(TaskViewPanelStateChangeReason reason)293 public void closePanel(TaskViewPanelStateChangeReason reason) { 294 closePanel(/* animated= */ true, reason); 295 } 296 297 /** Transitions the panel into the close state. */ closePanel(boolean animated, TaskViewPanelStateChangeReason reason)298 public void closePanel(boolean animated, TaskViewPanelStateChangeReason reason) { 299 PanelAnimator animator = 300 animated ? new ClosePanelAnimator(this, mCloseState.mBounds, 301 getAnimationScale(getContext())) : null; 302 303 setActiveState(mCloseState, animator, reason); 304 } 305 306 /** Transitions the panel into the open state using the expand animation. */ expandPanel(TaskViewPanelStateChangeReason reason)307 public void expandPanel(TaskViewPanelStateChangeReason reason) { 308 Point origin = new Point(mOpenState.mBounds.centerX(), mOpenState.mBounds.centerY()); 309 PanelAnimator animator = 310 new ExpandPanelAnimator(/* panel= */ this, origin, mOpenState.mBounds, mGripBar, 311 getAnimationScale(getContext())); 312 setActiveState(mOpenState, animator, reason); 313 } 314 315 /** Transitions the panel into the open state using the fade-in animation. */ fadeInPanel(TaskViewPanelStateChangeReason reason)316 public void fadeInPanel(TaskViewPanelStateChangeReason reason) { 317 setActiveState(mOpenState, 318 new FadeInPanelAnimator(/* panel= */ this, mTaskView, mOpenState.mBounds, 319 getAnimationScale(getContext())), reason); 320 } 321 322 /** Transitions the panel into the close state using the fade-out animation. */ fadeOutPanel(TaskViewPanelStateChangeReason reason)323 public void fadeOutPanel(TaskViewPanelStateChangeReason reason) { 324 PanelAnimator animator = new FadeOutPanelAnimator(/* panel= */ this, mTaskViewOverlay, 325 mTaskView, mCloseState.mBounds, mCloseState.mBounds.top, 326 getAnimationScale(getContext())); 327 setActiveState(mCloseState, animator, reason); 328 } 329 330 /** 331 * Transitions the panel into the full screen state. During 332 * transition,{@link mTaskViewOverlay} shows with given {@code drawable} at the center. 333 */ openFullScreenPanel(boolean animated, boolean showToolBar, int bottomAdjustment, TaskViewPanelStateChangeReason reason)334 public void openFullScreenPanel(boolean animated, boolean showToolBar, int bottomAdjustment, 335 TaskViewPanelStateChangeReason reason) { 336 mFullScreenState.mHasToolBar = showToolBar; 337 mFullScreenState.mBounds.bottom = ((ViewGroup) getParent()).getHeight() - bottomAdjustment; 338 setActiveState(mFullScreenState, animated ? createFullScreenPanelAnimator() : null, reason); 339 } 340 341 /** Sets the state change listener for the panel. */ setOnStateChangeListener(OnStateChangeListener listener)342 public void setOnStateChangeListener(OnStateChangeListener listener) { 343 mOnStateChangeListener = listener; 344 } 345 346 /** Sets the component that {@link mTaskViewOverlay} covers */ setComponentName(@onNull ComponentName componentName)347 public void setComponentName(@NonNull ComponentName componentName) { 348 mTaskViewOverlay.setComponentName(componentName); 349 mGripBar.update(componentName); 350 } 351 352 /** 353 * Returns the grip bar bounds of the current state. 354 * 355 * Note that the visual grip bar bounds might be different from the value here during 356 * transition animations. 357 * 358 * @param bounds The {@code Rect} that is used to return the data. 359 */ getGripBarBounds(Rect bounds)360 public void getGripBarBounds(Rect bounds) { 361 if (mActiveState.hasGripBar()) { 362 bounds.set(mActiveState.mBounds); 363 bounds.bottom = mActiveState.mBounds.top + mGripBar.getHeight(); 364 } else { 365 bounds.setEmpty(); 366 } 367 } 368 369 /** Updates the {@code TaskView} used in the panel. */ setTaskView(SurfaceView surfaceView)370 public void setTaskView(SurfaceView surfaceView) { 371 mTaskView = surfaceView; 372 mTaskViewContainer.addView(mTaskView); 373 onParentDimensionChanged(); 374 } 375 376 /** Updates the readiness state of the panel. */ setReady(boolean isReady)377 public void setReady(boolean isReady) { 378 mIsReady = isReady; 379 if (mIsReady) { 380 closePanel(createReason(ON_PANEL_READY)); 381 } 382 } 383 384 /** Returns whether the panel is ready. */ isReady()385 public boolean isReady() { 386 return mIsReady; 387 } 388 389 /** Refreshes the panel according to the given {@code Theme}. */ refresh(Resources.Theme theme)390 public void refresh(Resources.Theme theme) { 391 int backgroundColor = getResources().getColor(R.color.car_background, theme); 392 mTaskViewContainer.setBackgroundColor(backgroundColor); 393 mTaskViewOverlay.refresh(); 394 mGripBar.refresh(theme); 395 mBackgroundSurfaceView.refresh(theme); 396 } 397 398 @SuppressLint("ClickableViewAccessibility") setupGripBar()399 private void setupGripBar() { 400 mGripBar.setOnTouchListener(new OnPanelDragListener(getContext()) { 401 @Override 402 void onClick() { 403 closePanel(createReason(ON_GRIP_BAR_CLICKED)); 404 } 405 406 @Override 407 public void onDragBegin() { 408 } 409 410 @Override 411 public void onDrag(int deltaX, int deltaY) { 412 deltaY = Math.max(0, deltaY); 413 Rect rect = new Rect(mActiveState.mBounds); 414 rect.offset(/* dx= */ 0, deltaY); 415 updateBounds(rect); 416 } 417 418 @Override 419 public void onDragEnd(int deltaX, int deltaY) { 420 deltaY = Math.max(0, deltaY); 421 if (deltaY > mDragThreshold) { 422 closePanel(createReason(ON_GRIP_BAR_DRAG)); 423 } else { 424 openPanel(createReason(ON_GRIP_BAR_DRAG)); 425 } 426 } 427 }); 428 } 429 430 /** Returns the bounds of the task view once fully transitioned to the active state */ getTaskViewBounds(State state)431 public Rect getTaskViewBounds(State state) { 432 Rect bounds = new Rect(state.mBounds); 433 bounds.inset(mActiveState.mInsets); 434 435 if (state.hasGripBar()) { 436 bounds.top += mGripBar.getHeight(); 437 } 438 if (state.hasToolBar()) { 439 bounds.top += mToolBarView.getHeight(); 440 } 441 442 return bounds; 443 } 444 445 /** Updates the insets of the taskView for non-fullscreen states. */ setInsets(Insets insets)446 public void setInsets(Insets insets) { 447 if (insets.equals(mOpenState.mInsets)) { 448 return; 449 } 450 mOpenState.mInsets = insets; 451 mCloseState.mInsets = insets; 452 if (!isReady()) { 453 return; 454 } 455 updateInsets(mActiveState.mInsets); 456 recalculateBounds(); 457 updateBounds(mActiveState.mBounds); 458 } 459 460 /** Sets a fixed background color for the task view. */ setTaskViewBackgroundColor(int color)461 public void setTaskViewBackgroundColor(int color) { 462 mBackgroundSurfaceView.setFixedColor(color); 463 } 464 465 /** Should be called when the view is no longer in use. */ onDestroy()466 public void onDestroy() { 467 mTaskView = null; 468 mCurrentTask = null; 469 } 470 471 /** Should be called when the parent dimension changes. */ onParentDimensionChanged()472 public void onParentDimensionChanged() { 473 int parentWidth = ((ViewGroup) getParent()).getWidth(); 474 int parentHeight = ((ViewGroup) getParent()).getHeight(); 475 476 logIfDebuggable("onDimensionChanged: " + parentWidth + " " + parentHeight); 477 478 recalculateBounds(); 479 updateBounds(mActiveState.mBounds); 480 } 481 482 /** 483 * Set Callback for {@link ToolBarView} on {@link TaskViewPanel} 484 */ setToolBarCallback(ToolBarView.Callback callback)485 public void setToolBarCallback(ToolBarView.Callback callback) { 486 mToolBarView.registerToolbarCallback(callback); 487 } 488 489 /** 490 * Show/hide the content in {@link ToolBarView} 491 */ setToolBarViewVisibility(boolean isVisible)492 public void setToolBarViewVisibility(boolean isVisible) { 493 mToolBarView.updateToolBarContentVisibility(mActiveState.hasToolBar() && isVisible); 494 } 495 recalculateBounds()496 private void recalculateBounds() { 497 int parentWidth = ((ViewGroup) getParent()).getWidth(); 498 int parentHeight = ((ViewGroup) getParent()).getHeight(); 499 500 mOpenState.mBounds.set(0, mPanelTopMargin + mGripBar.getHeight(), parentWidth, 501 parentHeight); 502 mCloseState.mBounds.set(0, parentHeight, parentWidth, 503 parentHeight + mOpenState.mBounds.height()); 504 mFullScreenState.mBounds.set(0, 0, parentWidth, parentHeight); 505 } 506 setActiveState(State toState, PanelAnimator animator, TaskViewPanelStateChangeReason reason)507 private void setActiveState(State toState, PanelAnimator animator, 508 TaskViewPanelStateChangeReason reason) { 509 if (!isReady()) { 510 logIfDebuggable("Skipping state change. Not Ready."); 511 } 512 513 if (toState.mBounds.height() == 0) { 514 logIfDebuggable("Skipping state change. Not initialized."); 515 return; 516 } 517 518 State fromState = mActiveState; 519 logIfDebuggable("Panel( " + getTag() + ") active state changes from " + fromState 520 + " to " + toState + " with reason code = " + reason); 521 522 if (mActiveAnimator != null) { 523 // Should try to avoid cancelling panel animation, might cause flicker. 524 Log.e(TAG, "Cancelling the old panel animation"); 525 mActiveAnimator.cancel(); 526 mActiveAnimator = null; 527 mGripBar.setVisibility(mActiveState.hasGripBar() ? VISIBLE : GONE); 528 mToolBarView.setVisibility(GONE); 529 } 530 531 float animationScale = getAnimationScale(getContext()); 532 logIfDebuggable("animationScale = " + animationScale); 533 boolean animated = animator != null && animationScale != 0f; 534 onStateChangeStart(fromState, toState, animated, reason); 535 536 mActiveState = toState; 537 mActiveAnimator = animator; 538 539 updateInsets(mActiveState.mInsets); 540 541 if (animated) { 542 // Change toolbar and grip bar visibilities before the animation for better animation. 543 if (toState.hasToolBar() != fromState.hasToolBar()) { 544 mToolBarView.setVisibility(toState.hasToolBar() ? VISIBLE : GONE); 545 } 546 547 if (toState.hasGripBar() != fromState.hasGripBar()) { 548 mGripBar.setVisibility(toState.hasGripBar() ? VISIBLE : GONE); 549 } 550 551 post(() -> animator.animate(() -> { 552 mGripBar.setVisibility(mActiveState.hasGripBar() ? VISIBLE : GONE); 553 mToolBarView.setVisibility(mActiveState.hasToolBar() ? VISIBLE : GONE); 554 updateBounds(mActiveState.mBounds); 555 onStateChangeEnd(fromState, mActiveState, /* animated= */ true, reason); 556 })); 557 } else { 558 mGripBar.setVisibility(mActiveState.hasGripBar() ? VISIBLE : GONE); 559 mToolBarView.setVisibility(mActiveState.hasToolBar() ? VISIBLE : GONE); 560 updateBounds(mActiveState.mBounds); 561 mTaskViewOverlay.setVisibility(GONE); 562 onStateChangeEnd(fromState, mActiveState, /* animated= */ false, reason); 563 } 564 } 565 onStateChangeStart(State fromState, State toState, boolean animated, TaskViewPanelStateChangeReason reason)566 private void onStateChangeStart(State fromState, State toState, boolean animated, 567 TaskViewPanelStateChangeReason reason) { 568 if (mOnStateChangeListener != null) { 569 mOnStateChangeListener.onStateChangeStart(fromState, toState, animated, reason); 570 } 571 } 572 onStateChangeEnd(State fromState, State toState, boolean animated, TaskViewPanelStateChangeReason reason)573 private void onStateChangeEnd(State fromState, State toState, boolean animated, 574 TaskViewPanelStateChangeReason reason) { 575 mActiveAnimator = null; 576 if (mOnStateChangeListener != null) { 577 mOnStateChangeListener.onStateChangeEnd(fromState, toState, animated, reason); 578 } 579 } 580 581 /** Updates bounds for {@link mTaskView}. */ updateTaskViewBounds(Rect bounds)582 public void updateTaskViewBounds(Rect bounds) { 583 mTaskViewWindowBounds = bounds; 584 } 585 updateInsets(Insets insets)586 private void updateInsets(Insets insets) { 587 mTaskViewContainer.setPadding(insets.left, insets.top, insets.right, insets.bottom); 588 } 589 updateBounds(Rect bounds)590 private void updateBounds(Rect bounds) { 591 final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams(); 592 layoutParams.topMargin = bounds.top; 593 layoutParams.rightMargin = bounds.right; 594 layoutParams.width = bounds.width(); 595 layoutParams.height = bounds.height(); 596 setLayoutParams(layoutParams); 597 } 598 createFullScreenPanelAnimator()599 private FullScreenPanelAnimator createFullScreenPanelAnimator() { 600 Point offset = new Point(mOpenState.mBounds.left, mOpenState.mBounds.top); 601 Rect bounds = mFullScreenState.mBounds; 602 return new FullScreenPanelAnimator(this, bounds, offset, mTaskViewOverlay, 603 getAnimationScale(getContext())); 604 } 605 getAnimationScale(Context context)606 private static float getAnimationScale(Context context) { 607 return getFloat(context.getContentResolver(), ANIMATOR_DURATION_SCALE, /* def= */ 1.0f); 608 } 609 } 610