Atlas Device SDK (之前叫做 Realm) https://www.mongodb.com/docs/atlas/device-sdks/ Data layer 管理資料的一個選擇,有提供各個平台的 SDK ## 貨幣轉換 app ```kotlin= package com.example.currencyconverterapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.statusBarsPadding 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.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.currencyconverterapp.ui.theme.CurrencyConverterAppTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { CurrencyConverterAppTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), //color = MaterialTheme.colorScheme.background ) { CurrencyConverterLayout() } } } } } @Composable fun CurrencyConverterLayout() { var amountInput by remember { mutableStateOf("") } val amount = amountInput.toDoubleOrNull() ?: 0.0 val exchangeRate = 0.21 val targetAmount = amount * exchangeRate Column( modifier = Modifier .statusBarsPadding() .padding(horizontal = 40.dp) .verticalScroll(rememberScrollState()) .safeDrawingPadding(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "Currency Converter", modifier = Modifier .padding(bottom = 16.dp, top = 40.dp) .align(alignment = Alignment.Start) ) TextField( value = amountInput, onValueChange = { amountInput = it}, label = { Text("JPY") }, modifier = Modifier .padding(bottom = 32.dp) .fillMaxWidth() ) Text( text = "TWD: $targetAmount", style = MaterialTheme.typography.displayMedium ) } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { CurrencyConverterAppTheme { CurrencyConverterLayout() } } ``` 增加可以修改匯率與調整使用體驗 ```kotlin= package com.example.currencyconverterapp import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.currencyconverterapp.ui.theme.CurrencyConverterAppTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { CurrencyConverterAppTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), //color = MaterialTheme.colorScheme.background ) { CurrencyConverterLayout() } } } } } @Composable fun CurrencyConverterLayout() { var amountInput by remember { mutableStateOf("") } var exchangeRateInput by remember { mutableStateOf("0.21") } val amount = amountInput.toDoubleOrNull() ?: 0.0 val exchangeRate = exchangeRateInput.toDoubleOrNull() ?: 0.0 val targetAmount = amount * exchangeRate Column( modifier = Modifier .statusBarsPadding() .padding(horizontal = 40.dp) .verticalScroll(rememberScrollState()) .safeDrawingPadding(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = stringResource(R.string.currency_converter), modifier = Modifier .padding(bottom = 16.dp, top = 40.dp) .align(alignment = Alignment.Start) ) TextField( value = amountInput, onValueChange = { amountInput = it}, label = { Text(stringResource(R.string.jpy)) }, singleLine = true, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, imeAction = ImeAction.Next ), modifier = Modifier .padding(bottom = 32.dp) .fillMaxWidth() ) TextField( value = exchangeRateInput, onValueChange = { exchangeRateInput = it}, label = { Text(stringResource(R.string.jpy_to_twd)) }, singleLine = true, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, imeAction = ImeAction.Done ), modifier = Modifier .padding(bottom = 32.dp) .fillMaxWidth() ) Text( text = stringResource(R.string.twd_amount, targetAmount), style = MaterialTheme.typography.displayMedium ) } } @Preview(showBackground = true) @Composable fun CurrencyConverterLayoutPreview() { CurrencyConverterAppTheme { CurrencyConverterLayout() } } ``` ## 匯率換算 API * Free Currency Exchange Rates API * https://github.com/fawazahmed0/exchange-api * `https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/jpy.json` ## 課後練習 透過 Free Currency Exchage Rates API 取得 JPY to TWD 的預設匯率