init: repo

This commit is contained in:
2024-07-04 15:27:19 +08:00
commit a90302f7ba
293 changed files with 18994 additions and 0 deletions

1
Clock/app/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,90 @@
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
kotlin("kapt") version "1.9.0"
}
android {
namespace = "top.rrricardo.clock"
compileSdk = 34
defaultConfig {
applicationId = "top.rrricardo.clock"
minSdk = 28
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
// ViewModel
implementation(libs.androidx.lifecycle.viewmodel.ktx)
// ViewModel utilities for Compose
implementation(libs.androidx.lifecycle.viewmodel.compose)
// LiveData
implementation(libs.androidx.lifecycle.livedata.ktx)
// Lifecycles only (without ViewModel or LiveData)
implementation(libs.androidx.lifecycle.runtime.ktx.v270)
// Lifecycle utilities for Compose
implementation(libs.androidx.lifecycle.runtime.compose)
// Saved state module for ViewModel
implementation(libs.androidx.lifecycle.viewmodel.savedstate)
// Annotation processor
kapt(libs.androidx.lifecycle.compiler)
// Navigation Controller
implementation(libs.androidx.navigation.compose)
}

21
Clock/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 top.rrricardo.clock
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("top.rrricardo.clock", 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"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Clock"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.Clock">
<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,43 @@
package top.rrricardo.clock
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import top.rrricardo.clock.ui.ClockApp
import top.rrricardo.clock.ui.ZoneManager
import top.rrricardo.clock.ui.theme.ClockTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainApplication()
}
}
}
@Composable
fun MainApplication() {
val navigationController = rememberNavController()
ClockTheme {
NavHost(navController = navigationController, startDestination = "clock") {
composable(NavigationRouters.CLOCK) {
ClockApp(navigationController)
}
composable(NavigationRouters.ZONE_MANAGER) {
ZoneManager(navigationController)
}
}
}
}
object NavigationRouters {
const val CLOCK = "clock"
const val ZONE_MANAGER = "zoneManager"
}

View File

@@ -0,0 +1,42 @@
package top.rrricardo.clock.controller
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.util.Log
import android.view.animation.LinearInterpolator
import androidx.core.animation.addListener
import androidx.core.animation.doOnEnd
import top.rrricardo.clock.model.ClockViewModel
class AnimatorController(private val clockViewModel: ClockViewModel) {
private var valueAnimator : ValueAnimator? = null
fun start(start: Int, end: Int) {
if (valueAnimator == null) {
val animator = ValueAnimator.ofInt(start, end)
animator.interpolator = LinearInterpolator()
animator.addUpdateListener {
clockViewModel.second = it.animatedValue as Int
}
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
Log.i("Clock", "Animator stopped.")
clockViewModel.restore()
}
})
valueAnimator = animator
} else {
valueAnimator?.setIntValues(start, end)
}
valueAnimator?.duration = (end - start) * 1000L
valueAnimator?.start()
}
}

View File

@@ -0,0 +1,100 @@
package top.rrricardo.clock.model
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import top.rrricardo.clock.controller.AnimatorController
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
class ClockViewModel : ViewModel() {
val zones = listOf("Asia/Shanghai", "Asia/Tokyo", "America/Los_Angeles", "Europe/London")
/**
* 时钟当前的年份
*/
var year : Int by mutableIntStateOf(0)
/**
* 时钟当前的月份
*/
var month : Int by mutableIntStateOf(0)
/**
* 时钟当前的日期
*/
var day : Int by mutableIntStateOf(0)
/**
* 时钟当前的小时数
*/
var hour : Int by mutableIntStateOf(0)
/**
* 时钟当前的分钟数
*/
var minute : Int by mutableIntStateOf(0)
/**
* 时钟当前的秒数
*/
var second : Int by mutableIntStateOf(0)
/**
* 当前是否为数字时钟
*/
var isDigit : Boolean by mutableStateOf(false)
private val animatorController = AnimatorController(this)
private var currentZone : String = zones[0]
fun restore(zoneString: String = currentZone) {
Log.i("Clock", "Restoring of $zoneString is triggered.")
currentZone = zoneString
val now = ZonedDateTime.now(ZoneId.of(zoneString))
year = now.year
month = now.month.value
day = now.dayOfMonth
hour = now.hour
minute = now.minute
second = now.second
animatorController.start(second, 60)
}
fun formatDigitString(): String {
val hourString = if (hour >= 10) {
"$hour"
} else {
"0$hour"
}
val minuteString = if (minute >= 10) {
"$minute"
} else {
"0$minute"
}
val secondString = if (second >= 10) {
"$second"
} else {
"0$second"
}
return "${hourString}:${minuteString}:${secondString}"
}
fun changeMode() {
isDigit = !isDigit
}
}

View File

