├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── feature-request.yml └── workflows │ ├── build-release.yml │ ├── ci.yml │ └── unsigned_release.yml ├── .gitignore ├── .idea ├── .gitignore ├── AndroidProjectSystem.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── deploymentTargetSelector.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── ktlint.xml ├── migrations.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro ├── release │ └── output-metadata.json └── src │ ├── androidTest │ └── java │ │ └── net │ │ └── youapps │ │ └── calcyou │ │ └── ExampleInstrumentedTest.kt │ ├── debug │ └── res │ │ └── values │ │ └── strings.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── net │ │ │ └── youapps │ │ │ └── calcyou │ │ │ ├── CalculatorApplication.kt │ │ │ ├── Destination.kt │ │ │ ├── MainActivity.kt │ │ │ ├── NavHost.kt │ │ │ ├── data │ │ │ ├── CalculatorEvent.kt │ │ │ ├── Evaluator.kt │ │ │ ├── EventHandler.kt │ │ │ ├── InsertText.kt │ │ │ ├── KeyMap.kt │ │ │ ├── Operators.kt │ │ │ ├── Tokenizer.kt │ │ │ ├── converters │ │ │ │ ├── AngleConverter.kt │ │ │ │ ├── AreaConverter.kt │ │ │ │ ├── ConverterUnit.kt │ │ │ │ ├── DensityConverter.kt │ │ │ │ ├── DigitalStorageConverter.kt │ │ │ │ ├── EnergyConverter.kt │ │ │ │ ├── FactorUnit.kt │ │ │ │ ├── ForceConverter.kt │ │ │ │ ├── FrequencyConverter.kt │ │ │ │ ├── FuelConverter.kt │ │ │ │ ├── LengthConverter.kt │ │ │ │ ├── LightConverter.kt │ │ │ │ ├── MassConverter.kt │ │ │ │ ├── PowerConverter.kt │ │ │ │ ├── PressureConverter.kt │ │ │ │ ├── SpeedConverter.kt │ │ │ │ ├── TemperatureConverter.kt │ │ │ │ ├── TimeConverter.kt │ │ │ │ ├── TorqueConverter.kt │ │ │ │ ├── UnitConverter.kt │ │ │ │ ├── ViscosityConverter.kt │ │ │ │ └── VolumeConverter.kt │ │ │ └── graphing │ │ │ │ ├── CompiledExpression.kt │ │ │ │ ├── Constant.kt │ │ │ │ ├── Defaults.kt │ │ │ │ ├── EvalConfig.kt │ │ │ │ ├── Evaluator.kt │ │ │ │ ├── Function.kt │ │ │ │ ├── OffsetConverters.kt │ │ │ │ ├── Token.kt │ │ │ │ └── Window.kt │ │ │ ├── ui │ │ │ ├── CalculatorScreen.kt │ │ │ ├── MainScreen.kt │ │ │ ├── components │ │ │ │ ├── AddNewConstantDialog.kt │ │ │ │ ├── AddNewFunctionDialog.kt │ │ │ │ ├── CalculatorDisplay.kt │ │ │ │ ├── ColorSelectionDialog.kt │ │ │ │ ├── ConverterCard.kt │ │ │ │ ├── Keypad.kt │ │ │ │ ├── NavDrawerContent.kt │ │ │ │ ├── SwipePanels.kt │ │ │ │ └── buttons │ │ │ │ │ ├── CalculatorButton.kt │ │ │ │ │ ├── CalculatorTextButton.kt │ │ │ │ │ ├── KeyRow.kt │ │ │ │ │ ├── KeyboardKey.kt │ │ │ │ │ └── KeyboardPanel.kt │ │ │ ├── screens │ │ │ │ ├── CharacterInputScreen.kt │ │ │ │ ├── ConverterGridScreen.kt │ │ │ │ ├── ConverterScreen.kt │ │ │ │ └── graphing │ │ │ │ │ ├── CanvasView.kt │ │ │ │ │ ├── GraphingScreen.kt │ │ │ │ │ └── GraphingUtils.kt │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── viewmodels │ │ │ ├── CalculatorViewModel.kt │ │ │ └── GraphViewModel.kt │ └── res │ │ ├── drawable │ │ ├── bracket_l.xml │ │ ├── bracket_r.xml │ │ ├── delete.xml │ │ ├── divide.xml │ │ ├── equal.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_launcher_monochrome.xml │ │ ├── minus.xml │ │ ├── multiply.xml │ │ ├── percent.xml │ │ └── plus.xml │ │ ├── mipmap-anydpi-v26 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-az │ │ └── strings.xml │ │ ├── values-ca │ │ └── strings.xml │ │ ├── values-cs │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-el │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-et │ │ └── strings.xml │ │ ├── values-fa │ │ └── strings.xml │ │ ├── values-fi │ │ └── strings.xml │ │ ├── values-fil │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-hi │ │ └── strings.xml │ │ ├── values-ia │ │ └── strings.xml │ │ ├── values-in │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-iw │ │ └── strings.xml │ │ ├── values-lt │ │ └── strings.xml │ │ ├── values-nb-rNO │ │ └── strings.xml │ │ ├── values-night │ │ └── colors.xml │ │ ├── values-pl │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-pt │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-sv │ │ └── strings.xml │ │ ├── values-ta │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-uk │ │ └── strings.xml │ │ ├── values-vi │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── net │ └── youapps │ └── calcyou │ ├── EvaluatorTest.kt │ └── ExampleUnitTest.kt ├── arity ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── java │ └── org │ └── javia │ └── arity │ ├── ArityException.java │ ├── ByteStack.java │ ├── CompiledFunction.java │ ├── CompiledFunction.java.orig │ ├── Compiler.java │ ├── Compiler.java.orig │ ├── Complex.java │ ├── Constant.java │ ├── ContextFunction.java │ ├── Declaration.java │ ├── DeclarationParser.java │ ├── Derivative.java │ ├── DoubleStack.java │ ├── EvalContext.java │ ├── Function.java │ ├── FunctionAndName.java │ ├── FunctionStack.java │ ├── IsComplexException.java │ ├── Lexer.java │ ├── Lexer.java.orig │ ├── MoreMath.java │ ├── OptCodeGen.java │ ├── RPN.java │ ├── SimpleCodeGen.java │ ├── Symbol.java │ ├── Symbols.java │ ├── SyntaxException.java │ ├── Token.java │ ├── TokenConsumer.java │ ├── UnitTest.java │ ├── Util.java │ └── VM.java ├── build.gradle.kts ├── fastlane └── metadata │ └── android │ └── en-US │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ └── 5.png │ └── short_description.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Report an issue in the app 3 | labels: [bug] 4 | body: 5 | 6 | - type: textarea 7 | id: reproduce-steps 8 | attributes: 9 | label: Steps to reproduce 10 | description: Provide an example of the issue. 11 | placeholder: | 12 | Example: 13 | 1. First step 14 | 2. Second step 15 | 3. Issue here 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | id: expected-behavior 21 | attributes: 22 | label: Expected behavior 23 | placeholder: | 24 | Example: 25 | "This should happen..." 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: actual-behavior 31 | attributes: 32 | label: Actual behavior 33 | placeholder: | 34 | Example: 35 | "This happened instead..." 36 | validations: 37 | required: true 38 | 39 | - type: input 40 | id: Calc_You-version 41 | attributes: 42 | label: Calc You version 43 | description: | 44 | You can find your Calc You version in **About**. 45 | placeholder: | 46 | Example: "0.5.0" 47 | validations: 48 | required: true 49 | 50 | - type: input 51 | id: android-version 52 | attributes: 53 | label: Android version 54 | description: | 55 | You can find this somewhere in your Android settings. 56 | placeholder: | 57 | Example: "Android 12" 58 | validations: 59 | required: true 60 | 61 | - type: textarea 62 | id: other-details 63 | attributes: 64 | label: Other details 65 | placeholder: | 66 | Additional details and attachments. 67 | 68 | - type: checkboxes 69 | id: acknowledgements 70 | attributes: 71 | label: Acknowledgements 72 | description: Your issue will be closed if you haven't done these steps. 73 | options: 74 | - label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue. 75 | required: true 76 | - label: I have written a short but informative title. 77 | required: true 78 | - label: I will fill out all of the requested information in this form. 79 | required: true 80 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: ⭐ Feature request 2 | description: Suggest a feature to improve Calc You 3 | labels: [enhancement] 4 | body: 5 | 6 | - type: textarea 7 | id: feature-description 8 | attributes: 9 | label: Describe your suggested feature 10 | description: How can an existing source be improved? 11 | placeholder: | 12 | Example: 13 | "It could work like this..." 14 | validations: 15 | required: true 16 | 17 | - type: textarea 18 | id: other-details 19 | attributes: 20 | label: Other details 21 | placeholder: | 22 | Additional details and attachments. 23 | 24 | - type: checkboxes 25 | id: acknowledgements 26 | attributes: 27 | label: Acknowledgements 28 | description: Your issue will be closed if you haven't done these steps. 29 | options: 30 | - label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue. 31 | required: true 32 | - label: I'm not requesting to add a new unit converter or to modify an existing one. 33 | required: true 34 | - label: I have written a short but informative title. 35 | required: true 36 | - label: I will fill out all of the requested information in this form. 37 | required: true 38 | -------------------------------------------------------------------------------- /.github/workflows/build-release.yml: -------------------------------------------------------------------------------- 1 | name: Build and release app 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - 'v*.*' 8 | 9 | jobs: 10 | build: 11 | name: Build, sign and release app 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Set up JDK 19 | uses: actions/setup-java@v4 20 | with: 21 | distribution: "zulu" 22 | java-version: "17" 23 | cache: "gradle" 24 | 25 | - name: Build APK 26 | run: ./gradlew assembleRelease --no-daemon 27 | 28 | - name: Sign APK 29 | uses: ilharp/sign-android-release@v1 30 | id: sign 31 | with: 32 | signingKey: ${{ secrets.KEYSTORE }} 33 | keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }} 34 | keyStorePassword: ${{ secrets.SIGNING_STORE_PASSWORD }} 35 | keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }} 36 | buildToolsVersion: 35.0.0 37 | 38 | - name: Rename signed APK 39 | run: | 40 | mv "${{ steps.sign.outputs.signedFile }}" "app-release.apk" 41 | 42 | - name: Create changelog 43 | id: changelog 44 | uses: requarks/changelog-action@v1 45 | with: 46 | token: ${{ github.token }} 47 | tag: ${{ github.ref_name }} 48 | 49 | - name: Create release 50 | uses: softprops/action-gh-release@v2 51 | with: 52 | body: ${{ steps.changelog.outputs.changes }} 53 | files: "app-release.apk" 54 | fail_on_unmatched_files: true 55 | make_latest: true 56 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Create Debug Apk 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "README*.md" 7 | - "app/src/main/res/**" 8 | - ".github/**" 9 | pull_request: 10 | paths-ignore: 11 | - "README*.md" 12 | - "app/src/main/res/**" 13 | - ".github/**" 14 | 15 | jobs: 16 | debug-builds: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - name: Validate Gradle Wrapper 23 | uses: gradle/wrapper-validation-action@v2 24 | 25 | - name: Setup JDK 26 | uses: actions/setup-java@v4 27 | with: 28 | java-version: 17 29 | distribution: "temurin" 30 | cache: "gradle" 31 | 32 | - name: Build APK 33 | run: bash ./gradlew assembleDebug --stacktrace 34 | 35 | - name: Upload APK 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: app 39 | path: app/build/outputs/apk/debug/*.apk 40 | -------------------------------------------------------------------------------- /.github/workflows/unsigned_release.yml: -------------------------------------------------------------------------------- 1 | name: Create Unsigned Release APK 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | debug-builds: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | 13 | - name: Validate Gradle Wrapper 14 | uses: gradle/wrapper-validation-action@v2 15 | 16 | - name: Setup JDK 17 | uses: actions/setup-java@v4 18 | with: 19 | java-version: 17 20 | distribution: "temurin" 21 | 22 | - name: Build APK 23 | run: bash ./gradlew assembleRelease --stacktrace 24 | 25 | - name: Upload APK 26 | uses: actions/upload-artifact@v4 27 | with: 28 | name: app 29 | path: app/build/outputs/apk/release/*.apk 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/AndroidProjectSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/ktlint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | true 6 | false 7 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |

Calc You

5 |

Privacy focused calculator app built with MD3.

6 | 7 | 8 |
9 | License 10 | Downloads 11 | Last commit 12 | Repo size 13 | Stars 14 |
15 |
16 |
17 | 18 | 19 | 20 | --- 21 | 22 |
23 | Screenshots 24 |

25 | 26 | 27 | 28 |

29 |

30 | 31 | 32 |

