xref: /MusicFree/android/app/src/main/java/fun/upup/musicfree/lyricUtil/LyricView.kt (revision 5589cdf32b2bb0f641e5ac7bf1f6152cd6b9b70e)

<lambda>null1 package `fun`.upup.musicfree.lyricUtil
2 
3 import android.app.Activity
4 import android.content.Context
5 import android.graphics.Color
6 import android.graphics.PixelFormat
7 import android.graphics.drawable.ColorDrawable
8 import android.hardware.SensorManager
9 import android.os.Build
10 import android.util.DisplayMetrics
11 import android.util.Log
12 import android.view.Gravity
13 import android.view.MotionEvent
14 import android.view.OrientationEventListener
15 import android.view.View
16 import android.view.WindowManager
17 import android.widget.TextView
18 import com.facebook.react.bridge.ReactContext
19 
20 
21 
22 class LyricView(private val reactContext: ReactContext) : Activity(), View.OnTouchListener {
23 
24     private var windowManager: WindowManager? = null
25     private var orientationEventListener: OrientationEventListener? = null
26     private var layoutParams: WindowManager.LayoutParams? = null
27     private var tv: TextView? = null
28 
29     // 窗口信息
30     private var windowWidth = 0.0
31     private var windowHeight = 0.0
32     private var widthPercent = 0.0
33     private var leftPercent = 0.0
34     private var topPercent = 0.0
35 
36     override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
37         Log.d("touch", "Desktop Touch")
38         return false
39     }
40 
41     // 展示歌词窗口
42     fun showLyricWindow(initText: String?, options: Map<String, Any>) {
43         try {
44             if (windowManager == null) {
45                 windowManager = reactContext.getSystemService(WINDOW_SERVICE) as WindowManager
46                 layoutParams = WindowManager.LayoutParams()
47 
48                 val outMetrics = DisplayMetrics()
49                 windowManager?.defaultDisplay?.getMetrics(outMetrics)
50                 windowWidth = outMetrics.widthPixels.toDouble()
51                 windowHeight = outMetrics.heightPixels.toDouble()
52 
53                 layoutParams?.type = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
54                     WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
55                 else
56                     WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
57 
58                 /*
59                  * topPercent: number;
60                  * leftPercent: number;
61                  * align: number;
62                  * color: string;
63                  * backgroundColor: string;
64                  * widthPercent: number;
65                  * fontSize: number;
66                  */
67                 val topPercent = options["topPercent"]
68                 val leftPercent = options["leftPercent"]
69                 val align = options["align"]
70                 val color = options["color"]
71                 val backgroundColor = options["backgroundColor"]
72                 val widthPercent = options["widthPercent"]
73                 val fontSize = options["fontSize"]
74 
75                 this.widthPercent = widthPercent?.toString()?.toDouble() ?: 0.5
76 
77                 layoutParams?.width = (this.widthPercent * windowWidth).toInt()
78                 layoutParams?.height = WindowManager.LayoutParams.WRAP_CONTENT
79                 layoutParams?.gravity = Gravity.TOP or Gravity.START
80 
81                 this.leftPercent = leftPercent?.toString()?.toDouble() ?: 0.5
82                 layoutParams?.x = (this.leftPercent * (windowWidth - layoutParams!!.width)).toInt()
83                 layoutParams?.y = 0
84 
85                 layoutParams?.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
86                         WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
87                         WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
88                         WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
89                         WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
90 
91                 layoutParams?.format = PixelFormat.TRANSPARENT
92 
93                 tv = TextView(reactContext).apply {
94                     text = initText ?: ""
95                     textSize = fontSize?.toString()?.toFloat() ?: 14f
96                     setBackgroundColor(Color.parseColor(rgba2argb(backgroundColor?.toString() ?: "#84888153")))
97                     setTextColor(Color.parseColor(rgba2argb(color?.toString() ?: "#FFE9D2")))
98                     setPadding(12, 6, 12, 6)
99                     gravity = align?.toString()?.toInt() ?: Gravity.CENTER
100                 }
101                 windowManager?.addView(tv, layoutParams)
102 
103                 topPercent?.toString()?.toDouble()?.let { setTopPercent(it) }
104 
105                 listenOrientationChange()
106             }
107         } catch (e: Exception) {
108             hideLyricWindow()
109             throw e
110         }
111     }
112 
113     private fun listenOrientationChange() {
114         if (windowManager == null) return
115 
116         if (orientationEventListener == null) {
117             orientationEventListener = object : OrientationEventListener(reactContext, SensorManager.SENSOR_DELAY_NORMAL) {
118                 override fun onOrientationChanged(orientation: Int) {
119                     if (windowManager != null) {
120                         val outMetrics = DisplayMetrics()
121                         windowManager?.defaultDisplay?.getMetrics(outMetrics)
122                         windowWidth = outMetrics.widthPixels.toDouble()
123                         windowHeight = outMetrics.heightPixels.toDouble()
124                         layoutParams?.width = (widthPercent * windowWidth).toInt()
125                         layoutParams?.x = (leftPercent * (windowWidth - layoutParams!!.width)).toInt()
126                         layoutParams?.y = (topPercent * (windowHeight - tv!!.height)).toInt()
127                         windowManager?.updateViewLayout(tv, layoutParams)
128                     }
129                 }
130             }
131         }
132 
133         if (orientationEventListener?.canDetectOrientation() == true) {
134             orientationEventListener?.enable()
135         }
136     }
137 
138     private fun unlistenOrientationChange() {
139         orientationEventListener?.disable()
140     }
141 
142     private fun rgba2argb(color: String): String {
143         return if (color.length == 9) {
144             color[0] + color.substring(7, 9) + color.substring(1, 7)
145         } else {
146             color
147         }
148     }
149 
150     // 隐藏歌词窗口
151     fun hideLyricWindow() {
152         if (windowManager != null) {
153             tv?.let {
154                 try {
155                     windowManager?.removeView(it)
156                 } catch (e: Exception) {
157                     // Handle exception
158                 }
159                 tv = null
160             }
161             windowManager = null
162             layoutParams = null
163             unlistenOrientationChange()
164         }
165     }
166 
167     // 设置歌词内容
168     fun setText(text: String) {
169         tv?.text = text
170     }
171 
172     fun setAlign(gravity: Int) {
173         tv?.gravity = gravity
174     }
175 
176     fun setTopPercent(pct: Double) {
177         var percent = pct.coerceIn(0.0, 1.0)
178         tv?.let {
179             layoutParams?.y = (percent * (windowHeight - it.height)).toInt()
180             windowManager?.updateViewLayout(it, layoutParams)
181         }
182         this.topPercent = percent
183     }
184 
185     fun setLeftPercent(pct: Double) {
186         var percent = pct.coerceIn(0.0, 1.0)
187         tv?.let {
188             layoutParams?.x = (percent * (windowWidth - layoutParams!!.width)).toInt()
189             windowManager?.updateViewLayout(it, layoutParams)
190         }
191         this.leftPercent = percent
192     }
193 
194     fun setColors(textColor: String?, backgroundColor: String?) {
195         tv?.let {
196             textColor?.let { color -> it.setTextColor(Color.parseColor(rgba2argb(color))) }
197             backgroundColor?.let { color ->
198                 it.background = ColorDrawable(Color.parseColor(rgba2argb(color)))
199             }
200         }
201     }
202 
203     fun setWidth(pct: Double) {
204         var percent = pct.coerceIn(0.3, 1.0)
205         tv?.let {
206             val width = (percent * windowWidth).toInt()
207             val originalWidth = layoutParams?.width ?: 0
208             layoutParams?.x = if (width <= originalWidth) {
209                 layoutParams!!.x + (originalWidth - width) / 2
210             } else {
211                 layoutParams!!.x - (width - originalWidth) / 2
212             }.coerceAtLeast(0).coerceAtMost((windowWidth - width).toInt())
213             layoutParams?.width = width
214             windowManager?.updateViewLayout(it, layoutParams)
215         }
216         this.widthPercent = percent
217     }
218 
219     fun setFontSize(fontSize: Float) {
220         tv?.textSize = fontSize
221     }
222 }