@@ -0,0 +1,255 @@
package top.rrricardo.clock.ui
import android.util.Log
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.foundation.layout.size
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import top.rrricardo.clock.NavigationRouters
import top.rrricardo.clock.model.ClockViewModel
import kotlin.math.cos
import kotlin.math.sin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ClockApp(navigationController : NavController = rememberNavController()) {
val viewModel : ClockViewModel = viewModel()
Scaffold(
Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = {
Text(text = "世界时钟")
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary
)
)
},
floatingActionButton = {
FloatingActionButton(onClick = {
navigationController.navigate(NavigationRouters.ZONE_MANAGER)
}) {
Icon(Icons.Default.Settings, contentDescription = "Manager the added zones")
}
}
) { innerPadding ->
Column(
Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(top = 30.dp)
) {
Clocks(viewModel = viewModel)
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun Clocks(viewModel: ClockViewModel) {
val pageStates = rememberPagerState {
viewModel.zones.size
}
val interactionSource = remember {
MutableInteractionSource()
}
LaunchedEffect(key1 = pageStates.currentPage) {
Log.i("Clock", "Current page is ${pageStates.currentPage}.")
viewModel.restore(viewModel.zones[pageStates.currentPage])
}
HorizontalPager(state = pageStates) { pageState ->
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top
) {
Box(modifier = Modifier
.height(250.dp)
.clickable(
interactionSource = interactionSource,
indication = null
) {
viewModel.changeMode()
}
) {
Crossfade(
targetState = viewModel.isDigit,
label = "ClockOrDigit",
animationSpec = tween(500)
) {
if (it) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 60.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = viewModel.formatDigitString(),
fontSize = 28.sp
)
}
} else {
Clock(viewModel)
}
}
}
Text(
text = viewModel.zones[pageState],
modifier = Modifier.padding(vertical = 10.dp),
color = Color.LightGray,
fontSize = 18.sp
)
Text(
text = "${viewModel.year}${viewModel.month}${viewModel.day}",
fontSize = 24.sp
)
}
}
}
@Composable
private fun Clock(viewModel: ClockViewModel) {
val size = 240.dp
Box(
Modifier
.fillMaxWidth()
.height(250.dp),
contentAlignment = Alignment.Center
) {
Canvas(modifier = Modifier.size(size)) {
val radius = size.toPx() / 2
val hourLength = radius * 0.1
val minuteLength = radius * 0.05
for (i in 0..11) {
val hourAngel = i * Math.PI * 2 / 12
// 小时刻度
drawLine(
color = Color.DarkGray,
start = Offset(
(radius + (radius) * sin(hourAngel)).toFloat(),
(radius - radius * cos(hourAngel)).toFloat()
),
end = Offset(
(radius + (radius - hourLength) * sin(hourAngel)).toFloat(),
(radius - (radius - hourLength) * cos(hourAngel)).toFloat()
),
strokeWidth = 2.dp.toPx()
)
// 分钟刻度
for (j in 1..4) {
val minuteAngel = (i * 5 + j) * Math.PI * 2 / 60
drawLine(
color = Color.LightGray,
start = Offset(
(radius + radius * sin(minuteAngel)).toFloat(),
(radius - radius * cos(minuteAngel)).toFloat()
),
end = Offset(
(radius + (radius - minuteLength) * sin(minuteAngel)).toFloat(),
(radius - (radius - minuteLength) * cos(minuteAngel)).toFloat()
),
strokeWidth = 1.dp.toPx()
)
}
}
// 秒针
val secondPointerAngel = viewModel.second * Math.PI * 2 / 60
val secondPointerLength = radius * 0.95
drawLine(
color = Color.Red,
start = Offset(
(radius + secondPointerLength * sin(secondPointerAngel)).toFloat(),
(radius - secondPointerLength * cos(secondPointerAngel)).toFloat()
),
end = Offset(radius, radius),
strokeWidth = 2.dp.toPx()
)
// 分针
val minutePointerAngel = viewModel.minute * Math.PI * 2 / 60
val minutePointerLength = radius * 0.8
drawLine(
color = Color.Black,
start = Offset(
(radius + minutePointerLength * sin(minutePointerAngel)).toFloat(),
(radius - minutePointerLength * cos(minutePointerAngel)).toFloat()
),
end = Offset(radius, radius),
strokeWidth = 4.dp.toPx()
)
// 时针
val hourPointerAngel =
(viewModel.hour.toFloat() % 12 + viewModel.minute.toFloat() / 60) * Math.PI * 2 / 12
val hourPointerLength = radius * 0.6
drawLine(
color = Color.Red,
start = Offset(
(radius + hourPointerLength * sin(hourPointerAngel)).toFloat(),
(radius - hourPointerLength * cos(hourPointerAngel)).toFloat()
),
end = Offset(radius, radius),
strokeWidth = 8.dp.toPx()
)
}
}
}
@Preview
@Composable
fun ClockAppPreview() {
ClockApp()
}

View File

@@ -0,0 +1,98 @@
package top.rrricardo.clock.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import top.rrricardo.clock.NavigationRouters
import top.rrricardo.clock.model.ClockViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ZoneManager(navigationController: NavController = rememberNavController()) {
val viewModel: ClockViewModel = viewModel()
Scaffold(
Modifier.fillMaxSize(),
topBar = {
CenterAlignedTopAppBar(
title = {
Text(text = "添加或删除时区")
}, colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary
),
navigationIcon = {
IconButton(onClick = {
navigationController.popBackStack()
}) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back to main application"
)
}
}
)
}
) { padding ->
Column(
Modifier
.padding(padding)
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
viewModel.zones.forEach {
ZoneItem(zone = it)
}
OutlinedButton(modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 10.dp),
onClick = {}) {
Text(text = "添加时区",
color = MaterialTheme.colorScheme.primary,
fontSize = 20.sp)
}
}
}
}
@Composable
fun ZoneItem(zone: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 3.dp, horizontal = 5.dp),
horizontalArrangement = Arrangement.Center
) {
Text(text = zone, fontSize = 20.sp)
}
}
@Preview
@Composable
fun ZoneManagerPreview() {
ZoneManager()
}

View File

@@ -0,0 +1,11 @@
package top.rrricardo.clock.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@@ -0,0 +1,70 @@
package top.rrricardo.clock.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun ClockTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@@ -0,0 +1,34 @@
package top.rrricardo.clock.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

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,3 @@
<resources>
<string name="app_name">世界时钟</string>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Clock" 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 that 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 top.rrricardo.clock
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)
}
}