1 /* 2 * Copyright (C) 2021 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.statusbar.phone 17 18 import android.graphics.Point 19 import android.view.View 20 import android.view.ViewGroup 21 import android.view.ViewTreeObserver 22 import com.android.systemui.R 23 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator 24 import com.android.systemui.unfold.SysUIUnfoldComponent 25 import com.android.systemui.unfold.UNFOLD_STATUS_BAR 26 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider 27 import com.android.systemui.util.ViewController 28 import com.android.systemui.util.kotlin.getOrNull 29 import java.util.Optional 30 import javax.inject.Inject 31 import javax.inject.Named 32 33 /** Controller for [PhoneStatusBarView]. */ 34 class PhoneStatusBarViewController private constructor( 35 view: PhoneStatusBarView, 36 @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?, 37 private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?, 38 touchEventHandler: PhoneStatusBarView.TouchEventHandler 39 ) : ViewController<PhoneStatusBarView>(view) { 40 41 override fun onViewAttached() { 42 moveFromCenterAnimationController?.let { animationController -> 43 val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side) 44 val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area) 45 46 val viewsToAnimate = arrayOf( 47 statusBarLeftSide, 48 systemIconArea 49 ) 50 51 mView.viewTreeObserver.addOnPreDrawListener(object : 52 ViewTreeObserver.OnPreDrawListener { 53 override fun onPreDraw(): Boolean { 54 animationController.onViewsReady(viewsToAnimate) 55 mView.viewTreeObserver.removeOnPreDrawListener(this) 56 return true 57 } 58 }) 59 60 mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ -> 61 val widthChanged = right - left != oldRight - oldLeft 62 if (widthChanged) { 63 moveFromCenterAnimationController.onStatusBarWidthChanged() 64 } 65 } 66 } 67 68 progressProvider?.setReadyToHandleTransition(true) 69 } 70 71 override fun onViewDetached() { 72 progressProvider?.setReadyToHandleTransition(false) 73 moveFromCenterAnimationController?.onViewDetached() 74 } 75 76 init { 77 mView.setTouchEventHandler(touchEventHandler) 78 } 79 80 fun setImportantForAccessibility(mode: Int) { 81 mView.importantForAccessibility = mode 82 } 83 84 class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider { 85 override fun getViewCenter(view: View, outPoint: Point) = 86 when (view.id) { 87 R.id.status_bar_left_side -> { 88 // items aligned to the start, return start center point 89 getViewEdgeCenter(view, outPoint, isStart = true) 90 } 91 R.id.system_icon_area -> { 92 // items aligned to the end, return end center point 93 getViewEdgeCenter(view, outPoint, isStart = false) 94 } 95 else -> super.getViewCenter(view, outPoint) 96 } 97 98 /** 99 * Returns start or end (based on [isStart]) center point of the view 100 */ 101 private fun getViewEdgeCenter(view: View, outPoint: Point, isStart: Boolean) { 102 val isRtl = view.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL 103 val isLeftEdge = isRtl xor isStart 104 105 val viewLocation = IntArray(2) 106 view.getLocationOnScreen(viewLocation) 107 108 val viewX = viewLocation[0] 109 val viewY = viewLocation[1] 110 111 outPoint.x = viewX + if (isLeftEdge) view.height / 2 else view.width - view.height / 2 112 outPoint.y = viewY + view.height / 2 113 } 114 } 115 116 class Factory @Inject constructor( 117 private val unfoldComponent: Optional<SysUIUnfoldComponent>, 118 @Named(UNFOLD_STATUS_BAR) 119 private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider> 120 ) { 121 fun create( 122 view: PhoneStatusBarView, 123 touchEventHandler: PhoneStatusBarView.TouchEventHandler 124 ) = 125 PhoneStatusBarViewController( 126 view, 127 progressProvider.getOrNull(), 128 unfoldComponent.map { 129 it.getStatusBarMoveFromCenterAnimationController() 130 }.getOrNull(), 131 touchEventHandler 132 ) 133 } 134 } 135