33 |
34 | 35 | ## Features 36 | 37 | ### 1. Powerful Calculator: 38 | 39 | - **Basic Calculator**: Perform all your essential calculations with a simple and intuitive 40 | interface. 41 | - **Scientific Calculator**: Unleash the power of advanced functions like trigonometry, logarithms, 42 | and more. 43 | 44 | ### 2. Versatile Unit Converter: 45 | 46 | - Convert between a wide range of units across various categories like length, weight, volume, 47 | currency, and more. 48 | 49 | ### 3. Character Input Tool: 50 | 51 | - Access a vast library of mathematical symbols, including Greek letters, arrows, superscript, 52 | subscript, and much more. 53 | - Effortlessly write complex expressions and equations with just a few taps. 54 | 55 | ### CalcYou also boasts several features that enhance your experience: 56 | 57 | - Elegant and user-friendly interface: Switch between basic and scientific mode with a swipe. 58 | - History function: Easily access your past calculations for quick reference. 59 | - Material you theme: Uses a theme that suits your style and mood. 60 | 61 | 62 | 63 | ## Download 64 | 65 | [Get it on GitHub](https://github.com/you-apps/Calcyou/releases) 66 | [Get it on F-Droid](https://f-droid.org/en/packages/net.youapps.calcyou) 69 | 70 | 71 | 72 | 73 | ## Feedback and contributions 74 | 75 | ***All contributions are very welcome!*** 76 | 77 | * Feel free to join the [Matrix room](https://matrix.to/#/#you-apps:matrix.org) for discussions about the app. 78 | * Bug reports and feature requests can be submitted [here](https://github.com/you-apps/CalcYou/issues) (please make sure to fill out all the requested information properly!). 79 | * If you are a developer and wish to contribute to the app, please **fork** the project and submit a [**pull request**](https://help.github.com/articles/about-pull-requests/). 80 | 81 | ## Translation 82 | 83 | Translation status 84 | 85 | 86 | ## Credits 87 | * Icon design by [M00NJ](https://github.com/M00NJ) 88 | 89 | ## License 90 | 91 | Clock You is licensed under the [**GNU General Public License**](https://www.gnu.org/licenses/gpl.html): You can use, study and share it as you want. 92 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("org.jetbrains.kotlin.android") 4 | } 5 | 6 | android { 7 | namespace = "net.youapps.calcyou" 8 | compileSdk = 34 9 | 10 | defaultConfig { 11 | applicationId = "net.youapps.calcyou" 12 | minSdk = 21 13 | targetSdk = 34 14 | versionCode = 4 15 | versionName = "2.0" 16 | 17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary = true 20 | } 21 | } 22 | 23 | buildTypes { 24 | getByName("release") { 25 | isMinifyEnabled = true 26 | isShrinkResources = true 27 | proguardFiles( 28 | getDefaultProguardFile("proguard-android-optimize.txt"), 29 | "proguard-rules.pro" 30 | ) 31 | } 32 | getByName("debug") { 33 | applicationIdSuffix = ".debug" 34 | isDebuggable = true 35 | } 36 | } 37 | compileOptions { 38 | sourceCompatibility = JavaVersion.VERSION_17 39 | targetCompatibility = JavaVersion.VERSION_17 40 | } 41 | kotlinOptions { 42 | jvmTarget = "17" 43 | } 44 | buildFeatures { 45 | compose = true 46 | } 47 | composeOptions { 48 | kotlinCompilerExtensionVersion = "1.4.7" 49 | } 50 | packaging { 51 | resources { 52 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 53 | } 54 | } 55 | } 56 | 57 | dependencies { 58 | 59 | implementation("androidx.core:core-ktx:1.12.0") 60 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") 61 | implementation("androidx.activity:activity-compose:1.8.0") 62 | implementation(platform("androidx.compose:compose-bom:2023.10.01")) 63 | implementation("androidx.compose.ui:ui") 64 | implementation("androidx.compose.ui:ui-graphics") 65 | implementation("androidx.compose.ui:ui-tooling-preview") 66 | implementation("androidx.compose.material3:material3") 67 | implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2") 68 | implementation("androidx.compose.material:material-icons-extended:1.5.4") 69 | implementation("androidx.navigation:navigation-compose:2.7.5") 70 | implementation(project(mapOf("path" to ":arity"))) 71 | testImplementation("junit:junit:4.13.2") 72 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 73 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 74 | androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01")) 75 | androidTestImplementation("androidx.compose.ui:ui-test-junit4") 76 | debugImplementation("androidx.compose.ui:ui-tooling") 77 | debugImplementation("androidx.compose.ui:ui-test-manifest") 78 | } 79 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "net.youapps.calcyou", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 4, 15 | "versionName": "2.0", 16 | "outputFile": "app-release.apk" 17 | } 18 | ], 19 | "elementType": "File" 20 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/net/youapps/calcyou/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("net.youapps.calcyou", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CalcYou Debug 3 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/CalculatorApplication.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou 2 | 3 | import android.app.Application 4 | 5 | class CalculatorApplication : Application() -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.activity.enableEdgeToEdge 7 | import net.youapps.calcyou.ui.MainScreen 8 | import net.youapps.calcyou.ui.theme.CalcYouTheme 9 | 10 | class MainActivity : ComponentActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | enableEdgeToEdge() 14 | setContent { 15 | CalcYouTheme { 16 | // A surface container using the 'background' color from the theme 17 | MainScreen() 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/NavHost.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.ui.Modifier 5 | import androidx.navigation.NavHostController 6 | import androidx.navigation.compose.NavHost 7 | import androidx.navigation.compose.composable 8 | import net.youapps.calcyou.ui.CalculatorScreen 9 | import net.youapps.calcyou.ui.screens.CharacterInputScreen 10 | import net.youapps.calcyou.ui.screens.ConverterGridScreen 11 | import net.youapps.calcyou.ui.screens.ConverterScreen 12 | import net.youapps.calcyou.ui.screens.graphing.GraphingScreen 13 | 14 | @Composable 15 | fun AppNavHost(modifier: Modifier = Modifier, navHostController: NavHostController) { 16 | NavHost( 17 | modifier = modifier, 18 | navController = navHostController, 19 | startDestination = Destination.Calculator.route 20 | ) { 21 | composable(route = Destination.Calculator.route) { 22 | CalculatorScreen() 23 | } 24 | 25 | composable(route = Destination.Converters.route) { 26 | ConverterGridScreen(onNavigate = { 27 | navHostController.navigateTo(it.route) 28 | }) 29 | } 30 | 31 | composable(route = Destination.CharacterInput.route) { 32 | CharacterInputScreen() 33 | } 34 | 35 | composable(route = Destination.Graphing.route) { 36 | GraphingScreen() 37 | } 38 | 39 | Destination.Converter.values.forEach { converter -> 40 | composable(route = converter.route) { 41 | ConverterScreen(converter = converter.converter, converterName = converter.resId) 42 | } 43 | } 44 | } 45 | } 46 | 47 | fun NavHostController.navigateTo(route: String) = this.navigate(route) { 48 | launchSingleTop = true 49 | restoreState = true 50 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/CalculatorEvent.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data 2 | 3 | sealed interface CalculatorEvent { 4 | object Decimal : CalculatorEvent 5 | object Evaluate : CalculatorEvent 6 | object Delete : CalculatorEvent 7 | object DeleteAll : CalculatorEvent 8 | data class Number(val number: Int) : CalculatorEvent 9 | data class Operator(val simpleOperator: SimpleOperator) : CalculatorEvent 10 | data class SpecialOperator(val specialOperator: net.youapps.calcyou.data.SpecialOperator) : 11 | CalculatorEvent 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/Evaluator.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data 2 | 3 | import org.javia.arity.Symbols 4 | import org.javia.arity.Util 5 | 6 | 7 | class Evaluator(private val tokenizer: Tokenizer) { 8 | private val symbols: Symbols = Symbols() 9 | 10 | fun evaluate(expr: String): String? { 11 | var expr = expr 12 | expr = tokenizer.getNormalizedExpression(expr) 13 | if (expr.isEmpty()) { 14 | return null 15 | } 16 | // remove any trailing operators 17 | while (expr.isNotEmpty() && "+-/*".indexOf(expr[expr.length - 1]) != -1) { 18 | expr = expr.substring(0, expr.length - 1) 19 | } 20 | return try { 21 | val result: Double = symbols.eval(expr) 22 | if (java.lang.Double.isNaN(result)) { 23 | null 24 | } else { 25 | tokenizer.getLocalizedExpression( 26 | Util.doubleToString(result, MAX_DIGITS, ROUNDING_DIGITS) 27 | ) 28 | } 29 | } catch (e: Exception) { 30 | null 31 | } 32 | } 33 | 34 | companion object { 35 | private const val MAX_DIGITS = 12 36 | private const val ROUNDING_DIGITS = 5 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/EventHandler.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data 2 | 3 | import android.content.Context 4 | import androidx.compose.ui.text.TextRange 5 | import androidx.compose.ui.text.input.TextFieldValue 6 | 7 | class EventHandler( 8 | context: Context, 9 | private val onUpdateHistory: (String) -> Unit 10 | ) { 11 | private val tokenizer = Tokenizer(context) 12 | private val evaluator = Evaluator(tokenizer) 13 | 14 | fun processEvent(event: CalculatorEvent, currentText: TextFieldValue): TextFieldValue { 15 | return when (event) { 16 | is CalculatorEvent.Number -> { 17 | currentText.insertText(event.number.toString()) 18 | } 19 | 20 | is CalculatorEvent.Operator -> { 21 | currentText.insertText(event.simpleOperator.text) 22 | } 23 | 24 | CalculatorEvent.Delete -> { 25 | currentText.backSpace() 26 | } 27 | 28 | CalculatorEvent.DeleteAll -> { 29 | TextFieldValue("") 30 | } 31 | 32 | CalculatorEvent.Decimal -> { 33 | currentText.insertText(".") 34 | } 35 | 36 | CalculatorEvent.Evaluate -> { 37 | onUpdateHistory(currentText.text) 38 | val newText = evaluator.evaluate( 39 | tokenizer.getNormalizedExpression( 40 | currentText.text 41 | ) 42 | ) ?: "Error" 43 | TextFieldValue( 44 | newText, 45 | selection = TextRange(newText.length) 46 | ) 47 | } 48 | 49 | is CalculatorEvent.SpecialOperator -> { 50 | currentText.insertText(event.specialOperator.value) 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/InsertText.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data 2 | 3 | import androidx.compose.ui.text.TextRange 4 | import androidx.compose.ui.text.input.TextFieldValue 5 | import androidx.compose.ui.text.input.getSelectedText 6 | import androidx.compose.ui.text.input.getTextAfterSelection 7 | import androidx.compose.ui.text.input.getTextBeforeSelection 8 | 9 | fun TextFieldValue.insertText(insertText: String): TextFieldValue { 10 | val maxChars = text.length 11 | val textBeforeSelection = getTextBeforeSelection(maxChars) 12 | val textAfterSelection = getTextAfterSelection(maxChars) 13 | val newText = "$textBeforeSelection$insertText$textAfterSelection" 14 | val newCursorPosition = textBeforeSelection.length + insertText.length 15 | return TextFieldValue( 16 | text = newText, 17 | selection = TextRange(newCursorPosition) 18 | ) 19 | } 20 | 21 | fun TextFieldValue.backSpace(): TextFieldValue { 22 | val maxChars = text.length 23 | val textBeforeSelection = getTextBeforeSelection(maxChars) 24 | val textAfterSelection = getTextAfterSelection(maxChars) 25 | val selectedText = getSelectedText() 26 | if (textBeforeSelection.isEmpty() && selectedText.isEmpty()) return this 27 | val newText = 28 | if (selectedText.isEmpty()) { 29 | "${textBeforeSelection.dropLast(1)}$textAfterSelection" 30 | } else { 31 | "$textBeforeSelection$textAfterSelection" 32 | } 33 | val newCursorPosition = textBeforeSelection.length - if (selectedText.isEmpty()) 1 else 0 34 | return TextFieldValue( 35 | text = newText, 36 | selection = TextRange(newCursorPosition) 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/Operators.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data 2 | 3 | enum class SimpleOperator(val text: String, val value: String) { 4 | Plus("+", "+"), 5 | Minus("‒", "-"), 6 | Multiply("×", "*"), 7 | Divide("÷", "/"), 8 | Percent("%", "%") 9 | } 10 | 11 | enum class SpecialOperator(val text: String, val value: String = text) { 12 | LBracket("("), 13 | RBracket(")"), 14 | 15 | E("e"), 16 | PI("π"), 17 | Sin("sin", "sin("), 18 | Cos("cos", "cos("), 19 | Tan("tan", "tan("), 20 | Ln("ln", "ln("), 21 | Log("log", "log("), 22 | PowerE("eˣ", "e^("), 23 | Square("x²", "^2"), 24 | SquareRoot("√", "√("), 25 | Absolute("|x|", "|"), 26 | Power("xʸ", "^("), 27 | 28 | ASin("sin⁻¹", "asin("), 29 | ACos("cos⁻¹", "acos("), 30 | ATan("tan⁻¹", "atan("), 31 | SinH("sinh", "sinh("), 32 | CosH("cosh", "cosh("), 33 | TanH("tanh", "tanh("), 34 | ASinH("sinh⁻¹", "asinh("), 35 | ACosH("cosh⁻¹", "acosh("), 36 | ATanH("tanh⁻¹", "atanh("), 37 | Power2("2ˣ", "2^("), 38 | Cube("x³", "^3"), 39 | Factorial("x!", "!") 40 | 41 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/Tokenizer.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data 2 | 3 | import android.content.Context 4 | import java.text.DecimalFormatSymbols 5 | import java.util.Locale 6 | 7 | 8 | class Tokenizer(context: Context) { 9 | private val replacementMap: MutableMap = HashMap() 10 | 11 | init { 12 | var locale: Locale = context.resources.configuration.locale 13 | locale = Locale.Builder() 14 | .setLocale(locale) 15 | .setUnicodeLocaleKeyword("nu", "latn") 16 | .build() 17 | val symbols = DecimalFormatSymbols(locale) 18 | val zeroDigit: Char = symbols.zeroDigit 19 | replacementMap["."] = symbols.decimalSeparator.toString() 20 | for (i in 0..9) { 21 | replacementMap[i.toString()] = (i + zeroDigit.code).toChar().toString() 22 | } 23 | 24 | SimpleOperator.values().forEach { 25 | replacementMap[it.value] = it.text 26 | } 27 | replacementMap["Infinity"] = "∞" 28 | } 29 | 30 | fun getNormalizedExpression(expr: String): String { 31 | var exp = expr 32 | for ((key, value) in replacementMap) { 33 | exp = exp.replace(value, key) 34 | } 35 | return exp 36 | } 37 | 38 | fun getLocalizedExpression(expr: String): String { 39 | var exp = expr 40 | for ((key, value) in replacementMap) { 41 | exp = exp.replace(key, value) 42 | } 43 | return exp 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/AngleConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class AngleConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.degree, Math.PI / 180), 8 | FactorUnit(R.string.radian, 1.0), 9 | FactorUnit(R.string.gradian, Math.PI / 200), 10 | FactorUnit(R.string.turn, 2 * Math.PI) 11 | ) 12 | } 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/AreaConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class AreaConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.square_meter, 1.0), 8 | FactorUnit(R.string.square_kilometer, 1E6), 9 | FactorUnit(R.string.square_centimeter, 1E-4), 10 | FactorUnit(R.string.square_millimeter, 1E-7), 11 | FactorUnit(R.string.hectare, 1E4), 12 | FactorUnit(R.string.acre, 4046.8564224), 13 | FactorUnit(R.string.square_yard, 0.83612736), 14 | FactorUnit(R.string.square_foot, 0.09290304) 15 | ) 16 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/ConverterUnit.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | 4 | interface ConverterUnit { 5 | val name: Int 6 | fun convertFrom(value: Double): Double 7 | fun convertTo(value: Double): Double 8 | fun convert(outputUnit: ConverterUnit, value: Double): Double { 9 | return outputUnit.convertTo(this.convertFrom(value)) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/DensityConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class DensityConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.kilogram_per_cubic_meter, 1.0), 8 | FactorUnit(R.string.gram_per_cubic_centimeter, 1E3), 9 | FactorUnit(R.string.pound_per_cubic_foot, 16.018463), 10 | FactorUnit(R.string.pound_per_gallon, 0.11982642) 11 | ) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/DigitalStorageConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class DigitalStorageConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.bit, 1.0), 8 | FactorUnit(R.string._byte, 8.0), 9 | FactorUnit(R.string.kilobit, 1E3), 10 | FactorUnit(R.string.kilobyte, 8E3), 11 | FactorUnit(R.string.kibibyte, 8.0 * 1024), 12 | FactorUnit(R.string.megabit, 1E6), 13 | FactorUnit(R.string.megabyte, 8E6), 14 | FactorUnit(R.string.mebibyte, 8.0 * 1024 * 1024), 15 | FactorUnit(R.string.gigabit, 1E9), 16 | FactorUnit(R.string.gigabyte, 8E9), 17 | FactorUnit(R.string.gigibyte, 8.0 * 1024 * 1024 * 1024), 18 | FactorUnit(R.string.terabit, 1E12), 19 | FactorUnit(R.string.terabyte, 8E12), 20 | FactorUnit(R.string.tebibyte, 8.0 * 1024 * 1024 * 1024 * 1024), 21 | FactorUnit(R.string.petabit, 1E15), 22 | FactorUnit(R.string.petabyte, 8E15), 23 | FactorUnit(R.string.pebibyte, 8.0 * 1024 * 1024 * 1024 * 1024 * 1024), 24 | FactorUnit(R.string.exabit, 1E18), 25 | FactorUnit(R.string.exabyte, 8E18), 26 | FactorUnit(R.string.exbibyte, 8.0 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024) 27 | ) 28 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/EnergyConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class EnergyConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.joule, 1.0), 8 | FactorUnit(R.string.kilojoule, 1E3), 9 | FactorUnit(R.string.megajoule, 1E6), 10 | FactorUnit(R.string.calorie, 4.184), 11 | FactorUnit(R.string.kilocalorie, 4.184E3), 12 | FactorUnit(R.string.grams_of_carbohydrate, 4.0 * 4.184E3), 13 | FactorUnit(R.string.grams_of_fat, 9.0 * 4.184E3), 14 | FactorUnit(R.string.grams_of_protein, 4.0 * 4.184E3) 15 | ) 16 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/FactorUnit.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import androidx.annotation.StringRes 4 | 5 | class FactorUnit(@StringRes override val name: Int, val conversionFactor: Double) : ConverterUnit { 6 | override fun convertFrom(value: Double): Double { 7 | return value * conversionFactor 8 | } 9 | 10 | override fun convertTo(value: Double): Double { 11 | return value / conversionFactor 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/ForceConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class ForceConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.newton, 1.0), 8 | FactorUnit(R.string.kilonewton, 1E3), 9 | FactorUnit(R.string.dyne, 1E-5), 10 | FactorUnit(R.string.pound_force, 4.448222), 11 | FactorUnit(R.string.ounce_force, 0.2780139) 12 | ) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/FrequencyConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class FrequencyConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.hertz, 1.0), 8 | FactorUnit(R.string.kilohertz, 1E3), 9 | FactorUnit(R.string.megahertz, 1E6), 10 | FactorUnit(R.string.gigahertz, 1E9) 11 | ) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/FuelConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class FuelConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.liter, 1.0), 8 | FactorUnit(R.string.gallon_us, 3.78541), 9 | FactorUnit(R.string.gallon_uk, 4.54609), 10 | FactorUnit(R.string.barrel, 158.98729) 11 | ) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/LengthConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class LengthConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.meter, 1.0), 8 | FactorUnit(R.string.kilometer, 1E3), 9 | FactorUnit(R.string.centimeter, 1E-2), 10 | FactorUnit(R.string.millimeter, 1E-3), 11 | FactorUnit(R.string.mile, 1609.344), 12 | FactorUnit(R.string.yard, 0.9144), 13 | FactorUnit(R.string.foot, 0.3048), 14 | FactorUnit(R.string.inch, 0.0254), 15 | FactorUnit(R.string.nautical_mile, 1852.0) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/LightConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class LightConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.lumen, 1.0), 8 | FactorUnit(R.string.candela, 1.0), 9 | FactorUnit(R.string.lux, 1.0 / 3.14159), // Lumen per square meter 10 | FactorUnit(R.string.footcandle, 10.7639104167097) 11 | ) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/MassConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class MassConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.kilogram, 1.0), 8 | FactorUnit(R.string.gram, 1E-3), 9 | FactorUnit(R.string.milligram, 1E-6), 10 | FactorUnit(R.string.tonne, 1E3), 11 | FactorUnit(R.string.pound, 0.45359237), 12 | FactorUnit(R.string.ounce, 0.028349523125) 13 | ) 14 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/PowerConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class PowerConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.watt, 1.0), 8 | FactorUnit(R.string.kilowatt, 1E3), 9 | FactorUnit(R.string.megawatt, 1E6), 10 | FactorUnit(R.string.horsepower, 745.7), 11 | FactorUnit(R.string.btu_per_hour, 252.0) 12 | ) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/PressureConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class PressureConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.pascal, 1.0), 8 | FactorUnit(R.string.kilopascal, 1E3), 9 | FactorUnit(R.string.megapascal, 1E6), 10 | FactorUnit(R.string.gigapascal, 1E9), 11 | FactorUnit(R.string.bar, 1E5), 12 | FactorUnit(R.string.millibar, 1E2), 13 | FactorUnit(R.string.atmosphere, 101325.0), 14 | FactorUnit(R.string.psi, 6894.757293168), // Pounds per Square Inch 15 | FactorUnit(R.string.h2o, 249.082), // Inches of water 16 | ) 17 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/SpeedConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class SpeedConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.meter_per_second, 1.0), 8 | FactorUnit(R.string.kilometer_per_hour, 0.277777778), 9 | FactorUnit(R.string.mile_per_hour, 0.44704), 10 | FactorUnit(R.string.knot, 0.514444444), 11 | FactorUnit(R.string.feet_per_second, 0.3048) 12 | ) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/TemperatureConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import androidx.annotation.StringRes 4 | import net.youapps.calcyou.R 5 | 6 | class TemperatureConverter : UnitConverter { 7 | override val units: List = listOf( 8 | object : ConverterUnit { 9 | @StringRes 10 | override val name: Int = R.string.celsius 11 | 12 | override fun convertFrom(value: Double): Double { 13 | return value 14 | } 15 | 16 | override fun convertTo(value: Double): Double { 17 | return value 18 | } 19 | }, 20 | object : ConverterUnit { 21 | @StringRes 22 | override val name: Int = R.string.fahrenheit 23 | 24 | override fun convertFrom(value: Double): Double { 25 | return (value - 32) / 1.8 26 | } 27 | 28 | override fun convertTo(value: Double): Double { 29 | return (value * 1.8) + 32 30 | } 31 | }, 32 | object : ConverterUnit { 33 | @StringRes 34 | override val name: Int = R.string.kelvin 35 | 36 | override fun convertFrom(value: Double): Double { 37 | return value - 273.15 38 | } 39 | 40 | override fun convertTo(value: Double): Double { 41 | return value + 273.15 42 | } 43 | }, 44 | ) 45 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/TimeConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class TimeConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.second, 1.0), 8 | FactorUnit(R.string.minute, 60.0), 9 | FactorUnit(R.string.hour, 3.6E3), 10 | FactorUnit(R.string.day, 8.64E4), 11 | FactorUnit(R.string.week, 6.048E5) 12 | ) 13 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/TorqueConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class TorqueConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.newton_meter, 1.0), 8 | FactorUnit(R.string.kilonewton_meter, 1E3), 9 | FactorUnit(R.string.pound_foot, 1.3558179), 10 | FactorUnit(R.string.ounce_inch, 0.08333333) 11 | ) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/UnitConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | interface UnitConverter { 4 | val units: List 5 | 6 | fun convertAll(value: Double, unit: ConverterUnit): List> { 7 | return units.map { 8 | it to unit.convert(it, value) 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/ViscosityConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class ViscosityConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.pascal_second, 1.0), 8 | FactorUnit(R.string.centipoise, 0.01), 9 | FactorUnit(R.string.stoke, 1E4), 10 | FactorUnit(R.string.poiseuille, 1E9) 11 | ) 12 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/converters/VolumeConverter.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.converters 2 | 3 | import net.youapps.calcyou.R 4 | 5 | class VolumeConverter : UnitConverter { 6 | override val units: List = listOf( 7 | FactorUnit(R.string.liter, 1.0), 8 | FactorUnit(R.string.milliliter, 1E-3), 9 | FactorUnit(R.string.kiloliter, 1E3), 10 | FactorUnit(R.string.cubic_meter, 1E3), 11 | FactorUnit(R.string.cubic_centimeter, 1E-3), 12 | FactorUnit(R.string.cubic_decimeter, 1.0), 13 | FactorUnit(R.string.gallon, 3.785411784), 14 | FactorUnit(R.string.quart, 0.946352946), 15 | FactorUnit(R.string.pint, 0.473176473), 16 | FactorUnit(R.string.fluid_ounce, 0.02957352957), 17 | FactorUnit(R.string.tablespoon, 0.01478676479), 18 | FactorUnit(R.string.teaspoon, 0.00492892159) 19 | ) 20 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/graphing/CompiledExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | This code is a modified version of https://github.com/danielgindi/KotlinEval 3 | The original source code is licensed under MIT LICENSE 4 | */ 5 | 6 | package net.youapps.calcyou.data.graphing 7 | 8 | class CompiledExpression internal constructor( 9 | internal var root: Token, 10 | var configuration: EvalConfiguration 11 | ) { 12 | fun execute(): Double? = 13 | Evaluator.execute(expression = this) 14 | 15 | fun execute(constants: List>): Double? = 16 | Evaluator.execute(this, constants) 17 | 18 | fun setConstant(name: String, value: Double) { 19 | configuration.setConstant(name = name, value = value) 20 | } 21 | 22 | fun removeConstant(name: String) { 23 | configuration.removeConstant(name = name) 24 | } 25 | 26 | fun setFunction(name: String, func: EvalFunctionBlock) { 27 | configuration.setFunction(name = name, func = func) 28 | } 29 | 30 | fun removeFunction(name: String) { 31 | configuration.removeFunction(name = name) 32 | } 33 | 34 | fun clearConstants() { 35 | configuration.clearConstants() 36 | } 37 | 38 | fun clearFunctions() { 39 | configuration.clearConstants() 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/graphing/Constant.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.graphing 2 | 3 | data class Constant( 4 | val identifier: Char, 5 | val value: Double 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/graphing/EvalConfig.kt: -------------------------------------------------------------------------------- 1 | /* 2 | This code is a modified version of https://github.com/danielgindi/KotlinEval 3 | The original source code is licensed under MIT LICENSE 4 | */ 5 | 6 | package net.youapps.calcyou.data.graphing 7 | 8 | typealias EvalFunctionBlock = (args: List) -> Double 9 | 10 | class EvalConfiguration( 11 | var constants: MutableMap? = null, 12 | var functions: MutableMap? = null 13 | ) { 14 | 15 | internal var allOperators = listOf() 16 | private var _operatorOrder = listOf>() 17 | var operatorOrder: List> 18 | get() { 19 | return _operatorOrder 20 | } 21 | set(newValue) { 22 | val ops = mutableListOf() 23 | for (ops2 in newValue) 24 | ops.addAll(ops2) 25 | 26 | _operatorOrder = newValue 27 | allOperators = ops 28 | } 29 | 30 | var rightAssociativeOps = setOf() 31 | 32 | var varNameChars = setOf() 33 | 34 | var genericConstants = mutableMapOf() 35 | var genericFunctions = mutableMapOf() 36 | 37 | @Suppress("unused") 38 | fun setConstant(name: String, value: Double) { 39 | if (constants == null) { 40 | constants = mutableMapOf() 41 | } 42 | constants!![name] = value 43 | } 44 | 45 | @Suppress("unused") 46 | fun removeConstant(name: String) { 47 | if (constants == null) { 48 | return 49 | } 50 | constants?.remove(name) 51 | } 52 | 53 | @Suppress("unused") 54 | fun setFunction(name: String, func: EvalFunctionBlock) { 55 | if (functions == null) { 56 | functions = mutableMapOf() 57 | } 58 | functions!![name] = func 59 | } 60 | 61 | @Suppress("unused") 62 | fun removeFunction(name: String) { 63 | if (functions == null) { 64 | return 65 | } 66 | functions?.remove(name) 67 | } 68 | 69 | @Suppress("unused") 70 | fun clearConstants() { 71 | constants?.clear() 72 | } 73 | 74 | @Suppress("unused") 75 | fun clearFunctions() { 76 | functions?.clear() 77 | } 78 | 79 | init { 80 | this.operatorOrder = Defaults.defaultOperatorOrder 81 | this.rightAssociativeOps = Defaults.defaultRightAssociativeOps 82 | this.varNameChars = Defaults.defaultVarNameChars 83 | this.genericConstants = Defaults.defaultGenericConstants.toMutableMap() 84 | this.genericFunctions = Defaults.getDefaultGenericFunctions().toMutableMap() 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/graphing/Function.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.graphing 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | data class Function( 6 | val expression: String, 7 | val color: Color, 8 | val name: String, 9 | val compiled: CompiledExpression 10 | ) { 11 | fun execute(variableX: Float, constants: List): Float? { 12 | val variables = listOf( 13 | "x" to variableX.toDouble(), 14 | *constants.map { it.identifier.toString() to it.value }.toTypedArray() 15 | ) 16 | return compiled.execute(variables)?.toFloat() 17 | } 18 | 19 | companion object { 20 | fun create(expression: String, color: Color, functionName: String): Function { 21 | val compiled: CompiledExpression = Evaluator.compile(expression) 22 | return Function( 23 | expression, color, functionName, compiled 24 | ) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/graphing/OffsetConverters.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.graphing 2 | 3 | import androidx.compose.ui.geometry.Offset 4 | 5 | fun Offset.unitToPxCoordinates( 6 | window: Window, 7 | canvasWidth: Float, 8 | canvasHeight: Float 9 | ): Offset { 10 | return Offset( 11 | (x - window.xMin) / (window.xMax - window.xMin) * canvasWidth, 12 | (window.yMax - y) / (window.yMax - window.yMin) * canvasHeight 13 | ) 14 | } 15 | 16 | fun Offset.pxToUnitCoordinates( 17 | window: Window, 18 | canvasWidth: Float, 19 | canvasHeight: Float 20 | ): Offset { 21 | return Offset( 22 | window.xMin + x * (window.xMax - window.xMin) / canvasWidth, 23 | window.yMax - y * (window.yMax - window.yMin) / canvasHeight 24 | ) 25 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/graphing/Token.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.graphing 2 | 3 | sealed class Token(val position: Int?) { 4 | class Group( 5 | position: Int?, 6 | val tokens: MutableList 7 | ) : Token(position = position) 8 | 9 | class Call( 10 | position: Int? = null, 11 | var argumentsGroups: MutableList>? = null, 12 | var arguments: MutableList? = null, 13 | var function: EvalFunctionBlock 14 | ) : Token(position) 15 | 16 | class Var( 17 | position: Int? = null, 18 | var value: String 19 | ) : Token(position) 20 | 21 | class Number( 22 | position: Int? = null, 23 | var value: Double 24 | ) : Token(position) 25 | 26 | class Op( 27 | position: Int? = null, 28 | var value: Operator 29 | ) : Token(position) { 30 | var left: Token? = null 31 | var right: Token? = null 32 | } 33 | 34 | class LeftParen( 35 | position: Int? = null 36 | ) : Token(position) 37 | 38 | class RightParen( 39 | position: Int? = null 40 | ) : Token(position) 41 | 42 | class Comma( 43 | position: Int? = null 44 | ) : Token(position) 45 | } 46 | 47 | enum class Operator(val text: String) { 48 | ADD("+"), 49 | SUB("-"), 50 | MUL("*"), 51 | DIV("/"), 52 | POW("**"), 53 | MOD("%") 54 | } 55 | 56 | internal fun String.toOperator(): Operator = when (this) { 57 | "+" -> Operator.ADD 58 | "-" -> Operator.SUB 59 | "*" -> Operator.MUL 60 | "/" -> Operator.DIV 61 | "**" -> Operator.POW 62 | "%" -> Operator.MOD 63 | else -> throw IllegalArgumentException("Unknown operator: $this") 64 | } 65 | 66 | internal val Operator.length: Int 67 | get() = when (this) { 68 | Operator.POW -> 2 69 | else -> 1 70 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/data/graphing/Window.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.data.graphing 2 | 3 | /** 4 | * The graph is drawn relative to this window and the window is drawn on the canvas. 5 | * 6 | * @property xScale The horizontal scale of the window. 7 | * @property yScale The vertical scale of the window. 8 | * @property xMin The leftmost boundary of the x-coordinate. 9 | * @property yMin The bottommost boundary of the y-coordinate. 10 | * @property xMax The rightmost boundary of the x-coordinate. 11 | * @property yMax The topmost boundary of the y-coordinate. 12 | */ 13 | class Window { 14 | 15 | var xMin: Float = -20f 16 | var yMin: Float = -40f 17 | var xMax: Float = 20f 18 | var yMax: Float = 40f 19 | 20 | var xScale: Float = 4f 21 | var yScale: Float = 4f 22 | 23 | val minHorizontalGridLines: Int = 8 24 | val minVerticalGridLines: Int = 4 25 | 26 | val maxHorizontalGridLines: Int = 24 27 | val maxVerticalGridLines: Int = 12 28 | 29 | fun findAutoScale() { 30 | var xScale = this.xScale 31 | var yScale = this.yScale 32 | 33 | assert(xMax > xMin) 34 | assert(yMax > yMin) 35 | 36 | if (xScale <= 0f || yScale <= 0f) { 37 | //Invalid settings 38 | xScale = 4f 39 | yScale = 4f 40 | } 41 | var windowWidth = (xMax - xMin) / xScale 42 | var windowHeight = (yMax - yMin) / yScale 43 | 44 | while (windowWidth > maxVerticalGridLines) { 45 | xScale *= 2 46 | windowWidth = (xMax - xMin) / xScale 47 | } 48 | while (windowHeight > maxHorizontalGridLines) { 49 | yScale *= 2 50 | windowHeight = (yMax - yMin) / yScale 51 | } 52 | while (windowWidth < minVerticalGridLines) { 53 | xScale /= 2 54 | windowWidth = (xMax - xMin) / xScale 55 | } 56 | while (windowHeight < minHorizontalGridLines) { 57 | yScale /= 2 58 | windowHeight = (yMax - yMin) / yScale 59 | } 60 | this.xScale = xScale 61 | this.yScale = yScale 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/CalculatorScreen.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui 2 | 3 | import android.content.res.Configuration.ORIENTATION_LANDSCAPE 4 | import androidx.compose.foundation.ExperimentalFoundationApi 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.fillMaxWidth 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.material3.MaterialTheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.platform.LocalConfiguration 14 | import androidx.compose.ui.tooling.preview.Preview 15 | import androidx.compose.ui.unit.dp 16 | import androidx.lifecycle.viewmodel.compose.viewModel 17 | import net.youapps.calcyou.ui.components.CalculatorDisplay 18 | import net.youapps.calcyou.ui.components.CenterKeypadHorizontal 19 | import net.youapps.calcyou.ui.components.Keypad 20 | import net.youapps.calcyou.ui.components.SideKeypadHorizontal 21 | import net.youapps.calcyou.viewmodels.CalculatorViewModel 22 | 23 | @OptIn(ExperimentalFoundationApi::class) 24 | @Composable 25 | fun CalculatorScreen( 26 | modifier: Modifier = Modifier, 27 | calculatorViewModel: CalculatorViewModel = viewModel(factory = CalculatorViewModel.Factory) 28 | ) { 29 | val orientation = LocalConfiguration.current.orientation 30 | if (orientation == ORIENTATION_LANDSCAPE) { 31 | Row( 32 | modifier = Modifier 33 | .padding(8.dp) 34 | .fillMaxWidth() 35 | ) { 36 | Column(modifier = Modifier.weight(1f)) { 37 | SideKeypadHorizontal( 38 | onEvent = calculatorViewModel::onEvent, 39 | square = false, 40 | textStyle = MaterialTheme.typography.headlineSmall 41 | ) 42 | } 43 | Column(modifier = Modifier.weight(1f)) { 44 | CalculatorDisplay( 45 | calculatorViewModel, 46 | primaryTextStyle = MaterialTheme.typography.headlineMedium, 47 | secondaryTextStyle = MaterialTheme.typography.headlineSmall 48 | ) 49 | CenterKeypadHorizontal( 50 | calculatorViewModel::onEvent, 51 | textStyle = MaterialTheme.typography.headlineSmall, 52 | iconSize = 32.dp 53 | ) 54 | } 55 | } 56 | } else { 57 | Column( 58 | modifier = Modifier 59 | .padding(8.dp) 60 | .fillMaxWidth() 61 | .then(modifier), 62 | verticalArrangement = Arrangement.spacedBy(8.dp) 63 | ) { 64 | CalculatorDisplay(calculatorViewModel) 65 | Keypad(onEvent = calculatorViewModel::onEvent) 66 | } 67 | } 68 | } 69 | 70 | @Preview(showBackground = true) 71 | @Composable 72 | private fun DefaultPreview() { 73 | CalculatorScreen() 74 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/MainScreen.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui 2 | 3 | import android.content.res.Configuration.ORIENTATION_LANDSCAPE 4 | import android.content.res.Configuration.ORIENTATION_PORTRAIT 5 | import androidx.compose.foundation.layout.ExperimentalLayoutApi 6 | import androidx.compose.foundation.layout.Row 7 | import androidx.compose.foundation.layout.consumeWindowInsets 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.material.icons.Icons 11 | import androidx.compose.material.icons.rounded.Menu 12 | import androidx.compose.material3.CenterAlignedTopAppBar 13 | import androidx.compose.material3.DrawerValue 14 | import androidx.compose.material3.ExperimentalMaterial3Api 15 | import androidx.compose.material3.Icon 16 | import androidx.compose.material3.IconButton 17 | import androidx.compose.material3.ModalNavigationDrawer 18 | import androidx.compose.material3.NavigationRail 19 | import androidx.compose.material3.Scaffold 20 | import androidx.compose.material3.Text 21 | import androidx.compose.material3.rememberDrawerState 22 | import androidx.compose.runtime.Composable 23 | import androidx.compose.runtime.getValue 24 | import androidx.compose.runtime.mutableStateOf 25 | import androidx.compose.runtime.remember 26 | import androidx.compose.runtime.rememberCoroutineScope 27 | import androidx.compose.runtime.setValue 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.platform.LocalConfiguration 30 | import androidx.compose.ui.res.stringResource 31 | import androidx.navigation.compose.rememberNavController 32 | import kotlinx.coroutines.launch 33 | import net.youapps.calcyou.AppNavHost 34 | import net.youapps.calcyou.Destination 35 | import net.youapps.calcyou.R 36 | import net.youapps.calcyou.ui.components.NavDrawerContent 37 | import net.youapps.calcyou.ui.components.NavRailContent 38 | 39 | @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) 40 | @Composable 41 | fun MainScreen() { 42 | val navController = rememberNavController() 43 | val drawerState = rememberDrawerState(DrawerValue.Closed) 44 | val scope = rememberCoroutineScope() 45 | var currentDestination by remember { 46 | mutableStateOf(Destination.Calculator) 47 | } 48 | val orientation = LocalConfiguration.current.orientation 49 | ModalNavigationDrawer( 50 | drawerState = drawerState, 51 | gesturesEnabled = drawerState.isOpen || (orientation == ORIENTATION_LANDSCAPE), 52 | drawerContent = { 53 | NavDrawerContent(currentDestination = currentDestination, onDestinationSelected = { 54 | scope.launch { 55 | drawerState.close() 56 | } 57 | navController.popBackStack() 58 | navController.navigate(it.route) 59 | currentDestination = it 60 | 61 | }) 62 | } 63 | ) { 64 | Scaffold(topBar = { 65 | if (orientation == ORIENTATION_PORTRAIT) { 66 | CenterAlignedTopAppBar(title = { 67 | Text( 68 | text = stringResource( 69 | id = R.string.app_name 70 | ) 71 | ) 72 | }, navigationIcon = { 73 | IconButton(onClick = { scope.launch { drawerState.open() } }) { 74 | Icon(imageVector = Icons.Rounded.Menu, contentDescription = null) 75 | } 76 | }) 77 | } 78 | }) { paddingValues -> 79 | Row( 80 | Modifier 81 | .fillMaxSize() 82 | .consumeWindowInsets(paddingValues) 83 | .padding(paddingValues) 84 | ) { 85 | if (orientation == ORIENTATION_LANDSCAPE) { 86 | NavigationRail { 87 | NavRailContent(currentDestination = currentDestination) { 88 | navController.popBackStack() 89 | navController.navigate(it.route) 90 | currentDestination = it 91 | } 92 | } 93 | } 94 | AppNavHost( 95 | modifier = Modifier.weight(1f), 96 | navHostController = navController 97 | ) 98 | } 99 | 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/AddNewConstantDialog.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.shape.RoundedCornerShape 10 | import androidx.compose.foundation.text.KeyboardOptions 11 | import androidx.compose.material3.MaterialTheme 12 | import androidx.compose.material3.OutlinedTextField 13 | import androidx.compose.material3.Text 14 | import androidx.compose.material3.TextButton 15 | import androidx.compose.material3.surfaceColorAtElevation 16 | import androidx.compose.runtime.Composable 17 | import androidx.compose.runtime.getValue 18 | import androidx.compose.runtime.mutableStateOf 19 | import androidx.compose.runtime.remember 20 | import androidx.compose.runtime.setValue 21 | import androidx.compose.ui.Alignment 22 | import androidx.compose.ui.Modifier 23 | import androidx.compose.ui.draw.clip 24 | import androidx.compose.ui.res.stringResource 25 | import androidx.compose.ui.text.TextStyle 26 | import androidx.compose.ui.text.font.FontFamily 27 | import androidx.compose.ui.text.font.FontStyle 28 | import androidx.compose.ui.text.font.FontWeight 29 | import androidx.compose.ui.text.input.KeyboardType 30 | import androidx.compose.ui.unit.dp 31 | import androidx.compose.ui.window.Dialog 32 | import net.youapps.calcyou.R 33 | import net.youapps.calcyou.data.graphing.Constant 34 | 35 | @Composable 36 | fun AddNewConstantDialog( 37 | modifier: Modifier = Modifier, 38 | onConfirm: (constant: Constant) -> Unit, 39 | onCancel: () -> Unit, 40 | initialConstant: Constant, 41 | ) { 42 | Dialog(onCancel) { 43 | Box( 44 | modifier = modifier 45 | .clip(RoundedCornerShape(16.dp)) 46 | .background(MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp)) 47 | ) { 48 | Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { 49 | Text( 50 | text = stringResource(R.string.add_constant), 51 | style = MaterialTheme.typography.titleLarge 52 | ) 53 | var text by remember { mutableStateOf(initialConstant.value.toString()) } 54 | var isError by remember { mutableStateOf(false) } 55 | OutlinedTextField( 56 | value = text, 57 | onValueChange = { 58 | text = it 59 | isError = strToDouble(it) == null 60 | }, 61 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), 62 | prefix = { 63 | Text( 64 | text = "${initialConstant.identifier} = ", style = TextStyle( 65 | fontFamily = FontFamily.Serif, 66 | fontWeight = FontWeight.Medium, 67 | fontSize = MaterialTheme.typography.bodyLarge.fontSize, 68 | fontStyle = FontStyle.Italic 69 | ) 70 | ) 71 | }, 72 | textStyle = TextStyle( 73 | fontFamily = FontFamily.Serif, 74 | fontWeight = FontWeight.Medium, 75 | fontSize = MaterialTheme.typography.bodyLarge.fontSize, 76 | fontStyle = FontStyle.Italic 77 | ) 78 | ) 79 | Row(Modifier.align(Alignment.End)) { 80 | TextButton(onClick = onCancel) { 81 | Text(text = stringResource(id = android.R.string.cancel)) 82 | } 83 | TextButton(onClick = { 84 | val value = strToDouble(text) ?: return@TextButton 85 | onConfirm(initialConstant.copy(value = value)) 86 | }, enabled = !isError) { 87 | Text(text = stringResource(id = android.R.string.ok)) 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | private fun strToDouble(string: String): Double? { 96 | return string.replace(",", ".").toDoubleOrNull() 97 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/CalculatorDisplay.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components 2 | 3 | import androidx.compose.foundation.ExperimentalFoundationApi 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.clickable 6 | import androidx.compose.foundation.layout.Arrangement 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.ColumnScope 9 | import androidx.compose.foundation.layout.Row 10 | import androidx.compose.foundation.layout.fillMaxWidth 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.lazy.LazyColumn 13 | import androidx.compose.foundation.lazy.items 14 | import androidx.compose.foundation.shape.RoundedCornerShape 15 | import androidx.compose.foundation.text.BasicTextField 16 | import androidx.compose.material3.MaterialTheme 17 | import androidx.compose.material3.Text 18 | import androidx.compose.material3.surfaceColorAtElevation 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.runtime.CompositionLocalProvider 21 | import androidx.compose.ui.Alignment 22 | import androidx.compose.ui.Modifier 23 | import androidx.compose.ui.draw.clip 24 | import androidx.compose.ui.platform.LocalTextInputService 25 | import androidx.compose.ui.text.TextStyle 26 | import androidx.compose.ui.text.style.TextAlign 27 | import androidx.compose.ui.unit.dp 28 | import net.youapps.calcyou.viewmodels.CalculatorViewModel 29 | 30 | @OptIn(ExperimentalFoundationApi::class) 31 | @Composable 32 | fun ColumnScope.CalculatorDisplay( 33 | calculatorViewModel: CalculatorViewModel, 34 | primaryTextStyle: TextStyle = MaterialTheme.typography.displayMedium, 35 | secondaryTextStyle: TextStyle = MaterialTheme.typography.displaySmall 36 | ) { 37 | Column( 38 | modifier = Modifier 39 | .fillMaxWidth() 40 | .weight(1.0f) 41 | .clip(RoundedCornerShape(24.dp)) 42 | .background(MaterialTheme.colorScheme.surfaceColorAtElevation(10.dp)) 43 | ) { 44 | LazyColumn( 45 | Modifier 46 | .fillMaxWidth() 47 | .padding(horizontal = 16.dp, vertical = 8.dp) 48 | .weight(1f), 49 | horizontalAlignment = Alignment.End, 50 | verticalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.Bottom) 51 | ) { 52 | items(items = calculatorViewModel.history) { item -> 53 | Text( 54 | text = item, 55 | modifier = Modifier 56 | .animateItemPlacement() 57 | .clickable { 58 | calculatorViewModel.setExpression(item) 59 | }, 60 | textAlign = TextAlign.End, 61 | style = secondaryTextStyle, 62 | color = MaterialTheme.colorScheme.onSurface, 63 | ) 64 | } 65 | } 66 | Row( 67 | Modifier 68 | .background(MaterialTheme.colorScheme.surfaceColorAtElevation(50.dp)), 69 | horizontalArrangement = Arrangement.End 70 | ) { 71 | CompositionLocalProvider(LocalTextInputService provides null) { 72 | BasicTextField( 73 | value = calculatorViewModel.displayText, 74 | onValueChange = { 75 | calculatorViewModel.displayText = it 76 | }, 77 | singleLine = true, 78 | modifier = Modifier 79 | .fillMaxWidth() 80 | .padding(vertical = 8.dp, horizontal = 16.dp), 81 | maxLines = 1, 82 | textStyle = primaryTextStyle.plus( 83 | TextStyle( 84 | textAlign = TextAlign.End, 85 | color = MaterialTheme.colorScheme.onSurface 86 | ) 87 | ) 88 | ) 89 | } 90 | } 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/ConverterCard.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components 2 | 3 | import androidx.annotation.StringRes 4 | import androidx.compose.foundation.Image 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Spacer 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.height 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.size 12 | import androidx.compose.material3.Card 13 | import androidx.compose.material3.CardDefaults 14 | import androidx.compose.material3.ExperimentalMaterial3Api 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Text 17 | import androidx.compose.material3.surfaceColorAtElevation 18 | import androidx.compose.runtime.Composable 19 | import androidx.compose.ui.Alignment 20 | import androidx.compose.ui.Modifier 21 | import androidx.compose.ui.graphics.ColorFilter 22 | import androidx.compose.ui.graphics.vector.ImageVector 23 | import androidx.compose.ui.res.stringResource 24 | import androidx.compose.ui.text.style.TextOverflow 25 | import androidx.compose.ui.unit.dp 26 | 27 | @OptIn(ExperimentalMaterial3Api::class) 28 | @Composable 29 | fun ConverterCard(icon: ImageVector, @StringRes title: Int, onClick: () -> Unit) { 30 | Card( 31 | modifier = Modifier 32 | .fillMaxSize(), 33 | onClick = onClick, 34 | colors = CardDefaults.cardColors( 35 | containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp), 36 | contentColor = MaterialTheme.colorScheme.onSurface 37 | ) 38 | ) { 39 | Column( 40 | modifier = Modifier 41 | .fillMaxSize() 42 | .padding(20.dp), 43 | verticalArrangement = Arrangement.Center, 44 | horizontalAlignment = Alignment.CenterHorizontally 45 | ) { 46 | Image( 47 | imageVector = icon, 48 | contentDescription = null, 49 | modifier = Modifier.size(32.dp), 50 | colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary) 51 | ) 52 | Spacer(modifier = Modifier.height(8.dp)) 53 | Text( 54 | text = stringResource(id = title), 55 | style = MaterialTheme.typography.titleSmall, 56 | maxLines = 1, 57 | overflow = TextOverflow.Ellipsis 58 | ) 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/buttons/CalculatorButton.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components.buttons 2 | 3 | import android.view.SoundEffectConstants 4 | import androidx.annotation.DrawableRes 5 | import androidx.compose.foundation.ExperimentalFoundationApi 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.combinedClickable 8 | import androidx.compose.foundation.layout.Box 9 | import androidx.compose.foundation.layout.BoxScope 10 | import androidx.compose.foundation.layout.RowScope 11 | import androidx.compose.foundation.layout.aspectRatio 12 | import androidx.compose.foundation.layout.defaultMinSize 13 | import androidx.compose.foundation.layout.size 14 | import androidx.compose.foundation.shape.CircleShape 15 | import androidx.compose.material3.ButtonDefaults 16 | import androidx.compose.material3.Icon 17 | import androidx.compose.material3.MaterialTheme 18 | import androidx.compose.material3.Text 19 | import androidx.compose.material3.surfaceColorAtElevation 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.ui.Alignment 22 | import androidx.compose.ui.Modifier 23 | import androidx.compose.ui.draw.clip 24 | import androidx.compose.ui.graphics.Color 25 | import androidx.compose.ui.hapticfeedback.HapticFeedbackType 26 | import androidx.compose.ui.platform.LocalHapticFeedback 27 | import androidx.compose.ui.platform.LocalView 28 | import androidx.compose.ui.res.painterResource 29 | import androidx.compose.ui.text.TextStyle 30 | import androidx.compose.ui.unit.Dp 31 | import androidx.compose.ui.unit.dp 32 | 33 | @Composable 34 | fun RowScope.CalculatorButton( 35 | text: String, 36 | textColor: Color = MaterialTheme.colorScheme.onSurface, 37 | backgroundColor: Color = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp), 38 | square: Boolean = true, 39 | textStyle: TextStyle = MaterialTheme.typography.displaySmall, 40 | onClick: () -> Unit, 41 | onLongClick: (() -> Unit)? = null, 42 | ) { 43 | CalculatorButton( 44 | backgroundColor = backgroundColor, 45 | square = square, 46 | onClick = onClick, 47 | onLongClick = onLongClick 48 | ) { 49 | Text( 50 | text = text, 51 | color = textColor, 52 | style = textStyle 53 | ) 54 | } 55 | } 56 | 57 | @Composable 58 | fun RowScope.CalculatorButton( 59 | @DrawableRes iconRes: Int, 60 | textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant, 61 | backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant, 62 | square: Boolean = true, 63 | iconSize: Dp = 48.dp, 64 | onClick: () -> Unit, 65 | onLongClick: (() -> Unit)? = null, 66 | ) { 67 | CalculatorButton( 68 | backgroundColor = backgroundColor, 69 | square = square, 70 | onClick = onClick, 71 | onLongClick = onLongClick 72 | ) { 73 | Icon( 74 | painter = painterResource(id = iconRes), 75 | contentDescription = null, 76 | modifier = Modifier.size(iconSize), 77 | tint = textColor 78 | ) 79 | } 80 | } 81 | 82 | @OptIn(ExperimentalFoundationApi::class) 83 | @Composable 84 | fun RowScope.CalculatorButton( 85 | backgroundColor: Color = MaterialTheme.colorScheme.surfaceVariant, 86 | square: Boolean = true, 87 | onClick: () -> Unit, 88 | onLongClick: (() -> Unit)?, 89 | content: @Composable (BoxScope.() -> Unit) 90 | ) { 91 | val view = LocalView.current 92 | val haptic = LocalHapticFeedback.current 93 | Box( 94 | contentAlignment = Alignment.Center, 95 | modifier = Modifier 96 | .clip(CircleShape) 97 | .combinedClickable( 98 | onClick = { 99 | onClick.invoke() 100 | view.playSoundEffect(SoundEffectConstants.CLICK) 101 | }, onLongClick = { 102 | if (onLongClick != null) { 103 | onLongClick.invoke() 104 | haptic.performHapticFeedback(HapticFeedbackType.LongPress) 105 | } 106 | } 107 | ) 108 | .defaultMinSize( 109 | minWidth = ButtonDefaults.MinWidth, 110 | minHeight = ButtonDefaults.MinHeight 111 | ) 112 | .background(backgroundColor) 113 | .weight(1f) 114 | .let { 115 | if (square) it.aspectRatio(1f) else it 116 | }, 117 | content = content 118 | ) 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/buttons/CalculatorTextButton.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components.buttons 2 | 3 | import android.view.SoundEffectConstants 4 | import androidx.compose.foundation.ExperimentalFoundationApi 5 | import androidx.compose.foundation.combinedClickable 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.RowScope 8 | import androidx.compose.foundation.layout.aspectRatio 9 | import androidx.compose.foundation.shape.CircleShape 10 | import androidx.compose.material3.MaterialTheme 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.draw.clip 16 | import androidx.compose.ui.graphics.Color 17 | import androidx.compose.ui.hapticfeedback.HapticFeedbackType 18 | import androidx.compose.ui.platform.LocalHapticFeedback 19 | import androidx.compose.ui.platform.LocalView 20 | import androidx.compose.ui.text.TextStyle 21 | 22 | @OptIn(ExperimentalFoundationApi::class) 23 | @Composable 24 | fun RowScope.CalculatorTextButton( 25 | text: String, 26 | textColor: Color = MaterialTheme.colorScheme.onSurface, 27 | square: Boolean = true, 28 | textStyle: TextStyle = MaterialTheme.typography.displaySmall, 29 | onClick: () -> Unit, 30 | onLongClick: (() -> Unit)? = null 31 | ) { 32 | val view = LocalView.current 33 | val haptic = LocalHapticFeedback.current 34 | Box( 35 | contentAlignment = Alignment.Center, 36 | modifier = Modifier 37 | .clip(CircleShape) 38 | .combinedClickable( 39 | onClick = { 40 | onClick.invoke() 41 | view.playSoundEffect(SoundEffectConstants.CLICK) 42 | }, onLongClick = { 43 | if (onLongClick != null) { 44 | onLongClick.invoke() 45 | haptic.performHapticFeedback(HapticFeedbackType.LongPress) 46 | } 47 | } 48 | ) 49 | .weight(1f) 50 | .let { 51 | if (square) it.aspectRatio(1f) else it 52 | } 53 | ) { 54 | Text( 55 | text = text, 56 | color = textColor, 57 | style = textStyle 58 | ) 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/buttons/KeyRow.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components.buttons 2 | 3 | import androidx.compose.foundation.horizontalScroll 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.Row 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.rememberScrollState 10 | import androidx.compose.material3.MaterialTheme 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.unit.dp 15 | 16 | @Composable 17 | fun KeyRow( 18 | title: String, 19 | items: Array, 20 | onKeyPress: (String) -> Unit 21 | ) { 22 | Column(Modifier.fillMaxWidth()) { 23 | val scrollState = rememberScrollState() 24 | Text(text = title, style = MaterialTheme.typography.bodyMedium) 25 | Row( 26 | modifier = Modifier 27 | .horizontalScroll(scrollState) 28 | .padding(vertical = 8.dp), 29 | horizontalArrangement = Arrangement.spacedBy(8.dp) 30 | ) { 31 | items.forEach { item -> 32 | KeyboardKey(keyboardKey = item, onClick = { onKeyPress(item) }) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/buttons/KeyboardKey.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components.buttons 2 | 3 | import android.view.SoundEffectConstants 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.clickable 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.size 10 | import androidx.compose.foundation.shape.RoundedCornerShape 11 | import androidx.compose.material3.Icon 12 | import androidx.compose.material3.MaterialTheme 13 | import androidx.compose.material3.Text 14 | import androidx.compose.material3.surfaceColorAtElevation 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.ui.Alignment 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.draw.clip 19 | import androidx.compose.ui.graphics.vector.ImageVector 20 | import androidx.compose.ui.platform.LocalView 21 | import androidx.compose.ui.unit.dp 22 | 23 | @Composable 24 | fun KeyboardKey( 25 | keyboardKey: String, 26 | onClick: () -> Unit 27 | ) { 28 | val view = LocalView.current 29 | Box( 30 | modifier = Modifier 31 | .clip(shape = RoundedCornerShape(8.dp)) 32 | .background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)) 33 | .size(48.dp) 34 | .clickable { 35 | onClick() 36 | view.playSoundEffect(SoundEffectConstants.CLICK) 37 | }, 38 | contentAlignment = Alignment.Center 39 | ) { 40 | Text( 41 | keyboardKey, 42 | Modifier 43 | .padding(4.dp), 44 | style = MaterialTheme.typography.headlineSmall, 45 | color = MaterialTheme.colorScheme.onSurface 46 | 47 | ) 48 | } 49 | } 50 | 51 | @Composable 52 | fun KeyboardSpecialKey( 53 | keyboardKey: String, 54 | onClick: () -> Unit, 55 | icon: ImageVector? = null 56 | ) { 57 | val view = LocalView.current 58 | Box( 59 | modifier = Modifier 60 | .clip(RoundedCornerShape(50)) 61 | .height(48.dp) 62 | .background(MaterialTheme.colorScheme.secondaryContainer) 63 | .clickable { 64 | onClick() 65 | view.playSoundEffect(SoundEffectConstants.CLICK) 66 | }, 67 | contentAlignment = Alignment.Center 68 | ) { 69 | if (icon != null) { 70 | Icon( 71 | imageVector = icon, 72 | contentDescription = keyboardKey, 73 | modifier = Modifier.padding(horizontal = 16.dp), 74 | tint = MaterialTheme.colorScheme.onPrimaryContainer 75 | ) 76 | } else { 77 | Text( 78 | keyboardKey, 79 | Modifier 80 | .padding(vertical = 4.dp, horizontal = 16.dp), 81 | style = MaterialTheme.typography.bodyLarge, 82 | color = MaterialTheme.colorScheme.onPrimaryContainer 83 | ) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/components/buttons/KeyboardPanel.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.components.buttons 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.rememberScrollState 5 | import androidx.compose.foundation.verticalScroll 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.res.stringResource 9 | import net.youapps.calcyou.R 10 | import net.youapps.calcyou.data.KeyMap 11 | 12 | @Composable 13 | fun KeyboardPanel(onKeyPress: (String) -> Unit, modifier: Modifier = Modifier) { 14 | val scrollState = rememberScrollState() 15 | Column(modifier = modifier.verticalScroll(scrollState)) { 16 | KeyRow(title = stringResource(R.string.numbers), items = KeyMap.numbers, onKeyPress) 17 | KeyRow(title = stringResource(R.string.fractions), items = KeyMap.fractions, onKeyPress) 18 | KeyRow(title = stringResource(R.string.simple_math), items = KeyMap.simpleMath, onKeyPress) 19 | KeyRow( 20 | title = stringResource(R.string.common_symbols), 21 | items = KeyMap.scienceSymbols, 22 | onKeyPress 23 | ) 24 | KeyRow( 25 | title = stringResource(R.string.superscript), 26 | items = KeyMap.superscriptNumbers, 27 | onKeyPress 28 | ) 29 | KeyRow( 30 | title = stringResource(R.string.subscript), 31 | items = KeyMap.subscriptNumbers, 32 | onKeyPress 33 | ) 34 | KeyRow( 35 | title = stringResource(R.string.english_uppercase), 36 | items = KeyMap.englishUppercaseLetters, 37 | onKeyPress 38 | ) 39 | KeyRow( 40 | title = stringResource(R.string.english_lowercase), 41 | items = KeyMap.englishLowercaseLetters, 42 | onKeyPress 43 | ) 44 | KeyRow( 45 | title = stringResource(R.string.greek_uppercase), 46 | items = KeyMap.greekUppercaseLetters, 47 | onKeyPress 48 | ) 49 | KeyRow( 50 | title = stringResource(R.string.greek_lowercase), 51 | items = KeyMap.greekLowercaseLetters, 52 | onKeyPress 53 | ) 54 | KeyRow(title = stringResource(R.string.arrows), items = KeyMap.arrows, onKeyPress) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/screens/CharacterInputScreen.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.screens 2 | 3 | import androidx.compose.foundation.horizontalScroll 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.Row 7 | import androidx.compose.foundation.layout.fillMaxSize 8 | import androidx.compose.foundation.layout.fillMaxWidth 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.foundation.layout.size 11 | import androidx.compose.foundation.rememberScrollState 12 | import androidx.compose.foundation.shape.RoundedCornerShape 13 | import androidx.compose.material.icons.Icons 14 | import androidx.compose.material.icons.filled.Backspace 15 | import androidx.compose.material.icons.filled.KeyboardReturn 16 | import androidx.compose.material3.Divider 17 | import androidx.compose.material3.MaterialTheme 18 | import androidx.compose.material3.Text 19 | import androidx.compose.material3.TextField 20 | import androidx.compose.material3.TextFieldDefaults 21 | import androidx.compose.runtime.Composable 22 | import androidx.compose.runtime.getValue 23 | import androidx.compose.runtime.mutableStateOf 24 | import androidx.compose.runtime.remember 25 | import androidx.compose.runtime.setValue 26 | import androidx.compose.ui.Alignment 27 | import androidx.compose.ui.Modifier 28 | import androidx.compose.ui.draw.clip 29 | import androidx.compose.ui.graphics.Color 30 | import androidx.compose.ui.res.stringResource 31 | import androidx.compose.ui.text.input.TextFieldValue 32 | import androidx.compose.ui.tooling.preview.Preview 33 | import androidx.compose.ui.unit.dp 34 | import net.youapps.calcyou.R 35 | import net.youapps.calcyou.data.backSpace 36 | import net.youapps.calcyou.data.insertText 37 | import net.youapps.calcyou.ui.components.buttons.KeyboardPanel 38 | import net.youapps.calcyou.ui.components.buttons.KeyboardSpecialKey 39 | 40 | @Composable 41 | fun CharacterInputScreen() { 42 | Column( 43 | modifier = Modifier 44 | .padding(15.dp) 45 | .fillMaxSize() 46 | ) { 47 | var textFieldValue by remember { mutableStateOf(TextFieldValue("")) } 48 | TextField( 49 | value = textFieldValue, 50 | onValueChange = { textFieldValue = it }, 51 | placeholder = { 52 | Text( 53 | text = "E = mc²", 54 | style = MaterialTheme.typography.displaySmall, 55 | color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.5f) 56 | ) 57 | }, 58 | textStyle = MaterialTheme.typography.displaySmall, 59 | modifier = Modifier.fillMaxWidth(), 60 | colors = TextFieldDefaults.colors( 61 | focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant, 62 | unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant, 63 | disabledContainerColor = Color.Transparent, 64 | focusedIndicatorColor = Color.Transparent, 65 | unfocusedIndicatorColor = Color.Transparent, 66 | disabledIndicatorColor = Color.Transparent 67 | ), 68 | shape = RoundedCornerShape(16.dp) 69 | ) 70 | 71 | Divider( 72 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), 73 | modifier = Modifier 74 | .align(Alignment.CenterHorizontally) 75 | .padding(10.dp) 76 | .size(70.dp, 4.dp) 77 | .clip(RoundedCornerShape(50)) 78 | ) 79 | val rowScrollState = rememberScrollState() 80 | Row( 81 | modifier = Modifier 82 | .horizontalScroll(rowScrollState) 83 | .padding(vertical = 8.dp), 84 | horizontalArrangement = Arrangement.spacedBy(8.dp) 85 | ) { 86 | KeyboardSpecialKey( 87 | keyboardKey = stringResource(R.string.space), 88 | onClick = { textFieldValue = textFieldValue.insertText(" ") } 89 | ) 90 | KeyboardSpecialKey( 91 | keyboardKey = stringResource(R.string.carriage_return), 92 | icon = Icons.Default.KeyboardReturn, 93 | onClick = { textFieldValue = textFieldValue.insertText("\n") } 94 | ) 95 | KeyboardSpecialKey( 96 | keyboardKey = stringResource(R.string.backspace), 97 | icon = Icons.Default.Backspace, 98 | onClick = { 99 | textFieldValue = textFieldValue.backSpace() 100 | } 101 | ) 102 | } 103 | KeyboardPanel( 104 | onKeyPress = { 105 | textFieldValue = textFieldValue.insertText(it) 106 | }, modifier = Modifier 107 | .fillMaxWidth() 108 | .weight(1f) 109 | ) 110 | } 111 | } 112 | 113 | @Preview(showBackground = true) 114 | @Composable 115 | fun KeypadScreenPreview() { 116 | CharacterInputScreen() 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/screens/ConverterGridScreen.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.screens 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.lazy.grid.GridCells 7 | import androidx.compose.foundation.lazy.grid.LazyVerticalGrid 8 | import androidx.compose.foundation.lazy.grid.items 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.unit.dp 12 | import net.youapps.calcyou.Destination 13 | import net.youapps.calcyou.ui.components.ConverterCard 14 | 15 | @Composable 16 | fun ConverterGridScreen(onNavigate: (Destination) -> Unit) { 17 | LazyVerticalGrid( 18 | columns = GridCells.Adaptive(100.dp), 19 | horizontalArrangement = Arrangement.spacedBy(8.dp), 20 | verticalArrangement = Arrangement.spacedBy(8.dp), 21 | modifier = Modifier 22 | .fillMaxSize() 23 | .padding(horizontal = 8.dp) 24 | ) { 25 | items(Destination.Converter.values) { converter -> 26 | ConverterCard(icon = converter.icon, title = converter.resId, onClick = { 27 | onNavigate(converter) 28 | }) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/screens/graphing/CanvasView.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.screens.graphing 2 | 3 | import androidx.compose.foundation.Canvas 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.gestures.awaitEachGesture 6 | import androidx.compose.foundation.gestures.awaitFirstDown 7 | import androidx.compose.foundation.gestures.calculateCentroid 8 | import androidx.compose.foundation.gestures.calculatePan 9 | import androidx.compose.foundation.gestures.calculateZoom 10 | import androidx.compose.foundation.layout.Box 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.material3.MaterialTheme 13 | import androidx.compose.runtime.Composable 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.graphics.drawscope.scale 16 | import androidx.compose.ui.input.pointer.pointerInput 17 | import androidx.compose.ui.input.pointer.positionChange 18 | import androidx.compose.ui.text.rememberTextMeasurer 19 | import androidx.compose.ui.tooling.preview.Preview 20 | import androidx.lifecycle.viewmodel.compose.viewModel 21 | import net.youapps.calcyou.data.graphing.pxToUnitCoordinates 22 | import net.youapps.calcyou.viewmodels.GraphViewModel 23 | 24 | @Composable 25 | fun CanvasView(vm: GraphViewModel) { 26 | Box(Modifier.background(MaterialTheme.colorScheme.background)) { 27 | val textMeasurer = rememberTextMeasurer() 28 | val drawModifier = Modifier 29 | .fillMaxSize() 30 | .pointerInput(Unit) { 31 | awaitEachGesture { 32 | val downEvent = awaitFirstDown() 33 | if (downEvent.pressed != downEvent.previousPressed) downEvent.consume() 34 | do { 35 | val event = awaitPointerEvent() 36 | if (event.changes.size == 1) { 37 | val pan = event.changes 38 | .first() 39 | .positionChange() 40 | with(vm.window) { 41 | 42 | val xPan = pan.x * (xMax - xMin) / size.width.toFloat() 43 | val yPan = pan.y * (yMax - yMin) / size.height.toFloat() 44 | 45 | xMin -= xPan 46 | xMax -= xPan 47 | yMin += yPan 48 | yMax += yPan 49 | } 50 | event.changes 51 | .first() 52 | .consume() 53 | } else if (event.changes.size > 1) { 54 | with(vm.window) { 55 | 56 | val zoom = event.calculateZoom() 57 | val center = event 58 | .calculateCentroid() 59 | .pxToUnitCoordinates( 60 | vm.window, 61 | size.width.toFloat(), 62 | size.height.toFloat() 63 | ) 64 | 65 | xMin = center.x + (xMin - center.x) / zoom 66 | xMax = center.x + (xMax - center.x) / zoom 67 | yMin = center.y + (yMin - center.y) / zoom 68 | yMax = center.y + (yMax - center.y) / zoom 69 | 70 | val pan = event 71 | .calculatePan() 72 | 73 | 74 | val xPan = pan.x * (xMax - xMin) / size.width.toFloat() 75 | val yPan = pan.y * (yMax - yMin) / size.height.toFloat() 76 | 77 | xMin -= xPan 78 | xMax -= xPan 79 | yMin += yPan 80 | yMax += yPan 81 | } 82 | event.changes.forEach { it.consume() } 83 | } 84 | vm.window = vm.window 85 | } while (event.changes.any { it.pressed }) 86 | } 87 | } 88 | val gridLinesColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.2f) 89 | val gridAxesColor = MaterialTheme.colorScheme.onBackground 90 | Canvas(modifier = drawModifier) { 91 | val scale = 1f 92 | scale(scale) { 93 | renderCanvas(vm.window, vm, scale, textMeasurer, gridLinesColor, gridAxesColor) 94 | } 95 | } 96 | } 97 | } 98 | 99 | @Preview(showBackground = true) 100 | @Composable 101 | fun CanvasViewPreview() { 102 | CanvasView(vm = viewModel()) 103 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.platform.LocalContext 14 | import androidx.compose.ui.platform.LocalView 15 | import androidx.core.view.WindowCompat 16 | 17 | private val DarkColorScheme = darkColorScheme( 18 | primary = Purple80, 19 | secondary = PurpleGrey80, 20 | tertiary = Pink80 21 | ) 22 | 23 | private val LightColorScheme = lightColorScheme( 24 | primary = Purple40, 25 | secondary = PurpleGrey40, 26 | tertiary = Pink40 27 | 28 | /* Other default colors to override 29 | background = Color(0xFFFFFBFE), 30 | surface = Color(0xFFFFFBFE), 31 | onPrimary = Color.White, 32 | onSecondary = Color.White, 33 | onTertiary = Color.White, 34 | onBackground = Color(0xFF1C1B1F), 35 | onSurface = Color(0xFF1C1B1F), 36 | */ 37 | ) 38 | 39 | @Composable 40 | fun CalcYouTheme( 41 | darkTheme: Boolean = isSystemInDarkTheme(), 42 | // Dynamic color is available on Android 12+ 43 | dynamicColor: Boolean = true, 44 | content: @Composable () -> Unit 45 | ) { 46 | val colorScheme = when { 47 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 48 | val context = LocalContext.current 49 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 50 | } 51 | 52 | darkTheme -> DarkColorScheme 53 | else -> LightColorScheme 54 | } 55 | val view = LocalView.current 56 | if (!view.isInEditMode) { 57 | SideEffect { 58 | val window = (view.context as Activity).window 59 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme 60 | } 61 | } 62 | 63 | MaterialTheme( 64 | colorScheme = colorScheme, 65 | typography = Typography, 66 | content = content 67 | ) 68 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/viewmodels/CalculatorViewModel.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.viewmodels 2 | 3 | import android.content.Context 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateListOf 6 | import androidx.compose.runtime.mutableStateOf 7 | import androidx.compose.runtime.setValue 8 | import androidx.compose.ui.text.input.TextFieldValue 9 | import androidx.lifecycle.ViewModel 10 | import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY 11 | import androidx.lifecycle.viewmodel.initializer 12 | import androidx.lifecycle.viewmodel.viewModelFactory 13 | import net.youapps.calcyou.CalculatorApplication 14 | import net.youapps.calcyou.data.CalculatorEvent 15 | import net.youapps.calcyou.data.EventHandler 16 | 17 | class CalculatorViewModel(context: Context) : ViewModel() { 18 | 19 | private val eventHandler = 20 | EventHandler(context, onUpdateHistory = this::onUpdateHistory) 21 | var displayText by mutableStateOf(TextFieldValue("")) 22 | 23 | var history: MutableList = mutableStateListOf() 24 | 25 | fun setExpression(text: String) { 26 | displayText = TextFieldValue(text) 27 | } 28 | 29 | fun onEvent(event: CalculatorEvent) { 30 | displayText = eventHandler.processEvent(event, displayText) 31 | } 32 | 33 | private fun onUpdateHistory(item: String) { 34 | history.add(item) 35 | } 36 | 37 | companion object { 38 | val Factory = viewModelFactory { 39 | initializer { 40 | val application = this[APPLICATION_KEY] as CalculatorApplication 41 | CalculatorViewModel(application) 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/net/youapps/calcyou/viewmodels/GraphViewModel.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou.viewmodels 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import androidx.compose.runtime.getValue 6 | import androidx.compose.runtime.mutableIntStateOf 7 | import androidx.compose.runtime.mutableStateListOf 8 | import androidx.compose.runtime.mutableStateOf 9 | import androidx.compose.runtime.neverEqualPolicy 10 | import androidx.compose.runtime.setValue 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.lifecycle.AndroidViewModel 13 | import net.youapps.calcyou.R 14 | import net.youapps.calcyou.data.graphing.Defaults 15 | import net.youapps.calcyou.data.graphing.Evaluator 16 | import net.youapps.calcyou.data.graphing.Function 17 | import net.youapps.calcyou.data.graphing.Constant 18 | import net.youapps.calcyou.data.graphing.Window 19 | import net.youapps.calcyou.ui.components.rainbowColors 20 | import java.text.ParseException 21 | import kotlin.random.Random 22 | 23 | class GraphViewModel(private val application: Application) : AndroidViewModel(application) { 24 | private val context: Context 25 | get() = application.applicationContext 26 | var window by mutableStateOf(Window(), neverEqualPolicy()) 27 | val functions = mutableStateListOf() 28 | val constants = mutableStateListOf() 29 | 30 | private val random = Random(System.currentTimeMillis()) 31 | var isError by mutableStateOf(true) 32 | private set 33 | var errorText by mutableStateOf(context.getString(R.string.expression_is_empty)) 34 | private set 35 | 36 | var selectedFunctionIndex by mutableIntStateOf(-1) 37 | private set 38 | var functionName by mutableStateOf(Defaults.defaultFuncNameChars.first().toString()) 39 | var functionColor by mutableStateOf(rainbowColors.first()) 40 | 41 | var expression by mutableStateOf("") 42 | 43 | var selectedConstantIndex by mutableIntStateOf(-1) 44 | 45 | fun updateSelectedFunction(index: Int) { 46 | selectedFunctionIndex = index 47 | if (index == -1) { 48 | expression = "" 49 | functionName = getFuncName(functions.size) 50 | functionColor = getFuncColor(functions.size) 51 | return 52 | } 53 | val function = functions[index] 54 | functionName = function.name 55 | functionColor = function.color 56 | expression = function.expression 57 | } 58 | 59 | fun checkExpression(expression: String) { 60 | if (expression.isBlank()) { 61 | errorText = context.getString(R.string.expression_is_empty) 62 | isError = true 63 | return 64 | } 65 | try { 66 | val compiled = Evaluator.compile(expression) 67 | val variables = listOf( 68 | "x" to random.nextDouble(), 69 | *constants.map { it.identifier.toString() to it.value }.toTypedArray() 70 | ) 71 | compiled.execute(variables) 72 | isError = false 73 | } catch (e: ParseException) { 74 | errorText = e.message ?: context.getString(R.string.error_parsing_expression) 75 | isError = true 76 | } catch (e: Exception) { 77 | errorText = context.getString(R.string.error_parsing_expression) 78 | isError = true 79 | } 80 | } 81 | 82 | fun addFunction(expression: String, color: Color, functionName: String) { 83 | if (selectedFunctionIndex != -1) { 84 | functions[selectedFunctionIndex] = Function.create(expression, color, functionName) 85 | updateSelectedFunction(-1) 86 | return 87 | } 88 | functions.add(Function.create(expression, color, functionName)) 89 | } 90 | 91 | fun removeFunction(index: Int) { 92 | functions.removeAt(index) 93 | 94 | // update the other functions following the delete one with new function names and colors 95 | for (i in index until functions.size) { 96 | functions[index] = functions[index].copy(name = getFuncName(i), color = getFuncColor(i)) 97 | } 98 | } 99 | 100 | private fun getFuncName(index: Int): String { 101 | return Defaults.defaultFuncNameChars[ 102 | index % Defaults.defaultFuncNameChars.size 103 | ].toString() 104 | } 105 | 106 | private fun getFuncColor(index: Int): Color { 107 | return rainbowColors[index % rainbowColors.size] 108 | } 109 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/bracket_l.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bracket_r.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/divide.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/equal.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_monochrome.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/minus.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/multiply.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/percent.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/plus.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-fil/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-in/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Gigabyte 4 | Temperatura 5 | Terabyte 6 | Megabyte 7 | Volume 8 | Petabyte 9 | Megahertz 10 | Kelvin 11 | Massa 12 | Calcolatrice 13 | Valore 14 | Kilobyte 15 | Frequenza 16 | Gigahertz 17 | Hertz 18 | Exabyte 19 | Kilohertz 20 | Byte 21 | Unità 22 | Celsius 23 | Bit 24 | Fahrenheit 25 | Lunghezza 26 | Velocità 27 | Spazio 28 | Area 29 | Metri quadrati 30 | Chilometri quadrati 31 | Centimetri quadrati 32 | Millimetri quadrati 33 | MebiByte 34 | GigiByte 35 | TebiByte 36 | PebiByte 37 | KibiByte 38 | ExbiByte 39 | Petabit 40 | Exabit 41 | Kilobit 42 | Megabit 43 | Gigabit 44 | Terabit 45 | -------------------------------------------------------------------------------- /app/src/main/res/values-iw/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | טמפרטורה 4 | מחשבון 5 | אחסון 6 | שטח 7 | תדירות 8 | אורך 9 | מסה 10 | מהירות 11 | נפח 12 | ערך 13 | יחידה 14 | מטר מרובע 15 | קילומטר מרובע 16 | סנטימטר מרובע 17 | מילימטר מרובע 18 | ליטר 19 | מטר 20 | קילו-הרץ 21 | מגה-הרץ 22 | הרץ 23 | מיליליטר 24 | אקסהבית 25 | גיגה-הרץ 26 | רגל לשנייה 27 | הקטר 28 | אקר 29 | יארד רבוע 30 | רגל רבוע 31 | סיבית 32 | בית 33 | קילובית 34 | מגהבית 35 | גיגהבית 36 | טרהבית 37 | פטהבית 38 | קילומטר 39 | סנטימטר 40 | מילימטר 41 | מייל 42 | מייל ימי 43 | יארד 44 | רגל 45 | אינץ׳ 46 | קילוגרם 47 | גרם 48 | מיליגרם 49 | טון 50 | פאונד 51 | אונקייה 52 | מטר לשנייה 53 | קילומטר לשעה 54 | מייל לשעה 55 | קשר 56 | קילו-ליטר 57 | מטר מעוקב 58 | סנטימטר מעוקב 59 | קוד צבע RGB בהקס 60 | הוספת קבוע 61 | בורר צבע 62 | צבע ישן 63 | צבע חדש 64 | איור תרשימים 65 | הוספת פונקציה חדשה 66 | הביטוי ריק 67 | שגיאה בפענוח הביטוי 68 | גוון 69 | רוויה 70 | בהירות 71 | -------------------------------------------------------------------------------- /app/src/main/res/values-lt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Skaičiuotuvas 4 | Saugykla 5 | Temperatūra 6 | -------------------------------------------------------------------------------- /app/src/main/res/values-nb-rNO/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Gigabyte 4 | Gram 5 | Pint 6 | Kvadratfot 7 | Temperatur 8 | Fot per sekund 9 | Terabyte 10 | Megabyte 11 | Kilometer per time 12 | Volum 13 | Petabyte 14 | Megahertz 15 | Tonn 16 | Milliliter 17 | Kelvin 18 | Masse 19 | Kvart gallon 20 | Hektar 21 | Meter per sekund 22 | Kubikkmeter 23 | Kvadratmeter 24 | Kalkulator 25 | Spiseskje 26 | Verdi 27 | Yard 28 | Kvadratyard 29 | Kilobyte 30 | Kubikkcentimeter 31 | Acre 32 | Digital lagring 33 | Flytende unse 34 | Frekvens 35 | Kvadratkilometer 36 | Gigahertz 37 | Centimeter 38 | Engelsk mil 39 | Hertz 40 | Kvadratcentimeter 41 | Exabyte 42 | Miles per time 43 | Gallon 44 | Kilohertz 45 | Milligram 46 | Teskje 47 | Unse 48 | Tomme 49 | Millimeter 50 | Pund 51 | Kubikkdesimeter 52 | Byte 53 | Meter 54 | Knop 55 | Areal 56 | Enhet 57 | Kilometer 58 | Kvadratmillimeter 59 | Celcius 60 | Bit 61 | Liter 62 | Fahrenheit 63 | Lengde 64 | Kilogram 65 | Hastighet 66 | Fot 67 | Kiloliter 68 | Nautisk mil 69 | Enhetsomregning 70 | Hestekraft 71 | Sekund 72 | Time 73 | Kilokalori 74 | Vinkel 75 | Effekt 76 | Viskositet 77 | Kraft 78 | Tid 79 | Trykk 80 | Lys 81 | Kopier tekst 82 | Dreiemoment 83 | Brenselsforbruk 84 | Legg til ny funksjon 85 | Fargevelger 86 | Gammel farge 87 | Ny farge 88 | Minutt 89 | Døgn 90 | Uke 91 | Kalori 92 | Energi 93 | Atmosfære 94 | Kilogram per kubikkmeter 95 | Gram per kubikkcentimeter 96 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-sv/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Gigabyte 4 | Gram 5 | Kvadratfot 6 | Temperatur 7 | Terabyte 8 | Megabyte 9 | Kilometer i timmen 10 | Volym 11 | Petabyte 12 | Megahertz 13 | Milliliter 14 | Kelvin 15 | Massa 16 | Hektar 17 | Meter per sekund 18 | Kubikmeter 19 | Kvadratmeter 20 | Kalkylator 21 | Matsked 22 | Värde 23 | Yard 24 | Kvadratyard 25 | Kilobyte 26 | Kubikcentimeter 27 | Engelska tunnland 28 | Frekvens 29 | Kvadratkilometer 30 | Gigahertz 31 | Centimeter 32 | Engelsk mil 33 | Hertz 34 | Kvadratcentimeter 35 | Exabyte 36 | Kilohertz 37 | Milligram 38 | Tesked 39 | Engelsk Uns 40 | Tum 41 | Millimeter 42 | Engelsk Pund 43 | Byte 44 | Meter 45 | Knop 46 | Area 47 | Måttenhet 48 | Kilometer 49 | Kvadratmillimeter 50 | Celsius 51 | Bit 52 | Liter 53 | Fahrenheit 54 | Längd 55 | Kilogram 56 | Hastighet 57 | Fot 58 | -------------------------------------------------------------------------------- /app/src/main/res/values-vi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Giá trị 4 | Đơn vị 5 | Milimét 6 | Dặm 7 | Dặm hải lý 8 | Thước Anh 9 | Kilôgam 10 | Gam 11 | Miligam 12 | Tấn 13 | Mét trên giây 14 | Kilômét trên giờ 15 | Dặm trên giờ 16 | Hải lý 17 | Feet trên giây 18 | Mililít 19 | Lít 20 | Ounce chất lỏng 21 | Bộ chuyển đổi đơn vị 22 | Số : 23 | Phân số : 24 | Góc 25 | Độ nhớt 26 | Lực 27 | Toán đơn giản: 28 | Ký hiệu phổ biến: 29 | Phút 30 | Giây 31 | Giờ 32 | Tuần 33 | Ngày 34 | Khối lượng 35 | Mét vuông 36 | Centimét vuông 37 | Milimét vuông 38 | Kilômét vuông 39 | Hécta 40 | Thước Anh vuông 41 | Foot vuông 42 | Mét 43 | Kilômét 44 | Centimét 45 | Nhiệt độ 46 | Lưu trữ 47 | Diện tích 48 | Tần số 49 | CalcYou 50 | Kilôlít 51 | Mét khối 52 | Centimét khối 53 | Decimét khối 54 | Thìa canh 55 | Thìa cà phê 56 | Ounce-lực 57 | Công suất 58 | Năng lượng 59 | Nhiên liệu 60 | Thời gian 61 | Áp suất 62 | Ánh sáng 63 | Sao chép văn bản 64 | Máy tính 65 | Chiều dài 66 | Tốc độ 67 | Thể tích 68 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #EFF1F5 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/net/youapps/calcyou/EvaluatorTest.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou 2 | 3 | import net.youapps.calcyou.data.graphing.CompiledExpression 4 | import net.youapps.calcyou.data.graphing.Evaluator 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Assert.assertNotNull 7 | import org.junit.Test 8 | import kotlin.math.abs 9 | import kotlin.math.cos 10 | import kotlin.math.exp 11 | import kotlin.math.ln 12 | import kotlin.math.log 13 | import kotlin.math.log10 14 | import kotlin.math.pow 15 | import kotlin.math.round 16 | import kotlin.math.sin 17 | import kotlin.math.sqrt 18 | import kotlin.math.tan 19 | import kotlin.random.Random 20 | 21 | internal class EvaluatorTest { 22 | 23 | val testCases = mapOf( 24 | "sin(x)" to { x: Double -> sin(x) }, 25 | "cos(x)" to { x: Double -> cos(x) }, 26 | "tan(x)" to { x: Double -> tan(x) }, 27 | "sqrt(x)" to { x: Double -> sqrt(x) }, 28 | "abs(x)" to { x: Double -> abs(x) }, 29 | "log(x)" to { x: Double -> ln(x) }, 30 | "exp(x)" to { x: Double -> exp(x) }, 31 | "log10(x)" to { x: Double -> log10(x) }, 32 | "log(x,2)" to { x: Double -> log(x, 2.0) }, 33 | "sin(x) + cos(x)" to { x: Double -> sin(x) + cos(x) }, 34 | "sin(x) * cos(x)" to { x: Double -> sin(x) * cos(x) }, 35 | "sin(x) / cos(x)" to { x: Double -> sin(x) / cos(x) }, 36 | "sin(x) - cos(x)" to { x: Double -> sin(x) - cos(x) }, 37 | "sin(x) ** 2" to { x: Double -> sin(x) * sin(x) }, 38 | "x * x" to { x: Double -> x * x }, 39 | "x ** 2" to { x: Double -> x.pow(2) }, 40 | "pow(x,3)" to { x: Double -> x.pow(3) }, 41 | "2" to { _: Double -> 2.0 }, 42 | "sqrt(4)" to { _: Double -> sqrt(4.0) }, 43 | "round(x)" to { x: Double -> round(x) }, 44 | "sin(2*PI)" to { _: Double -> sin(2 * Math.PI) }, 45 | "x*(5+3*x)" to { x: Double -> x * (5 + 3 * x) }, 46 | //Todo: support function inside another function 47 | //"sin(cos(x))" to { x: Double -> sin(cos(x)) }, 48 | ) 49 | val random = Random(System.currentTimeMillis()) 50 | 51 | @Test 52 | fun `execute() should return correct answer for compiled expressions`() { 53 | testCases.forEach { expression, func -> 54 | val argument = random.nextDouble() 55 | val compiled: CompiledExpression = Evaluator.compile(expression) 56 | 57 | val answer = compiled.execute("x" to argument) 58 | 59 | assertNotNull(answer) 60 | 61 | val expected = func(argument) 62 | assertEquals(expected, answer!!, 1e-6) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/test/java/net/youapps/calcyou/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package net.youapps.calcyou 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /arity/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /arity/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-library") 3 | id("org.jetbrains.kotlin.jvm") 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | } -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/ArityException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | * Thrown when a {@link Function} is evaluated with a wrong number of arguments 21 | * (when the number of arguments is not equal to the function's arity). 22 | */ 23 | 24 | public class ArityException extends RuntimeException { 25 | public ArityException(String mes) { 26 | super(mes); 27 | } 28 | 29 | public ArityException(int nArgs) { 30 | this("Didn't expect " + nArgs + " arguments"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/ByteStack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | class ByteStack { 20 | private byte[] data = new byte[8]; 21 | private int size = 0; 22 | 23 | void clear() { 24 | size = 0; 25 | } 26 | 27 | void push(byte b) { 28 | if (size >= data.length) { 29 | byte[] newData = new byte[data.length << 1]; 30 | System.arraycopy(data, 0, newData, 0, data.length); 31 | data = newData; 32 | } 33 | data[size++] = b; 34 | } 35 | 36 | /* 37 | void pop(int cnt) { 38 | size -= cnt; 39 | } 40 | */ 41 | 42 | byte pop() { 43 | return data[--size]; 44 | } 45 | 46 | byte[] toArray() { 47 | byte[] trimmed = new byte[size]; 48 | System.arraycopy(data, 0, trimmed, 0, size); 49 | return trimmed; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Compiler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | * Compiles a textual arithmetic expression to a {@link Function}.

