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