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 contentViewMap.forEach { (i: Int, content: FloatingSheetContent<*>?) -> 120 content?.collapse() 121 } 122 } 123 124 /** 125 * Updates content view of [FloatingSheet] with transition animation 126 * 127 * @param type the integer or enum used to identify the content view 128 */ 129 fun updateContentViewWithAnimation(@FloatingSheetContentType type: Int) { 130 val transition = AutoTransition() 131 transition.duration = shortAnimTimeMillis 132 /** 133 * This line records changes you make to its views and applies a transition that animates 134 * the changes when the system redraws the user interface 135 */ 136 TransitionManager.beginDelayedTransition(floatingSheetContainer, transition) 137 138 updateContentView(type) 139 } 140 141 fun endContentViewAnimation() { 142 TransitionManager.endTransitions(floatingSheetContainer) 143 } 144 145 /** 146 * Updates content view of [FloatingSheet] 147 * 148 * @param type the integer or enum used to identify the content view 149 */ 150 fun updateContentView(@FloatingSheetContentType type: Int) { 151 contentViewMap.forEach { (i: Int, content: FloatingSheetContent<*>?) -> 152 content?.setVisibility(i == type) 153 } 154 } 155 156 /** 157 * Adds Floating Sheet Callback to connected [BottomSheetBehavior]. 158 * 159 * @param callback the callback for floating sheet state changes, has to be in the type of 160 * [BottomSheetBehavior.BottomSheetCallback] since the floating sheet behavior is currently 161 * based on [BottomSheetBehavior] 162 */ 163 fun addFloatingSheetCallback(callback: BottomSheetCallback) { 164 floatingSheetBehavior.addBottomSheetCallback(callback) 165 } 166 167 fun removeFloatingSheetCallback(callback: BottomSheetCallback) { 168 floatingSheetBehavior.removeBottomSheetCallback(callback) 169 } 170 } 171