From 1efcd9ed07af2876f8e6053476100d1a94c89d1c Mon Sep 17 00:00:00 2001 From: Perspective <115461545+Perspective2077@users.noreply.github.com> Date: Fri, 22 May 2026 22:23:11 +0530 Subject: [PATCH 1/2] Dynamic bar Music player Ui updates --- .../ui/compose/ExpandedMediaContent.kt | 293 +++++++----------- 1 file changed, 117 insertions(+), 176 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/axdynamicbar/ui/compose/ExpandedMediaContent.kt b/packages/SystemUI/src/com/android/systemui/axdynamicbar/ui/compose/ExpandedMediaContent.kt index 893bb13d96178..3bd35c835daee 100644 --- a/packages/SystemUI/src/com/android/systemui/axdynamicbar/ui/compose/ExpandedMediaContent.kt +++ b/packages/SystemUI/src/com/android/systemui/axdynamicbar/ui/compose/ExpandedMediaContent.kt @@ -1,6 +1,7 @@ package com.android.systemui.axdynamicbar.ui.compose import android.graphics.drawable.GradientDrawable +import android.media.AudioManager import android.graphics.drawable.LayerDrawable import android.util.TypedValue import android.widget.SeekBar @@ -45,7 +46,9 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.blur import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.toArgb @@ -58,9 +61,9 @@ import androidx.compose.ui.viewinterop.AndroidView import com.android.systemui.axdynamicbar.shared.IslandActions import com.android.systemui.axdynamicbar.model.IslandEvent import com.android.systemui.axdynamicbar.shared.* -import com.android.systemui.media.controls.ui.drawable.SquigglyProgress import com.android.systemui.res.R import kotlinx.coroutines.delay +import com.android.systemui.media.controls.ui.view.WaveformSeekBar private val AlbumArtSize = 80.dp private val PlayPauseSize = 56.dp @@ -78,77 +81,106 @@ internal fun MediaCard(event: IslandEvent.Media, interactor: IslandActions) { shape = ShapeCard, color = CardBg, ) { - Column(modifier = Modifier.fillMaxWidth()) { - Row( + Box(modifier = Modifier.fillMaxWidth()) { + + event.albumArt?.let { art -> + Image( + bitmap = art.toScaledBitmap(380.dp), + contentDescription = null, + modifier = Modifier + .matchParentSize() + .blur(32.dp), + contentScale = ContentScale.Crop, + ) + } ?: Box( modifier = Modifier - .fillMaxWidth() - .clickable { - interactor.openMediaApp() - interactor.collapseIsland() - } - .padding(SpaceXxl), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(SpaceXxl), - ) { - event.albumArt?.let { art -> - Image( - bitmap = art.toScaledBitmap(AlbumArtSize), - contentDescription = null, - modifier = Modifier.size(AlbumArtSize).clip(ShapeLg), - contentScale = ContentScale.Crop, + .matchParentSize() + .background(CardBg), + ) + + Box( + modifier = Modifier + .matchParentSize() + .background( + Brush.verticalGradient( + 0.0f to Color(0xFF000000).copy(alpha = 0.4f), + 0.45f to Color(0xFF000000).copy(alpha = 0.65f), + 1.0f to Color(0xFF000000).copy(alpha = 1.0f), + ) ) - } ?: Box( + ) + + Column(modifier = Modifier.fillMaxWidth()) { + Row( modifier = Modifier - .size(AlbumArtSize) - .clip(ShapeLg) - .background(accent.copy(alpha = AlphaFaint)), - contentAlignment = Alignment.Center, + .fillMaxWidth() + .clickable { + interactor.openMediaApp() + interactor.collapseIsland() + } + .padding(SpaceXxl), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(SpaceXxl), ) { - Icon(Icons.Filled.MusicNote, null, tint = accent, modifier = Modifier.size(36.dp)) - } + event.albumArt?.let { art -> + Image( + bitmap = art.toScaledBitmap(AlbumArtSize), + contentDescription = null, + modifier = Modifier.size(AlbumArtSize).clip(ShapeLg), + contentScale = ContentScale.Crop, + ) + } ?: Box( + modifier = Modifier + .size(AlbumArtSize) + .clip(ShapeLg) + .background(accent.copy(alpha = AlphaFaint)), + contentAlignment = Alignment.Center, + ) { + Icon(Icons.Filled.MusicNote, null, tint = accent, modifier = Modifier.size(36.dp)) + } - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(SpaceXs), - ) { - Text( - event.track.ifEmpty { stringResource(R.string.ax_dynamic_bar_now_playing) }, - color = OnCardText, - style = MaterialTheme.typography.titleMedium, - maxLines = 2, - overflow = TextOverflow.Ellipsis, - ) - if (event.artist.isNotEmpty()) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(SpaceXs), + ) { Text( - event.artist, - color = accent, - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, + event.track.ifEmpty { stringResource(R.string.ax_dynamic_bar_now_playing) }, + color = OnCardText, + style = MaterialTheme.typography.titleMedium, + maxLines = 2, overflow = TextOverflow.Ellipsis, ) + if (event.artist.isNotEmpty()) { + Text( + event.artist, + color = accent, + style = MaterialTheme.typography.bodyMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } + event.appIcon?.let { icon -> + Image( + bitmap = icon.toScaledBitmap(SizeIconSm), + contentDescription = null, + modifier = Modifier.size(SizeIconSm).clip(ShapeXs), + colorFilter = ColorFilter.tint(OnCardText), + ) } } - event.appIcon?.let { icon -> - Image( - bitmap = icon.toScaledBitmap(SizeIconSm), - contentDescription = null, - modifier = Modifier.size(SizeIconSm).clip(ShapeXs), - colorFilter = ColorFilter.tint(OnCardText), - ) - } - } - Column( - modifier = Modifier - .fillMaxWidth() - .background(accent.copy(alpha = AlphaFaint)) - .padding(horizontal = SpaceXxl, vertical = SpaceLg), - verticalArrangement = Arrangement.spacedBy(SpaceLg), - ) { - if (event.duration > 0L) { - MediaSeekBar(event, interactor, accent) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = SpaceXxl, vertical = SpaceLg), + verticalArrangement = Arrangement.spacedBy(SpaceLg), + ) { + if (event.duration > 0L) { + MediaSeekBar(event, interactor, accent) + } + MediaControls(event, interactor, accent) } - MediaControls(event, interactor, accent) } } } @@ -317,22 +349,16 @@ private fun MediaSeekBar( var displayFraction by remember { mutableStateOf(serverFraction) } val interactorRef = rememberUpdatedState(interactor) - - // Read the dismiss swipe lock provided by MagneticSwipeToDismiss val swipeLock = LocalDismissSwipeLock.current - // Smooth frame-interpolated progress when playing, snaps when paused or scrubbing LaunchedEffect(positionMs, durationMs, isPlaying) { if (isScrubbing) return@LaunchedEffect - displayFraction = serverFraction - if (!isPlaying || durationMs <= 0L) return@LaunchedEffect - val startWallMs = System.currentTimeMillis() val startProgressMs = positionMs while (true) { - delay(16L) // ~60 fps + delay(16L) if (isScrubbing) break val elapsed = System.currentTimeMillis() - startWallMs val interpolated = ((startProgressMs + elapsed).toFloat() / durationMs).coerceIn(0f, 1f) @@ -343,32 +369,25 @@ private fun MediaSeekBar( val displayMs = (displayFraction * durationMs).toLong() val accentArgb = accent.toArgb() - val trackAlphaArgb = accent.copy(alpha = AlphaSubtle).toArgb() - Column(verticalArrangement = Arrangement.spacedBy(SpaceXs)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - ) { - Text( - formatElapsedTime(displayMs), - color = SubtleGray, - style = MaterialTheme.typography.labelSmall, - ) - Text( - formatElapsedTime(durationMs), - color = SubtleGray, - style = MaterialTheme.typography.labelSmall, - ) - } + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(SpaceXs), + ) { + Text( + formatElapsedTime(displayMs), + color = SubtleGray, + style = MaterialTheme.typography.labelSmall, + ) Box( modifier = Modifier - .fillMaxWidth() + .weight(1f) .height(SeekBarHeight) .pointerInput(swipeLock) { awaitEachGesture { - awaitPointerEvent() // DOWN + awaitPointerEvent() swipeLock.value = true try { do { @@ -408,108 +427,30 @@ private fun MediaSeekBar( ) { AndroidView( factory = { context -> - SeekBar(context).apply { + WaveformSeekBar(context).apply { max = 10_000 - splitTrack = false - setPadding(0, 0, 0, 0) - // Disable direct touch — Compose handles all gestures above + setWaveformColor(accentArgb) + setThumbColor(accentArgb) isEnabled = false - - // Pill-shaped thumb - thumb = createSeekBarThumb(context, accentArgb) - thumbOffset = thumb.intrinsicWidth / 2 - - // Set up SquigglyProgress on the progress layer - val layer = (progressDrawable?.mutate() as? LayerDrawable) - if (layer != null) { - layer.findDrawableByLayerId(android.R.id.background) - ?.mutate()?.setTint(trackAlphaArgb) - - layer.findDrawableByLayerId(android.R.id.secondaryProgress) - ?.mutate()?.setTint( - com.android.internal.graphics.ColorUtils - .setAlphaComponent(accentArgb, 60) - ) - - val squiggle = SquigglyProgress().apply { - waveLength = context.resources.getDimensionPixelSize( - R.dimen.qs_media_seekbar_progress_wavelength - ).toFloat() - lineAmplitude = context.resources.getDimensionPixelSize( - R.dimen.qs_media_seekbar_progress_amplitude - ).toFloat() - phaseSpeed = context.resources.getDimensionPixelSize( - R.dimen.qs_media_seekbar_progress_phase - ).toFloat() - strokeWidth = context.resources.getDimensionPixelSize( - R.dimen.qs_media_seekbar_progress_stroke_width - ).toFloat() - setTint(accentArgb) - drawRemainingLine = false - transitionEnabled = false - animate = false - } - layer.setDrawableByLayerId(android.R.id.progress, squiggle) - progressDrawable = layer - } } }, update = { bar -> val target = (displayFraction * 10_000f).toInt().coerceIn(0, 10_000) - bar.progress = target - - // Re-tint thumb for accent color changes (e.g. track switch) - (bar.thumb as? GradientDrawable)?.setColor(accentArgb) - - val alpha = if (isPlaying) 255 else (255 * 0.55f).toInt() - bar.thumb?.alpha = alpha - - val layer = bar.progressDrawable as? LayerDrawable - - // Re-tint track colors - layer?.findDrawableByLayerId(android.R.id.background) - ?.setTint(trackAlphaArgb) - layer?.findDrawableByLayerId(android.R.id.secondaryProgress) - ?.setTint( - com.android.internal.graphics.ColorUtils - .setAlphaComponent(accentArgb, 60) - ) - - val squiggle = layer - ?.findDrawableByLayerId(android.R.id.progress) as? SquigglyProgress - - squiggle?.apply { - setTint(accentArgb) - setAlpha(alpha) - animate = isPlaying && !isScrubbing + if (bar.progress != target) bar.progress = target + when { + isPlaying && !bar.isPlaying -> bar.startWaveAnimation() + !isPlaying && bar.isPlaying -> bar.stopWaveAnimation() } - - layer?.alpha = alpha }, modifier = Modifier.fillMaxWidth().height(SeekBarHeight), ) } - } -} -/** - * Creates a pill-shaped thumb drawable for the seekbar. - */ -private fun createSeekBarThumb(context: android.content.Context, tintColor: Int): GradientDrawable { - val wPx = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 4f, context.resources.displayMetrics - ).toInt().coerceAtLeast(1) - val hPx = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 16f, context.resources.displayMetrics - ).toInt().coerceAtLeast(1) - val radiusPx = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 16f, context.resources.displayMetrics - ) - return GradientDrawable().apply { - shape = GradientDrawable.RECTANGLE - setSize(wPx, hPx) - cornerRadius = radiusPx - setColor(tintColor) + Text( + formatElapsedTime(durationMs), + color = SubtleGray, + style = MaterialTheme.typography.labelSmall, + ) } } From 122db1ecb17cd9e03e522900fbf7c0317179b83b Mon Sep 17 00:00:00 2001 From: Perspective <115461545+Perspective2077@users.noreply.github.com> Date: Fri, 22 May 2026 22:27:27 +0530 Subject: [PATCH 2/2] Add files via upload --- .../media/controls/ui/view/WaveformSeekBar.kt | 339 ++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 packages/SystemUI/src/com/android/systemui/media/controls/ui/view/WaveformSeekBar.kt diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/WaveformSeekBar.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/WaveformSeekBar.kt new file mode 100644 index 0000000000000..e49ff9d3284f6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/WaveformSeekBar.kt @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2025 AxionOS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.media.controls.ui.view + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PixelFormat +import android.graphics.RectF +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.animation.LinearInterpolator +import android.widget.SeekBar +import com.android.systemui.media.MediaSessionManager +import kotlin.math.* + +class WaveformSeekBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = android.R.attr.seekBarStyle, +) : SeekBar(context, attrs, defStyleAttr), MediaSessionManager.MediaDataListener { + + private val density = resources.displayMetrics.density + + private val progressPath = Path() + private val backgroundRect = RectF() + private val progressRect = RectF() + + private val trackHeight = 6f * density + private val cornerRadius = 8f * density + private val thumbRadius = 8f * density + + private val waveHeight = 12f * density + private val waveLength = 80f * density + private val minWaveTrackLength = waveLength * 1.0f + + private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.FILL + color = Color.WHITE + alpha = 77 + } + + private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.FILL + color = Color.WHITE + } + + private val thumbShadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.FILL + color = Color.BLACK + alpha = 60 + } + + private val thumbPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.FILL + color = Color.WHITE + setShadowLayer(4f * density, 0f, 2f * density, Color.argb(80, 0, 0, 0)) + } + + private var wavePhase = 0f + private var waveAmplitudeMultiplier = 0f + private var waveAnimator: ValueAnimator? = null + private var fadeAnimator: ValueAnimator? = null + var isPlaying = false + private set + + init { + thumb = TransparentDrawable() + splitTrack = false + progressDrawable = TransparentDrawable() + } + + fun startWaveAnimation() { + if (isPlaying && waveAnimator?.isRunning == true) return + isPlaying = true + + fadeAnimator?.cancel() + fadeAnimator = ValueAnimator.ofFloat(waveAmplitudeMultiplier, 1f).apply { + duration = 300L + addUpdateListener { + waveAmplitudeMultiplier = it.animatedValue as Float + invalidate() + } + start() + } + + waveAnimator?.cancel() + waveAnimator = ValueAnimator.ofFloat(0f, (2 * Math.PI).toFloat()).apply { + duration = 3500L + repeatCount = ValueAnimator.INFINITE + interpolator = LinearInterpolator() + addUpdateListener { + wavePhase = it.animatedValue as Float + invalidate() + } + start() + } + } + + fun stopWaveAnimation() { + isPlaying = false + waveAnimator?.cancel() + waveAnimator = null + + fadeAnimator?.cancel() + fadeAnimator = ValueAnimator.ofFloat(waveAmplitudeMultiplier, 0f).apply { + duration = 300L + addUpdateListener { + waveAmplitudeMultiplier = it.animatedValue as Float + invalidate() + } + start() + } + } + + fun regenerateWaveform(seed: Long = System.currentTimeMillis()) { + invalidate() + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + MediaSessionManager.get().addListener(this) + + if (isPlaying && waveAnimator?.isRunning != true) { + waveAnimator?.cancel() + waveAnimator = ValueAnimator.ofFloat(0f, (2 * Math.PI).toFloat()).apply { + duration = 3500L + repeatCount = ValueAnimator.INFINITE + interpolator = LinearInterpolator() + addUpdateListener { + wavePhase = it.animatedValue as Float + invalidate() + } + start() + } + } + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + MediaSessionManager.get().removeListener(this) + waveAnimator?.cancel() + } + + override fun onMediaColorsChanged(color: Int) { + post { setWaveformColor(color) } + } + + override fun onDraw(canvas: Canvas) { + val width = width.toFloat() + val height = height.toFloat() + val pLeft = paddingLeft.toFloat() + val pRight = paddingRight.toFloat() + + val drawWidth = width - pLeft - pRight + if (drawWidth <= 0) return + + val centerY = height / 2f + val trackTop = centerY - trackHeight / 2f + val trackBottom = centerY + trackHeight / 2f + + val ratio = if (max > 0) progress.toFloat() / max else 0f + val progressX = pLeft + drawWidth * ratio + + backgroundRect.set(pLeft, trackTop, pLeft + drawWidth, trackBottom) + canvas.drawRoundRect(backgroundRect, cornerRadius, cornerRadius, backgroundPaint) + + if (progressX > pLeft) { + if (waveAmplitudeMultiplier > 0.01f) { + drawWaveProgress(canvas, pLeft, progressX, centerY, trackTop, trackBottom) + } else { + progressRect.set(pLeft, trackTop, progressX, trackBottom) + canvas.drawRoundRect(progressRect, cornerRadius, cornerRadius, progressPaint) + } + } + canvas.drawCircle(progressX, centerY + 2 * density, thumbRadius, thumbShadowPaint) + canvas.drawCircle(progressX, centerY, thumbRadius, thumbPaint) + } + + private fun drawWaveProgress( + canvas: Canvas, + startX: Float, + endX: Float, + centerY: Float, + trackTop: Float, + trackBottom: Float + ) { + progressPath.reset() + + val progressLength = endX - startX + + val minLength = waveLength * 0.8f + val lengthRatio = (progressLength / minLength).coerceIn(0f, 1f) + val progressLengthFactor = (sqrt(lengthRatio) * 0.7f + 0.3f).coerceIn(0.3f, 1f) + + val edgeRadius = (trackHeight / 2f).coerceAtMost(cornerRadius) + + progressPath.moveTo(startX + edgeRadius, trackBottom) + + progressPath.arcTo( + startX, trackTop, + startX + edgeRadius * 2, trackBottom, + 90f, 90f, false + ) + + val waveStartX = startX + edgeRadius + val firstWaveY = calculateWaveY(waveStartX, waveStartX, endX, trackTop, progressLengthFactor) + progressPath.quadTo(startX, trackTop, waveStartX, firstWaveY) + + drawSmoothWave(waveStartX, endX, trackTop, progressLengthFactor) + + progressPath.lineTo(endX, trackTop) + progressPath.lineTo(endX, trackBottom) + progressPath.close() + + canvas.drawPath(progressPath, progressPaint) + } + + private fun calculateWaveY( + x: Float, + waveStartX: Float, + waveEndX: Float, + trackTop: Float, + progressLengthFactor: Float + ): Float { + val totalDist = waveEndX - waveStartX + if (totalDist <= 0) return trackTop + + val waveProgress = (x - waveStartX) / waveLength + val sinValue = sin(waveProgress * 2 * PI.toFloat() + wavePhase) + + val normalizedWave = (sinValue + 1f) / 2f + + val distFromStart = x - waveStartX + val distFromEnd = waveEndX - x + val taperZone = waveLength * 0.4f + + val envelope = when { + totalDist < taperZone * 2 -> { + val t = distFromStart / totalDist + 4f * t * (1f - t) + } + distFromStart < taperZone -> { + val t = distFromStart / taperZone + t * t * (3f - 2f * t) + } + distFromEnd < taperZone -> { + val t = distFromEnd / taperZone + t * t * (3f - 2f * t) + } + else -> 1f + }.coerceIn(0f, 1f) + + return trackTop - (normalizedWave * waveHeight * envelope * waveAmplitudeMultiplier * progressLengthFactor) + } + + private fun drawSmoothWave( + startX: Float, + endX: Float, + trackTop: Float, + progressLengthFactor: Float + ) { + if (endX <= startX) return + + val step = 2f * density + val points = mutableListOf>() + + var x = startX + while (x <= endX) { + val y = calculateWaveY(x, startX, endX, trackTop, progressLengthFactor) + points.add(Pair(x, y)) + x += step + } + + if (points.isEmpty() || points.last().first < endX) { + val y = calculateWaveY(endX, startX, endX, trackTop, progressLengthFactor) + points.add(Pair(endX, y)) + } + + if (points.size < 2) { + progressPath.lineTo(endX, trackTop) + return + } + + for (i in 0 until points.size - 1) { + val p0 = if (i > 0) points[i - 1] else points[i] + val p1 = points[i] + val p2 = points[i + 1] + val p3 = if (i + 2 < points.size) points[i + 2] else points[i + 1] + + val tension = 0.5f + + val cp1x = p1.first + (p2.first - p0.first) * tension / 3f + val cp1y = p1.second + (p2.second - p0.second) * tension / 3f + val cp2x = p2.first - (p3.first - p1.first) * tension / 3f + val cp2y = p2.second - (p3.second - p1.second) * tension / 3f + + progressPath.cubicTo(cp1x, cp1y, cp2x, cp2y, p2.first, p2.second) + } + } + + + fun setWaveformColor(color: Int) { + progressPaint.color = color + backgroundPaint.color = color + backgroundPaint.alpha = 77 + invalidate() + } + + fun setThumbColor(color: Int) { + thumbPaint.color = color + thumbPaint.setShadowLayer(4f * density, 0f, 2f * density, Color.argb(80, 0, 0, 0)) + invalidate() + } + + private class TransparentDrawable : Drawable() { + override fun draw(canvas: Canvas) {} + override fun setAlpha(alpha: Int) {} + override fun setColorFilter(colorFilter: ColorFilter?) {} + override fun getOpacity(): Int = PixelFormat.TRANSPARENT + } +}