1
0

chore: initial commit

Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
2025-11-17 08:02:58 -05:00
commit 3346eecb52
77 changed files with 4246 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

60
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,60 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "io.visus.solanim"
compileSdk {
version = release(36)
}
defaultConfig {
applicationId = "io.visus.solanim"
minSdk = 35
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.koin.androidx.compose)
implementation(libs.koin.androidx.compose.navigation)
implementation(project(":lib:solanim-ui"))
implementation(project(":lib:vulkan"))
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}

21
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package io.visus.solanim
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.visus.solanim", appContext.packageName)
}
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name=".MainApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SolAnim">
<activity
android:name=".presentation.ui.MainActivity"
android:exported="true"
android:theme="@style/Theme.SolAnim">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,20 @@
package io.visus.solanim
import android.app.Application
import io.visus.solanim.di.appModule
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.core.logger.Level
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MainApplication)
androidLogger(Level.DEBUG)
modules(appModule)
}
}
}

View File

@@ -0,0 +1,21 @@
package io.visus.solanim.di
import io.visus.solanim.presentation.viewmodel.MainViewModel
import io.visus.solanim.domain.repository.solar.SolarAnimationSettingsRepository
import io.visus.solanim.domain.repository.solar.SolarAnimationSettingsRepositoryImpl
import io.visus.solanim.domain.usecase.solar.GetSettingsUseCase
import io.visus.solanim.domain.usecase.solar.UpdateColorUseCase
import io.visus.solanim.domain.usecase.solar.UpdateRotationSpeedUseCase
import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.singleOf
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module
val appModule = module {
singleOf(::SolarAnimationSettingsRepositoryImpl) { bind<SolarAnimationSettingsRepository>() }
singleOf(::GetSettingsUseCase)
singleOf(::UpdateColorUseCase)
singleOf(::UpdateRotationSpeedUseCase)
viewModelOf(::MainViewModel)
}

View File

@@ -0,0 +1,9 @@
package io.visus.solanim.domain.model.solar
/**
* Default values for solar animation settings.
*/
object SolarAnimationDefaults {
const val DEFAULT_COLOR: Int = 0xFFFC9601.toInt()
const val DEFAULT_ROTATION_SPEED: Float = 1.0f
}

View File

@@ -0,0 +1,12 @@
package io.visus.solanim.domain.model.solar
/**
* Data class representing the settings for solar animation.
*
* @property color The color of the solar animation.
* @property rotationSpeed The rotation speed of the solar animation.
*/
data class SolarAnimationSettings(
val color: Int,
val rotationSpeed: Float
)

View File

@@ -0,0 +1,30 @@
package io.visus.solanim.domain.repository.solar
import io.visus.solanim.domain.model.solar.SolarAnimationSettings
import kotlinx.coroutines.flow.Flow
/**
* Repository interface for managing solar animation settings.
*/
interface SolarAnimationSettingsRepository {
/**
* Retrieves the current solar animation settings as a flow.
*
* @return A flow emitting the current [SolarAnimationSettings].
*/
fun getSettings(): Flow<SolarAnimationSettings>
/**
* Updates the color of the solar animation.
*
* @param color The new color value.
*/
suspend fun updateColor(color: Int)
/**
* Updates the rotation speed of the solar animation.
*
* @param speed The new rotation speed value.
*/
suspend fun updateRotationSpeed(speed: Float)
}

View File

@@ -0,0 +1,45 @@
package io.visus.solanim.domain.repository.solar
import io.visus.solanim.domain.model.solar.SolarAnimationDefaults
import io.visus.solanim.domain.model.solar.SolarAnimationSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
/**
* Implementation of [SolarAnimationSettingsRepository] using in-memory storage.
*/
class SolarAnimationSettingsRepositoryImpl : SolarAnimationSettingsRepository {
private val _settings: MutableStateFlow<SolarAnimationSettings> = MutableStateFlow(
SolarAnimationSettings(
color = SolarAnimationDefaults.DEFAULT_COLOR,
rotationSpeed = SolarAnimationDefaults.DEFAULT_ROTATION_SPEED
)
)
/**
* Retrieves the current solar animation settings as a flow.
*
* @return A flow emitting the current [SolarAnimationSettings].
*/
override fun getSettings(): Flow<SolarAnimationSettings> = _settings.asStateFlow()
/**
* Updates the color of the solar animation.
*
* @param color The new color value.
*/
override suspend fun updateColor(color: Int) {
_settings.update { it.copy(color = color) }
}
/**
* Updates the rotation speed of the solar animation.
*
* @param speed The new rotation speed value.
*/
override suspend fun updateRotationSpeed(speed: Float) {
_settings.update { it.copy(rotationSpeed = speed) }
}
}

View File

@@ -0,0 +1,12 @@
package io.visus.solanim.domain.usecase.solar
import io.visus.solanim.domain.repository.solar.SolarAnimationSettingsRepository
/**
* Use case for retrieving solar animation settings.
*
* @property repository The repository to access solar animation settings.
*/
class GetSettingsUseCase(private val repository: SolarAnimationSettingsRepository) {
operator fun invoke() = repository.getSettings()
}

View File

@@ -0,0 +1,12 @@
package io.visus.solanim.domain.usecase.solar
import io.visus.solanim.domain.repository.solar.SolarAnimationSettingsRepository
/**
* Use case for updating the color of the solar animation.
*
* @property repository The repository to access solar animation settings.
*/
class UpdateColorUseCase(private val repository: SolarAnimationSettingsRepository) {
suspend operator fun invoke(color: Int) = repository.updateColor(color)
}

View File