21 | */ 22 | class Compiler { 23 | private final SyntaxException exception = new SyntaxException(); 24 | private final Lexer lexer = new Lexer(exception); 25 | private final RPN rpn = new RPN(exception); 26 | private final DeclarationParser declParser = new DeclarationParser(exception); 27 | private final OptCodeGen codeGen = new OptCodeGen(exception); 28 | private final SimpleCodeGen simpleCodeGen = new SimpleCodeGen(exception); 29 | private final Declaration decl = new Declaration(); 30 | 31 | Function compileSimple(Symbols symbols, String expression) throws SyntaxException { 32 | rpn.setConsumer(simpleCodeGen.setSymbols(symbols)); 33 | lexer.scan(expression, rpn); 34 | return simpleCodeGen.getFun(); 35 | } 36 | 37 | Function compile(Symbols symbols, String source) throws SyntaxException { 38 | Function fun = null; 39 | decl.parse(source, lexer, declParser); 40 | if (decl.arity == DeclarationParser.UNKNOWN_ARITY) { 41 | try { 42 | fun = new Constant(compileSimple(symbols, decl.expression).evalComplex()); 43 | } catch (SyntaxException e) { 44 | if (e != SimpleCodeGen.HAS_ARGUMENTS) { 45 | throw e; 46 | } 47 | // fall-through (see below) 48 | } 49 | } 50 | 51 | if (fun == null) { 52 | // either decl.arity was set, or an HAS_ARGUMENTS exception ocurred above 53 | symbols.pushFrame(); 54 | symbols.addArguments(decl.args); 55 | try { 56 | rpn.setConsumer(codeGen.setSymbols(symbols)); 57 | lexer.scan(decl.expression, rpn); 58 | } finally { 59 | symbols.popFrame(); 60 | } 61 | int arity = decl.arity; 62 | if (arity == DeclarationParser.UNKNOWN_ARITY) { 63 | arity = codeGen.intrinsicArity; 64 | } 65 | fun = codeGen.getFun(arity); 66 | } 67 | fun.comment = source; 68 | return fun; 69 | } 70 | 71 | FunctionAndName compileWithName(Symbols symbols, String source) throws SyntaxException { 72 | return new FunctionAndName(compile(symbols, source), decl.name); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Compiler.java.orig: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | Compiles a textual arithmetic expression to a {@link Function}.

21 | */ 22 | class Compiler { 23 | private final SyntaxException exception = new SyntaxException(); 24 | private final Lexer lexer = new Lexer(exception); 25 | private final RPN rpn = new RPN(exception); 26 | private final DeclarationParser declParser = new DeclarationParser(exception); 27 | private final OptCodeGen codeGen = new OptCodeGen(exception); 28 | private final SimpleCodeGen simpleCodeGen = new SimpleCodeGen(exception); 29 | private final Declaration decl = new Declaration(); 30 | 31 | Function compileSimple(Symbols symbols, String expression) throws SyntaxException { 32 | rpn.setConsumer(simpleCodeGen.setSymbols(symbols)); 33 | lexer.scan(expression, rpn); 34 | return simpleCodeGen.getFun(); 35 | } 36 | 37 | Function compile(Symbols symbols, String source) throws SyntaxException { 38 | Function fun = null; 39 | decl.parse(source, lexer, declParser); 40 | if (decl.arity == DeclarationParser.UNKNOWN_ARITY) { 41 | try { 42 | fun = new Constant(compileSimple(symbols, decl.expression).evalComplex()); 43 | } catch (SyntaxException e) { 44 | if (e != SimpleCodeGen.HAS_ARGUMENTS) { 45 | throw e; 46 | } 47 | // fall-through (see below) 48 | } 49 | } 50 | 51 | if (fun == null) { 52 | // either decl.arity was set, or an HAS_ARGUMENTS exception ocurred above 53 | symbols.pushFrame(); 54 | symbols.addArguments(decl.args); 55 | try { 56 | rpn.setConsumer(codeGen.setSymbols(symbols)); 57 | lexer.scan(decl.expression, rpn); 58 | } finally { 59 | symbols.popFrame(); 60 | } 61 | int arity = decl.arity; 62 | if (arity == DeclarationParser.UNKNOWN_ARITY) { 63 | arity = codeGen.intrinsicArity; 64 | } 65 | fun = codeGen.getFun(arity); 66 | } 67 | fun.comment = source; 68 | return fun; 69 | } 70 | 71 | FunctionAndName compileWithName(Symbols symbols, String source) throws SyntaxException { 72 | return new FunctionAndName(compile(symbols, source), decl.name); 73 | } 74 | 75 | private static final Function EMPTY_FUNCS = {}; 76 | Function[] compileMultiple(Symbols symbols, String source) { 77 | codeGen.setSymbols(symbols); 78 | rpn.setConsumer(codeGen); 79 | int len = symbols.length(); 80 | ArrayList funcs = new ArrayList(); 81 | try { 82 | int pos = lexer.scan(source, rpn); 83 | funcs.add(codeGen.getFun(codeGen.intrinsicArity)); 84 | } catch (SyntaxException e) { 85 | // fall-through 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Constant.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | * A constant presented as a function, always evaluates to the same value. 21 | */ 22 | public class Constant extends Function { 23 | private Complex value; 24 | 25 | public Constant(Complex o) { 26 | value = new Complex(o); 27 | } 28 | 29 | //@Override 30 | 31 | /** 32 | * Returns the complex constant. 33 | */ 34 | public Complex evalComplex() { 35 | return value; 36 | } 37 | 38 | //@Override 39 | 40 | /** 41 | * Returns the complex constant as a real value. 42 | * 43 | * @see Complex.asReas() 44 | */ 45 | public double eval() { 46 | return value.asReal(); 47 | } 48 | 49 | public String toString() { 50 | return value.toString(); 51 | } 52 | 53 | public int arity() { 54 | return 0; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/ContextFunction.java: -------------------------------------------------------------------------------- 1 | package org.javia.arity; 2 | 3 | public abstract class ContextFunction extends Function { 4 | private static final double NO_ARGS[] = new double[0]; 5 | private static final Complex[] NO_ARGS_COMPLEX = new Complex[0]; 6 | private static EvalContext context = new EvalContext(); 7 | 8 | abstract public double eval(double args[], EvalContext context); 9 | 10 | abstract public Complex eval(Complex args[], EvalContext context); 11 | 12 | Complex[] toComplex(double args[], EvalContext context) { 13 | Complex argsC[]; 14 | switch (args.length) { 15 | case 0: 16 | argsC = NO_ARGS_COMPLEX; 17 | break; 18 | case 1: 19 | argsC = context.args1c; 20 | argsC[0].set(args[0], 0); 21 | break; 22 | case 2: 23 | argsC = context.args2c; 24 | argsC[0].set(args[0], 0); 25 | argsC[1].set(args[1], 0); 26 | break; 27 | default: 28 | argsC = new Complex[args.length]; 29 | for (int i = 0; i < args.length; ++i) { 30 | argsC[i] = new Complex(args[i], 0); 31 | } 32 | } 33 | return argsC; 34 | } 35 | 36 | //@Override 37 | public double eval() { 38 | return eval(NO_ARGS); 39 | } 40 | 41 | //@Override 42 | public double eval(double x) { 43 | synchronized (context) { 44 | return eval(x, context); 45 | } 46 | } 47 | 48 | //@Override 49 | public double eval(double x, double y) { 50 | synchronized (context) { 51 | return eval(x, y, context); 52 | } 53 | } 54 | 55 | //@Override 56 | public double eval(double args[]) { 57 | synchronized (context) { 58 | return eval(args, context); 59 | } 60 | } 61 | 62 | public double eval(double x, EvalContext context) { 63 | double args[] = context.args1; 64 | args[0] = x; 65 | return eval(args, context); 66 | } 67 | 68 | public double eval(double x, double y, EvalContext context) { 69 | double args[] = context.args2; 70 | args[0] = x; 71 | args[1] = y; 72 | return eval(args, context); 73 | } 74 | 75 | //@Override 76 | public Complex evalComplex() { 77 | return eval(NO_ARGS_COMPLEX); 78 | } 79 | 80 | //@Override 81 | public Complex eval(Complex x) { 82 | synchronized (context) { 83 | return eval(x, context); 84 | } 85 | } 86 | 87 | //@Override 88 | public Complex eval(Complex x, Complex y) { 89 | synchronized (context) { 90 | return eval(x, y, context); 91 | } 92 | } 93 | 94 | //@Override 95 | public Complex eval(Complex args[]) { 96 | synchronized (context) { 97 | return eval(args, context); 98 | } 99 | } 100 | 101 | public Complex eval(Complex x, EvalContext context) { 102 | Complex args[] = context.args1c; 103 | args[0] = x; 104 | return eval(args, context); 105 | } 106 | 107 | public Complex eval(Complex x, Complex y, EvalContext context) { 108 | Complex args[] = context.args2c; 109 | args[0] = x; 110 | args[1] = y; 111 | return eval(args, context); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Declaration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | class Declaration { 20 | private static final String NO_ARGS[] = {}; 21 | String name; 22 | String args[]; 23 | int arity; 24 | String expression; 25 | 26 | void parse(String source, Lexer lexer, DeclarationParser declParser) throws SyntaxException { 27 | int equalPos = source.indexOf('='); 28 | String decl; 29 | 30 | if (equalPos == -1) { 31 | decl = null; 32 | expression = source; 33 | name = null; 34 | args = NO_ARGS; 35 | arity = DeclarationParser.UNKNOWN_ARITY; 36 | } else { 37 | decl = source.substring(0, equalPos); 38 | expression = source.substring(equalPos + 1); 39 | lexer.scan(decl, declParser); 40 | name = declParser.name; 41 | args = declParser.argNames(); 42 | arity = declParser.arity; 43 | } 44 | /* 45 | if (arity == DeclarationParser.UNKNOWN_ARITY) { 46 | args = IMPLICIT_ARGS; 47 | } 48 | */ 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/DeclarationParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | import java.util.Vector; 20 | 21 | class DeclarationParser extends TokenConsumer { 22 | static final String NO_ARGS[] = new String[0]; 23 | static final int UNKNOWN_ARITY = -2; 24 | static final int MAX_ARITY = 5; 25 | 26 | String name; 27 | int arity; 28 | Vector args = new Vector(); 29 | 30 | private SyntaxException exception; 31 | 32 | DeclarationParser(SyntaxException e) { 33 | this.exception = e; 34 | } 35 | 36 | //@Override 37 | void start() { 38 | name = null; 39 | arity = UNKNOWN_ARITY; 40 | args.setSize(0); 41 | } 42 | 43 | //@Override 44 | void push(Token token) throws SyntaxException { 45 | switch (token.id) { 46 | case Lexer.CALL: 47 | if (name == null) { 48 | name = token.name; 49 | arity = 0; 50 | } else { 51 | throw exception.set("repeated CALL in declaration", token.position); 52 | } 53 | break; 54 | 55 | case Lexer.CONST: 56 | if (name == null) { 57 | name = token.name; 58 | arity = UNKNOWN_ARITY; 59 | } else if (arity >= 0) { 60 | args.addElement(token.name); 61 | ++arity; 62 | if (arity > MAX_ARITY) { 63 | throw exception.set("Arity too large " + arity, token.position); 64 | } 65 | } else { 66 | throw exception.set("Invalid declaration", token.position); 67 | } 68 | break; 69 | 70 | case Lexer.RPAREN: 71 | case Lexer.COMMA: 72 | case Lexer.END: 73 | break; 74 | 75 | default: 76 | throw exception.set("invalid token in declaration", token.position); 77 | } 78 | } 79 | 80 | String[] argNames() { 81 | if (arity > 0) { 82 | String argNames[] = new String[arity]; 83 | args.copyInto(argNames); 84 | return argNames; 85 | } else { 86 | return NO_ARGS; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Derivative.java: -------------------------------------------------------------------------------- 1 | package org.javia.arity; 2 | 3 | // f'(x)=Im(f(x+i*h)/h) 4 | public class Derivative extends Function { 5 | private static final double H = 1e-12; 6 | private static final double INVH = 1 / H; 7 | private final Function f; 8 | private Complex c = new Complex(); 9 | 10 | public Derivative(Function f) throws ArityException { 11 | this.f = f; 12 | f.checkArity(1); 13 | } 14 | 15 | public double eval(double x) { 16 | return f.eval(c.set(x, H)).im * INVH; 17 | } 18 | 19 | public int arity() { 20 | return 1; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/DoubleStack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | class DoubleStack { 20 | private double re[] = new double[8]; 21 | private double im[] = new double[8]; 22 | private int size = 0; 23 | 24 | void clear() { 25 | size = 0; 26 | } 27 | 28 | void push(double a, double b) { 29 | if (size >= re.length) { 30 | int newSize = re.length << 1; 31 | double newRe[] = new double[newSize]; 32 | double newIm[] = new double[newSize]; 33 | System.arraycopy(re, 0, newRe, 0, re.length); 34 | System.arraycopy(im, 0, newIm, 0, re.length); 35 | re = newRe; 36 | im = newIm; 37 | } 38 | re[size] = a; 39 | im[size] = b; 40 | ++size; 41 | } 42 | 43 | void pop(int cnt) { 44 | if (cnt > size) { 45 | throw new Error("pop " + cnt + " from " + size); 46 | } 47 | size -= cnt; 48 | } 49 | 50 | void pop() { 51 | --size; 52 | } 53 | 54 | double[] getRe() { 55 | double trimmed[] = new double[size]; 56 | System.arraycopy(re, 0, trimmed, 0, size); 57 | return trimmed; 58 | } 59 | 60 | double[] getIm() { 61 | boolean allZero = true; 62 | for (int i = 0; i < size; ++i) { 63 | if (im[i] != 0) { 64 | allZero = false; 65 | break; 66 | } 67 | } 68 | if (allZero) { 69 | return null; 70 | } 71 | double trimmed[] = new double[size]; 72 | System.arraycopy(im, 0, trimmed, 0, size); 73 | return trimmed; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/EvalContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | * To evaluate CompiledFunctions from multiple threads in parallel, 21 | * you need to create one EvalContext instance per thread, 22 | * and pass it to the eval() methods of CompiledFunction. 23 | */ 24 | 25 | public class EvalContext { 26 | static final int MAX_STACK_SIZE = 128; //if stack ever grows above this likely something is wrong 27 | final Complex stackComplex[] = new Complex[MAX_STACK_SIZE]; 28 | double stackRe[] = new double[MAX_STACK_SIZE]; 29 | int stackBase = 0; 30 | 31 | double args1[] = new double[1]; 32 | double args2[] = new double[2]; 33 | Complex args1c[]; 34 | Complex args2c[]; 35 | 36 | /** 37 | * Constructs a new EvalContext, ready to be used with CompiledFunction.eval(). 38 | */ 39 | public EvalContext() { 40 | for (int i = 0; i < MAX_STACK_SIZE; ++i) { 41 | stackComplex[i] = new Complex(); 42 | } 43 | args1c = new Complex[]{new Complex()}; 44 | args2c = new Complex[]{new Complex(), new Complex()}; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Function.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | * Base class for functions.

21 | * A function has an arity (the number of arguments), and a way for evaluation 22 | * given the values of the arguments.

23 | * To create user-defined functions, 24 | * derive from this class and override one of the eval() methods.

25 | *

26 | * If the user subclasses Function, he is responsible for the thread-safety of 27 | * his user-defined Functions. 28 | */ 29 | 30 | abstract public class Function { 31 | String comment; 32 | private Function cachedDerivate = null; 33 | 34 | /** 35 | * Gives the arity of this function. 36 | * 37 | * @return the arity (the number of arguments). Arity >= 0. 38 | */ 39 | abstract public int arity(); 40 | 41 | public Function getDerivative() { 42 | if (cachedDerivate == null) { 43 | cachedDerivate = new Derivative(this); 44 | } 45 | return cachedDerivate; 46 | } 47 | 48 | void setDerivative(Function deriv) { 49 | cachedDerivate = deriv; 50 | } 51 | 52 | /** 53 | * Evaluates an arity-0 function (a function with no arguments). 54 | * 55 | * @return the value of the function 56 | */ 57 | public double eval() { 58 | throw new ArityException(0); 59 | } 60 | 61 | /** 62 | * Evaluates a function with a single argument (arity == 1). 63 | */ 64 | public double eval(double x) { 65 | throw new ArityException(1); 66 | } 67 | 68 | /** 69 | * Evaluates a function with two arguments (arity == 2). 70 | */ 71 | public double eval(double x, double y) { 72 | throw new ArityException(2); 73 | } 74 | 75 | /** 76 | * Evaluates the function given the argument values. 77 | * 78 | * @param args array containing the arguments. 79 | * @return the value of the function 80 | * @throws ArityException if args.length != arity. 81 | */ 82 | public double eval(double args[]) { 83 | switch (args.length) { 84 | case 0: 85 | return eval(); 86 | case 1: 87 | return eval(args[0]); 88 | case 2: 89 | return eval(args[0], args[1]); 90 | } 91 | throw new ArityException(args.length); 92 | } 93 | 94 | 95 | /** 96 | * By default complex forwards to real eval is the arguments are real, 97 | * otherwise returns NaN. 98 | * This allow calling any real functions as a (restricted) complex one. 99 | */ 100 | public Complex evalComplex() { 101 | checkArity(0); 102 | return new Complex(eval(), 0); 103 | } 104 | 105 | /** 106 | * Complex evaluates a function with a single argument. 107 | */ 108 | public Complex eval(Complex x) { 109 | checkArity(1); 110 | return new Complex(x.im == 0 ? eval(x.re) : Double.NaN, 0); 111 | } 112 | 113 | /** 114 | * Complex evaluates a function with two arguments. 115 | */ 116 | public Complex eval(Complex x, Complex y) { 117 | checkArity(2); 118 | return new Complex(x.im == 0 && y.im == 0 ? eval(x.re, y.re) : Double.NaN, 0); 119 | } 120 | 121 | /** 122 | * Complex evaluates a function with an arbitrary number of arguments. 123 | */ 124 | public Complex eval(Complex args[]) { 125 | switch (args.length) { 126 | case 0: 127 | return evalComplex(); 128 | case 1: 129 | return eval(args[0]); 130 | case 2: 131 | return eval(args[0], args[1]); 132 | default: 133 | int len = args.length; 134 | checkArity(len); 135 | double reArgs[] = new double[len]; 136 | for (int i = args.length - 1; i >= 0; --i) { 137 | if (args[i].im != 0) { 138 | return new Complex(Double.NaN, 0); 139 | } 140 | reArgs[i] = args[i].re; 141 | } 142 | return new Complex(eval(reArgs), 0); 143 | } 144 | } 145 | 146 | public void checkArity(int nArgs) throws ArityException { 147 | if (arity() != nArgs) { 148 | throw new ArityException("Expected " + arity() + " arguments, got " + nArgs); 149 | } 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/FunctionAndName.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | * Encapsulates together a function and its name. 21 | * Is used to return both the function and its name 22 | * from Compiler.compileWithName(). 23 | */ 24 | 25 | public class FunctionAndName { 26 | public Function function; 27 | public String name; 28 | 29 | public FunctionAndName(Function fun, String name) { 30 | this.function = fun; 31 | this.name = name; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/FunctionStack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008-2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | class FunctionStack { 20 | private Function[] data = new Function[8]; 21 | private int size = 0; 22 | 23 | void clear() { 24 | size = 0; 25 | } 26 | 27 | void push(Function b) { 28 | if (size >= data.length) { 29 | Function[] newData = new Function[data.length << 1]; 30 | System.arraycopy(data, 0, newData, 0, data.length); 31 | data = newData; 32 | } 33 | data[size++] = b; 34 | } 35 | 36 | /* 37 | void pop(int cnt) { 38 | size -= cnt; 39 | } 40 | */ 41 | 42 | Function pop() { 43 | return data[--size]; 44 | } 45 | 46 | Function[] toArray() { 47 | Function[] trimmed = new Function[size]; 48 | System.arraycopy(data, 0, trimmed, 0, size); 49 | return trimmed; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/IsComplexException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | class IsComplexException extends Exception { 20 | } 21 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/OptCodeGen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2009 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /* Optimizing Code Generator 20 | Reads tokens in RPN (Reverse Polish Notation) order, 21 | and generates VM opcodes, 22 | doing constant-folding optimization. 23 | */ 24 | 25 | class OptCodeGen extends SimpleCodeGen { 26 | EvalContext context = new EvalContext(); 27 | int sp; 28 | Complex stack[] = context.stackComplex; 29 | 30 | double traceConstsRe[] = new double[1]; 31 | double traceConstsIm[] = new double[1]; 32 | Function traceFuncs[] = new Function[1]; 33 | byte traceCode[] = new byte[1]; 34 | CompiledFunction tracer = new CompiledFunction(0, traceCode, traceConstsRe, traceConstsIm, traceFuncs); 35 | 36 | int intrinsicArity; 37 | private boolean isPercent; 38 | 39 | OptCodeGen(SyntaxException e) { 40 | super(e); 41 | } 42 | 43 | //@Override 44 | void start() { 45 | super.start(); 46 | sp = -1; 47 | intrinsicArity = 0; 48 | isPercent = false; 49 | } 50 | 51 | //@Override 52 | void push(Token token) throws SyntaxException { 53 | // System.err.println("state " + getFun(0) + "; token " + token); 54 | final boolean prevWasPercent = isPercent; 55 | isPercent = false; 56 | byte op; 57 | switch (token.id) { 58 | case Lexer.NUMBER: 59 | op = VM.CONST; 60 | traceConstsRe[0] = token.value; 61 | traceConstsIm[0] = 0; 62 | break; 63 | 64 | case Lexer.CONST: 65 | case Lexer.CALL: 66 | Symbol symbol = getSymbol(token); 67 | if (token.isDerivative()) { 68 | op = VM.CALL; 69 | traceFuncs[0] = symbol.fun.getDerivative(); 70 | } else if (symbol.op > 0) { // built-in 71 | op = symbol.op; 72 | if (op >= VM.LOAD0 && op <= VM.LOAD4) { 73 | int arg = op - VM.LOAD0; 74 | if (arg + 1 > intrinsicArity) { 75 | intrinsicArity = arg + 1; 76 | } 77 | stack[++sp].re = Double.NaN; 78 | stack[sp].im = 0; 79 | code.push(op); 80 | //System.out.println("op " + VM.opcodeName[op] + "; sp " + sp + "; top " + stack[sp]); 81 | return; 82 | } 83 | } else if (symbol.fun != null) { // function call 84 | op = VM.CALL; 85 | traceFuncs[0] = symbol.fun; 86 | } else { // variable reference 87 | op = VM.CONST; 88 | traceConstsRe[0] = symbol.valueRe; 89 | traceConstsIm[0] = symbol.valueIm; 90 | } 91 | break; 92 | 93 | default: 94 | op = token.vmop; 95 | if (op <= 0) { 96 | throw new Error("wrong vmop: " + op); 97 | } 98 | if (op == VM.PERCENT) { 99 | isPercent = true; 100 | } 101 | } 102 | int oldSP = sp; 103 | traceCode[0] = op; 104 | if (op != VM.RND) { 105 | sp = tracer.execWithoutCheckComplex(context, sp, prevWasPercent ? -1 : -2); 106 | } else { 107 | stack[++sp].re = Double.NaN; 108 | stack[sp].im = 0; 109 | } 110 | 111 | //System.out.println("op " + VM.opcodeName[op] + "; old " + oldSP + "; sp " + sp + "; top " + stack[sp] + " " + stack[0]); 112 | 113 | //constant folding 114 | if (!stack[sp].isNaN() || op == VM.CONST) { 115 | int nPopCode = op == VM.CALL ? traceFuncs[0].arity() : VM.arity[op]; 116 | while (nPopCode > 0) { 117 | final byte pop = code.pop(); 118 | if (pop == VM.CONST) { 119 | consts.pop(); 120 | } else if (pop == VM.CALL) { 121 | Function f = funcs.pop(); 122 | nPopCode += f.arity() - 1; 123 | } else { 124 | nPopCode += VM.arity[pop]; 125 | } 126 | --nPopCode; 127 | } 128 | consts.push(stack[sp].re, stack[sp].im); 129 | op = VM.CONST; 130 | } else if (op == VM.CALL) { 131 | funcs.push(traceFuncs[0]); 132 | } 133 | code.push(op); 134 | } 135 | 136 | CompiledFunction getFun(int arity) { 137 | return new CompiledFunction(arity, code.toArray(), consts.getRe(), consts.getIm(), funcs.toArray()); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/SimpleCodeGen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /* Non-optimizing Code Generator 20 | Reads tokens in RPN (Reverse Polish Notation) order, 21 | and generates VM opcodes, 22 | without any optimization. 23 | */ 24 | 25 | class SimpleCodeGen extends TokenConsumer { 26 | static final SyntaxException HAS_ARGUMENTS = new SyntaxException(); 27 | 28 | ByteStack code = new ByteStack(); 29 | DoubleStack consts = new DoubleStack(); 30 | FunctionStack funcs = new FunctionStack(); 31 | 32 | //String argNames[]; 33 | Symbols symbols; 34 | 35 | SyntaxException exception; 36 | 37 | SimpleCodeGen(SyntaxException exception) { 38 | this.exception = exception; 39 | } 40 | 41 | SimpleCodeGen setSymbols(Symbols symbols) { 42 | this.symbols = symbols; 43 | return this; 44 | } 45 | 46 | //@Override 47 | void start() { 48 | code.clear(); 49 | consts.clear(); 50 | funcs.clear(); 51 | } 52 | 53 | Symbol getSymbol(Token token) throws SyntaxException { 54 | String name = token.name; 55 | boolean isDerivative = token.isDerivative(); 56 | if (isDerivative) { 57 | if (token.arity == 1) { 58 | name = name.substring(0, name.length() - 1); 59 | } else { 60 | throw exception.set("Derivative expects arity 1 but found " + token.arity, token.position); 61 | } 62 | } 63 | Symbol symbol = symbols.lookup(name, token.arity); 64 | if (symbol == null) { 65 | throw exception.set("undefined '" + name + "' with arity " + token.arity, token.position); 66 | } 67 | if (isDerivative && symbol.op > 0 && symbol.fun == null) { 68 | symbol.fun = CompiledFunction.makeOpFunction(symbol.op); 69 | } 70 | if (isDerivative && symbol.fun == null) { 71 | throw exception.set("Invalid derivative " + name, token.position); 72 | } 73 | return symbol; 74 | } 75 | 76 | void push(Token token) throws SyntaxException { 77 | byte op; 78 | switch (token.id) { 79 | case Lexer.NUMBER: 80 | op = VM.CONST; 81 | consts.push(token.value, 0); 82 | break; 83 | 84 | case Lexer.CONST: 85 | case Lexer.CALL: 86 | Symbol symbol = getSymbol(token); 87 | if (token.isDerivative()) { 88 | op = VM.CALL; 89 | funcs.push(symbol.fun.getDerivative()); 90 | } else if (symbol.op > 0) { // built-in 91 | op = symbol.op; 92 | if (op >= VM.LOAD0 && op <= VM.LOAD4) { 93 | throw HAS_ARGUMENTS.set("eval() on implicit function", exception.position); 94 | } 95 | } else if (symbol.fun != null) { // function call 96 | op = VM.CALL; 97 | funcs.push(symbol.fun); 98 | } else { // variable reference 99 | op = VM.CONST; 100 | consts.push(symbol.valueRe, symbol.valueIm); 101 | } 102 | break; 103 | 104 | default: 105 | op = token.vmop; 106 | if (op <= 0) { 107 | throw new Error("wrong vmop: " + op + ", id " + token.id + " in \"" + exception.expression + '"'); 108 | } 109 | } 110 | code.push(op); 111 | } 112 | 113 | CompiledFunction getFun() { 114 | return new CompiledFunction(0, code.toArray(), consts.getRe(), consts.getIm(), funcs.toArray()); 115 | } 116 | 117 | /* 118 | double getValue() { 119 | return new CompiledFunction(0, code.toArray(), consts.getRe(), consts.getIm(), funcs.toArray()).eval(); 120 | } 121 | */ 122 | } 123 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Symbol.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | public class Symbol { 20 | static final int CONST_ARITY = -3; 21 | byte op; 22 | Function fun; 23 | double valueRe, valueIm; 24 | boolean isConst = false; 25 | private String name; 26 | private int arity; 27 | 28 | private Symbol(String name, int arity, byte op, boolean isConst, int dummy) { 29 | setKey(name, arity); 30 | this.op = op; 31 | this.isConst = isConst; 32 | } 33 | 34 | Symbol(String name, Function fun) { 35 | setKey(name, fun.arity()); 36 | this.fun = fun; 37 | // this.comment = fun.comment; 38 | } 39 | 40 | Symbol(String name, double re, boolean isConst) { 41 | this(name, re, 0, isConst); 42 | } 43 | 44 | Symbol(String name, double re, double im, boolean isConst) { 45 | setKey(name, CONST_ARITY); 46 | valueRe = re; 47 | valueIm = im; 48 | this.isConst = isConst; 49 | } 50 | 51 | static Symbol makeArg(String name, int order) { 52 | return new Symbol(name, CONST_ARITY, (byte) (VM.LOAD0 + order), false, 0); 53 | } 54 | 55 | static Symbol makeVmOp(String name, int op) { 56 | return new Symbol(name, (int) VM.arity[op], (byte) op, true, 0); 57 | } 58 | 59 | static Symbol newEmpty(Symbol s) { 60 | return new Symbol(s.name, s.arity, (byte) 0, false, 0); 61 | } 62 | 63 | public String toString() { 64 | return "Symbol '" + name + "' arity " + arity + " val " + valueRe + " op " + op; 65 | } 66 | 67 | /* 68 | public String getComment() { 69 | return comment; 70 | } 71 | */ 72 | 73 | public String getName() { 74 | return name; 75 | } 76 | 77 | public int getArity() { 78 | return arity == CONST_ARITY ? 0 : arity; 79 | } 80 | 81 | boolean isEmpty() { 82 | return op == 0 && fun == null && valueRe == 0 && valueIm == 0; 83 | } 84 | 85 | Symbol setKey(String name, int arity) { 86 | this.name = name; 87 | this.arity = arity; 88 | return this; 89 | } 90 | 91 | public boolean equals(Object other) { 92 | Symbol symbol = (Symbol) other; 93 | return name.equals(symbol.name) && arity == symbol.arity; 94 | } 95 | 96 | public int hashCode() { 97 | return name.hashCode() + arity; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/SyntaxException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | /** 20 | * Thrown when the expression can't be compiled, because it's either not 21 | * well-formed (e.g. "1+"), or because some simbols aren't defined (e.g. "foo+2"). 22 | */ 23 | public class SyntaxException extends Exception { 24 | /** 25 | * The expression which caused the error. 26 | */ 27 | public String expression; 28 | 29 | /** 30 | * Explicative message (cause of error). 31 | */ 32 | public String message; 33 | 34 | /** 35 | * The position inside expression where the error occured. 36 | */ 37 | public int position; 38 | 39 | public String toString() { 40 | return "SyntaxException: " + message 41 | + " in '" + expression 42 | + "' at position " + position; 43 | } 44 | 45 | SyntaxException set(String str, int pos) { 46 | message = str; 47 | position = pos; 48 | fillInStackTrace(); 49 | return this; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/Token.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | class Token { 20 | //kind 21 | static final int 22 | PREFIX = 1, 23 | LEFT = 2, 24 | RIGHT = 3, 25 | SUFIX = 4; 26 | 27 | final int priority; 28 | final int assoc; 29 | final int id; 30 | final byte vmop; 31 | 32 | double value; //for NUMBER only 33 | String name = null; //for CONST & CALL 34 | int arity; 35 | int position; //pos inside expression 36 | 37 | Token(int id, int priority, int assoc, int vmop) { 38 | this.id = id; 39 | this.priority = priority; 40 | this.assoc = assoc; 41 | this.vmop = (byte) vmop; 42 | arity = id == Lexer.CALL ? 1 : Symbol.CONST_ARITY; 43 | } 44 | 45 | Token setPos(int pos) { 46 | this.position = pos; 47 | return this; 48 | } 49 | 50 | Token setValue(double value) { 51 | this.value = value; 52 | return this; 53 | } 54 | 55 | Token setAlpha(String alpha) { 56 | name = alpha; 57 | return this; 58 | } 59 | 60 | public boolean isDerivative() { 61 | int len; 62 | return name != null && (len = name.length()) > 0 && name.charAt(len - 1) == '\''; 63 | } 64 | 65 | public String toString() { 66 | switch (id) { 67 | case Lexer.NUMBER: 68 | return "" + value; 69 | case Lexer.CALL: 70 | return name + '(' + arity + ')'; 71 | case Lexer.CONST: 72 | return name; 73 | } 74 | return "" + id; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/TokenConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007-2008 Mihai Preda. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.javia.arity; 18 | 19 | abstract class TokenConsumer { 20 | void start() { 21 | } 22 | 23 | abstract void push(Token token) throws SyntaxException; 24 | } 25 | -------------------------------------------------------------------------------- /arity/src/main/java/org/javia/arity/VM.java: -------------------------------------------------------------------------------- 1 | // This file is automatically generated by the build.py script. Do not edit! 2 | 3 | /* 4 | * Copyright (C) 2008-2009 Mihai Preda. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package org.javia.arity; 20 | 21 | class VM { 22 | 23 | static final byte 24 | RESERVED = 0, 25 | CONST = 1, 26 | CALL = 2, 27 | ADD = 3, 28 | SUB = 4, 29 | MUL = 5, 30 | DIV = 6, 31 | MOD = 7, 32 | RND = 8, 33 | UMIN = 9, 34 | POWER = 10, 35 | FACT = 11, 36 | PERCENT = 12, 37 | SQRT = 13, 38 | CBRT = 14, 39 | EXP = 15, 40 | LN = 16, 41 | SIN = 17, 42 | COS = 18, 43 | TAN = 19, 44 | ASIN = 20, 45 | ACOS = 21, 46 | ATAN = 22, 47 | SINH = 23, 48 | COSH = 24, 49 | TANH = 25, 50 | ASINH = 26, 51 | ACOSH = 27, 52 | ATANH = 28, 53 | ABS = 29, 54 | FLOOR = 30, 55 | CEIL = 31, 56 | SIGN = 32, 57 | MIN = 33, 58 | MAX = 34, 59 | GCD = 35, 60 | COMB = 36, 61 | PERM = 37, 62 | LOAD0 = 38, 63 | LOAD1 = 39, 64 | LOAD2 = 40, 65 | LOAD3 = 41, 66 | LOAD4 = 42, 67 | REAL = 43, 68 | IMAG = 44; 69 | 70 | static final String[] opcodeName = {"reserved", "const", "call", "add", "sub", "mul", "div", "mod", "rnd", "umin", "power", "fact", "percent", "sqrt", "cbrt", "exp", "ln", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "abs", "floor", "ceil", "sign", "min", "max", "gcd", "comb", "perm", "load0", "load1", "load2", "load3", "load4", "real", "imag"}; 71 | 72 | static final byte[] arity = {0, 0, -1, 2, 2, 2, 2, 2, 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1}; 73 | 74 | static final byte[] builtins = {RND, SQRT, CBRT, SIN, COS, TAN, ASIN, ACOS, ATAN, SINH, COSH, TANH, ASINH, ACOSH, ATANH, EXP, LN, ABS, FLOOR, CEIL, SIGN, MIN, MAX, GCD, COMB, PERM, MOD, REAL, IMAG}; 75 | 76 | } 77 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id("com.android.application") version "8.1.2" apply false 4 | id("org.jetbrains.kotlin.android") version "1.8.21" apply false 5 | id("org.jetbrains.kotlin.jvm") version "1.8.21" apply false 6 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | CalcYou is a privacy focused calculator app that is not just a calculator. 2 | Solve everyday problems with the intuitive basic calculator, or delve into advanced functions with the powerful scientific calculator. 3 | Feeling lost in a sea of units? CalcYou's versatile converter effortlessly handles length, weight, volume, and more. 4 | Need to express complex equations? Access a vast library of symbols, including Greek letters, arrows, superscript, and subscript, all with a few taps. 5 | CalcYou's elegant interface, history function, and customizable themes make calculations a breeze, whether you're a student, engineer, scientist, or anyone who loves the power of numbers. 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Privacy Focused Calculator app built with MD3 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-apps/CalcYou/8e61a0bf33783a51a90ad0e9b6068e4fd43fa233/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | 16 | rootProject.name = "CalcYou" 17 | include(":app") 18 | include(":arity") 19 | --------------------------------------------------------------------------------