Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions packages/voltra/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,6 @@ dependencies {
// Google Tink (encryption for credential storage)
implementation "com.google.crypto.tink:tink-android:1.19.0"

// JSON parsing
implementation "com.google.code.gson:gson:2.10.1"

// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"

Expand All @@ -114,4 +111,5 @@ dependencies {
// Unit tests
testImplementation "junit:junit:4.13.2"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1"
testImplementation "org.robolectric:robolectric:4.14.1"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import voltra.models.VoltraNode
class GlanceFactory(
private val widgetId: String,
private val sharedElements: List<VoltraNode>? = null,
private val sharedStyles: List<Map<String, Any>>? = null,
private val sharedStyles: List<Map<String, Any?>>? = null,
private val widgetSize: DpSize? = null,
) {
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ object RemoteViewsGenerator {
context: Context,
node: VoltraNode,
sharedElements: List<VoltraNode>?,
sharedStyles: List<Map<String, Any>>?,
sharedStyles: List<Map<String, Any?>>?,
size: DpSize,
): RemoteViews {
// Create a new GlanceRemoteViews instance each time to avoid caching issues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ data class ResolvedStyle(

@Composable
fun resolveAndApplyStyle(
props: Map<String, Any>?,
sharedStyles: List<Map<String, Any>>?,
props: Map<String, Any?>?,
sharedStyles: List<Map<String, Any?>>?,
): ResolvedStyle {
val resolvedStyle = resolveStyle(props, sharedStyles)
val compositeStyle =
Expand All @@ -42,9 +42,9 @@ fun resolveAndApplyStyle(
* or {"s": {...}} for inline styles.
*/
private fun resolveStyle(
props: Map<String, Any>?,
sharedStyles: List<Map<String, Any>>?,
): Map<String, Any>? {
props: Map<String, Any?>?,
sharedStyles: List<Map<String, Any?>>?,
): Map<String, Any?>? {
if (props == null) return null

val styleRef = props["style"]
Expand All @@ -58,7 +58,7 @@ private fun resolveStyle(
is Map<*, *> -> {
// It's already an inline style
@Suppress("UNCHECKED_CAST")
styleRef as? Map<String, Any>
styleRef as? Map<String, Any?>
}

else -> {
Expand All @@ -82,7 +82,7 @@ private fun resolveStyle(
@Composable
fun applyClickableIfNeeded(
modifier: GlanceModifier,
props: Map<String, Any>?,
props: Map<String, Any?>?,
elementId: String?,
widgetId: String,
componentType: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import voltra.models.VoltraNode
data class VoltraRenderContext(
val widgetId: String,
val sharedElements: List<VoltraNode>? = null,
val sharedStyles: List<Map<String, Any>>? = null,
val sharedStyles: List<Map<String, Any?>>? = null,
val widgetSize: DpSize? = null,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,13 @@ import androidx.glance.appwidget.components.OutlineButton
import androidx.glance.appwidget.components.SquareIconButton
import androidx.glance.layout.Box
import androidx.glance.unit.ColorProvider
import com.google.gson.Gson
import voltra.glance.LocalVoltraRenderContext
import voltra.glance.applyClickableIfNeeded
import voltra.glance.resolveAndApplyStyle
import voltra.models.VoltraElement
import voltra.styling.JSColorParser
import voltra.styling.toColorProvider

private const val TAG = "ButtonRenderers"
private val gson = Gson()

@Composable
fun RenderButton(
element: VoltraElement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ import android.graphics.DashPathEffect
import android.graphics.Paint
import android.graphics.Path
import android.graphics.RectF
import android.util.Log
import androidx.compose.ui.graphics.toArgb
import voltra.styling.JSColorParser
import voltra.styling.VoltraColorValue

private const val TAG = "ChartBitmapRenderer"

private val DEFAULT_PALETTE =
intArrayOf(
0xFF4E79A7.toInt(), // blue
Expand Down Expand Up @@ -46,24 +43,19 @@ data class SectorPoint(
)

fun parseMarksJson(marksJson: String): List<WireMark> {
return try {
val gson = com.google.gson.Gson()
val type = object : com.google.gson.reflect.TypeToken<List<List<Any>>>() {}.type
val outer: List<List<Any>> = gson.fromJson(marksJson, type)
outer.mapNotNull { row ->
if (row.size < 3) return@mapNotNull null
val markType = row[0] as? String ?: return@mapNotNull null

@Suppress("UNCHECKED_CAST")
val data = row[1] as? List<List<Any>>

@Suppress("UNCHECKED_CAST")
val props = (row[2] as? Map<String, Any>) ?: emptyMap()
WireMark(markType, data, props)
}
} catch (e: Exception) {
Log.w(TAG, "Failed to parse marks JSON", e)
emptyList()
return parseMarksTuples(marksJson).mapNotNull { row ->
if (row.size < 3) return@mapNotNull null
val markType = row[0] as? String ?: return@mapNotNull null

@Suppress("UNCHECKED_CAST")
val data =
(row[1] as? List<*>)?.mapNotNull { point ->
(point as? List<*>)?.toList() as? List<Any>
}

@Suppress("UNCHECKED_CAST")
val props = row[2] as? Map<String, Any> ?: emptyMap()
WireMark(markType, data, props)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,7 @@ fun RenderChart(

@Composable
private fun parseForegroundStyleScale(json: String?): Map<String, Int>? {
if (json.isNullOrEmpty()) return null
val pairs: List<List<String>> =
try {
val gson = com.google.gson.Gson()
val type = object : com.google.gson.reflect.TypeToken<List<List<String>>>() {}.type
gson.fromJson(json, type)
} catch (e: Exception) {
Log.w(TAG, "Failed to parse foregroundStyleScale", e)
return null
}
val pairs = parseForegroundStyleScaleEntries(json) ?: return null

val map = mutableMapOf<String, Int>()
for (pair in pairs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import androidx.glance.LocalContext
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.components.TitleBar
import androidx.glance.text.FontFamily
import com.google.gson.Gson
import voltra.glance.LocalVoltraRenderContext
import voltra.glance.applyClickableIfNeeded
import voltra.glance.resolveAndApplyStyle
Expand All @@ -19,9 +18,6 @@ import voltra.payload.ComponentTypeID
import voltra.styling.JSColorParser
import voltra.styling.toColorProvider

private const val TAG = "ComplexRenderers"
private val gson = Gson()

@Composable
fun RenderTitleBar(
element: VoltraElement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,15 @@ fun RenderLazyVerticalGrid(
}
}

private fun extractHorizontalAlignment(props: Map<String, Any>?): Alignment.Horizontal =
private fun extractHorizontalAlignment(props: Map<String, Any?>?): Alignment.Horizontal =
when (props?.get("horizontalAlignment") as? String) {
"start" -> Alignment.Horizontal.Start
"center-horizontally" -> Alignment.Horizontal.CenterHorizontally
"end" -> Alignment.Horizontal.End
else -> Alignment.Horizontal.Start
}

private fun extractVerticalAlignment(props: Map<String, Any>?): Alignment.Vertical =
private fun extractVerticalAlignment(props: Map<String, Any?>?): Alignment.Vertical =
when (props?.get("verticalAlignment") as? String) {
"top" -> Alignment.Vertical.Top
"center" -> Alignment.Vertical.CenterVertically
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,23 @@ import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.drawable.Icon
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.glance.GlanceModifier
import androidx.glance.ImageProvider
import androidx.glance.LocalContext
import androidx.glance.action.Action
import androidx.glance.appwidget.action.actionStartActivity
import androidx.glance.layout.ContentScale
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import voltra.glance.LocalVoltraRenderContext
import voltra.images.VoltraImageManager
import voltra.models.VoltraElement
import voltra.models.VoltraNode
import voltra.payload.ComponentTypeID
import voltra.styling.CompositeStyle

private val gson = Gson()
private const val TAG = "RenderCommon"

fun getOnClickAction(
context: Context,
props: Map<String, Any>?,
props: Map<String, Any?>?,
widgetId: String,
componentId: String,
): Action {
Expand Down Expand Up @@ -66,30 +60,10 @@ fun extractImageProvider(sourceProp: Any?): ImageProvider? {
if (sourceProp == null) return null

val context = LocalContext.current
val sourceMap =
when (sourceProp) {
is String -> {
try {
val type = object : TypeToken<Map<String, Any>>() {}.type
gson.fromJson<Map<String, Any>>(sourceProp, type)
} catch (e: Exception) {
Log.w(TAG, "Failed to parse image source JSON: $sourceProp", e)
null
}
}

is Map<*, *> -> {
@Suppress("UNCHECKED_CAST")
sourceProp as? Map<String, Any>
}

else -> {
null
}
} ?: return null
val source = parseEncodedImageSource(sourceProp) ?: return null

val assetName = sourceMap["assetName"] as? String
val base64 = sourceMap["base64"] as? String
val assetName = source.assetName
val base64 = source.base64

if (assetName != null) {
// Try as drawable resource first
Expand All @@ -107,7 +81,7 @@ fun extractImageProvider(sourceProp: Any?): ImageProvider? {
return ImageProvider(Icon.createWithBitmap(bitmap))
}
} catch (e: Exception) {
Log.e(TAG, "Failed to decode preloaded image: $assetName", e)
android.util.Log.e("RenderCommon", "Failed to decode preloaded image: $assetName", e)
}
}
}
Expand All @@ -120,7 +94,7 @@ fun extractImageProvider(sourceProp: Any?): ImageProvider? {
return ImageProvider(Icon.createWithBitmap(bitmap))
}
} catch (e: Exception) {
Log.e(TAG, "Failed to decode base64 image", e)
android.util.Log.e("RenderCommon", "Failed to decode base64 image", e)
}
}

Expand Down
Loading
Loading