@@ -0,0 +1,12 @@
package io.visus.solanim.domain.usecase.solar
import io.visus.solanim.domain.repository.solar.SolarAnimationSettingsRepository
/**
* Use case for updating the rotation speed of the solar animation.
*
* @property repository The repository to access solar animation settings.
*/
class UpdateRotationSpeedUseCase(private val repository: SolarAnimationSettingsRepository) {
suspend operator fun invoke(rotationSpeed: Float) = repository.updateRotationSpeed(rotationSpeed)
}

View File

@@ -0,0 +1,136 @@
package io.visus.solanim.presentation.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.github.skydoves.colorpicker.compose.BrightnessSlider
import com.github.skydoves.colorpicker.compose.ColorEnvelope
import com.github.skydoves.colorpicker.compose.HsvColorPicker
import com.github.skydoves.colorpicker.compose.rememberColorPickerController
import io.visus.solanim.R
import io.visus.solanim.presentation.viewmodel.MainUiState
import io.visus.solanim.presentation.viewmodel.MainViewModel
import io.visus.solanim.ui.AppTheme
import io.visus.solanim.ui.components.Scaffold
import io.visus.solanim.ui.components.Slider
import io.visus.solanim.ui.components.SliderDefaults
import io.visus.solanim.ui.components.Text
import io.visus.solanim.ui.components.topbar.TopBar
import io.visus.solanim.ui.components.topbar.TopBarDefaults
import org.koin.androidx.viewmodel.ext.android.viewModel
class MainActivity : ComponentActivity() {
private val mainViewModel: MainViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.dark(
scrim = android.graphics.Color.TRANSPARENT
)
)
setContent {
val uiState by mainViewModel.uiState.collectAsState()
AppTheme {
App(
uiState = uiState,
onRotationSpeedValueChange = { mainViewModel.onRotationSpeedChange(it) },
onColorChange = { mainViewModel.onSelectColorChange(it.color.toArgb()) }
)
}
}
}
}
@Composable
fun App(
uiState: MainUiState,
onRotationSpeedValueChange: (Float) -> Unit,
onColorChange: (ColorEnvelope) -> Unit,
) {
Scaffold(
containerColor = Color.Transparent,
modifier = Modifier
.fillMaxSize()
.background(AppTheme.colors.defaultBackgroundGradient),
topBar = {
TopBar(
colors = TopBarDefaults.topBarColors(
containerColor = Color.Transparent,
),
modifier = Modifier
.fillMaxWidth()
) {
Column(
modifier = Modifier.padding(horizontal = 72.dp)
) {
Text(
text = stringResource(
R.string.rotation_speed,
uiState.rotationSpeed
),
color = AppTheme.colors.white,
)
Slider(
value = uiState.rotationSpeed,
onValueChange = onRotationSpeedValueChange,
valueRange = 0f..2f,
colors = SliderDefaults.colors(
thumbColor = AppTheme.colors.tertiary,
activeTrackColor = AppTheme.colors.tertiary,
inactiveTrackColor = AppTheme.colors.tertiary.copy(alpha = 0.3f),
),
modifier = Modifier.fillMaxWidth()
)
}
}
},
bottomBar = {
val colorPickerController = rememberColorPickerController()
Column(
modifier = Modifier
.fillMaxWidth()
.background(Color.Transparent)
.padding(vertical = 96.dp, horizontal = 32.dp)
) {
HsvColorPicker(
modifier = Modifier
.fillMaxWidth()
.height(150.dp),
controller = colorPickerController,
initialColor = Color(uiState.selectedColor),
onColorChanged = onColorChange
)
BrightnessSlider(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)
.height(16.dp),
controller = colorPickerController,
initialColor = Color(uiState.selectedColor)
)
}
}
) { }
}

View File

@@ -0,0 +1,66 @@
package io.visus.solanim.presentation.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.visus.solanim.domain.model.solar.SolarAnimationDefaults
import io.visus.solanim.domain.usecase.solar.GetSettingsUseCase
import io.visus.solanim.domain.usecase.solar.UpdateColorUseCase
import io.visus.solanim.domain.usecase.solar.UpdateRotationSpeedUseCase
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/**
* ViewModel for the main screen.
*/
class MainViewModel(
getSettingsUseCase: GetSettingsUseCase,
private val updateColorUseCase: UpdateColorUseCase,
private val updateRotationSpeedUseCase: UpdateRotationSpeedUseCase
) : ViewModel() {
val uiState: StateFlow<MainUiState> = getSettingsUseCase()
.map { settings ->
MainUiState(
selectedColor = settings.color,
rotationSpeed = settings.rotationSpeed
)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = MainUiState()
)
/**
* Updates the selected color.
*
* @param value The new color selected by the user.
*/
fun onSelectColorChange(value: Int) {
viewModelScope.launch {
updateColorUseCase(value)
}
}
/**
* Updates the rotation speed.
*
* @param value The new value from the rotation speed.
*/
fun onRotationSpeedChange(value: Float) {
viewModelScope.launch {
updateRotationSpeedUseCase(value)
}
}
}
/**
* UI state for the main screen.
*/
data class MainUiState(
val selectedColor: Int = SolarAnimationDefaults.DEFAULT_COLOR,
val rotationSpeed: Float = SolarAnimationDefaults.DEFAULT_ROTATION_SPEED
)

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@@ -0,0 +1,4 @@
<resources>
<string name="app_name">Solar Animation</string>
<string name="rotation_speed">Rotation Speed: %1$.2f</string>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.SolAnim" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older than API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@@ -0,0 +1,17 @@
package io.visus.solanim
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}