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