From 72af5eab47f2142e573a33edc70e6599d03d342f Mon Sep 17 00:00:00 2001 From: 22 <60903333+nini22P@users.noreply.github.com> Date: Sat, 22 Feb 2025 14:01:36 +0800 Subject: [PATCH 1/8] feat: add the --cwd argument --- packages/terre2/src/main.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/terre2/src/main.ts b/packages/terre2/src/main.ts index 455abf15..1afe9c12 100644 --- a/packages/terre2/src/main.ts +++ b/packages/terre2/src/main.ts @@ -1,6 +1,17 @@ +import * as process from 'process'; + +const args = process.argv.slice(2); +let cwd = process.cwd(); + +const cwdIndex = args.indexOf('--cwd'); +if (cwdIndex !== -1 && cwdIndex + 1 < args.length) { + cwd = args[cwdIndex + 1]; +} + +process.chdir(cwd); + import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; -import * as process from 'process'; import { _open } from './util/open'; import { urlencoded, json } from 'express'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; From 98cd9bb992fbcaeca39c77ae9778b16237271794 Mon Sep 17 00:00:00 2001 From: 22 <60903333+nini22P@users.noreply.github.com> Date: Sat, 22 Feb 2025 14:02:52 +0800 Subject: [PATCH 2/8] fix: add intl polyfill loader --- packages/terre2/intl-polyfill-loader.js | 13 +++++++++++++ packages/terre2/package.json | 7 ++++++- packages/terre2/standalone.js | 9 +++++++++ yarn.lock | 25 +++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/terre2/intl-polyfill-loader.js diff --git a/packages/terre2/intl-polyfill-loader.js b/packages/terre2/intl-polyfill-loader.js new file mode 100644 index 00000000..c3dcfa26 --- /dev/null +++ b/packages/terre2/intl-polyfill-loader.js @@ -0,0 +1,13 @@ +module.exports = function (source) { + const polyfillCode = ` + global.IntlPolyfill = require('intl/lib/core'); + require('intl/locale-data/jsonp/en'); + require('intl/locale-data/jsonp/zh'); + require('intl/locale-data/jsonp/ja'); + global.Intl = global.IntlPolyfill; + global.IntlPolyfill.__applyLocaleSensitivePrototypes(); + require('date-time-format-timezone/build/src/date-time-format-timezone-golden-zones-no-locale'); + `; + + return polyfillCode + source; +}; \ No newline at end of file diff --git a/packages/terre2/package.json b/packages/terre2/package.json index 2cce4439..56cce406 100644 --- a/packages/terre2/package.json +++ b/packages/terre2/package.json @@ -37,7 +37,11 @@ "@nestjs/websockets": "^10.3.6", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "date-time-format-timezone": "^1.0.22", + "intl": "^1.2.5", + "intl-pluralrules": "^2.0.1", "open": "^8.4.0", + "original-fs": "^1.2.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", @@ -53,6 +57,7 @@ "@nestjs/schematics": "^8.0.0", "@nestjs/testing": "^8.0.0", "@types/express": "^4.17.13", + "@types/intl": "^1.2.2", "@types/jest": "27.5.0", "@types/node": "^16.0.0", "@types/supertest": "^2.0.11", @@ -92,4 +97,4 @@ "coverageDirectory": "../coverage", "testEnvironment": "node" } -} +} \ No newline at end of file diff --git a/packages/terre2/standalone.js b/packages/terre2/standalone.js index 1b94dbd3..13445a76 100644 --- a/packages/terre2/standalone.js +++ b/packages/terre2/standalone.js @@ -19,6 +19,15 @@ module.exports = { }, exclude: /node_modules/, }, + { + test: /[\\/]src[\\/]main\.ts$/, + use: [ + { + loader: path.resolve(__dirname, 'intl-polyfill-loader.js'), + }, + ], + exclude: /node_modules/, + }, ], }, // 打包后的文件名称以及位置 diff --git a/yarn.lock b/yarn.lock index ff517da6..bec20f8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3942,6 +3942,11 @@ dependencies: "@types/node" "*" +"@types/intl@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/intl/-/intl-1.2.2.tgz#e39cd70968ad14b12cede5db14e558df24b33272" + integrity sha512-A4g5UEAA9c56JWKhc4LSJ9vzDMztjxVXDAXtVK4EbS3hW1V7Sz05VfcTgDQIvkOYJp1NqH222eg62P72HwltfQ== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.6" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz" @@ -5449,6 +5454,11 @@ date-fns@^3.6.0: resolved "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz" integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== +date-time-format-timezone@^1.0.22: + version "1.0.22" + resolved "https://registry.yarnpkg.com/date-time-format-timezone/-/date-time-format-timezone-1.0.22.tgz#6bb4713aac3bf36338738b59c374583cfcbe6246" + integrity sha512-4hEeKPpNlbFO05ldht9FwJEy2g1xL7kU3dTPY5hNSd1AyMjrrIeUS54kSWgt/KdttYshhjDMIonU+vCmL4NjVw== + debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -6943,6 +6953,16 @@ interpret@^1.0.0: resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +intl-pluralrules@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/intl-pluralrules/-/intl-pluralrules-2.0.1.tgz#de16c3df1e09437635829725e88ea70c9ad79569" + integrity sha512-astxTLzIdXPeN0K9Rumi6LfMpm3rvNO0iJE+h/k8Kr/is+wPbRe4ikyDjlLr6VTh/mEfNv8RjN+gu3KwDiuhqg== + +intl@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/intl/-/intl-1.2.5.tgz#82244a2190c4e419f8371f5aa34daa3420e2abde" + integrity sha512-rK0KcPHeBFBcqsErKSpvZnrOmWOj+EmDkyJ57e90YWaQNqbcivcqmKDlHEeNprDWOsKzPsh1BfSpPQdDvclHVw== + into-stream@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz" @@ -8542,6 +8562,11 @@ ora@5.4.1, ora@^5.1.0, ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +original-fs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/original-fs/-/original-fs-1.2.0.tgz#ea6c5117ee71fe6a9f809130de0590676370b5e8" + integrity sha512-IGo+qFumpIV65oDchJrqL0BOk9kr82fObnTesNJt8t3YgP6vfqcmRs0ofPzg3D9PKMeBHt7lrg1k/6L+oFdS8g== + os-name@4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz" From 9ec9c672f60d8395038d404724a047b2870cd31c Mon Sep 17 00:00:00 2001 From: 22 <60903333+nini22P@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:18:05 +0800 Subject: [PATCH 3/8] feat: add terre-android --- .github/workflows/build-terre.yml | 51 ++- .github/workflows/release.yml | 14 +- packages/origine2/src/config/info.ts | 2 +- packages/terre-android/.gitignore | 19 + packages/terre-android/LICENSE | 373 ++++++++++++++++ packages/terre-android/README.md | 3 + packages/terre-android/app/.gitignore | 1 + packages/terre-android/app/build.gradle.kts | 173 ++++++++ packages/terre-android/app/proguard-rules.pro | 21 + .../terre/ExampleInstrumentedTest.kt | 24 + .../app/src/main/AndroidManifest.xml | 32 ++ .../app/src/main/cpp/CMakeLists.txt | 50 +++ .../app/src/main/cpp/native-lib.cpp | 121 +++++ .../app/src/main/ic_launcher-playstore.png | Bin 0 -> 16751 bytes .../java/com/openwebgal/terre/MainActivity.kt | 420 ++++++++++++++++++ .../com/openwebgal/terre/TerreViewModel.kt | 48 ++ .../com/openwebgal/terre/ui/theme/Color.kt | 11 + .../com/openwebgal/terre/ui/theme/Theme.kt | 57 +++ .../com/openwebgal/terre/ui/theme/Type.kt | 34 ++ .../res/drawable/ic_launcher_background.xml | 170 +++++++ .../res/drawable/ic_launcher_foreground.xml | 30 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1504 bytes .../mipmap-hdpi/ic_launcher_foreground.webp | Bin 0 -> 862 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 3174 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 834 bytes .../mipmap-mdpi/ic_launcher_foreground.webp | Bin 0 -> 584 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1924 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 2014 bytes .../mipmap-xhdpi/ic_launcher_foreground.webp | Bin 0 -> 1136 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 4340 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2874 bytes .../mipmap-xxhdpi/ic_launcher_foreground.webp | Bin 0 -> 1698 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 6780 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3806 bytes .../ic_launcher_foreground.webp | Bin 0 -> 2080 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 9208 bytes .../app/src/main/res/values/colors.xml | 10 + .../res/values/ic_launcher_background.xml | 4 + .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/themes.xml | 5 + .../app/src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../com/openwebgal/terre/ExampleUnitTest.kt | 17 + packages/terre-android/build.gradle.kts | 6 + packages/terre-android/gradle.properties | 23 + .../terre-android/gradle/libs.versions.toml | 35 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + packages/terre-android/gradlew | 185 ++++++++ packages/terre-android/gradlew.bat | 89 ++++ packages/terre-android/settings.gradle.kts | 23 + release-android.sh | 50 +++ 54 files changed, 2149 insertions(+), 3 deletions(-) create mode 100644 packages/terre-android/.gitignore create mode 100644 packages/terre-android/LICENSE create mode 100644 packages/terre-android/README.md create mode 100644 packages/terre-android/app/.gitignore create mode 100644 packages/terre-android/app/build.gradle.kts create mode 100644 packages/terre-android/app/proguard-rules.pro create mode 100644 packages/terre-android/app/src/androidTest/java/com/openwebgal/terre/ExampleInstrumentedTest.kt create mode 100644 packages/terre-android/app/src/main/AndroidManifest.xml create mode 100644 packages/terre-android/app/src/main/cpp/CMakeLists.txt create mode 100644 packages/terre-android/app/src/main/cpp/native-lib.cpp create mode 100644 packages/terre-android/app/src/main/ic_launcher-playstore.png create mode 100644 packages/terre-android/app/src/main/java/com/openwebgal/terre/MainActivity.kt create mode 100644 packages/terre-android/app/src/main/java/com/openwebgal/terre/TerreViewModel.kt create mode 100644 packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Color.kt create mode 100644 packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Theme.kt create mode 100644 packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Type.kt create mode 100644 packages/terre-android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 packages/terre-android/app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 packages/terre-android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp create mode 100644 packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 packages/terre-android/app/src/main/res/values/colors.xml create mode 100644 packages/terre-android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 packages/terre-android/app/src/main/res/values/strings.xml create mode 100644 packages/terre-android/app/src/main/res/values/themes.xml create mode 100644 packages/terre-android/app/src/main/res/xml/backup_rules.xml create mode 100644 packages/terre-android/app/src/main/res/xml/data_extraction_rules.xml create mode 100644 packages/terre-android/app/src/test/java/com/openwebgal/terre/ExampleUnitTest.kt create mode 100644 packages/terre-android/build.gradle.kts create mode 100644 packages/terre-android/gradle.properties create mode 100644 packages/terre-android/gradle/libs.versions.toml create mode 100644 packages/terre-android/gradle/wrapper/gradle-wrapper.jar create mode 100644 packages/terre-android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/terre-android/gradlew create mode 100644 packages/terre-android/gradlew.bat create mode 100644 packages/terre-android/settings.gradle.kts create mode 100644 release-android.sh diff --git a/.github/workflows/build-terre.yml b/.github/workflows/build-terre.yml index 7031bae5..fa78bab0 100644 --- a/.github/workflows/build-terre.yml +++ b/.github/workflows/build-terre.yml @@ -51,7 +51,6 @@ jobs: run: sh release-linux-arm64.sh - name: Compress run: 7z a -tzip release/WebGAL_Terre_Linux_Arm64.zip release/* - - name: Upload Artifact uses: actions/upload-artifact@v4 with: @@ -122,3 +121,53 @@ jobs: with: name: WebGAL_Terre_Windows_Setup path: bundle/WebGal_Terre_Setup.exe + build-android: + name: Build Android Binary + runs-on: ubuntu-latest + env: + KEYSTORE: ${{ secrets.KEYSTORE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version-file: package.json + cache: 'yarn' + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 21 + - name: Decode and save keystore + if: ${{ env.KEYSTORE != '' }} + run: | + echo "${{ secrets.KEYSTORE }}" | base64 --decode > packages/terre-android/app/keystore.jks + - name: Save key.properties + run: | + echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> packages/terre-android/key.properties + echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> packages/terre-android/key.properties + echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> packages/terre-android/key.properties + echo "storeFile=keystore.jks" >> packages/terre-android/key.properties + - name: Build + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + run: sh release-android.sh + - name: Rename signed apk + if: ${{ env.KEYSTORE != '' }} + run: mv packages/terre-android/app/build/outputs/apk/release/app-release.apk release/WebGAL_Terre_Android.apk + - name: Upload Artifact + if: ${{ env.KEYSTORE != '' }} + uses: actions/upload-artifact@v4 + with: + name: WebGAL_Terre_Android + path: release/WebGAL_Terre_Android.apk + - name: Rename unsigned apk + if: ${{ env.KEYSTORE == '' }} + run: mv packages/terre-android/app/build/outputs/apk/release/app-release-unsigned.apk release/WebGAL_Terre_Android_unsigned.apk + - name: Upload unsigned Artifact + if: ${{ env.KEYSTORE == '' }} + uses: actions/upload-artifact@v4 + with: + name: WebGAL_Terre_Android_unsigned + path: release/WebGAL_Terre_Android_unsigned.apk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 38e724e2..b7478eca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: release: name: Release runs-on: ubuntu-latest - needs: ['build-linux','build-arm64','build-mac','build-windows','build-windows-nsis'] + needs: ['build-linux','build-arm64','build-mac','build-windows','build-windows-nsis','build-android'] steps: - name: Checkout uses: actions/checkout@v4 @@ -78,3 +78,15 @@ jobs: asset_path: WebGAL_Terre_Windows_Setup/WebGal_Terre_Setup.exe asset_name: WebGAL_Terre_Windows_Setup_${{ github.ref_name }}.exe asset_content_type: application/vnd.microsoft.portable-executable + + - name: Upload Android Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + KEYSTORE: ${{ secrets.KEYSTORE }} + if: ${{ env.KEYSTORE != '' }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: WebGAL_Terre_Android/WebGAL_Terre_Android.apk + asset_name: WebGAL_Terre_Android_${{ github.ref_name }}.apk + asset_content_type: application/vnd.android.package-archive diff --git a/packages/origine2/src/config/info.ts b/packages/origine2/src/config/info.ts index 1afca165..eee96cc3 100644 --- a/packages/origine2/src/config/info.ts +++ b/packages/origine2/src/config/info.ts @@ -5,5 +5,5 @@ export interface Info { export const __INFO: Info = { version: '4.5.11', - buildTime: '2025-01-01T11:02:25.731Z', // 编译时会通过 version-sync.js 自动更新 + buildTime: '2025-02-22T09:47:56.762Z', // 编译时会通过 version-sync.js 自动更新 }; diff --git a/packages/terre-android/.gitignore b/packages/terre-android/.gitignore new file mode 100644 index 00000000..4d5768e4 --- /dev/null +++ b/packages/terre-android/.gitignore @@ -0,0 +1,19 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +app/libnode/bin +app/libnode/include +app/libnode/nodejs-mobile-* +app/src/main/assets/terre \ No newline at end of file diff --git a/packages/terre-android/LICENSE b/packages/terre-android/LICENSE new file mode 100644 index 00000000..a612ad98 --- /dev/null +++ b/packages/terre-android/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/packages/terre-android/README.md b/packages/terre-android/README.md new file mode 100644 index 00000000..2f3206dd --- /dev/null +++ b/packages/terre-android/README.md @@ -0,0 +1,3 @@ +# WebGAL Terre Android + +Running with [nodejs-mobile](https://github.com/nodejs-mobile/nodejs-mobile) \ No newline at end of file diff --git a/packages/terre-android/app/.gitignore b/packages/terre-android/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/packages/terre-android/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/packages/terre-android/app/build.gradle.kts b/packages/terre-android/app/build.gradle.kts new file mode 100644 index 00000000..c06af525 --- /dev/null +++ b/packages/terre-android/app/build.gradle.kts @@ -0,0 +1,173 @@ +import java.io.FileInputStream +import java.security.MessageDigest +import java.nio.file.Files +import java.net.URI +import java.util.Properties +import java.util.zip.ZipInputStream + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) +} + +val keystoreProperties = Properties() +val keystorePropertiesFile = rootProject.file("key.properties") + +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) +} + +android { + namespace = "com.openwebgal.terre" + compileSdk = 35 + + defaultConfig { + applicationId = "com.openwebgal.terre" + minSdk = 24 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + externalNativeBuild { + cmake { + cppFlags("") + arguments("-DANDROID_STL=c++_shared") + } + } + ndk { + abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86_64")) + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + } + externalNativeBuild { + cmake { + path = file("src/main/cpp/CMakeLists.txt") + version = "3.22.1" + } + } + sourceSets { + getByName("main") { + jniLibs.srcDirs("libnode/bin/") + } + } + signingConfigs { + if (keystorePropertiesFile.exists()) + create("release") { + keyAlias = keystoreProperties["keyAlias"] as? String + ?: throw IllegalArgumentException("Key alias is missing") + keyPassword = keystoreProperties["keyPassword"] as? String + ?: throw IllegalArgumentException("Key password is missing") + storeFile = (keystoreProperties["storeFile"] as? String)?.let { file(it) } + ?: throw IllegalArgumentException("Store file is missing") + storePassword = keystoreProperties["storePassword"] as? String + ?: throw IllegalArgumentException("Store password is missing") + } + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.ui.tooling.preview) + implementation(libs.androidx.material3) + implementation(libs.androidx.browser) + implementation(libs.androidx.lifecycle.viewmodel.compose) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} + +abstract class DownloadNodejsTask : DefaultTask() { + @TaskAction + fun run() { + val url = + "https://github.com/nodejs-mobile/nodejs-mobile/releases/download/v18.20.4/nodejs-mobile-v18.20.4-android.zip" + val expectedMD5 = "4fe60de25381937b03642404513ec26b" + val zipFile = project.file("./libnode/nodejs-mobile-v18.20.4-android.zip") + val extractDir = project.file("./libnode") + + if (zipFile.exists()) { + val calculatedMD5 = MessageDigest.getInstance("MD5") + .digest(Files.readAllBytes(zipFile.toPath())) + .joinToString("") { "%02x".format(it) } + + if (calculatedMD5 != expectedMD5) { + zipFile.delete() + println("MD5 mismatch. File deleted: $zipFile") + } + } + + if (!zipFile.exists()) { + zipFile.parentFile.mkdirs() + println("Downloading Node.js from: $url") + zipFile.outputStream().use { os -> + URI.create(url).toURL().openStream().use { input -> + input.copyTo(os) + } + } + + val calculatedMD5 = MessageDigest.getInstance("MD5") + .digest(Files.readAllBytes(zipFile.toPath())) + .joinToString("") { "%02x".format(it) } + + if (calculatedMD5 != expectedMD5) { + throw GradleException("MD5 verification failed for $zipFile") + } + + println("Extracting Node.js to: $extractDir") + extractDir.mkdirs() + ZipInputStream(zipFile.inputStream()).use { zis -> + var entry = zis.nextEntry + while (entry != null) { + val targetFile = File(extractDir, entry.name) + if (entry.isDirectory) { + targetFile.mkdirs() + } else { + targetFile.parentFile.mkdirs() + targetFile.outputStream().use { fos -> + zis.copyTo(fos) + } + } + zis.closeEntry() + entry = zis.nextEntry + } + } + } + } +} + +tasks.register("downloadNodejs") + +tasks.named("preBuild") { + dependsOn("downloadNodejs") +} \ No newline at end of file diff --git a/packages/terre-android/app/proguard-rules.pro b/packages/terre-android/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/packages/terre-android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/packages/terre-android/app/src/androidTest/java/com/openwebgal/terre/ExampleInstrumentedTest.kt b/packages/terre-android/app/src/androidTest/java/com/openwebgal/terre/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..888fd52d --- /dev/null +++ b/packages/terre-android/app/src/androidTest/java/com/openwebgal/terre/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.openwebgal.terre + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.openwebgal.terre", appContext.packageName) + } +} \ No newline at end of file diff --git a/packages/terre-android/app/src/main/AndroidManifest.xml b/packages/terre-android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b75fdfdf --- /dev/null +++ b/packages/terre-android/app/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/terre-android/app/src/main/cpp/CMakeLists.txt b/packages/terre-android/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..3463dde9 --- /dev/null +++ b/packages/terre-android/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,50 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html. +# For more examples on how to use CMake, see https://github.com/android/ndk-samples. + +# Sets the minimum CMake version required for this project. +cmake_minimum_required(VERSION 3.22.1) + +# Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, +# Since this is the top level CMakeLists.txt, the project name is also accessible +# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level +# build script scope). +project("terre") + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. +# +# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define +# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} +# is preferred for the same purpose. +# +# In order to load a library into your app from Java/Kotlin, you must call +# System.loadLibrary() and pass the name of the library defined here; +# for GameActivity/NativeActivity derived applications, the same library name must be +# used in the AndroidManifest.xml file. +add_library(${CMAKE_PROJECT_NAME} SHARED + # List C/C++ source files with relative paths to this CMakeLists.txt. + native-lib.cpp) + +# Includes node's header files. +include_directories(${CMAKE_SOURCE_DIR}/../../../libnode/include/node/) + +# Imports the pre-built libnode shared library. +add_library(libnode SHARED IMPORTED) + +# Specifies the path to the libnode shared library. +set_target_properties(libnode + PROPERTIES IMPORTED_LOCATION + ${CMAKE_SOURCE_DIR}/../../../libnode/bin/${ANDROID_ABI}/libnode.so) + +# Specifies libraries CMake should link to your target library. You +# can link libraries from various origins, such as libraries defined in this +# build script, prebuilt third-party libraries, or Android system libraries. +target_link_libraries(${CMAKE_PROJECT_NAME} + # Link the libnode library. + libnode + # List libraries link to the target library + android + log) \ No newline at end of file diff --git a/packages/terre-android/app/src/main/cpp/native-lib.cpp b/packages/terre-android/app/src/main/cpp/native-lib.cpp new file mode 100644 index 00000000..9b084f11 --- /dev/null +++ b/packages/terre-android/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include "node.h" +#include +#include +#include + +int pipe_stdout[2]; +int pipe_stderr[2]; +pthread_t thread_stdout; +pthread_t thread_stderr; +const char *ADBTAG = "NODEJS-MOBILE"; + +void *thread_stderr_func(void *) { + ssize_t redirect_size; + char buf[2048]; + while ((redirect_size = read(pipe_stderr[0], buf, sizeof buf - 1)) > 0) { + //__android_log will add a new line anyway. + if (buf[redirect_size - 1] == '\n') + --redirect_size; + buf[redirect_size] = 0; + __android_log_write(ANDROID_LOG_ERROR, ADBTAG, buf); + } + return 0; +} + +void *thread_stdout_func(void *) { + ssize_t redirect_size; + char buf[2048]; + while ((redirect_size = read(pipe_stdout[0], buf, sizeof buf - 1)) > 0) { + //__android_log will add a new line anyway. + if (buf[redirect_size - 1] == '\n') + --redirect_size; + buf[redirect_size] = 0; + __android_log_write(ANDROID_LOG_INFO, ADBTAG, buf); + } + return 0; +} + +int start_redirecting_stdout_stderr() { + //set stdout as unbuffered. + setvbuf(stdout, 0, _IONBF, 0); + pipe(pipe_stdout); + dup2(pipe_stdout[1], STDOUT_FILENO); + + //set stderr as unbuffered. + setvbuf(stderr, 0, _IONBF, 0); + pipe(pipe_stderr); + dup2(pipe_stderr[1], STDERR_FILENO); + + if (pthread_create(&thread_stdout, 0, thread_stdout_func, 0) == -1) + return -1; + pthread_detach(thread_stdout); + + if (pthread_create(&thread_stderr, 0, thread_stderr_func, 0) == -1) + return -1; + pthread_detach(thread_stderr); + + return 0; +} + + +//node's libUV requires all arguments being on contiguous memory. +extern "C" jint JNICALL +Java_com_openwebgal_terre_MainActivity_startNodeWithArguments( + JNIEnv *env, + jobject /* this */, + jobjectArray arguments) { + + //argc + jsize argument_count = env->GetArrayLength(arguments); + + //Compute byte size need for all arguments in contiguous memory. + int c_arguments_size = 0; + for (int i = 0; i < argument_count; i++) { + c_arguments_size += strlen( + env->GetStringUTFChars((jstring) env->GetObjectArrayElement(arguments, i), 0)); + c_arguments_size++; // for '\0' + } + + //Stores arguments in contiguous memory. + char *args_buffer = (char *) calloc(c_arguments_size, sizeof(char)); + + //argv to pass into node. + char *argv[argument_count]; + + //To iterate through the expected start position of each argument in args_buffer. + char *current_args_position = args_buffer; + + //Populate the args_buffer and argv. + for (int i = 0; i < argument_count; i++) { + const char *current_argument = env->GetStringUTFChars( + (jstring) env->GetObjectArrayElement(arguments, i), 0); + + //Copy current argument to its expected position in args_buffer + strncpy(current_args_position, current_argument, strlen(current_argument)); + + //Save current argument start position in argv + argv[i] = current_args_position; + + //Increment to the next argument's expected position. + current_args_position += strlen(current_args_position) + 1; + } + //Start threads to show stdout and stderr in logcat. + if (start_redirecting_stdout_stderr() == -1) { + __android_log_write(ANDROID_LOG_ERROR, ADBTAG, + "Couldn't start redirecting stdout and stderr to logcat."); + } + + //Start node, with argc and argv. + return jint(node::Start(argument_count, argv)); +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_openwebgal_terre_MainActivity_stopNode( + JNIEnv *env, + jobject /* this */) { + return jint(node::Stop(reinterpret_cast(env))); +} \ No newline at end of file diff --git a/packages/terre-android/app/src/main/ic_launcher-playstore.png b/packages/terre-android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..977301892268fbbe399221f02c6dbe55f3c98b0f GIT binary patch literal 16751 zcmeHvX*`tC|Mrcf2$do`NwP$Cl0DgXk-Z|a@5W9Gl5LQEFOh5+yTK^i*k#{|LADt? zV=&J>)$jj2&->@|dEWe9@xp!XbD#4)-|zLkuIq@<(Nd-)XCMavfKpZEp&kGbgTE33 z7tVt}_I!qq0YF|%^`X3hkHzK;#K&MP^=LbK%wlX$qAf;!!)I~N%TH{=IzggSAiY}E zJ7LB;g5-wPH%-BIF*GToRJEJHcT$Ms^lt@E=$DV=KO-+QvqK+VQTVW%ez=8vwW}t4 zs8#2&(sLM?)h}E2GnF2?(sVfOF}q8R-wMXu0013p18_b^+MECog8%QY|6Paw2El(1 z!~d^LK%Ft}R*N;j=dqZ$EV_2{PkzCzS&?2>5+&Ye4UrIZ{Z)b_kQonzv2Czs-yM2eR zwJF(b4W~7unX3i^(DfH1Zaz(6DAVa|TcR;aN^uA`DD0og3hyWtv$c5qv7LM|^7M9E zM@QLXD>~S^MUl|TZT>X0ZSRL`FHbEjKUxTku`t)PpWw`2EaOh`==2&b!2$@QmCT@1 zCARs+rye)v6KpV%lg`Cv64s6TU3-3YgKr~}-h^w!((!Kg*Dqe@N&N{GIB3LC*R$5l zjE{~x$^_3m6g9jd3wH@CoSRypG9M6hQ!T-+^(KsfukESUFtu1|GV5EM&uy_$SKJlk z5f_M-3RMI(QD+K=GeC?IhrwT_ZHjnF!x8)D_aBNt!7+(l!$p&CMIa*^QAid zv#q61BR-*Ov0px?_XJbUasupNTuHj1=lqPkhqyi{B*05!Q)X_EC7xWv^&7(5kSR4 z-Bty-T3L8C`g(WW-wp3rPN68JcfqqJ7!w`t=7fAXH&29huis0>E^j?Mbl~$v?iFa0 zhG@*3Eq|XtcW5vqQ(L4Q;kq{jFObT)hUx*8j}#Z?yjoB0=I;*e_S%+ZKBWujGNC#2 z$OXW$UV-7DFpXSlQYXGZ|M^FAZM=CcZPvq*jFdF2jsM7ve#6^H>|OH}IqhtJ;y_;J)sLYY?~ z-k3(uJO_`Vt*>I42N%1+j?%7obVsdcVvvPnO0BNWLoQoB=aS_Vy{uX=g zCSe>@D*a|8#Kv|;*^@d&NB5vA5h&8 zgH<@9GcRD)kUP)R&f@tSL~Kp$whZ}q9}cYEhr zV;DPrRL}G5*H7}I=bDWZO%B1oH!GY)Hf3Li^-MoCb%1Bn2@<|~kQ=yVai^%9Y@v2B zl-)AGT|83@nd~yh{p$U!Z+#>x@m|W7QN|;)X6$R`s$7oko`D4>w+Q0-kBnTYf$UkY)U8bbWaZaN^_fbKY$?zA+WnFj^s*jj(^WJYc1vQA-+A&x zmm4K$0fpJI)rgyWrN4MLj-xzTDEOu-yAKkfc)FSGDh5QfWyV3K)- z(`3x`;arPK2Cx)Yt9Li8f7PFdk4X=)0I!MJYHMkM5}6N2flY3|_54kqjzek0=DvJ{uxyj03P!D{!SP5UHts7^k5Nn%7pfj?`m&gv_JRFun{%*Zf$5<@lP z{2=bm2buWK9+&R`uOt15*z%x3*3%67HBv_yeFr4pZ~-pQQip@^sY!+uP(GY6W}bKx%#RCw<|I#yiDwdd3uQ zBiaL&-88qZPAm}ODCsl4=ljB~y^+jB7dU5hhcu*id4#9pnQ{WoFuuJ{`tI^8!J^?O z?}!qC2RD07^Eh3-HZ2bQx$RJJ0P0g7&{#L0wrzjHW_@LGC}E7W$uz+hKG-5{S};Qx zPKC6&?0g?g|M;0g5H$Pr4dtztFf zVBpr?B{(ZfG`N)tJV2A*khQQ~r#HM__9Zf#8BseLu)o}6>d4UzF1p5j(&o9_kJEPt zB~F(1oUJxQ?CN{1`wF>fPyu&-|LC^nH|GLK*^wuLL=OiUzY_-02Cz z-=`Xv`9TCs0oG-E$;XGG=)EnbN0nN?@qm=%f+I$J1)z`n+<&Q8&&sh-B8k7_Rn1%v(b|n#IYT+V z-sf8aORVnzirY*vtrK& z>a==*N^p=JT|;4zWmgq(Y^B{aQGi?B(%^eC_VjG`yRkjrpOY3(p$=>Fby6DtoAG|_#?#U7 zZ0wDZ#0JnOm3odD=oZC*45JQ*AA4L|Q- zBZT=|VmbbK$gLpiny{B{cPmUkCrJA=FX+xJIg=ZcbNqu~#y^XN4r_ zdl&nC*(TfiRHKHIv z!1wD4L~Qsh_d{0HVU;NAoE9y z9)!I-rLWV$IBU)5OKT9ZVHE3GZu_%3>i{UD3<^N<^7=+AUYvslFZL|!R6RBlL=_^I zztFO?;KX%GDMExxc?2PpmcOXnG-^qkZI89?sf&r_n_W0Uy{$TMWjz#~0_WNHtaGm8 zCp)gLf0L6mgoT#|oNHS-0DY~)pwn56=x8siB7O1RatNYG9@?p1lRhUSGL<2>o9&~2 zv02fL4`_9$5X*aD;z3nC=X4@EFYbQe#}qQF-< z&Q;A3T8)mA49D*jdLU?%vwMkZDqY{m`n-H5_#GnES_s^zdm4#-YD6BA!yajXm_rb{ zofI9J5&sNqneuXqRs=<0QFoTIp1 z5YpTJ(jYj<{M@ztO79(5RI|uU0hNZwSHeV?m4OEZ{#PVe;`DXn}0atrI9azryWc7gFObFY%@Ol|MCC1MMvM)%u-lW`5z0hlB_ zk$q1)Io_7`-@RwKAuc#*eDzNRN(pYi;GH>oO&EuY$j{1q$K$*;d$vXmTvnavG9m{U zV29hHzBo+W{M{6M?H-tkXsZFJlA&yE5#OrEjs}Qr5^Fui-K7+vd?ZcA4x z+lXhS*Z1*TXk5PrY?6_NP+<@*MWsJ_Q&3zgTMYhReJ%)C?Yl52CjvwIH-6ePtEn2XL}R! zW-b7Q-jt(_QG3NLeb;)bi0ObM68Oye>IAVAB-TnuItkKUvD#18gK9XVSp*EL;ZL+^ zX)X2_Zcd&1+z1uu_44v}!UP|#C}kVHxekEnU&4VjWRC?pON_oTp#WATa6g=pujTA$ zZz14`>x{jPpA7Q@%)(N1FE(3socg9<+fIdNFtEJ1VgP!5!2OvPt$Ro1m;Brr=|Sur z02K+}f3jef1A;~!ijYr=HeJ?94%p?HgLBQXmRDxxScP(~hIF7(;nNY}5sudY_XlOT zijpSJmWLI7Q&}Ivi96d)Eu9^4RO&2B0h`O*)tS!F9N?ww9bk|e%beszJornGHm}Rp zLsozQ|6>W+OrR4Rf#-+lc0NZ>`t2t87Zhd7?#P6t_x1OPM{5?^UPY;!jLIVo`5_sO zxxfYf8RDl)Ni)m)#*DoQG+!%hes4s-qK&&vf%Q%OnO5aju(~xotpew{c?;bCBrT$Q ze!mQN_f7M^l(&kzXabd77@V$+UH{gEmOw zX2FR558dba!BQ&ajS&|%M1P}e2{_)C9^x<`NOC6H@@}*7*bZ@@GUG8t-DAVnz`a!x z=`9~SV3zeyEvo4T>8zH&GJE6ZJ0(xd70-i_lQJEhF;p~cp`qVXi(sU1VAxRl&a941 ze$h7Rcc>f_p(%AZ;YEh-XSv%&EO)=IQFLY8Dx%^pNCoV`HkU13vhAB(Pg@(}6Lvl} zIB3=YDu*J?)+mo|99E^~=fOOWo!jYfp1r<)jrbDA*0j8ADQ89v*TKc(!*p30Ol9b| z>S0rMvx-EB0g&XVmfAntZ4C{y@Y-pCh}|X+$v&;xq<|D5G{0(1P{^U%`Tf1+(_=XT z?f?>05@x86fU9ba?5Zc0^{`h2?5a0aUp-T-zSO1B%o&_s(5{?Scuwb>MLPY7v8_Ky zIndx&BkH`DJYW~}fRnSIk_Wo-Fevw9$vZ2KD{}1Y9SyVFeeQ?Klb5(fKyt%T^pO~d z+v!$_a}pbrQ#0w|FC9#jLL3vAlkU{Z1}Q}_F#6_QmAis`M6?ybtFD3n#T)PZD`Dhd zXXz}pX>p{Vb(0J%=qU>GB~Ycv;2`@_Y{xBg(JO+9!24#evd0r`uJRxAOFOkJ>%+4%rr^U5;yxBa zuaOC8TV}g|H_s<+#^csKg@es^sxFkUlZIRq01w{7zXwktC{}>w-b|DQo01$Z)Wwut zm<;B+-BaX_gNI)>AihIv+3@W7;1Di`$~Vl{Xh92~V8rY{#*_z)C*A?FQej$+O@uOL zvk*V|0i#-Ncv=}fae+MK$72=J5U8>xjFxlcQ(3jb`_*nT0RyX>+Jqr7+Ga1~t1w=R z+uO%|xYsg^y>H#B>J1`-WM5$<9Z?Hvm zlk_e{4N#J+9&{T2d99XC&J>K57hRI8oeCx=K`RG#Yx`62&O~fOSa+D^*9nq~Sm7}; zJ{2LbVK>XA1qRPKOfNXSq8qAipPX#0MubSQ?8tG3++=G6Ech&4FeRgCoN}Yrh|5 z+boEUafw{`rMQA6FSCOY{O~quGlYh2K3-{RTGm?w ziT%Cvd%m)--pj>w5ZrpxoJteT>v~XR-Ffu-GBG&0YXFjw@Eq79PDC0&IW@qH=YC<} zu>ikb*;E#Rys^2(k1DJ->|DGs@oHdFSF@V5>!8^H+!O6kGXRN!eC;rFA|;j6_;OC? zmbGrSV`f6(wt<@`(N@-5R?0i#2}#Zn{J7v1un<#xZ;l_~7RWGVNYf-xwuN3h9`Y1` zubFOA?Lci=It2lWyAy3Sp1rW?f}!xNm0&>@%mt5rT*Ap~N2_>cQUcwT;k~Lt=wvku zT9w2{n*1kh#y7F9=Ka>T-^)yWZBHaftv z&JISO{`_`vNe=MtBKYYl;2+ssv;f&d5NN4ZZ^wuXsnYJOBs)(g?p%{I0!Oba+) zy40}zVLDS^LtKKtC*!X_H4;h@f{q|I@-u%+Ngk2{ZtA-aB`T*qjiT*_&cbtzRwg;b zawh1mY>1^mV$7T6snj>$6mf`B2&onr&qA&} zYt1X^HDJWQU5UGfLxa!{)w8d(&Zys~)*$Gg;8bv&DV3ilNb!c5q~Y_k>^h(3Ie&W8 zRd74o3<0Fe!StG+DD9uKvC}Bk-4i3-3CC1Dav)-xLhA)S!#GG<&!)nS8!axw;A|s> zA?{bFva?bcYr)1sA?<45<}yP3au+87u2ca~=d{!CyW&&&o@Jhj^~U)){CSuLILWK| z02KWKwuaT#HW-hzgN@gEWUub7J>U0}2df2?wz>bjMB+VDLKinoEa(0661ct=11;xl ziCV#4Lg|(U(KnwK{@rk} z7^z&qb$cQ)`yp#=tp_ydRLd-N z28~3(zgP$urL9{Qk?);x0}Dl$DF8c#{pOU%x%lW8ke&s~=;e;XhZVG>++g@V>{}BY z)iAD?9^mnd;Nmh!SMDfL_5GPBAXz5`?%)MU8J~XnpV(QW<|7b8dSA*ZlC;ApDV ze&3|-5B9naba>scp({!%xq#1FI^3!&6t&ktMSvgtC`KevD_DL3L@oN&K+^N)V2-au zWO(R;NqXJ)3KQ}0dqRog1%f;wb<+MfN3;^F75vQhBIhOS&wrW%7;l#kzhf zbFIrB&;EURFxT8R7&(vraT{#uXtxZ0I1+FhlsqI7!%cuJDt_cP{_Lc00{V7w4~^d4 zBn6{gBZYWgXF;8BWP(T+g!M9djme|nK`2uIlTkrGd_Z6A*H*FUyS1U z#HjZG>3iKhJ|9<4;N;$GR8s)Pw~n@X)rQyK>PC@<6!RsY7nH4o_&KU_sYp`%IBg&_ zQP4SRNpXd|MZ*1b)Z=ioN^g^5D77gT>WgFnrVl-VM zCbRT9cS&(Yjh);Oie3O)M8=f$kBalw7L~c#$b7-3=)z1ZmuB z3XWgrY=eUEm-c;ytPlS>8Voux-o2hg^>U&oqT+ z2le^~EsmafJ5d`W{T;QM#8*@=az^x;{)F3DSp3zs0HhV37?% z<|i2?;M1n-+Y2`CSn3{ih+=r}jNHwjCp0bS33E7!*(>Y*L|hMSJM02u@@K zm)PcQvYX1iK6q2se^odY1HAMy>@Ppq6Ity*HyP@jhL_#TUX`+l&U6_3co662FpT5& zJd8Dd(KIX|wFxm*Wt;^?SiQ1DY7%wBDd#9WcAjs2cTD>pMss>8d)!R|ax9|<;*`!p z+m(G5+djm7>79xR3!=M=fjn;xZz;8Q`i{qN!f*6#ntz?u&Xp=KDwO=};f~&#HbLvC*W%y|V7c@D6x85cT=(8bED!>Oe{LuLFRg@r}+$EzU|2J)WX8Qd_z8rrtsM4?xIA_g;*6}$S9)L4O`|l%ovHk# zoI(}Wsv@Y2EM8$m$B4Z-$dt5H5IKK>Np5I9L|Jf|FCj zdWav0SLNrhlIQ?zLOet{n%QgS_AEClx(vq9qVRO;lsCqHP1z;T4yqD|bIyKNh1eha zG$Xd}J{@||+X?syWoAaFz#QS&gH8RB^=sthz(a7;nHDS6l_x2>=3CA22Q#?cppI-F z6u*zDRRz6_OC+{SGhuioB3=97hJ!cSu3VUY;$5O??1S^JmIXS0r!jBN1z~u`&)P8X zq2xj8G^XsUXrP6$<81n!=hOYPbYlpM#4333MdNFb({=Tpi^ zw|;3qEi{A}h||58gUpPt)$bc^vn+Lh)tgt>c4i()a~MsM`V~%N``m@g>SsZbF?-t| z+C)@TjTR)RJ;%jZPqXl6%Vdzmbb?xC+9zR6*~%P&P&^c+AR+~BegYA=KV(wYZ9BOT zrJ-Pm>VHzzGJSR+w7ZM59X~G$o8SC2(~-9aHs?Kuxa3qS?LJZJ7;`71Y7A%&2#ol^ z4jF|x$lpf={@(eyw!56bXfxP&L80zm zi|tue1iYWF%U+{Eez!OF{D*X5p_hFe{hR&p`r&{wyvWq=mGAK*HV0{$He*f0kY3m~ zX9Cmxo{qK+y&#Zh^!?+0G4~H6$ zx=EGuVYVf+b_5|Kpm6S%hr$AtQGVUbm}+EzDHe~p-;WPscK6U$J+jN+u5YR`ho_g#3AIfhwd=vhdnvge zCArKxm8wl+vuE!H(FMCbUl2zAM9S0QoiwMh-5l49S=s3t*w{mVZ26}57Ltx$hyIHO zWSdG>g}U}>$k-={;@L^pUHE3`>a@jY`wg@BDq=*SPqnY_aop~OTh?C#KqU*lU}-oG zm!%=YtZv0fSL6gJW$xBY1YJ@$Ln6jqK4RNQgf-ht%=aqk2CtV7P?3H7yJt6rH1QQU zn{k1k(6J!@L!P2_mmrTq$)c4f@RD9;*)i+MC7ZkY=6#j$W3<(jN8F2X-N$($4ZNne)xR^ zr7h|;52Ry^st^V_6B3?3OO=S@_Vo?8B7)lPa$!kxs%fH!Bg-Vm|oddR+cKIiWOTwUQpPcJQ8_T;G-&1*!jSBRyR z%{-747&;5x$UYOAc8>ngt|G5UOTi*t#7-uxX{NV+R_?9HLNdLxEVOU2@c!?1T|icN z7}wSmt;+qj#YfsR^kiqJnYtErFUrN7H7=FR58+`L`~uO?rd7$8=Icgj5>o{Ur zT6td|YJ6^W^bTC$_1hE1Zp1#Q43FR~W$(>8BWcT4N5)=<<4JtIMSgPBfD zyTxTx8Yivd+Vh`M98s~#ID#7ckWD$~6p?M%6J@s0sc6OIc5%HM}vQwAni`I zo}W=0WpM8c7+awoANh^0RoQ%FuAFK*XTsn!szjx`w*CA?x>J-uy<5CF(+y8PJ8}`b zRc{abl{ow2+T1@2y*2I@A=Qiza81H7rwO4RHYVqn)l6+p3&y2gvsl^VsUq7uN13{i z%tD5KQsV6EQD}FemRU!^n+myj4wCVoK~nf`GlFiWQz}h*_am)>)?~+OKO7ySuXFIW z^wKNLIA>VTcuFOFfi?Tfx9Hf{VHsRoV8>XF7ti}Ui4Tqw32)p`KGWaWr$~db%^IS& zU;73p>mEG)n8O~OdY>xI1wvVCRTV!AxZjB{?!9o;eQUr$SiF75HSd$|pDO30HhFfo z_C57g%%Jpr>tjok&Gyf$+U)Vp5kpC2mZu0pZw&VM6R9MSVnYHKL;!*QC+-7KFDBUr zsaZJD-&FFz%e~;rGWrVdG^YQ9ZjC=;b+_yJZd}ToWP74UUH8m!@yK_h%~(~la|xRG znl*`+Sx%iyBB(hS2FfFGFlw z{|0i`vq+KtUBW&znk*xn-GsM`M+Xmf%k~7F;vSV-Tz zz6+$eBL_e`pq^FR-$EH;OX$*0{O0*^C*qDeK%O@G+Y%DR-Titlvsfd5=Kob6VFdFIafT{^s=op3t?D%zKTTj-G<%;m; z$ubqqEJrjVc~78inP>?&xj#pKo}N791S*9M?+9~%^U=Zd2cNjG+lnAnqvaQyzqb&I zItgU(dgLNeo-#(C`?khL;?-&hbMl@Y|b&r6=G~+y4MG~Kna?; zK^c*;%hrrkL1FoS_DdK>ecCJeiw;o39A(dGQ)$IE=cSaVN{S`zHdF#cAwubHe(g6N+!9%YQ&8-ejM7g{7AB92LHC z50eA!OdcjtCer$UlL29n%4;lQE)_VvxVH~>EuJJy+0MFONm|9 z;pD*YCQmqfKOTQt3M`q|)#JEDeSNbgp>Z^MsaF)=u}lv(K} zgyyo6K|t*Um9CWHrt5shIV&nB8$V1_I9NBdQVd1L)wLrtGvQ zPFhHX-Rx@Q>xUC@mk)HiCnu$sLAlA;JmE6!f1(IfgSd%Nx^ne00oa!wGhk1X-!?dq{O@cAIMDRz7ONYtCC5Lf5%uQUt8(r`bT z7B!Wc1J{&0wtP){2)=m#BlzO7X%k49K0Vyoqx61^h}BIzf}cJ|sK&|ugT-3|Wc-YY zwdr%HAV_{x*M77&SF>!}e999e846(a!y(u`rmgZqY6TV_n+0gsWv;r)2J`>Rt15;? z)SzSS6n*KMv_-K9rcp%J^hbOr!70KD`lgGJlV&3MEtA&Fsga# z*j_h;6p0chnJ)bh(%G9Unu-J;%-kV(zymL0o`W*tvT$IB^6`at|DFKc10AY5&GwTA|shz>^_vv|`;{lWgOn z8`=l^p^W%x^SeYF$h*y8u*d2F4Y5cQgIRd$U+%sgOVs2wHC)Lsj*P2;acbCHnReS{Ujr*h5Gd+r5h@61 zrAGdeT`aP9;&rCYH$_js(KC{St8%4}B za}K}M?!!cN4NU82{2aZYtenr?D(op4iUCWEvil0tTSu})lAys=a_n-3=D`UGq{s0e ze?biUD%F$h=zx4)?XO%Cwc+b3L~y_HeDrLmV96F7QJ7?dTiN`rFmhL*h4YpV?hQFQkJI_d;+m2a^Bt8kv0&wN^k9c< ze0c~K2k-*WuFpcL?@HEuf8P?Zl;`LpQNyNA#HnPX+GbzOx9znk%4Lub*7=`NcB>eFa9$%Y<36F2HneXV<4ic%D)#hNl&bz^zSR7bOKG?cZyGkr`j<&*|Ii}Qs) zPsn`rlFE%N9ds|u?B3>jpFzT2j|XXuj2pjWu5%Tb-yaMr{l*?k6ohT=$#8PI1m1e+ z`v4Csy9ZvQP%Ad~adIBhVxuIeMvDTHgY!0L^#r8p zl6jqEFM>gO0emq7tt8A(V_{`sqYtcvwn_x^&fB~eQ$o5dq3lH;I?O}8&bbdnZO|o? zzTOGtlA_EDbWX8tpR4tHkB!CIq;zMSz9cZTT9W-Gr8TFAL&ik|3h}w+_9!@7 zUT%_0_U2WNHQm62Th?2=n-r9@xbO=VUJU_#~?bbRd_ z$Jv5=Bv{9L0mbG-)ZO;{10yB~7fuY|>~y79cB@nRX_2o5j2E}STM|A7VidQWmVV?X;L6V?H(a zT)Z^|QAq@GS`%9&;Q!0zWpyC=F}0<06X=8ObFIy;kfxi{XZiXQ`qu2)=el?)!)a9` zxm5OxG_cWMgSN}*ixmybfB8cI6F);l=r^-o*H|3}9s1u*Vu!#6-`TE#mb4g1(o`ZE zqiv;hHd4k%HS$u0-^AV8nLizcdha-M$^YJmo+xBgS4SD2?&6ET-8s+!ZQ6Ff!Yooa zSv*nQ;k+Mdid3?!zNqHGW@o?Wy2^md8j`Lztj4SUq*?w5!t~EY3%GieO>h8zAObH^ z!5RG~SB?o7$ zgz^-9HFBD9y3JoG+GtrSV!0;X8L;twzBepBE;5zLhXd03?D_H): Int + private external fun stopNode(): Int + + private val nodeDirName = "terre" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + TerreTheme { + MainScreen( + launchUrl = ::launchUrl, + start = ::start, + stop = ::stop, + ) + } + } + } + + fun start() { + try { + val executor = Executors.newSingleThreadExecutor() + executor.submit { + val nodeDir = + applicationContext.getExternalFilesDir(null)?.absolutePath + "/" + nodeDirName + copyAssets(nodeDir) + startNodeWithArguments( + arrayOf( + "node", + "$nodeDir/main.js", + "--cwd", + nodeDir + ) + ) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun stop() { + try { + stopNode() + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun copyAssets(nodeDir: String) { + if (wasAPKUpdated()) { + Log.i("ASSETS", "Start Copy Assets") +// val nodeDirReference = File(nodeDir) +// if (nodeDirReference.exists()) { +// deleteFolderRecursively(nodeDirReference) +// } + copyAssetFolder(applicationContext.assets, nodeDirName, nodeDir) + + saveLastUpdateTime() + + Log.i("ASSETS", "Copy Assets Finished") + } + } + + private fun deleteFolderRecursively(file: File): Boolean { + return try { + var res = true + val childFiles = file.listFiles() ?: return file.delete() + + for (childFile in childFiles) { + res = if (childFile.isDirectory) { + res and deleteFolderRecursively(childFile) + } else { + res and childFile.delete() + } + } + res and file.delete() + res + } catch (e: Exception) { + e.printStackTrace() + false + } + } + + private fun copyAssetFolder( + assetManager: AssetManager, + fromAssetPath: String, + toPath: String + ): Boolean { + return try { + val files = assetManager.list(fromAssetPath) ?: return copyAsset( + assetManager, + fromAssetPath, + toPath + ) + var res = true + + if (files.isEmpty()) { + res = copyAsset(assetManager, fromAssetPath, toPath) + } else { + File(toPath).mkdirs() + for (file in files) { + res = copyAssetFolder(assetManager, "$fromAssetPath/$file", "$toPath/$file") + } + } + res + } catch (e: Exception) { + e.printStackTrace() + false + } + } + + private fun copyAsset( + assetManager: AssetManager, + fromAssetPath: String, + toPath: String + ): Boolean { + var `in`: InputStream? = null + var out: OutputStream? = null + return try { + `in` = assetManager.open(fromAssetPath) + File(toPath).createNewFile() + out = FileOutputStream(toPath) + copyFile(`in`, out) + `in`.close() + out.flush() + out.close() + true + } catch (e: Exception) { + e.printStackTrace() + false + } finally { + try { + `in`?.close() + out?.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + private fun copyFile(inputStream: InputStream, outputStream: OutputStream) { + val buffer = ByteArray(1024) + var read: Int + while (inputStream.read(buffer).also { read = it } != -1) { + outputStream.write(buffer, 0, read) + } + } + + private fun launchUrl(url: String) { + val intent = Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(url) + } + try { + startActivity(intent) + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(this, "Could not open browser", Toast.LENGTH_SHORT).show() + } + } + + private fun wasAPKUpdated(): Boolean { + val prefs = + applicationContext.getSharedPreferences("NODEJS_MOBILE_PREFS", Context.MODE_PRIVATE) + val previousLastUpdateTime = prefs.getLong("NODEJS_MOBILE_APK_LastUpdateTime", 0) + var lastUpdateTime: Long = 1 + try { + val packageInfo = + applicationContext.packageManager.getPackageInfo(applicationContext.packageName, 0) + lastUpdateTime = packageInfo.lastUpdateTime + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + return lastUpdateTime != previousLastUpdateTime + } + + private fun saveLastUpdateTime() { + var lastUpdateTime: Long = 1 + try { + val packageInfo = + applicationContext.packageManager.getPackageInfo(applicationContext.packageName, 0) + lastUpdateTime = packageInfo.lastUpdateTime + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + val prefs = + applicationContext.getSharedPreferences("NODEJS_MOBILE_PREFS", Context.MODE_PRIVATE) + val editor = prefs.edit() + editor.putLong("NODEJS_MOBILE_APK_LastUpdateTime", lastUpdateTime) + editor.apply() + } +} + +@Composable +fun MainScreen( + terreViewModel: TerreViewModel = viewModel(), + launchUrl: (String) -> Unit, + start: () -> Unit, + stop: () -> Unit, +) { + + val isCopyingAssets by terreViewModel.isCopyingAssets.collectAsState() + + LaunchedEffect(Unit) { + terreViewModel.startNode(start) + } + + Scaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + TopAppBar( + terreViewModel = terreViewModel, + launchUrl = launchUrl, + start = start, + stop = stop + ) + }, + ) { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + if (isCopyingAssets) + Box(modifier = Modifier.fillMaxSize()) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + CircularProgressIndicator() + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = "正在复制资产,请稍候...", + style = MaterialTheme.typography.bodyMedium + ) + } + } + LogcatView(terreViewModel) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TopAppBar( + terreViewModel: TerreViewModel, + launchUrl: (String) -> Unit, + start: () -> Unit, + stop: () -> Unit, +) { + val isNodeRunning by terreViewModel.isNodeRunning.collectAsState() + TopAppBar( + title = { Text("WebGAL Terre") }, + actions = { +// if (!isNodeRunning) { +// TextButton(onClick = { +// terreViewModel.startNode(start) +// }) { +// Text("启动") +// } +// } + if (isNodeRunning) { + TextButton(onClick = { + launchUrl("http://localhost:3001/") + }) { + Text("打开浏览器") + } + } + TextButton(onClick = { + terreViewModel.stopNode(stop) + }) { + Text("关闭") + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.inversePrimary + ) + + ) +} + +private const val ADBTAG = "NODEJS-MOBILE" + +@Composable +fun LogcatView(terreViewModel: TerreViewModel) { + val coroutineScope = rememberCoroutineScope() + val scrollState = rememberScrollState() + val logLines by terreViewModel.logLines.collectAsState() + + LaunchedEffect(key1 = Unit) { + coroutineScope.launch(Dispatchers.IO) { + try { + clearLogcat() + + val process = ProcessBuilder("logcat").redirectErrorStream(true).start() + val bufferedReader = BufferedReader(InputStreamReader(process.inputStream)) + + var line: String? + while (bufferedReader.readLine().also { line = it } != null && isActive) { + line?.let { logLine -> + if (logLine.contains(ADBTAG) || logLine.contains("ASSETS") || logLine.contains( + "E/" + ) + ) { + withContext(Dispatchers.Main) { + terreViewModel.addLogLine( + logLine.split("$ADBTAG:").last().replace("\u001B", "").trim() + ) + } + } + } + } + process.destroy() + Log.d("LogcatView", "Logcat reading stopped.") + bufferedReader.close() + } catch (e: IOException) { + Log.e( + "LogcatView", + "Error reading logcat in coroutine: ${e.message}" + ) + } + } + } + + LaunchedEffect(logLines.size) { + if (logLines.isNotEmpty()) { + scrollState.animateScrollTo(scrollState.maxValue) + } + } + + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(scrollState) + ) { + logLines.forEach { line -> + Text( + line, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp), + ) + } + } +} + +fun clearLogcat() { + try { + val process = Runtime.getRuntime().exec("logcat -c") + process.waitFor() + } catch (e: IOException) { + Log.e("LogcatView", "Error clearing logcat: ${e.message}") + } catch (e: InterruptedException) { + Log.e("LogcatView", "Error clearing logcat: ${e.message}") + Thread.currentThread().interrupt() + } +} \ No newline at end of file diff --git a/packages/terre-android/app/src/main/java/com/openwebgal/terre/TerreViewModel.kt b/packages/terre-android/app/src/main/java/com/openwebgal/terre/TerreViewModel.kt new file mode 100644 index 00000000..e989d693 --- /dev/null +++ b/packages/terre-android/app/src/main/java/com/openwebgal/terre/TerreViewModel.kt @@ -0,0 +1,48 @@ +package com.openwebgal.terre + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +class TerreViewModel : ViewModel() { + private val MAX_LOG_LINES = 500 + + private val _isCopyingAssets = MutableStateFlow(false) + val isCopyingAssets: StateFlow = _isCopyingAssets.asStateFlow() + + private val _isNodeRunning = MutableStateFlow(false) + val isNodeRunning: StateFlow = _isNodeRunning.asStateFlow() + + private val _logLines = MutableStateFlow>(emptyList()) + val logLines: StateFlow> = _logLines.asStateFlow() + + fun updateIsCopyingAssets(isCopyingAssets: Boolean) { + _isCopyingAssets.value = isCopyingAssets + } + + fun startNode(start: () -> Unit) { + if (!_isNodeRunning.value) { + start() + _isNodeRunning.value = true + } + } + + fun stopNode(stop: () -> Unit) { + if (_isNodeRunning.value) { + stop() + _isNodeRunning.value = false + } + } + + fun addLogLine(line: String) { + _logLines.value += line + if (_logLines.value.size > MAX_LOG_LINES) { + _logLines.value = _logLines.value.drop(1) + } + } + + fun clearLogLines() { + _logLines.value = emptyList() + } +} \ No newline at end of file diff --git a/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Color.kt b/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Color.kt new file mode 100644 index 00000000..ad1031cc --- /dev/null +++ b/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.openwebgal.terre.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Theme.kt b/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Theme.kt new file mode 100644 index 00000000..b35b717b --- /dev/null +++ b/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Theme.kt @@ -0,0 +1,57 @@ +package com.openwebgal.terre.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun TerreTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Type.kt b/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Type.kt new file mode 100644 index 00000000..7489b848 --- /dev/null +++ b/packages/terre-android/app/src/main/java/com/openwebgal/terre/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.openwebgal.terre.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/packages/terre-android/app/src/main/res/drawable/ic_launcher_background.xml b/packages/terre-android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/packages/terre-android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/terre-android/app/src/main/res/drawable/ic_launcher_foreground.xml b/packages/terre-android/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/packages/terre-android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..036d09bc --- /dev/null +++ b/packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..036d09bc --- /dev/null +++ b/packages/terre-android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/terre-android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/packages/terre-android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..fc8c08953acba35b51256e6aa53ce04d53c3128f GIT binary patch literal 1504 zcmV<61t0oSNk&H41pok7MM6+kP&iD?1pojqN5Byf=0MQ4ZDRhoxBUzvVgedXSKnJh zLCW?|aq|}tx^ck+-IyuNnOQGY5YdI1Jo7K(U&6W=V?9??_3*97h{(N9)kMgiN-ZL7-Z;=4<15gAR;0TqzDyKCew_cB8PWWTNNwA!|1 z`Q6>nO7aaXa)@L)D^N);$z9U_GlLDF>)4~PZQF+G{-7_&&Hw+rok4BeW^CKGZQJIx zZQJ+vix&7-u_|||Qm0m>KDDZ|z{wJsLX{S9I%|MTkp=DmahobboB^`I?P6zylQYQ0 zDA6Z&O0AL_4g&xLn|iiw+qP}nwr$(Cz00<3+qM&j4FHhM|IgH&SI$;#+nAI7GjiL; z5vR|o&+Z#&@f}FXZhr=)pAt-{P=wTBV#ZtV%T@05a>|!cxGW zf;YkzqdT!m#(8WbxnkZ3o3AQj08GYVg5}c+o2=^+X~!v^u<1r7BWwsCOch@Ih(jP1^S@= zNDKv$#>R3hFI6bV{6L^dirlxu7lJ`~HP!)dpFsmr&vtK3k2W`}Gz%@JspnO1v5>DB zHkvpew$=RZ#8vaZTP_tcxOhRmZP@?6UbG+xg}OC4Ept?U5SZ$5+3T>*0U%l_>m!8V zpyrv1|I0@d2_~(u!PfbxRU{qN4(D8s+b#{ZFakVcBCYIsMf_0SBnhM`Q;fWC zpIN1ud!=lSwj0&+ke{DA-6YDHfB*h%jNyIv;(1*4)~jVsr6qB~@Q~|ixBFfeDQ4tk zTFh)8{Plg0)Wc??loS?rHC9&M`7DNv%q-`04t)P+5~qou+hbiDohhnR<9=O1+fu_} zr$$2EC;VSOwZk~#I@j5b`_1k1oog-ooqAS6lFdZv;P2md;_Af+p})U2MLHV9iJP0H zD?qB>^SeWZ**Ha7x2~Y>h8tyiyHcZDljFG8+>^w_h%*)>>qQG$r5M~`GKtZECeyTT z6wgG5d>U)+?CJq`hN%_8rQW1+ulMX3Gv0@*W$aFEON`b9S|R=Ic>v;QXi2L>X}i-} z0GRlKj%y3;cj^||>N$^#zDD2!z<~fY(nRwH1JMEO3_a?6<13yx#`g z1U2HQi0yjG971xl44H*$es{ag%7B|tBpqJYWuLpBceO$}J|KX5j4(VD9k|5LZGNq_ z&;6@^cTgvsYmue6oV3qyxnq?n00DvbD>t%n&;6qB-yNo;^y>=llfOrcYld+3qWCG> zZ~m$o{4nGv0boVlNPa&`SROV-hrORB;Qj~F_ES7zSlk^m{?J#7Poyaf4L@+vj1}c literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/packages/terre-android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..77dcdf0a6e0c2f4ee5aff0aadaa81be6932203b2 GIT binary patch literal 862 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 MsMNs^@a|EGC~nB6g_%_gQT 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 K8H{hWPzC@bbD#_W literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/packages/terre-android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..28af855f48dc6124b3bba34a61b76e0a17b4cb96 GIT binary patch literal 2014 zcmV<42O;=UNk&H22LJ$9MM6+kP&iD<2LJ#sU%(d-HHU(>ZJ3rn?YBc9A|?RuIS4to zFE{=_Rc`*FFk)s_#>~uk^ma9)NOh?DUCnMJ{HqzIikND+NOM)Z23`ls)c`lZ&8PvT z*MJJZ8jM}Ike8_fIrD4*Zs8)0i?BOz1MJW-Q%HeZ+qO+-e|o+nmCim7v2C+sXD8KD zN?4L)o3{GeU$$-A_I+#h-Q#&vhhtmY@hY}$+jg0i2^gcvN|`=&s$?{_D%-fe?*!1% zw(Yo`4K%iG+qP}nwr$(CtM3cnZwhe+U?)@9s&^N!)K0#&l0AZ*+{bQXzk3Mbs*^G! zuqKdBWjbR5#t5t#*mkO<$^h(^|2oqBe`QR-7{#uCj>4=Q2moLv{r?}ct!&%2-Hgj& z0DxsHX|-(|vuzvc`ac0U$htoSlqF%%5_Ap3)D%i_YKmKV_aB4U7an2>RiY_XJ#nKH z{OsyKqLb4U_C~&po;V9sSf|2072c@udLs1WleL#SlQ_l2iG;>Pg(E5iDhh-I8JgoZ zODyQ*Fol+cVNubsNyslve?Nr$mEenKrrrEb8Y*ipPVperqK8nv3HfhG@sx^uI}Cx2 z%(cY9g$t^x}vkPOU06SJ~P*YfIi9w zeeB(`vDQUB+0?+5d1GrzIjV;LFs&Xgu$oj(Jk+DWoVL4g;euIpr-OQ_g8$^Xsq~73 zsJm*HO*8(<=Mj-No>~|n*-aI)M%jAm5P*g0--1vmEa2l=zHHEOs*`#WXEU`Z6RQOt zW-3~VcbHii^I)4%_b?<}X^>H>8mf@?DHiZ>x7D8Q^juzE-lTFALqB)RW-@tQ(rIBN z|9$+kQTkttu<|uvKhg|~R~l*MDVvZ^`5b~ShK=Ncy03PDgSl6%Ek@XZ=&s_(T^yze~JXllY>AnnfAkyd>hv zFCM|F;R-ldIKTo9b5D(Sg9dYXMv(xGwD!U-!?;JYpohmg*b710|6%8$Ko9eXdosf> zgUPup>fb~cZ9Ra4l$pmN+cJ8+%>3p0lmKwbhior1K7 zxy<Z?9{k?%J1Tf z3;SrmLC*O7U$84=Gnw1_08B3Rha#*4;>_v0l7Beb*2(V?wpllSgc5;cyOU zBR}}=)LSdI1rXRso${HZ$6O zUdp@duG(F^c=1*`MwvrkPTds;NOqGktL*^ZTdPHZhKaW_qB!FhYtkn<;0KGMTDH94% z(QiWimxHGK|6{YhP}N#iCT@@o07}#)Ae2Ahe`xyq`E#eKbX-Q< zq5%M?8Jh-0S*9~ z@OLm;)|*$GN~c*z=6>x=KVDs_$MaU`N4FiB`|W92Z(0lc1Y-~VTtD{{a#R4|ef*&4 zApkc2z#fjdCgTamTqfa|OADTGOlNle1AFA#!+ezmpasAPfJyk( w^`rS$Zse-~zyd(_cfj`byaFHJEA;WbBEdgA`MrFtkh{`9T?_w7S2(y70OnceC;$Ke literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/packages/terre-android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..a42dc96b0af729eb3a2274beab580c63ee9f7c26 GIT binary patch literal 1136 zcmV-$1dsbtNk&F!1ONb6MM6+kP&iCn1ONap*T6Lpwb+&<$&n)o|Nqna&{fqfLvoKJ zhJY+*4I2i`MMwYufIypO+qP}nwr$(CZQHhO+qQ8tO~Bmx|JMJv{=fDAt^fbAt$Vk?VjMOVC<+sauEChTF=kV$Z2Q-F8^__4D$;9%y6mz5zGvO z@(gB2?wox>d^UMkDp%%@ir~yVRDGHmK4o!UiC=t_{gpusT=ZwgzzM6Fkx^=2jWJxn zD>METmzi;uxe_}wx=QdXVznGEjjfV41INEa`klrbz>NL6e>GtFRVtQ78)#Z)a273> zS(yRnqp-aaPx9&~OiLrPOwKSV?8f>w1G9p(_y5bV#PRe*kGJLeiOei5w{kNhJbz)y zE>wf3+|Hh#4tI4P+Vw>x>RDUL=%|-<=K~Vy)-K7Po9Ng z=BNCI;(NegUzNG5mPT-CmXlx5XJi(EayGAw_0^$OCXDBBW;mAcGW^Q?;=Md94Zd>c zDt$9Ut}K^=SB86~d1>GQ1`LwynL$-R#q|}riUlq}d`mOms<_My z?(gsK9s5@$WY*T&37MIhmlO0y4%<6$@QK+huKE*{C;BVEc{M^iucU2|g0Ip75tlOu`EhBIG*+U2j8jX#4EHexCRyU{YDLyz8+ZV67MO&t)6Jc{*x99Dv0ShDP>DVZ=KK0<_Tm3o`W6g@ zZ}nLk#0?x~wwP4J4~)t3?~gt2c#c9|nxO+!dHf*$S%K}^(o{Yn9^q55+&hkWZHBJz z9laAa1L>|*%~9l)wu?N>>Y`NY*VcnMPB3PMT1Dp&7`x>ghv>7Zr97Fbq%bqq97=PXGnHhvPnI0CZ6rumwxehFY@Y8!k!)+*u{ZZ5wv*bn z-EVbf+jeaKDt^Tue_QYSJkh+f9zc(!^U8v4+q9!fo^8aoZ5wUdwr$(CZM(l>+qSLy zP8I~WjT}jGRi~;ByL&g`3v{e)JGNH?jcwbuZQHhO+qRwj2%h)({`dJZ3EN4qwCRlFPH`xyVhI2Yquj5{#y!+7AS+r4>JT=V&d zHGKN%SH30y_!-MsRHqJD(p4-@!{|E3nHcwC42(djf)GuV(i>x(aj5NfR()~`-`oaedpU=3KOqyc6ALHWi$UENQ zDri*1RKk77JA|P0r1!tMF%3ThusSxw_!7pQj3yLvBw9>Ms39TfEgp0yfQ+yACIGTk z$*L9)W8B5)JF*Eus6_T1f}Ze;?*z!`I@$=ZDw>XQIiuNk2wn7!tf0ricm_bDeuGI1 zf|gXP;^&MuxI#C%4T7D02e6WQ?FO)_#n>I0$O>JR74-jq0I>901Cx6gZAb{6m1v+; zA@(BBf{ZS1Dda;&Wkd-B^cg{e3Xk&G=sH*<98^I+nCl8dYWFR8~IlGlf!x{^X z8dnHPs}xj2KO;;ah15_j-c$MiG8WNN>6CG`~on(s&UOQ>p0)=}GR*sM;ZVP&6;L2{oKalz)252YC3jGlEK>m-rk`KY!zIguK?TT%!}Ref zI@u(-kO(M6BMb_v0;wft0Azt+ZHyxM0Rw`nE_O*d7AX@5Gi;xIepJkRSQYSn1oVLP z9ny=2CTc#>R-9lh(n!e((9SHTJjp|~8neKJ>@e+hnf|V#vuVBrcpR)=d-CK-y4&)d znA5(;*lQIT0EmA%T%*j)69f$34`(7^2IYQ|qK1fxw-RQ3Z*+4?AP*X~q&;=yvNb6; z1TZc&ko@lgRsQ5aB49>oB>n?Zo#)254?9nu_|AQ$Jjo>g zx>&RRLH^n8RnYz;;6gG4pMk;;%4Z2el%Nt3`YbdqJplD&Z2XmIUieSts}T(0w9VrpTmd|av%^L%wnR=7#%on7)D<;GjB?JrMx$Q#aGg)Qiuvt);cZRT*cvuPP)vNIOEXdJX?!G$K@sj;k2Hs!pV3dC%=tI7OT` z3fW1FZOawyEX^*#tMSCk^OnImRhhpF|;8^srG7QqZ`Xtp^p5i#uOv5v% z6YW~g2UkZ$p-D%KdWflw;kqsGnfd(GivBFqb8XdNULx^UocX1Z34}PWJjPtzgq%nQ zg?hC3D`Bv@e~2b6%CM9Eu8KV97!p!HW*~|8uIz)CioW^Aoybv7%3{3b@WIsR6P|V^ zG5u}nF8qAzoeID~Di?kS+LL7a>4`SkKHK=KkrR)dLIjM(vfg?3RngI)9eGDC6e68u zH&Ka8r9933mwaZm4_x{CIo(Yer20o`ZcMVTk5?v@rFn|KX{6v5Yoz4zRuKDYy(He` z!=eli7Wdp;&5FKS$_h>7m%XHW={rWm`k7zq;Va@Ug|X)Y3mVfbugybXdfM#`BRt<BxBL229V`;7D)%tf zY@nKe;h#%oX8$9QI*3g>L>`5XfL|+mj~vUovtKdhw67AE2BQ*vX>6_no1&z>E(2Bm z{1tz=9J}u*Np_O(y;kF>qwYhoZ}91#6M1;)YxLlLjzBQV2Y}{VR?_y;M1l}Q!oB22 zS*E8tc!TyDbg)XdMG^XK+2Al?Qf~OQh%Q8iCS63z_*5s{h__Ya-3(h`JQ|Qe zp^1~XY=$rh*ggH6L4`}m2!-(2^r8SO9#4M_#Ea&4H<#2x5h9*@iE=a92=sP) z0faTbb`}Cjjlc~`%x7klN1MgI0phQLtADyUq>m}))F$`RToyrU{sLd}s^?uuBN&mO zJc`(!JngxlqF6iY$A#?ks31&~3xy8I@K9xXu47Dk{)*m#t9}VqqgRmzY&C=o=Yfy( zhxiZpajYgPp0>gF4}QDR{~xqI*VRj46<=2WIL|A#n%)RFb2@JukgXx@TGpG1zO!z; zxCJoFRp2o%b(wWE>0T0T+|9M>gk-ZyTK%jJxV~i+s5QTBECk9*!Dr$#M2!CES64wt z*Lv2YE`9F8&$w9@5SCK2 z{m<2tvFk&&=+ZXLmO^P2Wu}WK)L?VVQ4pzFUFTk|C(kj@C8!<*EdSytfDu|~`RXKP z=pvYDWg350!RJu!5<0RSsxfx+@&v55UPM?ar3`wLU@BO{`{!GnW0Xc(aMGn$#r zDyXec(z2Aa2 zp81&b>1wJ6f)tXlT45~b<)+coIw|B3OJk-~Ek$5f!u#f5Y3={msn7HB4eBnpq%-r8 z{+K~H_c>s&dTljuS=655M=EhB&NlxgAF2(1f3e1A5s^2{yA>Gw^v$1-ftc`F9pxfW zPiKoq6|2H)*PLubtiMO>8|=*X(f-;m>2BgH`>BqU%@bwB!MZ?Eh=9+2UWbEpH&efO zcI7=7t+-tKcCbg(L%TBc^0vc%1Zj;-pL}1>vp_6Z9t}s|bS)B-0 z>D&3||8_Uf1V`3!kP0;d>mttQ$J}QNgJ0+Osh2<(gKt{4M%HjSXp=Lodf{jU4n6$( zdAIZyhI;yM1t$I?a2$O}2ehj1R5nOXg%mmW%@H9=sv8`<`bzg z`_)JX^b_9=fnXAA46ikCmhFw|TK~N=bB&TX6apCIkdNI$^MJR-V-Lx^9rx;7@BNwI zo|u8x>hsEIX*y+%oLr@1Vl5^Jta3AMTV#jYFBJ3UpXY8Efu&qb5O)hN>Qv9$!nNHL zW4qpKY=^#GoMBs|b8TO49;r&xw=pI@nF`fZT1?vpPH;t@OZISGSHtg0O;_LRZm(sG zrP8gBee7dxJCxU&PBF&3yVZ%J)O6Ly&pg2uc@1nE|DyO94hG2CE&i($U2DVySL8JH z)`l$A!;N_jJnrFFdL^~BPK3O1*J~&wC{u}g_YTV8Z;~jfqP6toPCxKvry4%uS||Ed zd^gpa#xxLcFF%QT{fU*Dwjr<|KEV}v%=!+DS+e`&MM~{`&lu zVYvye$a#V*a@%3@H3iPN?Gc}z;~g9Km`^`piZuo7@XVX$hP6EZuYH<-e3rf(3>J6B zoF$IbNG8*yp2Ql16YclSYqhns3>h+{rR`eZ-0wv8_ScD@WsDQiGC`>6E%BrOd){TKj+AaxQdi+qP}nwr$(CZCC%_{_nNd`u}fXP^Pn8ay(V03imoGjWNsM z6PLzkrkXN7_Ym7SlUrb$(n;r3s%^7nH|4}mJ5_K2PR^PxQ^ltXORM8GoHkw7R9Smh zQ#lL(ux$GOADnI5wvE~h90mYDrjnX%+sn4?8v8#1a^(M0WaXbcw+MH4eeUk=GQCwV zC*uNeMozW8HH}LbIeP+9og#HTg1cM%EM0ElZmGw_OX_YHc#>)QI0kp%B7{Ux&T@f` z8#w}LR}ndE2$@EtIwv4@NnZv@l5Nvg4gC#8d!J|9w(a-rrvG^zNV09KYIX~Umb<&V z#U1C~`(IT?z%(-SK+?AD*m8WoZQHiZY6*6uM%D`4sP@v)@Sn$_?dI;oJpr^EwZK2o z)&VO%=-8q^&7eXCui77H8YSYiaRwC`M=4EpJ`0k3zgC6EIa8fihsfX9ypmEnP9+A> zaSD%)Be6Xsc7wz|k=Q>H`xE&=mu^jbIgZ8$9a{wkQH4k93Xh#3F>sDVK|;=-Ah|Z^ zAZ&wZit&ax7t|vKH4=%?f*M0WX~_~Us39&SqjV6aadZRcNc3MrOhNUZCwp&*itT&o zI4lNgJHB&~*f}rN5cISrHhjFQ{J0TP1CDV3M3PS?`92L%krC)b6-M7U^U#-M%IHb$4T_t8G%f(YFVLbpy1P@%=`~q5~j82j6sTx^$mKkRkBh# z59xbz8)FXu0n6KLM?c%iPdM$`Vhg}q?o3APP9f<4p%klT)ZSR^t8mazk(Xx7h}s_~ zns>@_N*4^Uxr0BTbWXzGcUVOftWg!PJXuepok!n5*w&o%RX7UT_=uP66|#RfGRMUS z0K06KVH+*Pf-h^R!da)QQ(0f9?FBR7EV8W!d&=B^gtIQ`IQ-QK+^6oXJ(M+N&SB`M za47h&*wiLmC0;OnP~QPSU2)?om~PJ&4rYTXbz?vHZ*JqPt1?cbAEisXiP_pkyyeeu z5c;#O##9jmM(dAYlP-$94ZdZKu5{x)cWUb+Ecw$uSGs6}F*`yqaCHM8`4db3)TvCn zNnkISO=IqxYn7^X(fgTPSFbApwD+M<%_WpBNOir?LJ>PtT*90eFV48%4L6E?3~OZN zIPzPG$(C&h+5>3jzd}dhpQg&97-!T!D$%SnS1p+}Y3(7=2BYs4&ui{-7Qe-bOfqi= ztFJpDu$si%4d$-n&-MT|3D=&BG$&uSG>KOV*;o#R;<=2!zGLP?IAIxvZZ1A(?V=40 z(pCKFqi~{ek~T>fZA~p*tg0wH_Sj>sTy$?^*}vjBb5%Th!K}K(v4>5ztST577AWpC7gD7Gen#G0NKAAc`2mx&&45ED;=^N0yZ`YFPRIMHOHrnH|a0W ze{_Wqja4TEvLYub-g0c|pwat7{%M0Qk!}<7(&8VTIj+9W0+e^h(@gPZ=O+2?HnZ%5 z-iSUJv6{#~#zgb3&LVXuT@b+k6>m~U1odxX5sW+O13@b5&Ima9;az9ysJENM<1w1V z84nb1P%{T78LYMj?tYj&ev*cE01n%pd(Pg(v)N(_Q5jB|efHVENVkm}8Wfj2Xcaj^ z^hVTH7?SSOcffh<-9V?vTb)L~8f3Qe&{-nQ?cS1To&1JI^!gThnidw#EW9sZUh1n_ zSZoJj_OovxvyR4iNZ$bjudSL|xP|bYxdK+UV>RLjn@nji7yzKJ!C)@3$rJ{ew%NQl zfC&b1uc1)3$S7imkNMp*V~qw!1gUP^B%a^P80sSa-e(_Yu}^Oqr|jb@{_3SBHtFKv z5gYXdF)e1WqS6U_PUJB`=DnngXXOLt5WQ&%OPsbhi-KL(;IgNb$mG-$V zUV8|aQ?boP(joj~i~d8(h{KK`^fWFfz$y(+!DKR2??O^ZFXisTjHgAA3}d^4uH_i9Q@@_uu6HZvYv#5jz0Lck!S`P{vz@w}r#xvwsVMC*?v02XTpV8QD<&?4TXH-7y1@o6^6L{<{SzUq@;(j_zOV{Nw5 zQ|4Jy)aQIXYs&2WTI|fp8*L}-NhX+f)yHA@UrplK0YHQ0*2HSx=|kr5*yW*~>)91H+i>>Y740y}HEnVNaQx`b=Msd%qWd(hE@9`Y9b& zOR;R$QrRJrYxgz@)0{&r7{k0JCf!BG$IAQ_apAY=Cizr6j9Sk~>W@E_?23W!qW4Fz zPM3S}vsE={W)5e$zbum3Qw3lzkVWZ@%W{gDw8U47F&HiaY+0U{&9WSWRWyzNU{;1x zUk7lknamCChcXpV!fsEvH`ggaJ(4`p(IeS(F|6Y7s=<*R2CMiA@lb zJ{LSGw*0NZ_ORJ zpu_PUKw|bAv;g>Adb8Dt#24=8MGBDvtjjb;t`da|MZ`Z{$ zTjexHakDf49)M`88bGH9FlH|j_3Ui@Vl{wVdn%1l{EVgPrT_@F0OHZFMSb*W?fEIX YG{&e(GM1m8M!@`h#;6K20@ALP0x(#EN&o-= literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/packages/terre-android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..2d1718f8b32526bcb0718269759accad81365b3d GIT binary patch literal 1698 zcmV;T23`45Nk&GR1^@t8MM6+kP&iDD1^@srL%~oGwfL4K$&w_=w&MSP8XrX6E?^I8 zRn^@tdB0~|DueSD*%2WD0007QYPN0Lwr$(CZQHhO+qQ9=vzJyA&{zLo{eSiU)&E!j zU;Tge|JDCj|6l!o_5XjFg=fL))6)~ljpRH%Jw1^a>3rJM;sSk7OI1i=i3@P9+A~BF z_8Zws5*#BRv#E^*zGtm>l2A^S(;{vgnC=UVT}9vjo*cl3*H0895)> zD5wv^xrEXzwf}|oK<~&Om;-wS%3&}ML`{={!npw2NnGA0L0RAdC$aVBFlYvxrb$=% zQJ^RJWIhRnf&e0ko<0oY0m*3+P&603Nfdrg!m3a!5|1Ofcziictc&(SBZ(xTN#qxX zh3#SB`CR1oG~+LR6){PuaZIANNG%kP@>64y_<>@cFoIlCorExvB(KYDfqMifFtBj; zK1n|G4Pc^OCjmy=-8PdzMG2I+f#2O$l7ROLy;PT@Tj$1F^(|uySZ)9_Z6`^vbwl$z zxrfvqlKk#&M@h`BSu0@OVz1iY^<3*I)Vg6OvD|Gb3Egh^o|5Q4POMABIagu}_ioxr zlyn<}NZ55FNfL1fN}gX~n<+j;NH@zQLc0YgQPWMSaM*{_I-g`-Gy;+e^lqZa746+} zlDz5Wl0OG2(NPhx_8A$crklZCzTcbIrXRq!P7lVuVhScih= zrr4Y()LxSqe3^iCMeH&8ttrVvABsz=s(EU^lYCENW6&;=d{1KRQ1pNbIEjg}oUy5q z@r;!m2N1nA-HctJnJFn3c#n8qpt^2DF)P;l zkGp4@%LP&+Ru_mKO&h!$z*Iksz&?&}jD7AZnTU8@U-vG+=GZF8@9NOhB^=jZ2VUeovb(ILcQ z(Dzt3c&unl1lbCFeqDpA#5;O9l5y3>nhh zOy7GMC|U#g+a;Z+1j1jbzv=cx7~)DY+-65 zNob!CRKSa~yIzh2a1|8ql6d&ctLQu-EGG#lsooo3l4xnPAW0tKoeor=i{jp0T;bCF zCrRj>4$70{2lm~dd`$w5v|C2eel|dNQS9~Z=##{9cM~tJf1V5^Qg8R;Uqp2`RR5EN z%*h~pKyE+tNxWh0?*3EcoD2pA*mgq$lElv&KxJNhg>o)fFaAN>O+b<;9w73RBxKJ8 z_rXk$h0nZeoN;`a+s11EWaH4;=_LOwB=X0fOR0+U2jkI@1p3Aq!4#8w6i zrwi?@h&wUh0&RoPU6Om0m!uVYCr00iub;iFV3Z^l`!cZtiuuI2=);OvMc{&8E|cW; zOfXbjz}N@jwAz(sNU zVY_ch;sxc3R#JW-Jr7!Ri9M*BDJ>3e0BgUMa5@nXd@iwpW(iJz7C?Dl#IJ(wU%5XK zn*7U>PXaJ}?(RoatV{uC0o7FetJ-h@N3z0$qt&Wrs)cwfNqn?-fA2h(2U3gQ(ARM& z;k;r)<5SgqUyWAYk|<7#{2Rb}1iy>ny+qAb|8%zM`Id24Twvw@8}yg*>2W8KlhsgP sSY7l+AQKpxQN?(XjH?(Xh>-QC^Y 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 literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..3aa0d6eebadb0b1c25aeb02106b6af21ee71ac0d GIT binary patch literal 3806 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 UgUm`PM*@{n%v5(-0_mhK0#1oqy8r+H literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..e52d1054b46df406707202fac9bddb7b486a429b GIT binary patch literal 2080 zcmV+*2;cWoNk&E(2mk@&Duh$N!K2AOAo8fBgUW|MCCh|HuE2|Nkp29^w(@Btq{a))~_Ksb3C3zQ6o! z1EB&@!Z~Ce^ytxyTc0Zb5Z-=Sk9K7)vBR`?_$LMDQ12+{B2J$Z&HBv_X5(;8D!@aA1}Q8Y0Ev1fQEku#TO{?2H3WOW;bpfoj`t)qD6!_2>zDBMTUow&be2)?9! z=4j?@uu0%Xei}}G?HQ;Gco_`Tjg<6cXwN{H4d|E?u|KmvRBz0=WPi_;)X)>+-Q8w} z{xkUz+`S{CTgausXMKa{iMinRtw>6KZ5kJo(B$giiqb)R!e}*6ycNyKqfNsjiAq`q zy5bMf>qz!a2s#emX>1zCFX##)2K&d%VZIahJN~Lo3zp;u9G1J=Jap)U8uZqUP$ZVX-&tf9f}U~HX9n!jJixXQed>=>B;&8ar7&!MpoAA_4!iU!HSQlPeCDUVgJ z8*&$G?OZO0^bNGCqznoM1gnAMt$dKJy*uGoRWL>-LWhQUgQ^(yXaVc3v;z&!VB0wu z(1O^iBG=t*9$|&fx|i;@K~)3-i>eYC)c?gS8b4@u(yg1!zOfx-RF#v1 z?^T5ycvh7U!IdSZJza=p6{k9gz}y!bsm+nzHvR`yRprj$NmaobA6z@&%o3G#{*tQt zl#<#Uq5Y#~5K&bnZ;)111_H5LDY&nxz27y$gLN!b1_x?`H-mPFH>j-MQNij+XaqM6 z$I!S6vxV+PqdW*48u|@_v^E%qznS8Y+L=v5>3u@6yW3ce`~rM2?NM9Z4aV>aqT= zO7&FqUlahV}|BWEh5~j#zPu0V2A@Vcd&NJo7Vj3$f zo0^*kj+!I$2JKs?4%j0$<8?qAC6?dL<$eU`w}l$ragH2ZJmsXBns1fO4?KyUAF4 z@$Vjg;we?ZI`UTg+gdV(`whCVw4kU;Txc#^%~c?OuM2mJQu7|Q?PnASEEFd=m*^TCgboI-oL&7@ASUx_|?5RvdZ3?_G*8RRQ@ z-J_##sZ<9vTAK{gLQ-B=HXoGK6g9Lbn+yv03()H*W`-}B*mxewewphFUMKQ1IDG+) z=DvIHmt8ytZYJo2nU%+(J=u3+PNLV*ojOP@nfO}QIyVx&*Z09sNJ4{e&!I$9KQUCiw8Oav>217wh$|0{HHHoZ+ zxKnf9VAZKPUjU=6gk=S|sw94gk#?L+{W_0lM z-eF{HrB?du6&Ud^VEQLfTmxk^ecxf_9C~Ww93YP%i_!_az_X*FS}ZA9Ltdd(^kRbh zrld@|HM+yhJSW}Jen=w9w zjKSSg#X|B+>b)RP2k8LO(XVx2YLh;=tN68l9{)f7fBgUW|MCCh|HuE2{~!N9{(t=c K`2X?$|NjYl4jQ}w literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/packages/terre-android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..15a83ab3eb6a95a500080757ef7a0d06cd5bcad9 GIT binary patch literal 9208 zcmVR`~ek))*jSz0hQ>MXe8pc7L~+W$0Trv0WbvyKy@ZSh6s~z zcem=UJMJ`htFDRaM?|AHcXz0)btbgBPl0ug!CILkd0EEfSHEj=Yw`XkGz2cVaK{P6f z?vUs%6+S?KWNq8==3hrfop+cGbQ9hR|50t*{PTU!9Xq4iT7hl5QUnMf0Liv(S1YO7 zw$r}Ajm@@g+qSK<&4J*yjU-7w2{u6zL;0o!`eJ8}nu zX6bwZ-hpsvbKV)K(*aN_?N**q!XXf}ZQGF3{nEB=I~!T_{Uxa}p0#b;Hb={`ReO-y zw%w*lyZXKoXxldJW{k$RoqSudZQHhu+O}=mwryK4Hh%lHB}%qix6K7P04^@Tyjp6k zKd>9^|9Z!xBZ?gDoEb-oZscfdxOJ!a6 z_!rEME0lv)x)RxcWkl07-6ZF{K!*B7HJyr*u5}S0+c;C#aPzvL`e#7HS{M3FqWQ(9 z>bOw-_M!UiPtyrm)Hr^&mOkwM(JpY~$Q0GJ07c}Il6JC2kp$fVMFC28p$cLMU>V{V z(j&wNDEI^7H^e`P{}8ZkUO8Zi*}MJgA1`Y*$~%wEvN=T*&~&mm0gZA>+o*(#EM4Ra zk?BG!#6HBUSD2e1ffGaZO3Z)sxegHY31KY0rku2ZPKi$X*Fe&XK5;+3oxUdHClW~IhTVI67l0+UKpg+h_ z9p`Q|Mic}6#8>1~V3m@0jI@@%SD?m+5bgm_feD(Pm zV=+7U)KueWJwWL%?s3sHV{99DM=oP%#@LAW{DRVeuC_TqTn-`yVwcbRJ(kQ8Jj}=U z2ZE`o@#NJs#^e&4Zvk<{8nA|Q+A+2Z-zZe@d%u=4}W9;*2Cs7(uy6a>k0B#r) znxmvKj7|7AMVlI>PF5eHjj>>arC9m=jJ?zgrJ8bMP6~#27V1)F(;Jg2G-YZuIuI-o z>QZhKq|)eAGnr8q!gGYQhjW!~?w_VvCoaW&GAEk|@r4b#jin0t>SKVuga{-lEft_T zL;=oKg&F1)D9kaWbfGS$+NtrB0QE8!RL`=khgn#G=E-zP;|p~$(S|&e&sgvkOI7j( zxis|C99b%}EHglzJZp$+eum2A*%m86f6tL|xlq>}=J@{_2)IGKW(R~>>QkY%1S>%I zPKyu%E39=fE>w3K=ByxIB1_F><*Ih43%S|4(7bOT zE(NBka~KVaT$OL>JhTg;wKVE9tq8G!EY(+z(z9eX6OC0_#*q*vN5G)ImJkYX5n#Ug@q+&ooKBr0rOGORSr*+P6| zL5mP_6FEx5NrGObzBHPY4mn6$f+5yEkE(?%nU^EU3=K`%kZAy>1X<*$e07W$-)@+a zX1Xv!m_SU36qy_)B&{Rb=t4@*NZFJQpnEo`MHH1$F-XbKQ=(iWBSEf+g!kp(2sznC z-Xn{)T{Z>X9bRfmH<5-;#jv3*!XW6#QACPhzOAd&xB_XjLhX-;96<(7kouB9Zc|#+ zL~S8v8Fm4eJao7hQbaarCzcYIukLyo%+f(1bTkYyWycd){cVwwDy7k5)6%df;T(Z1 zRl4v+0Lr#G5}a!bG!qee$kUP~k0j1qZOLzc-8Pa;gQk6t?%9;vFGKW6FK=~hdT3cs zo3B{_^K8m-v*nSUeYa{lDVg83)B*kE5R;S%7#cnlXniFQfD z3~78<0jS#Ml%$@i;Q4hHK{k9JPmq+v+6XPlkcpL4tuWYD~l_Bloo&r4SOE0CS5p{ z|2hB-?W2n8$4d(fb%rsVeBg(Tml%zr@~tdpI?

(S}(%2q;Uo0uor}`-&$9wkBy; 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 OTP0*#$l&vsvPJ>F+qf10 literal 0 HcmV?d00001 diff --git a/packages/terre-android/app/src/main/res/values/colors.xml b/packages/terre-android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/packages/terre-android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/packages/terre-android/app/src/main/res/values/ic_launcher_background.xml b/packages/terre-android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..036a7729 --- /dev/null +++ b/packages/terre-android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #005CAF + \ No newline at end of file diff --git a/packages/terre-android/app/src/main/res/values/strings.xml b/packages/terre-android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..dd9ad4a0 --- /dev/null +++ b/packages/terre-android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + WebGAL Terre + \ No newline at end of file diff --git a/packages/terre-android/app/src/main/res/values/themes.xml b/packages/terre-android/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..c99534a8 --- /dev/null +++ b/packages/terre-android/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +