• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 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.wallpaper.widget
17 
18 import android.annotation.IntDef
19 import android.content.Context
20 import android.transition.AutoTransition
21 import android.transition.TransitionManager
22 import android.util.AttributeSet
23 import android.view.LayoutInflater
24 import android.view.ViewGroup
25 import android.widget.FrameLayout
26 import androidx.appcompat.content.res.AppCompatResources
27 import com.android.wallpaper.R
28 import com.android.wallpaper.util.SizeCalculator
29 import com.android.wallpaper.widget.floatingsheetcontent.FloatingSheetContent
30 import com.google.android.material.bottomsheet.BottomSheetBehavior
31 import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
32 import java.util.function.Consumer
33 
34 /** A `ViewGroup` which provides the specific actions for the user to interact with. */
35 class FloatingSheet(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
36 
37     companion object {
38 
39         @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
40         @IntDef(CUSTOMIZE, INFORMATION, EFFECTS)
41         @Retention(AnnotationRetention.SOURCE)
42         annotation class FloatingSheetContentType
43 
44         const val CUSTOMIZE = 0
45         const val INFORMATION = 1
46         const val EFFECTS = 2
47     }
48 
49     private val floatingSheetView: ViewGroup
50     private val floatingSheetContainer: ViewGroup
51     private val floatingSheetBehavior: BottomSheetBehavior<ViewGroup>
52     private val contentViewMap:
53         MutableMap<@FloatingSheetContentType Int, FloatingSheetContent<*>?> =
54         HashMap()
55 
56     // The system "short" animation time duration, in milliseconds. This
57     // duration is ideal for subtle animations or animations that occur
58     // very frequently.
59     private val shortAnimTimeMillis: Long
60 
61     init {
62         LayoutInflater.from(context).inflate(R.layout.floating_sheet, this, true)
63         floatingSheetView = requireViewById(R.id.floating_sheet_content)
64         SizeCalculator.adjustBackgroundCornerRadius(floatingSheetView)
65         setColor(context)
66         floatingSheetContainer = requireViewById(R.id.floating_sheet_container)
67         floatingSheetBehavior = BottomSheetBehavior.from(floatingSheetContainer)
68         floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
69         shortAnimTimeMillis = resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
70     }
71 
72     /**
73      * Binds the `floatingSheetContent` with an id that can be used identify and switch between
74      * floating sheet content
75      *
76      * @param floatingSheetContent the content object with view being added to the floating sheet
77      */
78     fun putFloatingSheetContent(
79         @FloatingSheetContentType type: Int,
80         floatingSheetContent: FloatingSheetContent<*>
81     ) {
82         floatingSheetContent.initView()
83         contentViewMap[type] = floatingSheetContent
84         floatingSheetView.addView(floatingSheetContent.contentView)
85     }
86 
87     /** Dynamic update color with `Context`. */
88     fun setColor(context: Context) {
89         // Set floating sheet background.
90         floatingSheetView.background =
91             AppCompatResources.getDrawable(context, R.drawable.floating_sheet_background)
92         if (floatingSheetView.childCount > 0) {
93             // Update the bottom sheet content view if any.
94             floatingSheetView.removeAllViews()
95             contentViewMap.values.forEach(
96                 Consumer { floatingSheetContent: FloatingSheetContent<*>? ->
97                     floatingSheetContent?.let {
98                         it.recreateView()
99                         floatingSheetView.addView(it.contentView)
100                     }
101                 }
102             )
103         }
104     }
105 
106     /** Returns `true` if the state of bottom sheet is collapsed. */
107     val isFloatingSheetCollapsed: Boolean
108         get() = floatingSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN
109 
110     /** Expands [FloatingSheet]. */
111     fun expand() {
112         floatingSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
113     }
114 
115     /** Collapses [FloatingSheet]. */
116     fun collapse() {
117         floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
118         endContentViewAnimation()
119     }
120 
121     /**
122      * Updates content view of [FloatingSheet] with transition animation
123      *
124      * @param type the integer or enum used to identify the content view
125      */
126     fun updateContentViewWithAnimation(@FloatingSheetContentType type: Int) {
127         val transition = AutoTransition()
128         transition.duration = shortAnimTimeMillis
129         /**
130          * This line records changes you make to its views and applies a transition that animates
131          * the changes when the system redraws the user interface
132          */
133         TransitionManager.beginDelayedTransition(floatingSheetContainer, transition)
134 
135         updateContentView(type)
136     }
137 
138     fun endContentViewAnimation() {
139         TransitionManager.endTransitions(floatingSheetContainer)
140     }
141 
142     /**
143      * Updates content view of [FloatingSheet]
144      *
145      * @param type the integer or enum used to identify the content view
146      */
147     fun updateContentView(@FloatingSheetContentType type: Int) {
148         contentViewMap.forEach { (i: Int, content: FloatingSheetContent<*>?) ->
149             content?.setVisibility(i == type)
150         }
151     }
152 
153     /**
154      * Adds Floating Sheet Callback to connected [BottomSheetBehavior].
155      *
156      * @param callback the callback for floating sheet state changes, has to be in the type of
157      *   [BottomSheetBehavior.BottomSheetCallback] since the floating sheet behavior is currently
158      *   based on [BottomSheetBehavior]
159      */
160     fun addFloatingSheetCallback(callback: BottomSheetCallback?) {
161         floatingSheetBehavior.addBottomSheetCallback(callback!!)
162     }
163 }
164