feat: add installable Lingji Android shell

This commit is contained in:
QiuSW
2026-08-07 18:00:38 +08:00
parent de480ee9bb
commit 730bc528be
43 changed files with 1007 additions and 125 deletions
+122 -19
View File
@@ -1,26 +1,110 @@
import groovy.json.JsonSlurper
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.kapt)
alias(libs.plugins.hilt)
}
android {
namespace = "net.opcapp.flash"
compileSdk = 34
defaultConfig {
applicationId = "net.opcapp.flash"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "0.1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
javaCompileOptions {
annotationProcessorOptions {
arguments += mapOf("room.schemaLocation" to "$projectDir/schemas")
}
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
buildFeatures {
compose = true
buildConfig = false
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
packaging {
resources.excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
testOptions {
unitTests.isReturnDefaultValues = true
}
}
kotlin {
jvmToolchain(17)
sourceSets {
main {
kotlin.srcDir("src/main/java")
}
test {
kotlin.srcDir("src/test/java")
}
}
}
kapt {
correctErrorTypes = true
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = "17"
}
dependencies {
val composeBom = platform(libs.androidx.compose.bom)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(composeBom)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
kapt(libs.androidx.room.compiler)
implementation(libs.androidx.datastore.preferences)
implementation(libs.hilt.android)
kapt(libs.hilt.compiler)
testImplementation(libs.junit)
androidTestImplementation(composeBom)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}
tasks.test {
tasks.withType<Test>().configureEach {
useJUnit()
testLogging {
events("failed", "skipped")
@@ -30,25 +114,44 @@ tasks.test {
tasks.register("verifyDependencyLicenses") {
group = "verification"
description = "Ensures every resolved JVM runtime/test artifact has a reviewed license entry."
description = "Ensures every resolved debug runtime/test artifact has a reviewed license entry."
doLast {
@Suppress("UNCHECKED_CAST")
val manifest = JsonSlurper().parse(rootProject.file("config/dependency-licenses.json")) as Map<String, Any>
@Suppress("UNCHECKED_CAST")
val components = manifest.getValue("components") as List<Map<String, Any>>
@Suppress("UNCHECKED_CAST")
val groupPolicies = manifest.getValue("groupPolicies") as List<Map<String, Any>>
val reviewedCoordinates = components.mapNotNull { it["coordinates"] as String? }.toSet()
val resolvedCoordinates = configurations.getByName("testRuntimeClasspath")
.resolvedConfiguration
.resolvedArtifacts
.map { artifact ->
val id = artifact.moduleVersion.id
"${id.group}:${id.name}:${id.version}"
val resolvedCoordinates = listOf("debugRuntimeClasspath", "debugUnitTestRuntimeClasspath")
.flatMap { configurationName ->
configurations.getByName(configurationName)
.resolvedConfiguration
.resolvedArtifacts
.mapNotNull { artifact ->
if (artifact.id.componentIdentifier !is org.gradle.api.artifacts.component.ModuleComponentIdentifier) {
return@mapNotNull null
}
val id = artifact.moduleVersion.id
"${id.group}:${id.name}:${id.version}"
}
}
.toSet()
val missing = resolvedCoordinates - reviewedCoordinates
val missing = resolvedCoordinates.filterNot { coordinates ->
if (coordinates in reviewedCoordinates) return@filterNot true
val group = coordinates.substringBefore(":")
groupPolicies.any { policy ->
val prefix = policy.getValue("groupPrefix") as String
group == prefix || group.startsWith("$prefix.")
}
}
check(missing.isEmpty()) {
"Dependencies missing reviewed license entries: ${missing.sorted().joinToString()}"
}
logger.lifecycle(
"Dependency license coverage passed for ${resolvedCoordinates.size} artifacts " +
"with ${groupPolicies.size} reviewed group policies.",
)
}
}
+1
View File
@@ -0,0 +1 @@
# Project-specific R8 rules will be added when release minification is enabled.
@@ -0,0 +1,24 @@
package net.opcapp.flash.app
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@get:Rule
val composeRule = createAndroidComposeRule<MainActivity>()
@Test
fun domainFixtureCanBeVerifiedFromHome() {
composeRule.onNodeWithText("灵机").assertIsDisplayed()
composeRule.onNodeWithText("验证起卦核心").performClick()
composeRule.onNodeWithText("复 24 → 坤 2").assertIsDisplayed()
composeRule.onNodeWithText("初爻动").assertIsDisplayed()
}
}
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name=".app.LingjiApplication"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Lingji"
android:usesCleartextTraffic="false">
<activity
android:name=".app.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,34 @@
package net.opcapp.flash.app
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import net.opcapp.flash.feature.home.DevelopmentHomeScreen
import net.opcapp.flash.feature.result.DomainVerificationScreen
private object Destination {
const val Home = "home"
const val DomainResult = "domain-result"
}
@Composable
fun LingjiApp() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Destination.Home,
) {
composable(Destination.Home) {
DevelopmentHomeScreen(
onVerifyDomain = { navController.navigate(Destination.DomainResult) },
)
}
composable(Destination.DomainResult) {
DomainVerificationScreen(
onBack = navController::popBackStack,
)
}
}
}
@@ -0,0 +1,7 @@
package net.opcapp.flash.app
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class LingjiApplication : Application()
@@ -0,0 +1,19 @@
package net.opcapp.flash.app
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import dagger.hilt.android.AndroidEntryPoint
import net.opcapp.flash.core.designsystem.LingjiTheme
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LingjiTheme {
LingjiApp()
}
}
}
}
@@ -0,0 +1,84 @@
package net.opcapp.flash.core.designsystem
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Typography
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
private val Paper = Color(0xFFF5F0E6)
private val RaisedPaper = Color(0xFFFFFBF3)
private val Ink = Color(0xFF26231F)
private val MutedInk = Color(0xFF665F55)
private val Cinnabar = Color(0xFFA33A2B)
private val DarkPaper = Color(0xFF1E1B18)
private val DarkRaisedPaper = Color(0xFF292520)
private val PaleInk = Color(0xFFEAE2D5)
private val PaleCinnabar = Color(0xFFFFB4A5)
private val LightColors = lightColorScheme(
primary = Cinnabar,
onPrimary = Color.White,
primaryContainer = Color(0xFFF4DAD3),
onPrimaryContainer = Color(0xFF3D0902),
background = Paper,
onBackground = Ink,
surface = RaisedPaper,
onSurface = Ink,
surfaceVariant = Color(0xFFE8E0D3),
onSurfaceVariant = MutedInk,
outline = Color(0xFF81786C),
)
private val DarkColors = darkColorScheme(
primary = PaleCinnabar,
onPrimary = Color(0xFF5F160C),
primaryContainer = Color(0xFF7F261A),
onPrimaryContainer = Color(0xFFFFDAD2),
background = DarkPaper,
onBackground = PaleInk,
surface = DarkRaisedPaper,
onSurface = PaleInk,
surfaceVariant = Color(0xFF4C453D),
onSurfaceVariant = Color(0xFFD1C6B8),
outline = Color(0xFF9C9184),
)
private val LingjiTypography = Typography().let { defaults ->
defaults.copy(
displaySmall = defaults.displaySmall.copy(
fontFamily = FontFamily.Serif,
fontWeight = FontWeight.SemiBold,
),
headlineMedium = defaults.headlineMedium.copy(
fontFamily = FontFamily.Serif,
fontWeight = FontWeight.Medium,
),
titleLarge = defaults.titleLarge.copy(
fontFamily = FontFamily.Serif,
fontWeight = FontWeight.Medium,
),
bodyLarge = defaults.bodyLarge.copy(fontFamily = FontFamily.SansSerif),
bodyMedium = defaults.bodyMedium.copy(fontFamily = FontFamily.SansSerif),
labelLarge = defaults.labelLarge.copy(
fontFamily = FontFamily.SansSerif,
fontWeight = FontWeight.SemiBold,
),
)
}
@Composable
fun LingjiTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
MaterialTheme(
colorScheme = if (darkTheme) DarkColors else LightColors,
typography = LingjiTypography,
content = content,
)
}
@@ -1,4 +1,4 @@
package brainwave.core.model
package net.opcapp.flash.core.model
data class HexagramContent(
val kingWenNumber: Int,
@@ -1,6 +1,6 @@
package brainwave.data.content
package net.opcapp.flash.data.content
import brainwave.core.model.HexagramContent
import net.opcapp.flash.core.model.HexagramContent
interface HexagramContentRepository {
val contentVersion: String
@@ -1,4 +1,4 @@
package brainwave.domain.casting
package net.opcapp.flash.domain.casting
data class CastComputation internal constructor(
val roundsBottomUp: List<CastRound>,
@@ -1,4 +1,4 @@
package brainwave.domain.casting
package net.opcapp.flash.domain.casting
data class CastRecordDto(
val schemaVersion: Int,
@@ -1,4 +1,4 @@
package brainwave.domain.casting
package net.opcapp.flash.domain.casting
enum class CoinSide(val score: Int) {
CHARACTER(2),
@@ -1,4 +1,4 @@
package brainwave.domain.casting
package net.opcapp.flash.domain.casting
object HexagramCatalog {
private enum class Trigram(val patternBottomUp: String) {
@@ -0,0 +1,131 @@
package net.opcapp.flash.feature.home
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
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.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import net.opcapp.flash.R
@Composable
fun DevelopmentHomeScreen(
onVerifyDomain: () -> Unit,
modifier: Modifier = Modifier,
) {
Surface(
modifier = modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
Column(
modifier = Modifier
.windowInsetsPadding(WindowInsets.safeDrawing)
.verticalScroll(rememberScrollState())
.padding(horizontal = 24.dp, vertical = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(R.string.development_build),
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
)
Spacer(Modifier.height(28.dp))
HexagramSeal()
Spacer(Modifier.height(20.dp))
Text(
text = stringResource(R.string.app_name),
style = MaterialTheme.typography.displaySmall,
color = MaterialTheme.colorScheme.onBackground,
)
Text(
text = stringResource(R.string.home_tagline),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 8.dp),
)
Spacer(Modifier.height(32.dp))
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
shape = RoundedCornerShape(20.dp),
) {
Column(
modifier = Modifier.padding(20.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = stringResource(R.string.core_check_title),
style = MaterialTheme.typography.titleLarge,
)
Text(
text = stringResource(R.string.core_check_body),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Button(
onClick = onVerifyDomain,
modifier = Modifier
.fillMaxWidth()
.height(52.dp),
shape = RoundedCornerShape(14.dp),
contentPadding = PaddingValues(horizontal = 20.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(stringResource(R.string.verify_casting_core))
}
}
}
Spacer(Modifier.height(32.dp))
Text(
text = stringResource(R.string.local_data_notice),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
)
}
}
}
@Composable
private fun HexagramSeal() {
Surface(
color = MaterialTheme.colorScheme.primary,
shape = RoundedCornerShape(18.dp),
modifier = Modifier.height(72.dp),
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.padding(horizontal = 22.dp),
) {
Text(
text = stringResource(R.string.seal_character),
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onPrimary,
)
}
}
}
@@ -0,0 +1,164 @@
package net.opcapp.flash.feature.result
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
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.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import net.opcapp.flash.R
import net.opcapp.flash.domain.casting.CastEngine
import net.opcapp.flash.domain.casting.CastRound
import net.opcapp.flash.domain.casting.LineValue
import net.opcapp.flash.domain.casting.Polarity
@Composable
fun DomainVerificationScreen(
onBack: () -> Unit,
modifier: Modifier = Modifier,
) {
val result = remember {
CastEngine.cast(
listOf(
CastRound.fromLineValue(LineValue.OLD_YANG),
CastRound.fromLineValue(LineValue.YOUNG_YIN),
CastRound.fromLineValue(LineValue.YOUNG_YIN),
CastRound.fromLineValue(LineValue.YOUNG_YIN),
CastRound.fromLineValue(LineValue.YOUNG_YIN),
CastRound.fromLineValue(LineValue.YOUNG_YIN),
),
)
}
Surface(
modifier = modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
Column(
modifier = Modifier
.windowInsetsPadding(WindowInsets.safeDrawing)
.verticalScroll(rememberScrollState())
.padding(horizontal = 24.dp, vertical = 12.dp),
) {
TextButton(
onClick = onBack,
modifier = Modifier.height(48.dp),
) {
Text(stringResource(R.string.back))
}
Spacer(Modifier.height(24.dp))
Text(
text = stringResource(R.string.core_check_passed),
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
)
Text(
text = stringResource(
R.string.fixture_result,
result.primaryHexagramId.value,
result.transformedHexagramId.value,
),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(top = 12.dp),
)
Text(
text = stringResource(R.string.moving_line_result),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 8.dp),
)
Spacer(Modifier.height(36.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
HexagramDiagram(
linesBottomUp = result.primaryPatternBottomUp.linesBottomUp,
description = stringResource(R.string.primary_hexagram_description),
)
HexagramDiagram(
linesBottomUp = result.transformedPatternBottomUp.linesBottomUp,
description = stringResource(R.string.transformed_hexagram_description),
)
}
Spacer(Modifier.height(36.dp))
Text(
text = stringResource(R.string.fixture_explanation),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
}
@Composable
private fun HexagramDiagram(
linesBottomUp: List<Polarity>,
description: String,
) {
val ink = MaterialTheme.colorScheme.onBackground
val lineWidth = with(LocalDensity.current) { 58.dp.toPx() }
val gap = with(LocalDensity.current) { 12.dp.toPx() }
val strokeWidth = with(LocalDensity.current) { 5.dp.toPx() }
Canvas(
modifier = Modifier
.size(width = 88.dp, height = 136.dp)
.semantics { contentDescription = description },
) {
val verticalStep = size.height / 6f
linesBottomUp.forEachIndexed { index, polarity ->
val y = size.height - verticalStep * (index + 0.5f)
val left = (size.width - lineWidth) / 2f
val right = left + lineWidth
if (polarity == Polarity.YANG) {
drawLine(
color = ink,
start = Offset(left, y),
end = Offset(right, y),
strokeWidth = strokeWidth,
cap = StrokeCap.Square,
)
} else {
val center = size.width / 2f
drawLine(
color = ink,
start = Offset(left, y),
end = Offset(center - gap / 2f, y),
strokeWidth = strokeWidth,
cap = StrokeCap.Square,
)
drawLine(
color = ink,
start = Offset(center + gap / 2f, y),
end = Offset(right, y),
strokeWidth = strokeWidth,
cap = StrokeCap.Square,
)
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Lingji" parent="android:style/Theme.Material.NoActionBar">
<item name="android:fontFamily">sans</item>
<item name="android:statusBarColor">#1E1B18</item>
<item name="android:navigationBarColor">#1E1B18</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowActionModeOverlay">true</item>
</style>
</resources>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">灵机</string>
<string name="development_build">内部构建 · 开发验证页</string>
<string name="home_tagline">本机起卦 · 离线可用</string>
<string name="seal_character">易</string>
<string name="core_check_title">领域核心已接入</string>
<string name="core_check_body">用固定六爻夹具验证卦象、变卦与动爻计算链路。</string>
<string name="verify_casting_core">验证起卦核心</string>
<string name="local_data_notice">当前验证页不写入记录。正式功能将默认保存在本机,只有明确发起 AI 解读时才会联网。</string>
<string name="back">返回</string>
<string name="core_check_passed">计算通过</string>
<string name="fixture_result">复 %1$d → 坤 %2$d</string>
<string name="moving_line_result">初爻动</string>
<string name="fixture_explanation">固定输入为初爻老阳、其余五爻少阴。结果来自纯 Kotlin 起卦核心,不是界面写死的演示值。</string>
<string name="primary_hexagram_description">本卦复,第二十四卦</string>
<string name="transformed_hexagram_description">变卦坤,第二卦</string>
</resources>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Lingji" parent="android:style/Theme.Material.Light.NoActionBar">
<item name="android:fontFamily">sans</item>
<item name="android:statusBarColor">#F5F0E6</item>
<item name="android:navigationBarColor">#F5F0E6</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowActionModeOverlay">true</item>
</style>
</resources>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<exclude domain="root" path="." />
<exclude domain="file" path="." />
<exclude domain="database" path="." />
<exclude domain="sharedpref" path="." />
<exclude domain="external" path="." />
</full-backup-content>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup disableIfNoEncryptionCapabilities="true">
<exclude domain="root" path="." />
<exclude domain="file" path="." />
<exclude domain="database" path="." />
<exclude domain="sharedpref" path="." />
<exclude domain="external" path="." />
</cloud-backup>
<device-transfer>
<exclude domain="root" path="." />
<exclude domain="file" path="." />
<exclude domain="database" path="." />
<exclude domain="sharedpref" path="." />
<exclude domain="external" path="." />
</device-transfer>
</data-extraction-rules>
@@ -1,6 +1,6 @@
package brainwave.data.content
package net.opcapp.flash.data.content
import brainwave.core.model.HexagramContent
import net.opcapp.flash.core.model.HexagramContent
class FakeHexagramContentRepository(
override val contentVersion: String = "fixture-only-v1",
@@ -1,6 +1,6 @@
package brainwave.data.content
package net.opcapp.flash.data.content
import brainwave.core.model.HexagramContent
import net.opcapp.flash.core.model.HexagramContent
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
@@ -1,4 +1,4 @@
package brainwave.domain.casting
package net.opcapp.flash.domain.casting
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse