From d4573cacc141748c44bb6bbea7528bdd46afc3d5 Mon Sep 17 00:00:00 2001 From: A404M Date: Wed, 18 Dec 2024 03:28:02 +0330 Subject: initial commit --- app/.gitignore | 1 + app/build.gradle.kts | 60 ++++ app/proguard-rules.pro | 21 ++ app/release/app-release.apk | Bin 0 -> 692395 bytes app/release/baselineProfiles/0/app-release.dm | Bin 0 -> 3521 bytes app/release/baselineProfiles/1/app-release.dm | Bin 0 -> 3475 bytes app/release/output-metadata.json | 37 +++ .../a404m/calculator/ExampleInstrumentedTest.kt | 27 ++ app/src/main/AndroidManifest.xml | 28 ++ app/src/main/ic_launcher-playstore.png | Bin 0 -> 2688 bytes .../main/java/com/a404m/calculator/MainActivity.kt | 27 ++ .../main/java/com/a404m/calculator/MathHelper.kt | 309 +++++++++++++++++++++ .../com/a404m/calculator/model/CalcButtonModel.kt | 139 +++++++++ .../main/java/com/a404m/calculator/ui/page/Home.kt | 120 ++++++++ .../java/com/a404m/calculator/ui/theme/Color.kt | 11 + .../java/com/a404m/calculator/ui/theme/Theme.kt | 58 ++++ .../java/com/a404m/calculator/ui/theme/Type.kt | 34 +++ .../java/com/a404m/calculator/ui/util/Container.kt | 31 +++ .../main/res/drawable/ic_launcher_background.xml | 170 ++++++++++++ .../main/res/drawable/ic_launcher_foreground.xml | 16 ++ app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 558 bytes .../main/res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 1972 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 446 bytes .../main/res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1156 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 702 bytes .../main/res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 2746 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 944 bytes .../main/res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 4174 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 1266 bytes .../main/res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 5964 bytes app/src/main/res/values/colors.xml | 10 + app/src/main/res/values/ic_launcher_background.xml | 4 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/themes.xml | 5 + app/src/main/res/xml/backup_rules.xml | 13 + app/src/main/res/xml/data_extraction_rules.xml | 19 ++ .../java/com/a404m/calculator/ExampleUnitTest.kt | 20 ++ 39 files changed, 1173 insertions(+) create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/release/app-release.apk create mode 100644 app/release/baselineProfiles/0/app-release.dm create mode 100644 app/release/baselineProfiles/1/app-release.dm create mode 100644 app/release/output-metadata.json create mode 100644 app/src/androidTest/java/com/a404m/calculator/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/ic_launcher-playstore.png create mode 100644 app/src/main/java/com/a404m/calculator/MainActivity.kt create mode 100644 app/src/main/java/com/a404m/calculator/MathHelper.kt create mode 100644 app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt create mode 100644 app/src/main/java/com/a404m/calculator/ui/page/Home.kt create mode 100644 app/src/main/java/com/a404m/calculator/ui/theme/Color.kt create mode 100644 app/src/main/java/com/a404m/calculator/ui/theme/Theme.kt create mode 100644 app/src/main/java/com/a404m/calculator/ui/theme/Type.kt create mode 100644 app/src/main/java/com/a404m/calculator/ui/util/Container.kt create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/ic_launcher_background.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/test/java/com/a404m/calculator/ExampleUnitTest.kt (limited to 'app') diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..6848831 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) +} + +android { + namespace = "com.a404m.calculator" + compileSdk = 35 + + defaultConfig { + applicationId = "com.a404m.calculator" + minSdk = 21 + targetSdk = 35 + versionCode = 2 + versionName = "0.1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = true + isShrinkResources = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + } +} + +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) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/app/release/app-release.apk b/app/release/app-release.apk new file mode 100644 index 0000000..4763c61 Binary files /dev/null and b/app/release/app-release.apk differ diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm new file mode 100644 index 0000000..2df2b65 Binary files /dev/null and b/app/release/baselineProfiles/0/app-release.dm differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm new file mode 100644 index 0000000..829ee1a Binary files /dev/null and b/app/release/baselineProfiles/1/app-release.dm differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..4b3565e --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,37 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.a404m.calculator", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 2, + "versionName": "0.1.0", + "outputFile": "app-release.apk" + } + ], + "elementType": "File", + "baselineProfiles": [ + { + "minApi": 28, + "maxApi": 30, + "baselineProfiles": [ + "baselineProfiles/1/app-release.dm" + ] + }, + { + "minApi": 31, + "maxApi": 2147483647, + "baselineProfiles": [ + "baselineProfiles/0/app-release.dm" + ] + } + ], + "minSdkVersionForDexing": 21 +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/a404m/calculator/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/a404m/calculator/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..785fe9a --- /dev/null +++ b/app/src/androidTest/java/com/a404m/calculator/ExampleInstrumentedTest.kt @@ -0,0 +1,27 @@ +package com.a404m.calculator + +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( + "com.a404m.calculator", + appContext.packageName + ) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8d7bca4 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..2d94637 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/a404m/calculator/MainActivity.kt b/app/src/main/java/com/a404m/calculator/MainActivity.kt new file mode 100644 index 0000000..9a40e9b --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/MainActivity.kt @@ -0,0 +1,27 @@ +package com.a404m.calculator + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.a404m.calculator.ui.page.HomePage +import com.a404m.calculator.ui.theme.CalculatorTheme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + CalculatorTheme { + HomePage() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/a404m/calculator/MathHelper.kt b/app/src/main/java/com/a404m/calculator/MathHelper.kt new file mode 100644 index 0000000..fa6cfc7 --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/MathHelper.kt @@ -0,0 +1,309 @@ +package com.a404m.calculator + +import android.util.Log +import com.a404m.calculator.MathHelper.Operator.Kind +import java.nio.charset.UnsupportedCharsetException + +object MathHelper { + private val operatorStrings = arrayOf( + '+', + '-', + '×', + '÷', + '%', + ) + private val numberStrings = arrayOf( + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '.', + ) + + private val plus = Operator( + '+', + Kind.PREFIX, + { + Operand(it.first().value) + } + ) + private val minus = Operator( + '-', + Kind.PREFIX, + { + Operand(-it.first().value) + } + ) + private val add = Operator( + '+', + Kind.INFIX, + { + Operand(it.first().value+it.last().value) + } + ) + private val sub = Operator( + '-', + Kind.INFIX, + { + Operand(it.first().value-it.last().value) + } + ) + private val mul = Operator( + '×', + Kind.INFIX, + { + Operand(it.first().value*it.last().value) + } + ) + private val div = Operator( + '÷', + Kind.INFIX, + { + Operand(it.first().value/it.last().value) + } + ) + private val rem = Operator( + '%', + Kind.INFIX, + { + Operand(it.first().value%it.last().value) + } + ) + private val operatorOrders = arrayOf( + arrayOf( + plus, + minus, + ), + arrayOf( + mul, + div, + rem, + ), + arrayOf( + add, + sub, + ) + ) + private val operators = arrayOf( + plus, + minus, + add, + sub, + mul, + div, + rem, + ) + + fun eval(expression: String): String { + return try { + parse(lex(expression)).eval().toString() + } catch (e: Exception) { + e.message ?: "Exception" + } + } + + private fun parse(lexed: ArrayList): Operator { + for (order in operatorOrders) { + if (lexed.size == 1) { + break + } + for (i in lexed.indices) { + val item = lexed[i] + if (item is BasicOperator) { + var op = item.toOperator( + lexed, + i + ) + if (op in order) { + op = op.cloneWithoutOperands() + Log.d( + "A404M", + "parse: $op" + ) + lexed[i] = op + when (op.kind) { + Kind.NONE -> {} + Kind.INFIX -> { + op.operands.add(lexed.removeAt(i - 1)) + op.operands.add(lexed.removeAt(i)) + } + + Kind.PREFIX -> { + op.operands.add(lexed.removeAt(i + 1)) + } + + Kind.POSTFIX -> { + op.operands.add(lexed.removeAt(i - 1)) + } + } + break + } + } + } + } + return if (lexed.size != 1) { + throw UnsupportedOperationException("lexed.size = ${lexed.size}: $lexed") + } else if (lexed.first() is Operator) { + lexed.first() as Operator + } else if (lexed.first() is Operand) { + plus.cloneWithoutOperands().cloneWithOperator(arrayListOf(lexed.first())) + } else { + throw UnsupportedOperationException("unsupported type ${lexed.first().javaClass}") + } + } + + private fun lex(expression: String): ArrayList { + val items = arrayListOf() + + var i = 0 + while (i < expression.length) { + when (val c = expression[i]) { + in numberStrings -> { + val start = i + while (++i < expression.length && expression[i] in numberStrings); + items.add( + Operand( + value = expression.substring( + start, + i + ).toDouble() + ) + ) + --i + } + + in operatorStrings -> { + items.add( + BasicOperator(c) + ) + } + + else -> throw UnsupportedCharsetException(c.toString()) + } + ++i + } + + return items + } + + private class Operator( + val operator: Char, + val kind: Kind, + val operate: (operands:List)->Operand, + val operands: ArrayList = arrayListOf(), + ) { + fun cloneWithoutOperands():Operator{ + return Operator( + operator, + kind, + operate, + ) + } + + fun cloneWithOperator(operands: ArrayList): Operator { + return Operator( + operator, + kind, + operate, + operands, + ) + } + + fun eval(): Operand { + Log.d( + "A404M", + "eval: $operator $operands" + ) + if(operandSize() != operands.size){ + throw UnsupportedOperationException("Not enough operands") + } + + val evaledOperands = operands.map { + if(it is Operator){ + it.eval() + }else{ + it as Operand + } + } + + return operate(evaledOperands) + } + + fun operandSize(): Int { + return when (kind) { + Kind.INFIX -> 2 + Kind.PREFIX, Kind.POSTFIX -> 1 + Kind.NONE -> 0 + } + } + + override fun toString(): String { + return "$operator $kind $operands" + } + + enum class Kind { + NONE, + INFIX, + PREFIX, + POSTFIX, + } + } + + private class BasicOperator( + val operator: Char, + ) { + fun toOperator( + items: List, + i: Int + ): Operator { + val left = items.getOrElse( + i - 1, + { Any() }, + ) is Operand + + val right = items.getOrElse( + i + 1, + { Any() }, + ) is Operand + + val k:Kind + + if (left) { + if (right) { + k = Kind.INFIX + } else { + k = Kind.POSTFIX + } + } else if (right) { + k = Kind.PREFIX + } else { + k = Kind.NONE + } + + return operators.first { + it.operator == operator && it.kind == k + } + } + + override fun toString(): String { + return operator.toString(); + } + } + + private class Operand( + val value: Double, + ){ + override fun toString(): String { + if(value % 1 == 0.0){ + return "%.0f".format(value) + } + return value.toString() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt b/app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt new file mode 100644 index 0000000..435acef --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/model/CalcButtonModel.kt @@ -0,0 +1,139 @@ +package com.a404m.calculator.model + +import com.a404m.calculator.MathHelper + +data class CalcButtonModel( + val text: String, + val weight:Float = 1F, + val op: (expression: String) -> String, +) { + companion object { + val K_0 = CalcButtonModel( + text = "0", + op = { + it + '0' + } + ) + val K_1 = CalcButtonModel( + text = "1", + op = { + it + '1' + } + ) + val K_2 = CalcButtonModel( + text = "2", + op = { + it + '2' + } + ) + val K_3 = CalcButtonModel( + text = "3", + op = { + it + '3' + } + ) + val K_4 = CalcButtonModel( + text = "4", + op = { + it + '4' + } + ) + val K_5 = CalcButtonModel( + text = "5", + op = { + it + '5' + } + ) + val K_6 = CalcButtonModel( + text = "6", + op = { + it + '6' + } + ) + val K_7 = CalcButtonModel( + text = "7", + op = { + it + '7' + } + ) + val K_8 = CalcButtonModel( + text = "8", + op = { + it + '8' + } + ) + val K_9 = CalcButtonModel( + text = "9", + op = { + it + '9' + } + ) + val K_PLUS = CalcButtonModel( + text = "+", + op = { + it + '+' + } + ) + val K_MINUS = CalcButtonModel( + text = "-", + op = { + it + '-' + } + ) + val K_MULTIPLY = CalcButtonModel( + text = "×", + op = { + it + '×' + } + ) + val K_DIVISION = CalcButtonModel( + text = "÷", + op = { + it + '÷' + } + ) + val K_REMINDER = CalcButtonModel( + text = "%", + op = { + it + '%' + } + ) + val K_EQUAL = CalcButtonModel( + text = "=", + op = { + MathHelper.eval(it) + } + ) + val K_DOT = CalcButtonModel( + text = ".", + op = { + it + '.' + } + ) + val K_C = CalcButtonModel( + text = "C", + op = { + "" + } + ) + val K_BACKSPACE = CalcButtonModel( + text = "⌫", + op = { + if (it.length <= 1) { + "" + } else { + it.substring( + 0, + it.length - 1, + ) + } + } + ) + val K_SWITCH_MODE = CalcButtonModel( + text = "\u2026", + op = { + it + } + ) + } +} diff --git a/app/src/main/java/com/a404m/calculator/ui/page/Home.kt b/app/src/main/java/com/a404m/calculator/ui/page/Home.kt new file mode 100644 index 0000000..ba9a21e --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/ui/page/Home.kt @@ -0,0 +1,120 @@ +package com.a404m.calculator.ui.page + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.a404m.calculator.model.CalcButtonModel +import com.a404m.calculator.ui.theme.CalculatorTheme +import com.a404m.calculator.ui.util.CalcGrid + +@Composable +fun HomePage(modifier: Modifier = Modifier) { + var expression by rememberSaveable { + mutableStateOf("") + } + Scaffold( + modifier = modifier, + ) { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize(), + verticalArrangement = Arrangement.Bottom, + ) { + Text( + modifier = Modifier.padding(8.dp).fillMaxWidth(), + text = expression, + style = MaterialTheme.typography.titleLarge, + textAlign = TextAlign.Right, + fontSize = 35.sp, + ) + HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp)) + CalcGrid( + modifier = Modifier + .padding(4.dp), + columns = 4, + items = arrayOf( + CalcButtonModel.K_C, + CalcButtonModel.K_BACKSPACE, + CalcButtonModel.K_REMINDER, + CalcButtonModel.K_DIVISION, + CalcButtonModel.K_7, + CalcButtonModel.K_8, + CalcButtonModel.K_9, + CalcButtonModel.K_MULTIPLY, + CalcButtonModel.K_4, + CalcButtonModel.K_5, + CalcButtonModel.K_6, + CalcButtonModel.K_MINUS, + CalcButtonModel.K_1, + CalcButtonModel.K_2, + CalcButtonModel.K_3, + CalcButtonModel.K_PLUS, + CalcButtonModel.K_0, + CalcButtonModel.K_DOT, + CalcButtonModel.K_SWITCH_MODE, + CalcButtonModel.K_EQUAL, + ), + ) { + val backColor = MaterialTheme.colorScheme.primary + Box( + modifier = Modifier + .padding(4.dp) + .height(70.dp) + .weight(it.weight) + .clip( + shape = RoundedCornerShape(10.dp), + ) + .background( + color = backColor, + shape = RoundedCornerShape(10.dp), + ) + .clickable { + expression = it.op(expression) + }, + contentAlignment = Alignment.Center, + ) { + Text( + text = it.text, + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.contentColorFor(backColor) + ) + } + } + } + } +} + +@Preview +@Composable +private fun HomePagePreview() { + CalculatorTheme { + HomePage( + modifier = Modifier.fillMaxSize(), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/a404m/calculator/ui/theme/Color.kt b/app/src/main/java/com/a404m/calculator/ui/theme/Color.kt new file mode 100644 index 0000000..6a4fd22 --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.a404m.calculator.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) \ No newline at end of file diff --git a/app/src/main/java/com/a404m/calculator/ui/theme/Theme.kt b/app/src/main/java/com/a404m/calculator/ui/theme/Theme.kt new file mode 100644 index 0000000..20d6120 --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package com.a404m.calculator.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.ui.platform.LocalContext + +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 CalculatorTheme( + 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 + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/a404m/calculator/ui/theme/Type.kt b/app/src/main/java/com/a404m/calculator/ui/theme/Type.kt new file mode 100644 index 0000000..42ec494 --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.a404m.calculator.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 + ) + */ +) \ No newline at end of file diff --git a/app/src/main/java/com/a404m/calculator/ui/util/Container.kt b/app/src/main/java/com/a404m/calculator/ui/util/Container.kt new file mode 100644 index 0000000..ba27afe --- /dev/null +++ b/app/src/main/java/com/a404m/calculator/ui/util/Container.kt @@ -0,0 +1,31 @@ +package com.a404m.calculator.ui.util + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun CalcGrid( + modifier: Modifier = Modifier, + columns: Int, + items: Array, + builder: @Composable (T) -> Unit, +) { + Column( + modifier = modifier, + ) { + var index = 0 + while (index != items.size) { + Row { + for (j in 0 until columns) { + if(index == items.size){ + break + } + builder(items[index]) + ++index + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..c5eec94 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..ee62035 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..6452b49 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..8f60d53 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..3cd3848 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..9cb323b Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..6941df4 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..eb514e1 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..749d7c1 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..e404ad0 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..f7f408b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..3376515 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #35185A + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..665ca8d --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Calculator + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..f244678 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +