From 853548225c4e2541841e7602d7fff2ed122ab455 Mon Sep 17 00:00:00 2001 From: "Alberto Duarte (PWC)" Date: Mon, 9 Oct 2023 16:54:46 +0100 Subject: Initial commit --- .gitignore | 15 + .idea/.gitignore | 3 + .idea/compiler.xml | 6 + .idea/gradle.xml | 20 ++ .idea/inspectionProfiles/Project_Default.xml | 41 +++ .idea/kotlinc.xml | 6 + .idea/misc.xml | 10 + app/.gitignore | 1 + app/build.gradle | 85 ++++++ app/proguard-rules.pro | 21 ++ .../pwc/calculatorv2/ExampleInstrumentedTest.kt | 24 ++ app/src/main/AndroidManifest.xml | 28 ++ app/src/main/java/com/pwc/calculatorv2/DB.kt | 13 + .../com/pwc/calculatorv2/data/dao/FunctionDao.kt | 51 ++++ .../data/datasources/FunctionListDataSource.kt | 70 +++++ .../pwc/calculatorv2/data/models/FunctionDTO.kt | 28 ++ .../data/repositories/FunctionListRepository.kt | 121 ++++++++ .../data/responses/FunctionListResponse.kt | 8 + .../data/viewmodels/CalculatorViewModel.kt | 316 +++++++++++++++++++++ .../presentation/activities/MainActivity.kt | 31 ++ .../presentation/navigation/AppScreens.kt | 7 + .../presentation/screens/BottomNavScreen.kt | 60 ++++ .../presentation/screens/CalculatorScreen.kt | 276 ++++++++++++++++++ .../presentation/screens/FunctionListScreen.kt | 72 +++++ .../presentation/screens/WipeScreen.kt | 31 ++ .../calculatorv2/presentation/ui/theme/Color.kt | 11 + .../calculatorv2/presentation/ui/theme/Theme.kt | 70 +++++ .../pwc/calculatorv2/presentation/ui/theme/Type.kt | 34 +++ .../presentation/ui/widgets/BottomNav.kt | 32 +++ .../res/drawable-v24/ic_launcher_foreground.xml | 30 ++ .../main/res/drawable/ic_launcher_background.xml | 170 +++++++++++ app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 6 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../main/res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../main/res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../main/res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes app/src/main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../main/res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../main/res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes app/src/main/res/values/colors.xml | 10 + 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/pwc/calculatorv2/ExampleUnitTest.kt | 17 ++ build.gradle | 6 + gradle.properties | 23 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 ++++++++++++ gradlew.bat | 89 ++++++ settings.gradle | 16 ++ 56 files changed, 2095 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/misc.xml create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/pwc/calculatorv2/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/com/pwc/calculatorv2/DB.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/data/dao/FunctionDao.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/data/datasources/FunctionListDataSource.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/data/models/FunctionDTO.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/data/repositories/FunctionListRepository.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/data/responses/FunctionListResponse.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/data/viewmodels/CalculatorViewModel.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/activities/MainActivity.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/navigation/AppScreens.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/screens/BottomNavScreen.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/screens/CalculatorScreen.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/screens/FunctionListScreen.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/screens/WipeScreen.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Color.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Theme.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Type.kt create mode 100644 app/src/main/java/com/pwc/calculatorv2/presentation/ui/widgets/BottomNav.kt create mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_launcher_background.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/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/pwc/calculatorv2/ExampleUnitTest.kt create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..eaf91e2 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..8fabff5 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..02a60b0 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..44ca2d9 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,41 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..6513be7 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0ad17cb --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file 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 b/app/build.gradle new file mode 100644 index 0000000..b4889d8 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,85 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id 'kotlin-kapt' +} + +android { + namespace 'com.pwc.calculatorv2' + compileSdk 33 + + defaultConfig { + applicationId "com.pwc.calculatorv2" + minSdk 24 + targetSdk 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + viewBinding { + enabled = true + } + } + + buildTypes { + release { + minifyEnabled 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.3.2' + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.10.1' + implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.activity:activity-compose:1.7.2' + implementation platform('androidx.compose:compose-bom:2022.10.00') + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-graphics' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.material:material:1.4.3' + implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' + implementation "androidx.fragment:fragment-ktx:1.5.7" + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'com.google.android.material:material:1.9.0' + implementation 'androidx.navigation:navigation-compose:2.5.3' + + implementation 'androidx.compose.runtime:runtime-livedata:1.0.7' + + implementation("androidx.room:room-runtime:2.5.0") + annotationProcessor("androidx.room:room-compiler:2.5.0") + kapt("androidx.room:room-compiler:2.5.0") + + + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00') + androidTestImplementation 'androidx.compose.ui:ui-test-junit4' + debugImplementation 'androidx.compose.ui:ui-tooling' + debugImplementation 'androidx.compose.ui: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/src/androidTest/java/com/pwc/calculatorv2/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/pwc/calculatorv2/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..793eb0d --- /dev/null +++ b/app/src/androidTest/java/com/pwc/calculatorv2/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.pwc.calculatorv2 + +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.pwc.calculatorv2", 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..733dbab --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/pwc/calculatorv2/DB.kt b/app/src/main/java/com/pwc/calculatorv2/DB.kt new file mode 100644 index 0000000..ca4af18 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/DB.kt @@ -0,0 +1,13 @@ +package com.pwc.calculatorv2 + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.pwc.calculatorv2.data.dao.FunctionDao +import com.pwc.calculatorv2.data.models.Function + +//?? + +@Database(entities = [Function::class], version = 1, exportSchema = false) +abstract class AppDatabase : RoomDatabase() { + abstract fun functionDao(): FunctionDao +} diff --git a/app/src/main/java/com/pwc/calculatorv2/data/dao/FunctionDao.kt b/app/src/main/java/com/pwc/calculatorv2/data/dao/FunctionDao.kt new file mode 100644 index 0000000..341f961 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/data/dao/FunctionDao.kt @@ -0,0 +1,51 @@ +package com.pwc.calculatorv2.data.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query +import com.pwc.calculatorv2.data.models.Function + +@Dao +interface FunctionDao { + @Query("SELECT * FROM Function") + fun getAll(): List + + @Query("SELECT * FROM Function WHERE function LIKE '%+%'") + fun getPlus(): List + + @Query("SELECT * FROM Function WHERE function LIKE '%-%'") + fun getMinus(): List + + @Query("SELECT * FROM Function WHERE function LIKE '%*%'") + fun getPer(): List + + @Query("SELECT * FROM Function WHERE function LIKE '%/%'") + fun getDivide(): List + + @Insert + fun insertAll(functionList: List) + + @Insert + fun insert(function: Function) + + @Delete + fun delete(function: Function) + + @Query("DELETE FROM Function") + fun wipe() + + @Query("DELETE FROM Function WHERE function LIKE '%+%'") + fun wipePlus() + + @Query("DELETE FROM Function WHERE function LIKE '%-%'") + fun wipeMinus() + + @Query("DELETE FROM Function WHERE function LIKE '%*%'") + fun wipePer() + + @Query("DELETE FROM Function WHERE function LIKE '%/%'") + fun wipeDivided() + + +} \ No newline at end of file diff --git a/app/src/main/java/com/pwc/calculatorv2/data/datasources/FunctionListDataSource.kt b/app/src/main/java/com/pwc/calculatorv2/data/datasources/FunctionListDataSource.kt new file mode 100644 index 0000000..aaee7b3 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/data/datasources/FunctionListDataSource.kt @@ -0,0 +1,70 @@ +package com.pwc.calculatorv2.data.datasources + +import android.content.Context +import androidx.room.Room +import com.pwc.calculatorv2.AppDatabase +import com.pwc.calculatorv2.data.models.Function + +class FunctionListDataSource (context: Context){ + private val db: AppDatabase = Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "calculator" + ).build() + + fun getFunctionList(): List { + return db.functionDao().getAll() + } + + fun getPlusFunctionList(): List { + return db.functionDao().getPlus() + } + + fun getMinusFunctionList(): List { + return db.functionDao().getMinus() + } + + fun getPerFunctionList(): List { + return db.functionDao().getPer() + } + + fun getDividedFunctionList(): List { + return db.functionDao().getDivide() + } + + fun addFunction(function: Function): List { + db.functionDao().insert(function) + return db.functionDao().getAll() + } + + fun removeFunction(function: Function): List { + db.functionDao().delete(function) + return db.functionDao().getAll() + } + + fun wipeFunctionList(): List { + db.functionDao().wipe() + return db.functionDao().getAll() + } + + fun wipePlusFunctionList(): List { + db.functionDao().wipePlus() + return db.functionDao().getAll() + } + + fun wipeMinusFunctionList(): List { + db.functionDao().wipeMinus() + return db.functionDao().getAll() + } + + fun wipePerFunctionList(): List { + db.functionDao().wipePer() + return db.functionDao().getAll() + } + + fun wipeDividedFunctionList(): List { + db.functionDao().wipeDivided() + return db.functionDao().getAll() + } + +} diff --git a/app/src/main/java/com/pwc/calculatorv2/data/models/FunctionDTO.kt b/app/src/main/java/com/pwc/calculatorv2/data/models/FunctionDTO.kt new file mode 100644 index 0000000..b8b73a2 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/data/models/FunctionDTO.kt @@ -0,0 +1,28 @@ +package com.pwc.calculatorv2.data.models + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import java.util.Date + +@Entity +@TypeConverters(DateConverter::class) +data class Function( + @PrimaryKey (autoGenerate = true) var id: Int = 0, + @ColumnInfo var function: String = "", + @ColumnInfo var date: Date = Date() +) + +class DateConverter { + @TypeConverter + fun fromDate(date: Date?): Long? { + return date?.time + } + + @TypeConverter + fun toDate(timestamp: Long?): Date? { + return timestamp?.let { Date(it) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pwc/calculatorv2/data/repositories/FunctionListRepository.kt b/app/src/main/java/com/pwc/calculatorv2/data/repositories/FunctionListRepository.kt new file mode 100644 index 0000000..d3b0682 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/data/repositories/FunctionListRepository.kt @@ -0,0 +1,121 @@ +package com.pwc.calculatorv2.data.repositories + +import android.content.Context +import com.pwc.calculatorv2.data.datasources.FunctionListDataSource +import com.pwc.calculatorv2.data.responses.FunctionListResponse +import kotlinx.coroutines.flow.flow +import com.pwc.calculatorv2.data.models.Function +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn + +class FunctionListRepository (context: Context) { + private val dataSource = FunctionListDataSource(context) + + fun getFunctionList() = flow { + try { + val functionList = dataSource.getFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn(Dispatchers.IO) + + fun getPlusFunctionList() = flow { + try { + val functionList = dataSource.getPlusFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn(Dispatchers.IO) + + fun getMinusFunctionList() = flow { + try { + val functionList = dataSource.getMinusFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn(Dispatchers.IO) + + fun getPerFunctionList() = flow { + try { + val functionList = dataSource.getPerFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn(Dispatchers.IO) + + fun getDividedFunctionList() = flow { + try { + val functionList = dataSource.getDividedFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn(Dispatchers.IO) + + fun addFunction(function: Function) = flow { + try { + val functionList = dataSource.addFunction(function) + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn((Dispatchers.IO)) + + fun removeFunction(function: Function) = flow { + try { + val functionList = dataSource.removeFunction(function) + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn((Dispatchers.IO)) + + fun wipeFunctionList() = flow { + try { + val functionList = dataSource.wipeFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn((Dispatchers.IO)) + + fun wipePlusFunctionList() = flow { + try { + val functionList = dataSource.wipePlusFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn((Dispatchers.IO)) + + fun wipeMinusFunctionList() = flow { + try { + val functionList = dataSource.wipeMinusFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn((Dispatchers.IO)) + + fun wipePerFunctionList() = flow { + try { + val functionList = dataSource.wipePerFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn((Dispatchers.IO)) + + fun wipeDividedFunctionList() = flow { + try { + val functionList = dataSource.wipeDividedFunctionList() + emit(FunctionListResponse.Success( functionList )) + } catch (e: Exception) { + emit(FunctionListResponse.Error(e.message ?: "Error")) + } + }.flowOn((Dispatchers.IO)) +} \ No newline at end of file diff --git a/app/src/main/java/com/pwc/calculatorv2/data/responses/FunctionListResponse.kt b/app/src/main/java/com/pwc/calculatorv2/data/responses/FunctionListResponse.kt new file mode 100644 index 0000000..b2a75ca --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/data/responses/FunctionListResponse.kt @@ -0,0 +1,8 @@ +package com.pwc.calculatorv2.data.responses + +import com.pwc.calculatorv2.data.models.Function + +sealed class FunctionListResponse { + data class Success(val functionList: List) : FunctionListResponse() + data class Error(val error: String): FunctionListResponse() +} diff --git a/app/src/main/java/com/pwc/calculatorv2/data/viewmodels/CalculatorViewModel.kt b/app/src/main/java/com/pwc/calculatorv2/data/viewmodels/CalculatorViewModel.kt new file mode 100644 index 0000000..bbb270a --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/data/viewmodels/CalculatorViewModel.kt @@ -0,0 +1,316 @@ +package com.pwc.calculatorv2.data.viewmodels + +import android.content.Context +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pwc.calculatorv2.data.repositories.FunctionListRepository +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import com.pwc.calculatorv2.data.models.Function +import com.pwc.calculatorv2.data.responses.FunctionListResponse +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +class CalculatorViewModel(context: Context, private val repository: FunctionListRepository = FunctionListRepository(context)) : ViewModel() { + private val mutableState = MutableStateFlow(CalculatorViewState()) + val calculatorViewState: StateFlow = mutableState + val index = mutableStateOf(-1) + + + fun getFunctionList() { + mutableState.value = mutableState.value.copy(isLoading = true) + repository.getFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + index.value = mutableState.value.functionList?.lastIndex ?: -1 + } + .launchIn(viewModelScope) + } + + fun getPlusFunctionList() { + mutableState.value = mutableState.value.copy(isLoading = true) + repository.getPlusFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun getMinusFunctionList() { + mutableState.value = mutableState.value.copy(isLoading = true) + repository.getMinusFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun getPerFunctionList() { + mutableState.value = mutableState.value.copy(isLoading = true) + repository.getPerFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun getDivideFunctionList() { + mutableState.value = mutableState.value.copy(isLoading = true) + repository.getDividedFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + private fun addFunction(function: Function) { + repository.addFunction(function) + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + index.value = mutableState.value.functionList?.lastIndex ?: -1 + } + .launchIn(viewModelScope) + } + + fun removeFunction(function: Function) { + repository.removeFunction(function) + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun wipeFunctionList() { + repository.wipeFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun wipePlusFunctionList() { + repository.wipePlusFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun wipeMinusFunctionList() { + repository.wipeMinusFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun wipePerFunctionList() { + repository.wipePerFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun wipeDividedFunctionList() { + repository.wipeDividedFunctionList() + .onEach { response -> + when (response) { + is FunctionListResponse.Success -> + mutableState.value = + mutableState.value.copy(functionList = response.functionList) + is FunctionListResponse.Error -> + mutableState.value = + mutableState.value.copy(error = response.error) + } + } + .launchIn(viewModelScope) + } + + fun showLast( + ):String { + var string = "" + val functionList = mutableState.value.functionList + if (!functionList.isNullOrEmpty()){ + if (index.value < 0 || index.value > functionList.lastIndex){ + index.value = functionList.lastIndex + } + string = functionList[index.value].function + index.value = index.value - 1 + return string + } + return string + } + + fun calculateResult( + expression: MutableState, + ) { + // Calculate the result of the expression + val expressionValue = expression.value + val result = try { + if(expressionValue != "" || expressionValue.contains(" ")){ + val numericResult = evaluateExpression(expressionValue) + numericResult.toString() + } else if(!expressionValue.contains(" ")){ + expressionValue + } + else + { + "" + } + } catch (e: Exception) { + "ErrorHello" + } + + addFunction(Function(function = expressionValue)) + expression.value = result + } + + private fun evaluateExpression(expression: String): Double { + var modifiedExpression = expression + + while (modifiedExpression.contains("(")) { + val openingIndex = modifiedExpression.lastIndexOf("(") + val closingIndex = modifiedExpression.indexOf(")", openingIndex) + val innerExpression = modifiedExpression.substring(openingIndex + 1, closingIndex) + val innerResult = evaluateExpression(innerExpression) + + // Replace the inner expression (within parentheses) with its result + modifiedExpression = + modifiedExpression.replace("($innerExpression)", innerResult.toString()) +// modifiedExpression = modifiedExpression.replaceRange( +// openingIndex -1, +// closingIndex, +// innerResult.toString() +// ) + + } + + val parts = modifiedExpression.split(" ").toMutableList() + var operator: String = "" + var result: Double = 0.0 + + + while (parts.contains("*")) { + multiply(parts) + } + while (parts.contains("/")) { + divide(parts) + } + + parts.forEachIndexed { _ , unit -> + if (unit.toDoubleOrNull() != null) { + val operand = unit.toDouble() + when (operator) { + "" -> result = operand + "+" -> result += operand + "-" -> result -= operand + } + } else { + operator = unit.trim() + } + } + + return result + } + + + private fun divide(parts: MutableList) { + val indexOfSymbol = parts.indexOf("/") + parts[indexOfSymbol] = (parts[indexOfSymbol - 1].toDouble() / parts[indexOfSymbol + 1].toDouble()).toString() + parts.removeAt(indexOfSymbol - 1) + parts.removeAt(indexOfSymbol) + } + + private fun multiply(parts: MutableList) { + val indexOfSymbol = parts.indexOf("*") + parts[indexOfSymbol] = (parts[indexOfSymbol - 1].toDouble() * parts[indexOfSymbol + 1].toDouble()).toString() + parts.removeAt(indexOfSymbol - 1) + parts.removeAt(indexOfSymbol) + } + +} +data class CalculatorViewState( + val functionList: List? = null, + val error: String? = null, + val isLoading: Boolean = false +) diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/activities/MainActivity.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/activities/MainActivity.kt new file mode 100644 index 0000000..59a91f0 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/activities/MainActivity.kt @@ -0,0 +1,31 @@ +package com.pwc.calculatorv2.presentation.activities + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.pwc.calculatorv2.presentation.screens.BottomNavScreen +import com.pwc.calculatorv2.presentation.ui.theme.CalculatorV2Theme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + CalculatorV2Theme { + // A surface container using the 'background' color from the theme + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + BottomNavScreen() + } + } + } + } +} diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/navigation/AppScreens.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/navigation/AppScreens.kt new file mode 100644 index 0000000..0418d2b --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/navigation/AppScreens.kt @@ -0,0 +1,7 @@ +package com.pwc.calculatorv2.presentation.navigation + +sealed class AppScreens(val route: String) { + object CalculatorScreen: AppScreens("calculator_screen") + object FunctionListScreen: AppScreens("function_list_screen") + object WipeScreen: AppScreens("wipe_screen") +} diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/screens/BottomNavScreen.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/BottomNavScreen.kt new file mode 100644 index 0000000..1f05318 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/BottomNavScreen.kt @@ -0,0 +1,60 @@ +package com.pwc.calculatorv2.presentation.screens + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.pwc.calculatorv2.data.viewmodels.CalculatorViewModel +import com.pwc.calculatorv2.presentation.navigation.AppScreens +import com.pwc.calculatorv2.presentation.ui.widgets.BottomNav +import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.LocalContext + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BottomNavScreen() { + val context = LocalContext.current + val bottomNavController = rememberNavController() + val viewModel by remember { mutableStateOf(CalculatorViewModel(context)) } + LaunchedEffect(Unit) { + viewModel.getFunctionList() + } + + Scaffold( + bottomBar = { + BottomNav(bottomNavController) + }, + ) { + Surface( + modifier = Modifier + .fillMaxSize() + .padding(bottom = it.calculateBottomPadding()), + color = MaterialTheme.colorScheme.background + ) { + NavHost( + navController = bottomNavController, + startDestination = AppScreens.CalculatorScreen.route + ) { + composable(AppScreens.CalculatorScreen.route) { + CalculatorScreen(viewModel) + } + composable(AppScreens.FunctionListScreen.route) { + FunctionListScreen(viewModel) + } + composable(AppScreens.WipeScreen.route) { + WipeScreen(viewModel) + } + } + } + } +} diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/screens/CalculatorScreen.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/CalculatorScreen.kt new file mode 100644 index 0000000..5cef814 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/CalculatorScreen.kt @@ -0,0 +1,276 @@ +package com.pwc.calculatorv2.presentation.screens + +import android.annotation.SuppressLint +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.layout.width +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.pwc.calculatorv2.data.viewmodels.CalculatorViewModel +import com.pwc.calculatorv2.data.viewmodels.CalculatorViewState + +@SuppressLint("StateFlowValueCalledInComposition") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CalculatorScreen( + viewModel: CalculatorViewModel, +) { + + val expression = remember { mutableStateOf("") } + + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center) + { + Column( + modifier = Modifier.width(width = 300.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + TextField( + value = expression.value, + onValueChange = { expression.value = it }, + modifier = Modifier.width(300.dp), + textStyle = TextStyle(fontSize = 24.sp, textAlign = TextAlign.End) + ) + + Column( + modifier = Modifier + .padding(16.dp) + .width(300.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 0.dp), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Button( + onClick = { viewModel.calculatorViewState.value?.let { expression.value = viewModel.showLast() } }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "^", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, " ( ") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "(", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, " ) ") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = ")", color = Color.Black) + } + Button( + onClick = { erase(expression) }, + modifier = Modifier.width(57.dp) + ) { + Text(text = "<", color = Color.Black) + } + } + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Button( + onClick = { appendToExpression(expression, "7") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "7", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, "8") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "8", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, "9") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "9", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, " + ") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "+", color = Color.Black) + } + } + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Button( + onClick = { appendToExpression(expression, "4") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "4", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, "5") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "5", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, "6") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "6", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, " - ") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "-", color = Color.Black) + } + } + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Button( + onClick = { appendToExpression(expression, "1") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "1", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, "2") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "2", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, "3") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "3", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, " * ") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "*", color = Color.Black) + } + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Button( + onClick = { appendDotToExpression(expression) }, + modifier = Modifier.width(70.dp) + ) { + Text(text = ".", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, "0") }, + modifier = Modifier.width(75.dp) + ) { + Text(text = "0", color = Color.Black) + } + Button( + onClick = { viewModel.calculateResult(expression) }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "=", color = Color.Black) + } + Button( + onClick = { appendToExpression(expression, " / ") }, + modifier = Modifier.width(70.dp) + ) { + Text(text = "/", color = Color.Black) + } + } + // Add more rows of buttons for other numbers and operators + + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { + Button( + onClick = { clearExpression(expression) }, + modifier = Modifier.width(145.dp) + ) { + Text(text = "Clear", color = Color.Black) + } + } + } + } +} +} + +fun erase(expression: MutableState) { + // erase last char from given expression + if (expression.value.isNotEmpty()){ + if(expression.value.last() == " ".last()){ + expression.value = expression.value.dropLast(3) + } else { + expression.value = expression.value.dropLast(1) + } + } +} + +fun appendToExpression(expression: MutableState, text: String) { + // Append the given text to the expression + if (text == " ) " || text == " ( ") { + if(expression.value.isNotEmpty()){ + if (expression.value.last() == ' ') { + expression.value = expression.value.substring(0, expression.value.length - 1) + text + } else { + expression.value += text + } + }else{ + expression.value += text + } + } else if (expression.value.isNotEmpty() && expression.value.last() == ' ' && text.toDoubleOrNull() == null) { + if (expression.value.substring(expression.value.length - 2) == ") " || expression.value.substring(expression.value.length - 2) == "( ") { + expression.value = expression.value.substring(0, expression.value.length - 1) + text + } else { + expression.value = expression.value.substring(0, expression.value.length - 3) + text + } + } else { + expression.value += text + if (expression.value.isNotEmpty() && expression.value.first() == ' ') { + expression.value = "" + } + } +} + + +fun appendDotToExpression(expression: MutableState) { + // Append the given text to the expression + val lastWord = expression.value.trim().split(" ").last() + if(lastWord.toDoubleOrNull() != null || lastWord.isEmpty()){ + if(!lastWord.contains(".")){ + expression.value += "." + } + } +} + + +fun clearExpression(expression: MutableState) { + // Clear the expression + expression.value = "" +} diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/screens/FunctionListScreen.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/FunctionListScreen.kt new file mode 100644 index 0000000..01f9ea8 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/FunctionListScreen.kt @@ -0,0 +1,72 @@ +package com.pwc.calculatorv2.presentation.screens + +import android.content.Context +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.lazy.LazyColumn +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.pwc.calculatorv2.data.viewmodels.CalculatorViewModel +import java.text.SimpleDateFormat + +@Composable +fun FunctionListScreen( + viewModel: CalculatorViewModel, +) { + + val calculatorViewState = viewModel.calculatorViewState.collectAsState() + val dateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm") + + + Column() { + Row() { + Button(onClick = { viewModel.getFunctionList() }) { + Text(text = "ALL") + } + Button(onClick = { viewModel.getPlusFunctionList() }) { + Text(text = "+") + } + Button(onClick = { viewModel.getMinusFunctionList() }) { + Text(text = "-") + } + Button(onClick = { viewModel.getPerFunctionList() }) { + Text(text = "*") + } + Button(onClick = { viewModel.getDivideFunctionList() }) { + Text(text = "/") + } + } + LazyColumn( + modifier = Modifier + .fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + calculatorViewState.value.functionList?.forEach() { response -> + item { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + ) { + Text(text = response.function) + Text(text = dateFormat.format(response.date)) + Button(onClick = {viewModel.removeFunction(response) }) { + Text(text = "X") + } + } + } + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/screens/WipeScreen.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/WipeScreen.kt new file mode 100644 index 0000000..45810ad --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/screens/WipeScreen.kt @@ -0,0 +1,31 @@ +package com.pwc.calculatorv2.presentation.screens + +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import com.pwc.calculatorv2.data.viewmodels.CalculatorViewModel + +@Composable +fun WipeScreen(viewModel: CalculatorViewModel) { + + Column() { + Button(onClick = { viewModel.wipeFunctionList()}) { + Text(text = "Wipe") + } + Button(onClick = { viewModel.wipePlusFunctionList()}) { + Text(text = "Wipe +") + } + Button(onClick = { viewModel.wipeMinusFunctionList()}) { + Text(text = "Wipe -") + } + Button(onClick = { viewModel.wipePerFunctionList()}) { + Text(text = "Wipe *") + } + Button(onClick = { viewModel.wipeDividedFunctionList()}) { + Text(text = "Wipe /") + } + } + + +} diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Color.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Color.kt new file mode 100644 index 0000000..47ec008 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.pwc.calculatorv2.presentation.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/pwc/calculatorv2/presentation/ui/theme/Theme.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Theme.kt new file mode 100644 index 0000000..5568c46 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Theme.kt @@ -0,0 +1,70 @@ +package com.pwc.calculatorv2.presentation.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 CalculatorV2Theme( + 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 + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Type.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Type.kt new file mode 100644 index 0000000..424eda5 --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.pwc.calculatorv2.presentation.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/pwc/calculatorv2/presentation/ui/widgets/BottomNav.kt b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/widgets/BottomNav.kt new file mode 100644 index 0000000..1ba2d4c --- /dev/null +++ b/app/src/main/java/com/pwc/calculatorv2/presentation/ui/widgets/BottomNav.kt @@ -0,0 +1,32 @@ +package com.pwc.calculatorv2.presentation.ui.widgets + +import androidx.compose.runtime.Composable +import androidx.compose.material.BottomNavigation +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.navigation.NavController +import com.pwc.calculatorv2.presentation.navigation.AppScreens + +@Composable +fun BottomNav(navController: NavController) { + BottomNavigation( + + ) { + Button(onClick = { + navController.navigate(AppScreens.CalculatorScreen.route) + }) { + Text(text = "Calculator") + } + Button(onClick = { + navController.navigate(AppScreens.FunctionListScreen.route) + }) { + Text(text = "History") + } + Button(onClick = { + navController.navigate(AppScreens.WipeScreen.route) + }) { + Text(text = "Wipe") + } + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ 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/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ 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..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ 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..c209e78 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..b2dfe3d 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..4f0f1d6 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..62b611d 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..948a307 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..1b9a695 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..28d4b77 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..9287f50 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..aa7d642 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..9126ae3 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/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..ecda9dc --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + calculatorV2 + \ 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..ae54f44 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +