Class com.greybox.projectmesh.ExampleUnitTest
+ +|
+
+
+
|
+
+
+
+100%
+successful + |
+
-
+
- +Tests + +
Tests
+| Test | +Duration | +Result | +
|---|---|---|
| addition_isCorrect | +0.002s | +passed | +
From a615bb03003d248c1178b38f4fcbe899b04f45a3 Mon Sep 17 00:00:00 2001 From: Thalia Wood <42354895+polygeist111@users.noreply.github.com> Date: Mon, 26 Jan 2026 12:40:15 -0700 Subject: [PATCH 01/87] Initial Commit of CI Pipeline Original development was done in a fork, and certain processes changed many additional files. A fresh commit (manually copied new files) ensures changes can be better tracked. This commit introduces all workflows, NPM dependencies, and the build-reports directory (for a web view of CI outputs). The last feature will not work until a GB admin enables Pages on the repo. --- .github/README.md | 43 ++ .github/workflows/build_artifacts.yml | 112 ++++ .github/workflows/format_and_lint.yml | 102 ++++ .github/workflows/reporting_orchestrator.yml | 136 +++++ .../workflows/test_and_report_coverage.yml | 143 +++++ .prettierignore | 3 + README.md | 18 +- build_reports/.gitignore | 8 + build_reports/README.md | 5 + build_reports/index.html | 81 +++ build_reports/styles.css | 97 ++++ package-lock.json | 537 ++++++++++++++++++ package.json | 15 + 13 files changed, 1296 insertions(+), 4 deletions(-) create mode 100644 .github/README.md create mode 100644 .github/workflows/build_artifacts.yml create mode 100644 .github/workflows/format_and_lint.yml create mode 100644 .github/workflows/reporting_orchestrator.yml create mode 100644 .github/workflows/test_and_report_coverage.yml create mode 100644 .prettierignore create mode 100644 build_reports/.gitignore create mode 100644 build_reports/README.md create mode 100644 build_reports/index.html create mode 100644 build_reports/styles.css create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 000000000..6b47a7354 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,43 @@ +# Mesh CI + +--- + +## Pipeline Flow + +The CI pipeline behaves as follows: + +1. run Prettier on web-related filetypes, as well as XML + a. Do not use prettier-plugin-kotlin, it is not maintained and errors regularly +2. On push, run super-linter. For all possible cases, run autofix (this covers Kotlin) + +- Needs updating + +--- + +## General TODO + +### Ongoing + +- For all workflows, add current build summary as commit/pr comments +- Alter system to use GitHub Pages instead of github HTML preview. This will fix CSS on previews + - Add script to change iFrame title and onscreen title + pass/fail indicator +- Add orchestrator workflow for all reporting tasks + - determine how to allow multiple reusable workflows to share build cache + - add link to super-linter actions output + - onsider whether to delete reports files after use + - add setting on manual run whether to commit anything/deploy to pages +- replace build-artifacts setup gradle with coverage setup gradle + +### Backlog + +- Integrate with Dokka +- Test auto UML diagramming +- Break workflows out into multiple intelligently-grouped jobs for improved execution visibility + +--- + +## Integration TODO + +- Update Super-Linter reusable workflow to point locally instead of to Thalia Wood's (polygeist111) copy +- Update any GitHub pages links in workflow doc to point to correct GB Page +- Petition Chris to configure GitHub pages on the repo \ No newline at end of file diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml new file mode 100644 index 000000000..55f31cca7 --- /dev/null +++ b/.github/workflows/build_artifacts.yml @@ -0,0 +1,112 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle + +name: Build APKs +run-name: ${{ github.event.inputs.custom_run_name || github.workflow }} + +on: + # run when called by another workflow + workflow_call: + inputs: + skip_commit: + description: "Skip committing reports" + required: false + type: boolean + default: false + + # run on demand + workflow_dispatch: + inputs: + custom-run-name: + description: "Custom name for this Actions run" + required: false + type: string + +# cancel any previously-started, yet still active runs of this workflow on the same branch +concurrency: + group: ${{ github.ref }}-Build APKs + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Notify User to Scroll + run: | + echo "## Scroll to the end of the page for artifacts :arrow_down:" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "---" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + - name: Check Out Code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + + # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. + # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md + - name: Setup Gradle + uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + + #- name: Change wrapper permissions + # run: chmod +x ./gradlew + + - name: Build with Gradle Wrapper + run: ./gradlew build + continue-on-error: true # allows APK build even if tests fail + + - name: Upload App Debug APK + uses: actions/upload-artifact@v4 + with: + name: app-debug-apk # Optional: Name your artifact + path: app/build/outputs/apk/debug/app-debug.apk # Path to your build output (e.g., JARs, WARs) + retention-days: 1 # Optional: Set a custom retention period in days + + - name: Upload App Release APK + uses: actions/upload-artifact@v5 + with: + name: app-release-apk # Optional: Name your artifact + path: app/build/outputs/apk/release/app-release-unsigned.apk # Path to your build output (e.g., JARs, WARs) + retention-days: 1 # Optional: Set a custom retention period in days + # NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html). + # If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version. + # + # - name: Setup Gradle + # uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 + # with: + # gradle-version: '8.9' + # + # - name: Build with Gradle 8.9 + # run: gradle build + + dependency-submission: + name: Submit Dependencies + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Check Out Code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "temurin" + + # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. + # See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 diff --git a/.github/workflows/format_and_lint.yml b/.github/workflows/format_and_lint.yml new file mode 100644 index 000000000..b087b8c26 --- /dev/null +++ b/.github/workflows/format_and_lint.yml @@ -0,0 +1,102 @@ +name: Lint Code Base +run-name: ${{ github.event.inputs.custom_run_name || github.workflow }} + +on: + # run when called by another workflow + workflow_call: + inputs: + skip_commit: + description: "Skip committing reports" + required: false + type: boolean + default: false + + # run on demand + workflow_dispatch: + inputs: + custom-run-name: + description: "Custom name for this Actions run" + required: false + type: string + +# cancel any previously-started, yet still active runs of this workflow on the same branch +concurrency: + group: ${{ github.ref }}-Lint Code Base + cancel-in-progress: true + +permissions: read-all + +# adapted from https://mskelton.medium.com/auto-formatting-code-using-prettier-and-github-actions-ed458f58b7df +jobs: + call-gradle-linter: + name: Call Gradle Linter + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Check Out Code + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + + - name: Set Up Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Call Debug and Release Linters + # ostensibly, ./gradlew lint should run both, but in practice on my (Thalia Wood's), it only runs the Debug linter + run: | + ./gradlew lintDebug + ./gradlew lintRelease + + - name: Configure Git Identity (if called individually) + if: ${{ inputs.skip_commit != true }} + run: | + git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + + - name: Copy Linting Reports to Viewing Directory (if called individually) + if: ${{ inputs.skip_commit != true }} + run: | + mkdir ./build_reports/lint_results_debug + cp ./app/build/reports/lint-results-debug.html ./build_reports/lint_report_debug/lint_report_debug.html + mkdir ./build_reports/lint_results_release + cp ./app/build/reports/lint-results-release.html ./build_reports/lint_report_release/lint_report_release.html + git add --force ./build_reports/ + git commit -m "Relocate coverage report for viewing" + git push + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Lint Debug Artifact (if called by orchestrator) + if: ${{ inputs.skip_commit == true }} + uses: actions/upload-artifact@v5 + with: + name: lint_report_debug_artifact + path: ./app/build/reports/lint-results-debug.html + retention-days: 1 + + - name: Upload Lint Release Artifact (if called by orchestrator) + if: ${{ inputs.skip_commit == true }} + uses: actions/upload-artifact@v5 + with: + name: lint_report_release_artifact + path: ./app/build/reports/lint-results-release.html + retention-days: 1 + + call-super-linter: + name: Call Super-Linter + permissions: + contents: read # clone the repo to lint + statuses: write # read/write to repo custom statuses + + uses: polygeist111/super-linter-workflow/.github/workflows/reusable-super-linter.yaml@main + # TODO: update url to point to gb reusable linter, not thalia's + + # with: + ### A regex to exclude files from linting + ### defaults to empty + # filter-regex-exclude: diff --git a/.github/workflows/reporting_orchestrator.yml b/.github/workflows/reporting_orchestrator.yml new file mode 100644 index 000000000..4232fee55 --- /dev/null +++ b/.github/workflows/reporting_orchestrator.yml @@ -0,0 +1,136 @@ +name: Compile Build Reports +run-name: ${{ github.event.inputs.custom_run_name || github.workflow }} + +on: + # run anytime a PR is merged to main or a direct push to main + push: + branches: [main] + + # run on any push to a PR branch + pull_request: + + # run on demand + workflow_dispatch: + inputs: + custom-run-name: + description: "Custom name for this Actions run" + required: false + type: string + +# cancel any previously-started, yet still active runs of this workflow on the same branch +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + format-and-lint: + name: Format and Lint + uses: ./.github/workflows/format_and_lint.yml + with: + skip_commit: true + + test-and-report-coverage: + name: Test and Report Coverage + uses: ./.github/workflows/test_and_report_coverage.yml + with: + skip_commit: true + + upload-apks: + name: Upload APKs + # needs: build-and-test # uses cached build from prior job + uses: ./.github/workflows/build_artifacts.yml + + finalize-report-viewer: + name: Finalize Report Viewer + needs: [format-and-lint, test-and-report-coverage] + if: always() + runs-on: ubuntu-latest + permissions: + contents: write + actions: write + id-token: write + pages: write + environment: + name: github-pages + url: ${{steps.deployment.outputs.page_url}} + steps: + - name: Check Out Code + uses: actions/checkout@v4 + + - name: Ensure Report Dir is empty + run: | + export GLOBIGNORE="./build_reports/index.html:./build_reports/styles.css:./build_reports/README.md:./build_reports/.gitignore" + rm -rf ./build_reports/* + unset GLOBIGNORE + + - name: Download All Artifacts + uses: actions/download-artifact@v5 + with: + path: ./build_reports/ + # TODO: edit to never pull APKs + + - name: Relocate Linting Reports to Dirs + run: | + rm -rf ./build_reports/app-debug-apk 2> /dev/null + rm -rf ./build_reports/app-release-apk 2> /dev/null + + mkdir ./build_reports/coverage_report + cp -a ./build_reports/coverage_report_artifact/. ./build_reports/coverage_report/ + rm -r ./build_reports/coverage_report_artifact + + mkdir ./build_reports/lint_report_debug + cp ./build_reports/lint_report_debug_artifact/lint-results-debug.html ./build_reports/lint_report_debug/lint_report_debug.html + rm -r ./build_reports/lint_report_debug_artifact + + ls ./build_reports + echo "----" + ls ./build_reports/lint_report_debug + + sed -i 's/1px; 1px;/1px 1px;/g' ./build_reports/lint_report_debug/lint_report_debug.html + + mkdir ./build_reports/lint_report_release + cp ./build_reports/lint_report_release_artifact/lint-results-release.html ./build_reports/lint_report_release/lint_report_release.html + rm -r ./build_reports/lint_report_release_artifact + + ls ./build_reports + echo "----" + ls ./build_reports/lint_report_release + + sed -i 's/1px; 1px;/1px 1px;/g' ./build_reports/lint_report_release/lint_report_release.html + + mkdir ./build_reports/test_report_debug + cp -a ./build_reports/test_report_debug_artifact/. ./build_reports/test_report_debug/ + rm -r ./build_reports/test_report_debug_artifact + + mkdir ./build_reports/test_report_release + cp -a ./build_reports/test_report_release_artifact/. ./build_reports/test_report_release/ + rm -r ./build_reports/test_report_release_artifact + + - name: Configure Git Identity + run: | + git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + + - name: Commit Reports to Viewing Directory + run: | + git add --force ./build_reports/ + git commit -m "Relocate reports for viewing" + git push + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Reports Viewer Directory to Pages + uses: actions/upload-pages-artifact@v3 + with: + path: ./build_reports + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Link Pages in Build Summary + run: | + echo "All reports can be viewed at ${{ steps.deployment.outputs.page_url }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "---" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test_and_report_coverage.yml b/.github/workflows/test_and_report_coverage.yml new file mode 100644 index 000000000..901f2b7c8 --- /dev/null +++ b/.github/workflows/test_and_report_coverage.yml @@ -0,0 +1,143 @@ +name: Measure Test Coverage +run-name: ${{ github.event.inputs.custom_run_name || github.workflow }} + +on: + # run when called by another workflow + workflow_call: + inputs: + skip_commit: + description: "Skip committing reports" + required: false + type: boolean + default: false + + # run on demand + workflow_dispatch: + inputs: + custom-run-name: + description: "Custom name for this Actions run" + required: false + type: string + +# cancel any previously-started, yet still active runs of this workflow on the same branch +concurrency: + group: ${{ github.ref }}-Measure Test Coverage + cancel-in-progress: true + +jobs: + test_and_report_coverage: + name: Test and Report Coverage + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Check Out Code + uses: actions/checkout@v4 + + - name: Define Coverage Minimums + run: | + echo "min-coverage-overall=80" >> $GITHUB_ENV + echo "min-coverage-changed-files=80" >> $GITHUB_ENV + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + + - name: Set Up Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Generate Kover Coverage Report + id: koverXmlReport + run: ./gradlew koverXmlReport --continue + continue-on-error: true + + - name: Download Kover CLI (only on test failure) + if: steps.koverXmlReport.outcome == 'failure' + run: wget -O kover-cli.jar https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kover-cli/0.9.3/kover-cli-0.9.3.jar + # ensure the versions in the link match the kover version in the top-level build.gradle.kts file + + - name: Format Kover XML Coverage Report Via CLI (only on test failure) + if: steps.koverXmlReport.outcome == 'failure' + run: java -jar kover-cli.jar report ./app/build/kover/bin-reports/testDebugUnitTest.ic --classfiles ./app/build/tmp/kotlin-classes --src ./app/src/main/java --xml ./app/build/reports/kover/report.xml + + - name: Format Kover HTML Coverage Report Via CLI (only on test failure) + if: steps.koverXmlReport.outcome == 'failure' + run: java -jar kover-cli.jar report ./app/build/kover/bin-reports/testDebugUnitTest.ic --classfiles ./app/build/tmp/kotlin-classes --src ./app/src/main/java --html ./app/build/reports/kover/html/ + + - name: Add Coverage Report to PR + id: kover + uses: mi-kas/kover-report@v1 + with: + path: ${{ github.workspace }}/app/build/reports/kover/report.xml + title: Code Coverage + update-comment: true + min-coverage-overall: ${{ env.min-coverage-overall }} + min-coverage-changed-files: ${{ env.min-coverage-changed-files }} + coverage-counter-type: LINE + + - name: Configure Git Identity (if called individually) + if: ${{ inputs.skip_commit != true }} + run: | + git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + + - name: Copy Coverage Report & Test Outputs to Viewing Directory (if called individually) + if: ${{ inputs.skip_commit != true }} + run: | + cp -r ./app/build/reports/kover/html/ ./build_reports/coverage_report + cp -r ./app/build/reports/tests/testDebugUnitTest ./build_reports/test_report_debug + cp -r ./app/build/reports/tests/testReleaseUnitTest ./build_reports/test_report_release + git add --force ./build_reports/ + git commit -m "Relocate coverage report for viewing" + git push + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Coverage Report Artifact (if called by orchestrator) + if: ${{ inputs.skip_commit == true }} + uses: actions/upload-artifact@v5 + with: + name: coverage_report_artifact + path: ./app/build/reports/kover/html + retention-days: 1 + + - name: Upload Test Debug Artifact (if called by orchestrator) + if: ${{ inputs.skip_commit == true }} + uses: actions/upload-artifact@v5 + with: + name: test_report_debug_artifact + path: ./app/build/reports/tests/testDebugUnitTest + retention-days: 1 + + - name: Upload Test Release Artifact (if called by orchestrator) + if: ${{ inputs.skip_commit == true }} + uses: actions/upload-artifact@v5 + with: + name: test_report_release_artifact + path: ./app/build/reports/tests/testReleaseUnitTest + retention-days: 1 + + - name: Add Summary to Workflow Run + run: | + echo "| Type | Coverage | Passing |" >> $GITHUB_STEP_SUMMARY + echo "|---|---|---|" >> $GITHUB_STEP_SUMMARY + echo "| Overall Coverage | ${{ steps.kover.outputs.coverage-overall }}% | ${{ steps.kover.outputs.coverage-overall >= env.min-coverage-overall && ':white_check_mark:' || ':x:' }} |" >> $GITHUB_STEP_SUMMARY + echo "| Changed File Coverage | ${{ steps.kover.outputs.coverage-changed-files }}% | ${{ steps.kover.outputs.coverage-changed-files >= env.min-coverage-changed-files && ':white_check_mark:' || ':x:' }} |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "(Changed File Coverage is currently bugged, disregard)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Full coverage report can be viewed at https://html-preview.github.io/?url=https://github.com/polygeist111/Project-Mesh/blob/main/build_reports/coverage_report/index.html" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "---" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + # TODO: update coverage report link to point to GB + # configure Mesh for GitHub Pages (need to ask a higher-up) + + - name: Verify Test/Build Outcome + if: steps.koverXmlReport.outcome == 'failure' + run: | + echo "Tests (or build) failed, marking workflow as failed" + exit 1 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..00543e568 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +build +coverage +node_modules \ No newline at end of file diff --git a/README.md b/README.md index ddb1d424c..9fc333137 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,29 @@ # Project Mesh -------------------- + +--- + Grey-box.ca New version by wil-mesh-rmit -Project Mesh runs locally on an android device. Build the project then either export the APK and send to the device, or use ADB to install. The app only works on physical devices, and not the android simulator. +Project Mesh runs locally on an android device. Build the project then either export the APK and send to the device, or use ADB to install. The app only works on physical devices, and not the android simulator. + +
+ | Correctness + | |
| 2 | warning +ScopedStorage: Affected by scoped storage |
| 1 | warning +InlinedApi: Using inlined constants on older versions |
| 1 | warning +OldTargetApi: Target SDK attribute is not targeting latest version |
| 2 | warning +SimpleDateFormat: Implied locale in date format |
| 1 | warning +UnusedAttribute: Attribute unused on older versions |
| 1 | warning +AppBundleLocaleChanges: App Bundle handling of runtime locale changes |
| 1 | warning +RedundantLabel: Redundant label on activity |
| 3 | warning +AndroidGradlePluginVersion: Obsolete Android Gradle Plugin Version |
| 70 | warning +GradleDependency: Obsolete Gradle Dependency |
| 1 | warning +LockedOrientationActivity: Incompatible screenOrientation value |
| 6 | warning +SimilarGradleDependency: Multiple Versions Gradle Dependency |
| 1 | warning +DiscouragedApi: Using discouraged APIs |
| Security + | |
| 3 | warning +TrustAllX509TrustManager: Insecure TLS/SSL trust manager |
| Performance + | |
| 5 | warning +ObsoleteSdkInt: Obsolete SDK_INT Version Check |
| 1 | warning
+AutoboxingStateCreation: State<T> will autobox values assigned to this state. Use a specialized state type instead. |
| 19 | warning +UnusedResources: Unused resources |
| Productivity + | |
| 30 | warning +UseTomlInstead: Use TOML Version Catalog Instead |
| Internationalization + | |
| 2 | warning +HardcodedText: Hardcoded text |
| Included Additional Checks (70) + | |
| Disabled Checks (41) + |
+ 37 <uses-permission + 38 android:name="android.permission.ACCESS_COARSE_LOCATION" /> + 39 + 40 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + 41 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + 42 <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> ++ +../../src/main/AndroidManifest.xml:41:
+ 38 android:name="android.permission.ACCESS_COARSE_LOCATION" /> + 39 + 40 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + 41 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + 42 <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> + 43 + 44 <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> ++ +
+ 66 0 -> { // Request Nearby Wi-Fi Permission + 67 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + 68 !hasPermission(context, Manifest.permission.NEARBY_WIFI_DEVICES)) { + 69 nearbyWifiPermissionLauncher.launch(Manifest.permission.NEARBY_WIFI_DEVICES) + 70 } else { + 71 currentStep = 1 + 72 } ++ +
+ 14 defaultConfig { + 15 applicationId = "com.greybox.projectmesh" + 16 minSdk = 26 + 17 targetSdk = 34 + 18 versionCode = 1 + 19 versionName = "1.0" ++ +
+ 503 horizontalArrangement = Arrangement.End + 504 ) { + 505 Text( + 506 text = SimpleDateFormat("HH:mm").format(Date(chatMessage.dateReceived)), + 507 style = MaterialTheme.typography.labelSmall + 508 ) + 509 } ++ +../../src/main/java/com/greybox/projectmesh/messaging/utils/MessageUtils.kt:6:
+ 3 object MessageUtils { + 4 fun formatTimestamp(timestamp: Long): String { + 5 //Adding timestamp formatting logic + 6 return java.text.SimpleDateFormat("HH:mm").format(timestamp) + 7 } + 8 + 9 fun generateChatId(sender: String, receiver: String): String { ++ +
+ 31 or higher, you must declare the NEARBY_WIFI_DEVICES permission. + 32 --> + 33 <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" + 34 android:usesPermissionFlags="neverForLocation" /> + 35 <uses-permission + 36 android:name="android.permission.ACCESS_FINE_LOCATION" /> + 37 <uses-permission+ +
+ 242 private fun updateLocale(languageCode: String): Locale { + 243 val locale = Locale(languageCode) + 244 val config = resources.configuration + 245 config.setLocale(locale) + 246 @Suppress("DEPRECATION") + 247 resources.updateConfiguration(config, resources.displayMetrics) + 248 return locale ++ +
+ 73 <activity + 74 android:name=".MainActivity" + 75 android:exported="true" + 76 android:label="@string/app_name" + 77 android:theme="@style/Theme.ProjectMesh.Launcher"> + 78 <intent-filter> + 79 <action android:name="android.intent.action.MAIN" /> ++ +
+ 1 [versions] + 2 agp = "8.5.1" + 3 datastoreCoreVersion = "1.1.1" + 4 datastorePreferences = "1.1.1" + 5 kotlin = "1.9.0" ++ +../../../gradle/libs.versions.toml:2:
+ 1 [versions] + 2 agp = "8.5.1" + 3 datastoreCoreVersion = "1.1.1" + 4 datastorePreferences = "1.1.1" + 5 kotlin = "1.9.0" ++ +../../../gradle/libs.versions.toml:2:
+ 1 [versions] + 2 agp = "8.5.1" + 3 datastoreCoreVersion = "1.1.1" + 4 datastorePreferences = "1.1.1" + 5 kotlin = "1.9.0" ++ +
+ 61 } + 62 + 63 dependencies { + 64 implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6") + 65 implementation(libs.androidx.core.ktx) + 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) ++ +../../build.gradle.kts:71:
+ 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) ++ +../../build.gradle.kts:72:
+ 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) + 75 implementation(libs.androidx.ui) ++ +../../build.gradle.kts:73:
+ 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) + 75 implementation(libs.androidx.ui) + 76 implementation(libs.androidx.ui.graphics) ++ +../../build.gradle.kts:96:
+ 93 debugImplementation(libs.androidx.ui.tooling) + 94 debugImplementation(libs.androidx.ui.test.manifest) + 95 implementation("com.github.UstadMobile.Meshrabiya:lib-meshrabiya:0.1d10-snapshot") + 96 implementation("com.github.seancfoley:ipaddress:5.3.3") + 97 implementation("com.squareup.okhttp3:okhttp:4.10.0") + 98 implementation("org.nanohttpd:nanohttpd:2.3.1") + 99 implementation (libs.material) ++ ++ 65 More Occurrences... + +
+ 93 </provider> + 94 <activity + 95 android:name="com.journeyapps.barcodescanner.CaptureActivity" + 96 android:screenOrientation="portrait" + 97 android:stateNotNeeded="true" + 98 tools:replace="android:screenOrientation" /> + 99 <activity android:name=".debug.CrashScreenActivity" android:process=":error_handler" android:exported="false"></activity>+ +
+ 24 + 25 [libraries] + 26 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } + 27 androidx-datastore-core-v111 = { module = "androidx.datastore:datastore-core", version.ref = "datastoreCoreVersion" } + 28 androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } + 29 androidx-foundation = { module = "androidx.compose.foundation:foundation" } + 30 junit = { group = "junit", name = "junit", version.ref = "junit" } ++ +../../../gradle/libs.versions.toml:27:
+ 24 + 25 [libraries] + 26 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } + 27 androidx-datastore-core-v111 = { module = "androidx.datastore:datastore-core", version.ref = "datastoreCoreVersion" } + 28 androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } + 29 androidx-foundation = { module = "androidx.compose.foundation:foundation" } + 30 junit = { group = "junit", name = "junit", version.ref = "junit" } ++ +../../../gradle/libs.versions.toml:27:
+ 24 + 25 [libraries] + 26 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } + 27 androidx-datastore-core-v111 = { module = "androidx.datastore:datastore-core", version.ref = "datastoreCoreVersion" } + 28 androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } + 29 androidx-foundation = { module = "androidx.compose.foundation:foundation" } + 30 junit = { group = "junit", name = "junit", version.ref = "junit" } ++ +../../../gradle/libs.versions.toml:46:
+ 43 lib-meshrabiya = { module = "com.github.UstadMobile.Meshrabiya:lib-meshrabiya", version.ref = "libMeshrabiya" } + 44 androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } + 45 material = { group = "com.google.android.material", name = "material", version.ref = "material" } + 46 androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" } + 47 androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } + 48 androidx-datastore-preferences-core = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "datastorePreferencesCore" } + 49 androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } ++ +../../../gradle/libs.versions.toml:46:
+ 43 lib-meshrabiya = { module = "com.github.UstadMobile.Meshrabiya:lib-meshrabiya", version.ref = "libMeshrabiya" } + 44 androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } + 45 material = { group = "com.google.android.material", name = "material", version.ref = "material" } + 46 androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" } + 47 androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } + 48 androidx-datastore-preferences-core = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "datastorePreferencesCore" } + 49 androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } ++ +../../../gradle/libs.versions.toml:46:
+ 43 lib-meshrabiya = { module = "com.github.UstadMobile.Meshrabiya:lib-meshrabiya", version.ref = "libMeshrabiya" } + 44 androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } + 45 material = { group = "com.google.android.material", name = "material", version.ref = "material" } + 46 androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" } + 47 androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } + 48 androidx-datastore-preferences-core = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "datastorePreferencesCore" } + 49 androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } ++ +
+ 93 </provider> + 94 <activity + 95 android:name="com.journeyapps.barcodescanner.CaptureActivity" + 96 android:screenOrientation="portrait" + 97 android:stateNotNeeded="true" + 98 tools:replace="android:screenOrientation" /> + 99 <activity android:name=".debug.CrashScreenActivity" android:process=":error_handler" android:exported="false"></activity>+ +
+ 16 private const val CHANNEL_NAME = "File Receive Notifications" + 17 + 18 fun createNotificationChannel(context: Context) { + 19 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + 20 val channel = NotificationChannel( + 21 CHANNEL_ID, + 22 CHANNEL_NAME, ++ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:67:
+ 64 if (currentStep == 6) return@LaunchedEffect + 65 when (currentStep) { + 66 0 -> { // Request Nearby Wi-Fi Permission + 67 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + 68 !hasPermission(context, Manifest.permission.NEARBY_WIFI_DEVICES)) { + 69 nearbyWifiPermissionLauncher.launch(Manifest.permission.NEARBY_WIFI_DEVICES) + 70 } else { ++ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:75:
+ 72 } + 73 } + 74 1 -> { // Request Location Permission + 75 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + 76 !hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)) { + 77 locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION) + 78 } else { ++ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:135:
+ 132 /** Function to Check If Battery Optimization is Disabled */ + 133 fun isBatteryOptimizationDisabled(context: Context): Boolean { + 134 val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + 135 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + 136 powerManager.isIgnoringBatteryOptimizations(context.packageName) + 137 } else { + 138 true // Battery optimization doesn't apply below Android 6.0+ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:144:
+ 141 + 142 /** Function to Prompt User to Disable Battery Optimization */ + 143 fun promptDisableBatteryOptimization(context: Context) { + 144 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + 145 val message = SpannableString( + 146 "To ensure uninterrupted background functionality and maintain a stable connection, " + + 147 "please disable battery optimization for this app.\n\n" + ++ +
+ 129 mutableStateOf(settingPref.getString( + 130 "language", "en") ?: "en") + 131 } + 132 var restartServerKey by remember {mutableStateOf(0)} + 133 var deviceName by remember { + 134 mutableStateOf(settingPref.getString("device_name", Build.MODEL) ?: Build.MODEL) + 135 } ++ +
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + 3 xmlns:app="http://schemas.android.com/apk/res-auto" + 4 xmlns:tools="http://schemas.android.com/tools" + 5 android:id="@+id/main"+ +../../src/main/res/layout/activity_main.xml:2:
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <androidx.constraintlayout.widget.ConstraintLayout + 3 xmlns:android="http://schemas.android.com/apk/res/android" + 4 xmlns:app="http://schemas.android.com/apk/res-auto" + 5 xmlns:tools="http://schemas.android.com/tools"+ +../../src/main/res/values/colors.xml:3:
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <resources> + 3 <color name="purple_200">#FFBB86FC</color> + 4 <color name="purple_500">#FF6200EE</color> + 5 <color name="purple_700">#FF3700B3</color> + 6 <color name="teal_200">#FF03DAC5</color>+ +../../src/main/res/values/colors.xml:4:
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <resources> + 3 <color name="purple_200">#FFBB86FC</color> + 4 <color name="purple_500">#FF6200EE</color> + 5 <color name="purple_700">#FF3700B3</color> + 6 <color name="teal_200">#FF03DAC5</color> + 7 <color name="teal_700">#FF018786</color>+ +../../src/main/res/values/colors.xml:5:
+ 2 <resources> + 3 <color name="purple_200">#FFBB86FC</color> + 4 <color name="purple_500">#FF6200EE</color> + 5 <color name="purple_700">#FF3700B3</color> + 6 <color name="teal_200">#FF03DAC5</color> + 7 <color name="teal_700">#FF018786</color> + 8 <color name="black">#FF000000</color>+ ++ 14 More Occurrences... + +
+ 61 } + 62 + 63 dependencies { + 64 implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6") + 65 implementation(libs.androidx.core.ktx) + 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) ++ +../../build.gradle.kts:68:
+ 65 implementation(libs.androidx.core.ktx) + 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) + 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") ++ +../../build.gradle.kts:69:
+ 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) + 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") ++ +../../build.gradle.kts:71:
+ 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) ++ +../../build.gradle.kts:72:
+ 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) + 75 implementation(libs.androidx.ui) ++ ++ 25 More Occurrences... + +
+ 15 android:layout_marginTop="9dp" + 16 android:layout_marginEnd="13dp" + 17 android:layout_marginBottom="9dp" + 18 android:text="TextView" + 19 app:layout_constraintBottom_toBottomOf="parent" + 20 app:layout_constraintEnd_toEndOf="parent" + 21 app:layout_constraintStart_toStartOf="parent"+ +../../src/main/res/layout/activity_main.xml:15:
+ 12 android:layout_height="wrap_content" + 13 android:layout_marginStart="24dp" + 14 android:layout_marginTop="24dp" + 15 android:text="Project Mesh" + 16 android:textSize="24sp" + 17 app:layout_constraintStart_toStartOf="parent" + 18 app:layout_constraintTop_toTopOf="parent" /> ++ +
lint.xml configuration files in the project directories.
+
+ @SuppressLint annotation in the Java codetools:ignore attribute in the XML filebuild.gradle file, as explained belowlint.xml configuration file in the projectlint.xml configuration file passed to lint via the --config flag@SuppressLint("id") annotation on the class, method or variable declaration closest to the warning instance you want to disable. The id can be one or more issue id's, such as "UnusedResources" or {"UnusedResources","UnusedIds"}, or it can be "all" to suppress all lint warnings in the given scope.//noinspection id comment on the line before the statement with the error.tools:ignore="id" attribute on the element containing the error, or one of its surrounding elements. You also need to define the namespace for the tools prefix on the root element in your document, next to the xmlns:android declaration:xmlns:tools="http://schemas.android.com/tools"build.gradle file, add a section like this:
+android {
+ lintOptions {
+ disable 'TypographyFractions','TypographyQuotes'
+ }
+}
+
+warning or error instead of disable to change the severity of issues.lint.xml and place it at the root directory of the module in which it applies.lint.xml file is something like the following:+<?xml version="1.0" encoding="UTF-8"?> +<lint> + <!-- Ignore everything in the test source set --> + <issue id="all"> + <ignore path="\*/test/\*" /> + </issue> + + <!-- Disable this given check in this project --> + <issue id="IconMissingDensityFolder" severity="ignore" /> + + <!-- Ignore the ObsoleteLayoutParam issue in the given files --> + <issue id="ObsoleteLayoutParam"> + <ignore path="res/layout/activation.xml" /> + <ignore path="res/layout-xlarge/activation.xml" /> + <ignore regexp="(foo|bar)\.java" /> + </issue> + + <!-- Ignore the UselessLeaf issue in the given file --> + <issue id="UselessLeaf"> + <ignore path="res/layout/main.xml" /> + </issue> + + <!-- Change the severity of hardcoded strings to "error" --> + <issue id="HardcodedText" severity="error" /> +</lint> ++
$ lint --ignore UnusedResources,UselessLeaf /my/project/path| Correctness + | |
| 2 | warning +ScopedStorage: Affected by scoped storage |
| 1 | warning +InlinedApi: Using inlined constants on older versions |
| 1 | warning +OldTargetApi: Target SDK attribute is not targeting latest version |
| 2 | warning +SimpleDateFormat: Implied locale in date format |
| 1 | warning +UnusedAttribute: Attribute unused on older versions |
| 1 | warning +AppBundleLocaleChanges: App Bundle handling of runtime locale changes |
| 1 | warning +RedundantLabel: Redundant label on activity |
| 2 | warning +AndroidGradlePluginVersion: Obsolete Android Gradle Plugin Version |
| 52 | warning +GradleDependency: Obsolete Gradle Dependency |
| 1 | warning +LockedOrientationActivity: Incompatible screenOrientation value |
| 4 | warning +SimilarGradleDependency: Multiple Versions Gradle Dependency |
| 1 | warning +DiscouragedApi: Using discouraged APIs |
| Security + | |
| 3 | warning +TrustAllX509TrustManager: Insecure TLS/SSL trust manager |
| Performance + | |
| 5 | warning +ObsoleteSdkInt: Obsolete SDK_INT Version Check |
| 1 | warning
+AutoboxingStateCreation: State<T> will autobox values assigned to this state. Use a specialized state type instead. |
| 19 | warning +UnusedResources: Unused resources |
| Productivity + | |
| 30 | warning +UseTomlInstead: Use TOML Version Catalog Instead |
| Internationalization + | |
| 2 | warning +HardcodedText: Hardcoded text |
| Included Additional Checks (67) + | |
| Disabled Checks (41) + |
+ 37 <uses-permission + 38 android:name="android.permission.ACCESS_COARSE_LOCATION" /> + 39 + 40 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + 41 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + 42 <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> ++ +../../src/main/AndroidManifest.xml:41:
+ 38 android:name="android.permission.ACCESS_COARSE_LOCATION" /> + 39 + 40 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + 41 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + 42 <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> + 43 + 44 <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> ++ +
+ 66 0 -> { // Request Nearby Wi-Fi Permission + 67 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + 68 !hasPermission(context, Manifest.permission.NEARBY_WIFI_DEVICES)) { + 69 nearbyWifiPermissionLauncher.launch(Manifest.permission.NEARBY_WIFI_DEVICES) + 70 } else { + 71 currentStep = 1 + 72 } ++ +
+ 14 defaultConfig { + 15 applicationId = "com.greybox.projectmesh" + 16 minSdk = 26 + 17 targetSdk = 34 + 18 versionCode = 1 + 19 versionName = "1.0" ++ +
+ 503 horizontalArrangement = Arrangement.End + 504 ) { + 505 Text( + 506 text = SimpleDateFormat("HH:mm").format(Date(chatMessage.dateReceived)), + 507 style = MaterialTheme.typography.labelSmall + 508 ) + 509 } ++ +../../src/main/java/com/greybox/projectmesh/messaging/utils/MessageUtils.kt:6:
+ 3 object MessageUtils { + 4 fun formatTimestamp(timestamp: Long): String { + 5 //Adding timestamp formatting logic + 6 return java.text.SimpleDateFormat("HH:mm").format(timestamp) + 7 } + 8 + 9 fun generateChatId(sender: String, receiver: String): String { ++ +
+ 31 or higher, you must declare the NEARBY_WIFI_DEVICES permission. + 32 --> + 33 <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" + 34 android:usesPermissionFlags="neverForLocation" /> + 35 <uses-permission + 36 android:name="android.permission.ACCESS_FINE_LOCATION" /> + 37 <uses-permission+ +
+ 242 private fun updateLocale(languageCode: String): Locale { + 243 val locale = Locale(languageCode) + 244 val config = resources.configuration + 245 config.setLocale(locale) + 246 @Suppress("DEPRECATION") + 247 resources.updateConfiguration(config, resources.displayMetrics) + 248 return locale ++ +
+ 73 <activity + 74 android:name=".MainActivity" + 75 android:exported="true" + 76 android:label="@string/app_name" + 77 android:theme="@style/Theme.ProjectMesh.Launcher"> + 78 <intent-filter> + 79 <action android:name="android.intent.action.MAIN" /> ++ +
+ 1 [versions] + 2 agp = "8.5.1" + 3 datastoreCoreVersion = "1.1.1" + 4 datastorePreferences = "1.1.1" + 5 kotlin = "1.9.0" ++ +../../../gradle/libs.versions.toml:2:
+ 1 [versions] + 2 agp = "8.5.1" + 3 datastoreCoreVersion = "1.1.1" + 4 datastorePreferences = "1.1.1" + 5 kotlin = "1.9.0" ++ +
+ 61 } + 62 + 63 dependencies { + 64 implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6") + 65 implementation(libs.androidx.core.ktx) + 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) ++ +../../build.gradle.kts:71:
+ 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) ++ +../../build.gradle.kts:72:
+ 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) + 75 implementation(libs.androidx.ui) ++ +../../build.gradle.kts:73:
+ 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) + 75 implementation(libs.androidx.ui) + 76 implementation(libs.androidx.ui.graphics) ++ +../../build.gradle.kts:96:
+ 93 debugImplementation(libs.androidx.ui.tooling) + 94 debugImplementation(libs.androidx.ui.test.manifest) + 95 implementation("com.github.UstadMobile.Meshrabiya:lib-meshrabiya:0.1d10-snapshot") + 96 implementation("com.github.seancfoley:ipaddress:5.3.3") + 97 implementation("com.squareup.okhttp3:okhttp:4.10.0") + 98 implementation("org.nanohttpd:nanohttpd:2.3.1") + 99 implementation (libs.material) ++ ++ 47 More Occurrences... + +
+ 93 </provider> + 94 <activity + 95 android:name="com.journeyapps.barcodescanner.CaptureActivity" + 96 android:screenOrientation="portrait" + 97 android:stateNotNeeded="true" + 98 tools:replace="android:screenOrientation" /> + 99 <activity android:name=".debug.CrashScreenActivity" android:process=":error_handler" android:exported="false"></activity>+ +
+ 24 + 25 [libraries] + 26 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } + 27 androidx-datastore-core-v111 = { module = "androidx.datastore:datastore-core", version.ref = "datastoreCoreVersion" } + 28 androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } + 29 androidx-foundation = { module = "androidx.compose.foundation:foundation" } + 30 junit = { group = "junit", name = "junit", version.ref = "junit" } ++ +../../../gradle/libs.versions.toml:27:
+ 24 + 25 [libraries] + 26 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } + 27 androidx-datastore-core-v111 = { module = "androidx.datastore:datastore-core", version.ref = "datastoreCoreVersion" } + 28 androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } + 29 androidx-foundation = { module = "androidx.compose.foundation:foundation" } + 30 junit = { group = "junit", name = "junit", version.ref = "junit" } ++ +../../../gradle/libs.versions.toml:46:
+ 43 lib-meshrabiya = { module = "com.github.UstadMobile.Meshrabiya:lib-meshrabiya", version.ref = "libMeshrabiya" } + 44 androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } + 45 material = { group = "com.google.android.material", name = "material", version.ref = "material" } + 46 androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" } + 47 androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } + 48 androidx-datastore-preferences-core = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "datastorePreferencesCore" } + 49 androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } ++ +../../../gradle/libs.versions.toml:46:
+ 43 lib-meshrabiya = { module = "com.github.UstadMobile.Meshrabiya:lib-meshrabiya", version.ref = "libMeshrabiya" } + 44 androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } + 45 material = { group = "com.google.android.material", name = "material", version.ref = "material" } + 46 androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" } + 47 androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" } + 48 androidx-datastore-preferences-core = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "datastorePreferencesCore" } + 49 androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } ++ +
+ 93 </provider> + 94 <activity + 95 android:name="com.journeyapps.barcodescanner.CaptureActivity" + 96 android:screenOrientation="portrait" + 97 android:stateNotNeeded="true" + 98 tools:replace="android:screenOrientation" /> + 99 <activity android:name=".debug.CrashScreenActivity" android:process=":error_handler" android:exported="false"></activity>+ +
+ 16 private const val CHANNEL_NAME = "File Receive Notifications" + 17 + 18 fun createNotificationChannel(context: Context) { + 19 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + 20 val channel = NotificationChannel( + 21 CHANNEL_ID, + 22 CHANNEL_NAME, ++ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:67:
+ 64 if (currentStep == 6) return@LaunchedEffect + 65 when (currentStep) { + 66 0 -> { // Request Nearby Wi-Fi Permission + 67 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + 68 !hasPermission(context, Manifest.permission.NEARBY_WIFI_DEVICES)) { + 69 nearbyWifiPermissionLauncher.launch(Manifest.permission.NEARBY_WIFI_DEVICES) + 70 } else { ++ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:75:
+ 72 } + 73 } + 74 1 -> { // Request Location Permission + 75 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + 76 !hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)) { + 77 locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION) + 78 } else { ++ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:135:
+ 132 /** Function to Check If Battery Optimization is Disabled */ + 133 fun isBatteryOptimizationDisabled(context: Context): Boolean { + 134 val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + 135 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + 136 powerManager.isIgnoringBatteryOptimizations(context.packageName) + 137 } else { + 138 true // Battery optimization doesn't apply below Android 6.0+ +../../src/main/java/com/greybox/projectmesh/views/RequestPermissionScreen.kt:144:
+ 141 + 142 /** Function to Prompt User to Disable Battery Optimization */ + 143 fun promptDisableBatteryOptimization(context: Context) { + 144 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + 145 val message = SpannableString( + 146 "To ensure uninterrupted background functionality and maintain a stable connection, " + + 147 "please disable battery optimization for this app.\n\n" + ++ +
+ 129 mutableStateOf(settingPref.getString( + 130 "language", "en") ?: "en") + 131 } + 132 var restartServerKey by remember {mutableStateOf(0)} + 133 var deviceName by remember { + 134 mutableStateOf(settingPref.getString("device_name", Build.MODEL) ?: Build.MODEL) + 135 } ++ +
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + 3 xmlns:app="http://schemas.android.com/apk/res-auto" + 4 xmlns:tools="http://schemas.android.com/tools" + 5 android:id="@+id/main"+ +../../src/main/res/layout/activity_main.xml:2:
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <androidx.constraintlayout.widget.ConstraintLayout + 3 xmlns:android="http://schemas.android.com/apk/res/android" + 4 xmlns:app="http://schemas.android.com/apk/res-auto" + 5 xmlns:tools="http://schemas.android.com/tools"+ +../../src/main/res/values/colors.xml:3:
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <resources> + 3 <color name="purple_200">#FFBB86FC</color> + 4 <color name="purple_500">#FF6200EE</color> + 5 <color name="purple_700">#FF3700B3</color> + 6 <color name="teal_200">#FF03DAC5</color>+ +../../src/main/res/values/colors.xml:4:
+ 1 <?xml version="1.0" encoding="utf-8"?> + 2 <resources> + 3 <color name="purple_200">#FFBB86FC</color> + 4 <color name="purple_500">#FF6200EE</color> + 5 <color name="purple_700">#FF3700B3</color> + 6 <color name="teal_200">#FF03DAC5</color> + 7 <color name="teal_700">#FF018786</color>+ +../../src/main/res/values/colors.xml:5:
+ 2 <resources> + 3 <color name="purple_200">#FFBB86FC</color> + 4 <color name="purple_500">#FF6200EE</color> + 5 <color name="purple_700">#FF3700B3</color> + 6 <color name="teal_200">#FF03DAC5</color> + 7 <color name="teal_700">#FF018786</color> + 8 <color name="black">#FF000000</color>+ ++ 14 More Occurrences... + +
+ 61 } + 62 + 63 dependencies { + 64 implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6") + 65 implementation(libs.androidx.core.ktx) + 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) ++ +../../build.gradle.kts:68:
+ 65 implementation(libs.androidx.core.ktx) + 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) + 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") ++ +../../build.gradle.kts:69:
+ 66 implementation(libs.androidx.lifecycle.runtime.ktx) + 67 implementation(libs.androidx.activity.compose) + 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") ++ +../../build.gradle.kts:71:
+ 68 implementation("ch.acra:acra-http:5.11.0") + 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) ++ +../../build.gradle.kts:72:
+ 69 implementation("ch.acra:acra-dialog:5.11.0") + 70 implementation(platform(libs.androidx.compose.bom)) + 71 implementation("androidx.compose.material3:material3:1.2.1") + 72 implementation("androidx.compose.material:material-icons-core:1.6.8") + 73 implementation("androidx.compose.material:material-icons-extended-android:1.6.8") + 74 implementation(libs.androidx.foundation) + 75 implementation(libs.androidx.ui) ++ ++ 25 More Occurrences... + +
+ 15 android:layout_marginTop="9dp" + 16 android:layout_marginEnd="13dp" + 17 android:layout_marginBottom="9dp" + 18 android:text="TextView" + 19 app:layout_constraintBottom_toBottomOf="parent" + 20 app:layout_constraintEnd_toEndOf="parent" + 21 app:layout_constraintStart_toStartOf="parent"+ +../../src/main/res/layout/activity_main.xml:15:
+ 12 android:layout_height="wrap_content" + 13 android:layout_marginStart="24dp" + 14 android:layout_marginTop="24dp" + 15 android:text="Project Mesh" + 16 android:textSize="24sp" + 17 app:layout_constraintStart_toStartOf="parent" + 18 app:layout_constraintTop_toTopOf="parent" /> ++ +
lint.xml configuration files in the project directories.
+
+ @SuppressLint annotation in the Java codetools:ignore attribute in the XML filebuild.gradle file, as explained belowlint.xml configuration file in the projectlint.xml configuration file passed to lint via the --config flag@SuppressLint("id") annotation on the class, method or variable declaration closest to the warning instance you want to disable. The id can be one or more issue id's, such as "UnusedResources" or {"UnusedResources","UnusedIds"}, or it can be "all" to suppress all lint warnings in the given scope.//noinspection id comment on the line before the statement with the error.tools:ignore="id" attribute on the element containing the error, or one of its surrounding elements. You also need to define the namespace for the tools prefix on the root element in your document, next to the xmlns:android declaration:xmlns:tools="http://schemas.android.com/tools"build.gradle file, add a section like this:
+android {
+ lintOptions {
+ disable 'TypographyFractions','TypographyQuotes'
+ }
+}
+
+warning or error instead of disable to change the severity of issues.lint.xml and place it at the root directory of the module in which it applies.lint.xml file is something like the following:+<?xml version="1.0" encoding="UTF-8"?> +<lint> + <!-- Ignore everything in the test source set --> + <issue id="all"> + <ignore path="\*/test/\*" /> + </issue> + + <!-- Disable this given check in this project --> + <issue id="IconMissingDensityFolder" severity="ignore" /> + + <!-- Ignore the ObsoleteLayoutParam issue in the given files --> + <issue id="ObsoleteLayoutParam"> + <ignore path="res/layout/activation.xml" /> + <ignore path="res/layout-xlarge/activation.xml" /> + <ignore regexp="(foo|bar)\.java" /> + </issue> + + <!-- Ignore the UselessLeaf issue in the given file --> + <issue id="UselessLeaf"> + <ignore path="res/layout/main.xml" /> + </issue> + + <!-- Change the severity of hardcoded strings to "error" --> + <issue id="HardcodedText" severity="error" /> +</lint> ++
$ lint --ignore UnusedResources,UselessLeaf /my/project/path|
+
+
+
|
+
+
+
+100%
+successful + |
+
| Test | +Duration | +Result | +
|---|---|---|
| addition_isCorrect | +0.002s | +passed | +
|
+
+
+
|
+
+
+
+100%
+successful + |
+
| Package | +Tests | +Failures | +Ignored | +Duration | +Success rate | +
|---|---|---|---|---|---|
| +com.greybox.projectmesh + | +1 | +0 | +0 | +0.002s | +100% | +
| Class | +Tests | +Failures | +Ignored | +Duration | +Success rate | +
|---|---|---|---|---|---|
| +com.greybox.projectmesh.ExampleUnitTest + | +1 | +0 | +0 | +0.002s | +100% | +
|
+
+
+
|
+
+
+
+100%
+successful + |
+
| Class | +Tests | +Failures | +Ignored | +Duration | +Success rate | +
|---|---|---|---|---|---|
| +ExampleUnitTest + | +1 | +0 | +0 | +0.002s | +100% | +
|
+
+
+
|
+
+
+
+100%
+successful + |
+
| Test | +Duration | +Result | +
|---|---|---|
| addition_isCorrect | +0.004s | +passed | +
|
+
+
+
|
+
+
+
+100%
+successful + |
+
| Package | +Tests | +Failures | +Ignored | +Duration | +Success rate | +
|---|---|---|---|---|---|
| +com.greybox.projectmesh + | +1 | +0 | +0 | +0.004s | +100% | +
| Class | +Tests | +Failures | +Ignored | +Duration | +Success rate | +
|---|---|---|---|---|---|
| +com.greybox.projectmesh.ExampleUnitTest + | +1 | +0 | +0 | +0.004s | +100% | +
|
+
+
+
|
+
+
+
+100%
+successful + |
+
| Class | +Tests | +Failures | +Ignored | +Duration | +Success rate | +
|---|---|---|---|---|---|
| +ExampleUnitTest + | +1 | +0 | +0 | +0.004s | +100% | +
duration