├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build.yml │ ├── buildFoss.yml │ ├── release.yml │ └── releaseFoss.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── sensify │ │ └── sensor │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── io │ │ │ └── sensify │ │ │ ├── sensor │ │ │ ├── domains │ │ │ │ ├── chart │ │ │ │ │ ├── ChartConstants.kt │ │ │ │ │ ├── ChartDataHandler.kt │ │ │ │ │ ├── entity │ │ │ │ │ │ ├── ModelChartDataSet.kt │ │ │ │ │ │ ├── ModelChartUiUpdate.kt │ │ │ │ │ │ └── ModelLineChart.kt │ │ │ │ │ └── mpchart │ │ │ │ │ │ ├── MpChartDataManager.kt │ │ │ │ │ │ ├── MpChartViewBinder.kt │ │ │ │ │ │ ├── MpChartViewUpdater.kt │ │ │ │ │ │ └── axis │ │ │ │ │ │ └── MpChartTimestampAxisFormatter.kt │ │ │ │ ├── permissions │ │ │ │ │ ├── PermissionsManager.kt │ │ │ │ │ └── permissionsState.kt │ │ │ │ └── sensors │ │ │ │ │ ├── SensorHelper.kt │ │ │ │ │ ├── SensorsComposables.kt │ │ │ │ │ ├── SensorsConstants.kt │ │ │ │ │ ├── packets │ │ │ │ │ ├── ModelSensorPacket.kt │ │ │ │ │ ├── SensorPacketConfig.kt │ │ │ │ │ └── SensorPacketsProvider.kt │ │ │ │ │ └── provider │ │ │ │ │ ├── ModelSensor.kt │ │ │ │ │ └── SensorsProvider.kt │ │ │ └── ui │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── components │ │ │ │ └── chart │ │ │ │ │ └── mpchart │ │ │ │ │ ├── MpChartLineAxis.kt │ │ │ │ │ ├── MpChartLineComet.kt │ │ │ │ │ └── base │ │ │ │ │ ├── IMpChartLineView.kt │ │ │ │ │ └── MpChartLineView.kt │ │ │ │ ├── composables │ │ │ │ └── LazyListState.kt │ │ │ │ ├── fix │ │ │ │ └── material3 │ │ │ │ │ └── PagerTab.kt │ │ │ │ ├── navigation │ │ │ │ └── NavDirectionsApp.kt │ │ │ │ ├── pages │ │ │ │ ├── SplashPage.kt │ │ │ │ ├── about │ │ │ │ │ ├── AboutPage.kt │ │ │ │ │ └── sections │ │ │ │ │ │ └── AboutCommunity.kt │ │ │ │ ├── home │ │ │ │ │ ├── HomePage.kt │ │ │ │ │ ├── HomeViewModel.kt │ │ │ │ │ ├── items │ │ │ │ │ │ ├── HomeSensorChartItem.kt │ │ │ │ │ │ └── HomeSensorItem.kt │ │ │ │ │ ├── model │ │ │ │ │ │ └── ModelHomeSensor.kt │ │ │ │ │ ├── sections │ │ │ │ │ │ ├── HomeHeader.kt │ │ │ │ │ │ └── HomeSensorGraphPager.kt │ │ │ │ │ └── state │ │ │ │ │ │ └── HomeUiState.kt │ │ │ │ └── sensor │ │ │ │ │ ├── SensorPage.kt │ │ │ │ │ ├── SensorViewModel.kt │ │ │ │ │ ├── SensorViewModelFactory.kt │ │ │ │ │ └── sections │ │ │ │ │ ├── SensorChart.kt │ │ │ │ │ ├── SensorCurrentValue.kt │ │ │ │ │ ├── SensorDetail.kt │ │ │ │ │ └── SensorHeader.kt │ │ │ │ ├── resource │ │ │ │ ├── effects │ │ │ │ │ └── ColoredShadow.kt │ │ │ │ ├── sensors │ │ │ │ │ └── SensorsIcons.kt │ │ │ │ ├── themes │ │ │ │ │ ├── JLThemeBase.kt │ │ │ │ │ └── JlThemeM3.kt │ │ │ │ └── values │ │ │ │ │ ├── JlResColors.kt │ │ │ │ │ ├── JlResDimens.kt │ │ │ │ │ ├── JlResShapes.kt │ │ │ │ │ ├── JlResStyles.kt │ │ │ │ │ └── JlResTxtStyles.kt │ │ │ │ └── utils │ │ │ │ └── MpAndridChartFormatter.kt │ │ │ └── util │ │ │ └── QueueFixedLength.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_github.xml │ │ ├── ic_gyroscope.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_logo.xml │ │ ├── ic_night_clear.xml │ │ ├── ic_round_keyboard_arrow_left_24.xml │ │ ├── ic_round_keyboard_arrow_right_24.xml │ │ ├── ic_round_settings_24.xml │ │ ├── ic_sensify_logo.xml │ │ ├── ic_sensor_brightness.xml │ │ ├── ic_sensor_compass.xml │ │ ├── ic_sensor_gravity.xml │ │ ├── ic_sensor_gyroscope.xml │ │ ├── ic_sensor_humidity.xml │ │ ├── ic_sensor_linear_acceleration.xml │ │ ├── ic_sensor_magnet.xml │ │ ├── ic_sensor_pressure.xml │ │ ├── ic_sensor_proximity.xml │ │ ├── ic_sensor_rotation.xml │ │ ├── ic_sensor_temprature.xml │ │ ├── ic_sensor_unknown.xml │ │ ├── ic_sunny.xml │ │ ├── image_1.jpeg │ │ ├── image_2.jpeg │ │ ├── image_3.jpeg │ │ ├── image_4.jpeg │ │ ├── image_5.jpeg │ │ ├── image_6.jpeg │ │ ├── pic_about_gradient.png │ │ ├── pic_launcher_eye.png │ │ ├── pic_logo.png │ │ ├── pic_person1.jpg │ │ ├── pic_person2.jpg │ │ ├── pic_sensify_logo.png │ │ └── splash_screen_background.xml │ │ ├── font │ │ ├── jost_bold.ttf │ │ ├── jost_light.ttf │ │ ├── jost_medium.ttf │ │ ├── jost_regular.ttf │ │ ├── lato_black.ttf │ │ ├── lato_bold.ttf │ │ ├── lato_regular.ttf │ │ └── roboto_regular.ttf │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ ├── play │ └── res │ │ └── values │ │ └── ccd.xml │ └── test │ └── java │ └── io │ └── sensify │ └── sensor │ └── ExampleUnitTest.kt ├── build.gradle ├── foss.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── sensify-all.png └── sensify-logo.png ├── ref.md └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Android Build 2 | 3 | on: 4 | workflow_dispatch: 5 | # push: 6 | # branches: 7 | # - 'floss' 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # 1 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | 16 | # 2 17 | - name: Create file 18 | run: cat /home/runner/work/sensify-android/sensify-android/app/google-services.json | base64 19 | 20 | # 3 21 | - name: Putting data 22 | env: 23 | DATA: ${{ secrets.GOOGLE_SERVICES_JSON }} 24 | run: echo $DATA > /home/runner/work/sensify-android/sensify-android/app/google-services.json 25 | 26 | # 4 27 | - name: Generate Release APK 28 | run: ./gradlew assembleRelease 29 | # 5 30 | - name: Sign APK 31 | uses: r0adkll/sign-android-release@v1 32 | # ID used to access action output 33 | id: sign_app 34 | with: 35 | releaseDirectory: app/build/outputs/apk/release 36 | signingKeyBase64: ${{ secrets.SIGNING_KEY }} 37 | alias: ${{ secrets.ALIAS }} 38 | keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} 39 | keyPassword: ${{ secrets.KEY_PASSWORD }} 40 | # 6 41 | - uses: actions/upload-artifact@master 42 | with: 43 | name: release.apk 44 | path: ${{steps.sign_app.outputs.signedReleaseFile}} 45 | # 7 46 | - uses: actions/upload-artifact@master 47 | with: 48 | name: mapping.txt 49 | path: app/build/outputs/mapping/release/mapping.txt 50 | 51 | -------------------------------------------------------------------------------- /.github/workflows/buildFoss.yml: -------------------------------------------------------------------------------- 1 | name: Android Foss Build 2 | 3 | on: 4 | workflow_dispatch: 5 | # push: 6 | # branches: 7 | # - 'floss' 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # 1 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | # 2 16 | - name: Generate Release APK 17 | run: ./gradlew assembleFossRelease 18 | # 3 19 | - name: Sign APK 20 | uses: r0adkll/sign-android-release@v1 21 | # ID used to access action output 22 | id: sign_app 23 | with: 24 | releaseDirectory: app/build/outputs/apk/foss/release 25 | signingKeyBase64: ${{ secrets.SIGNING_KEY }} 26 | alias: ${{ secrets.ALIAS }} 27 | keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} 28 | keyPassword: ${{ secrets.KEY_PASSWORD }} 29 | # 4 30 | - uses: actions/upload-artifact@master 31 | with: 32 | name: release.apk 33 | path: ${{steps.sign_app.outputs.signedReleaseFile}} 34 | # 5 35 | - uses: actions/upload-artifact@master 36 | with: 37 | name: mapping.txt 38 | path: app/build/outputs/mapping/release/mapping.txt 39 | 40 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Github Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | # push: 6 | # branches: 7 | # - 'floss' 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # 1 13 | - uses: dawidd6/action-download-artifact@v2 14 | with: 15 | github_token: ${{secrets.GITHUB_TOKEN}} 16 | workflow: build.yml 17 | workflow_conclusion: success 18 | branch: floss 19 | name: release.apk 20 | path: downloads 21 | if_no_artifact_found: fail 22 | # 2 23 | - name: Generate release tag 24 | id: tag 25 | run: | 26 | echo "::set-output name=release_tag::Sensify_$(date +"%Y.%m.%d_%H-%M")" 27 | # 3 28 | - name: Release 29 | uses: softprops/action-gh-release@v1 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | with: 33 | tag_name: ${{ steps.tag.outputs.release_tag }} 34 | files: downloads/app-release-unsigned-signed.apk 35 | -------------------------------------------------------------------------------- /.github/workflows/releaseFoss.yml: -------------------------------------------------------------------------------- 1 | name: Github Publish Foss 2 | 3 | on: 4 | workflow_dispatch: 5 | # push: 6 | # branches: 7 | # - 'floss' 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # 1 13 | - uses: dawidd6/action-download-artifact@v2 14 | with: 15 | github_token: ${{secrets.GITHUB_TOKEN}} 16 | workflow: buildFoss.yml 17 | workflow_conclusion: success 18 | branch: floss 19 | name: release.apk 20 | path: downloads 21 | if_no_artifact_found: fail 22 | # 2 23 | - name: Generate release tag 24 | id: tag 25 | run: | 26 | echo "::set-output name=release_tag::Sensify_FOSS_$(date +"%Y.%m.%d_%H-%M")" 27 | # 3 28 | - name: Release 29 | uses: softprops/action-gh-release@v1 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | with: 33 | tag_name: ${{ steps.tag.outputs.release_tag }} 34 | files: downloads/release-signed.apk 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | /.idea 17 | .gradlerc 18 | .bashrc 19 | *.jks -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Citizen Code of Conduct 2 | 3 | ## 1. Purpose 4 | 5 | A primary goal of Sensify Android is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). 6 | 7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior. 8 | 9 | We invite all those who participate in Sensify Android to help us create safe and positive experiences for everyone. 10 | 11 | ## 2. Open [Source/Culture/Tech] Citizenship 12 | 13 | A supplemental goal of this Code of Conduct is to increase open [source/culture/tech] citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. 14 | 15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. 16 | 17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. 18 | 19 | ## 3. Expected Behavior 20 | 21 | The following behaviors are expected and requested of all community members: 22 | 23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community. 24 | * Exercise consideration and respect in your speech and actions. 25 | * Attempt collaboration before conflict. 26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech. 27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. 28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations. 29 | 30 | ## 4. Unacceptable Behavior 31 | 32 | The following behaviors are considered harassment and are unacceptable within our community: 33 | 34 | * Violence, threats of violence or violent language directed against another person. 35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language. 36 | * Posting or displaying sexually explicit or violent material. 37 | * Posting or threatening to post other people's personally identifying information ("doxing"). 38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. 39 | * Inappropriate photography or recording. 40 | * Inappropriate physical contact. You should have someone's consent before touching them. 41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances. 42 | * Deliberate intimidation, stalking or following (online or in person). 43 | * Advocating for, or encouraging, any of the above behavior. 44 | * Sustained disruption of community events, including talks and presentations. 45 | 46 | ## 5. Weapons Policy 47 | 48 | No weapons will be allowed at Sensify Android events, community spaces, or in other spaces covered by the scope of this Code of Conduct. Weapons include but are not limited to guns, explosives (including fireworks), and large knives such as those used for hunting or display, as well as any other item used for the purpose of causing injury or harm to others. Anyone seen in possession of one of these items will be asked to leave immediately, and will only be allowed to return without the weapon. Community members are further expected to comply with all state and local laws on this matter. 49 | 50 | ## 6. Consequences of Unacceptable Behavior 51 | 52 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated. 53 | 54 | Anyone asked to stop unacceptable behavior is expected to comply immediately. 55 | 56 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event). 57 | 58 | ## 7. Reporting Guidelines 59 | 60 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. junkielabs.dev@gmail.com. 61 | 62 | 63 | 64 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress. 65 | 66 | ## 8. Addressing Grievances 67 | 68 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify JunkieLabs.in with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. 69 | 70 | 71 | 72 | ## 9. Scope 73 | 74 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues--online and in-person--as well as in all one-on-one communications pertaining to community business. 75 | 76 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members. 77 | 78 | ## 10. Contact info 79 | 80 | junkielabs.dev@gmail.com 81 | 82 | ## 11. License and attribution 83 | 84 | The Citizen Code of Conduct is distributed by [Stumptown Syndicate](http://stumptownsyndicate.org) under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). 85 | 86 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). 87 | 88 | _Revision 2.3. Posted 6 March 2017._ 89 | 90 | _Revision 2.2. Posted 4 February 2016._ 91 | 92 | _Revision 2.1. Posted 23 June 2014._ 93 | 94 | _Revision 2.0, adopted by the [Stumptown Syndicate](http://stumptownsyndicate.org) board on 10 January 2013. Posted 17 March 2013._ 95 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Alwasy create working feature branch from dev. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Junkie Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Sensify : Sensor Visualizer in Jetpack Compose 4 | 5 | 6 | [![API](https://img.shields.io/badge/API-24%2B-brightgreen.svg?style=for-the-badge)](https://android-arsenal.com/api?level=24) ![Compose Version](https://img.shields.io/badge/Compose-1.2.0-brightgreen?style=for-the-badge) 7 | 8 | > Help in analysing your Android phone's sensors with graphs and visuals. 9 | 10 | 11 | 12 | ![](images/sensify-all.png) 13 | 14 | 15 | ## [Download it on Google Play here](https://play.google.com/store/apps/details?id=io.sensify.sensor) 16 | 17 | 18 | 19 | ## Feature Roadmap 20 | - 2022: configure sensor delay 21 | - 2023: Beautiful Visuals of for each sensors. 22 | 23 | 24 | ## Goals 25 | - A very good UI for user to interact. 26 | - Use of android sensors for different utilities. 27 | - [Use Cases](https://github.com/JunkieLabs/sensify-android/wiki/Use-Cases) 28 | 29 | ## Features: 30 | 31 | Whole sensors functionality is implemented inside domain folder, where SensorProvider used for listing available sensors and SensorPacketProvider for get packets . 32 | 33 | * Realtime **sensor outputs** into charts. 34 | * Implemented in Jetpack Compose. 35 | * MVVM Architecture used for this App. 36 | * Usage of Kotlin Flow, Jetpack State, Singletons etc. 37 | * Theming in M3 for Light and Dark. 38 | 39 | ## Tech Stack 40 | 41 | This project takes advantage of best practices of common libraryies and tools in android. 42 | 43 | * [Kotlin](https://kotlinlang.org/) 44 | * [Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) - for background operations 45 | * [Flow](https://developer.android.com/kotlin/flow) - alternate of RxJava 46 | * [Jetpack Compose](https://developer.android.com/jetpack/compose) 47 | * [Jetpack libraries](https://developer.android.com/jetpack): 48 | * [Navigation](https://developer.android.com/topic/libraries/architecture/navigation/) - in-app navigation 49 | * [Lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle) - perform an action when lifecycle state changes 50 | * [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) - store and manage UI-related data in a lifecycle conscious way 51 | * [Material Design 3](https://m3.material.io/develop/android/jetpack-compose) 52 | 53 | 54 | 55 |
56 | 57 | ## Show your love :heart: by giving a :star: on this project. 58 | 59 |
60 | 61 | 62 | # Open Source Credits 63 | 64 | 65 | - Charts: [MpAndroidChart](https://github.com/PhilJay/MPAndroidChart) 66 | - Thank you to everyone who tried out this app and opened issues, suggested features, provided translations, or tested debug builds for me 67 | - Thanks to @KunwarManish2008 for helping in several features 68 | 69 | 70 | 71 | # License 72 | 73 | [![License](https://img.shields.io/:license-mit-blue.svg?style=for-the-badge)](https://badges.mit-license.org) 74 | 75 | - **[MIT license](LICENSE)** 76 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release 3 | src/play/google-services.json -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/io/sensify/sensor/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("io.sensify.sensor", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 23 | 24 | 32 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/ChartConstants.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart 2 | 3 | /** 4 | * Created by Niraj on 13-09-2022. 5 | */ 6 | object ChartConstants { 7 | 8 | 9 | const val DIRECTION_START_END = 1 10 | const val DIRECTION_END_START = 2 11 | 12 | 13 | const val REFRESH_RATE = 20 14 | 15 | 16 | 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/ChartDataHandler.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart 2 | 3 | import android.hardware.Sensor 4 | import android.hardware.SensorManager 5 | import android.util.Log 6 | import io.sensify.sensor.domains.chart.entity.ModelChartUiUpdate 7 | import io.sensify.sensor.domains.chart.entity.ModelLineChart 8 | import io.sensify.sensor.domains.sensors.SensorsConstants 9 | import io.sensify.sensor.domains.sensors.packets.ModelSensorPacket 10 | import kotlinx.coroutines.* 11 | import kotlinx.coroutines.flow.MutableSharedFlow 12 | import kotlinx.coroutines.flow.asSharedFlow 13 | 14 | /** 15 | * Created by Niraj on 13-09-2022. 16 | */ 17 | class ChartDataHandler(var sensorType: Int) { 18 | 19 | private var mCurrentPacket: ModelSensorPacket? =null 20 | private var mVisibleNum = -1 21 | private var mLengthSample = 100 22 | 23 | private val mLockDataAdd = Any() 24 | 25 | // TODO use this in ui handler 26 | private var mAddDirection = ChartConstants.DIRECTION_START_END 27 | var mModelLineChart: ModelLineChart 28 | 29 | var mPre = mutableListOf() 30 | 31 | var mUIRefreshDelay = SensorManager.SENSOR_DELAY_UI 32 | var mDataRefreshDelay = SensorManager.SENSOR_DELAY_UI 33 | 34 | var mDataTypesIndexed = mutableListOf() 35 | 36 | // var mPre 37 | 38 | var mDataComputationScope = CoroutineScope(Job() + Dispatchers.Default) 39 | var mDataAddScope = CoroutineScope(Job() + Dispatchers.Default) 40 | 41 | private val _mSensorPacketFlow = MutableSharedFlow(replay = 0) 42 | val mSensorPacketFlow = _mSensorPacketFlow.asSharedFlow() 43 | 44 | 45 | init { 46 | mModelLineChart = ModelLineChart( 47 | mLengthSample, mVisibleNum 48 | ) 49 | } 50 | 51 | fun destroy() { 52 | 53 | mDataComputationScope.cancel() 54 | mDataAddScope.cancel() 55 | 56 | 57 | } 58 | 59 | 60 | fun addDataSet( 61 | dataType: Int, 62 | color: Int, 63 | label: String, 64 | data: Array, 65 | isHidden: Boolean 66 | ) { 67 | mDataTypesIndexed.add(dataType) 68 | 69 | // TODO check for data added 70 | mModelLineChart.addDataType(dataType, color, label, data, isHidden) 71 | 72 | } 73 | 74 | fun addEntry(sensorPacket: ModelSensorPacket) { 75 | 76 | 77 | synchronized(mLockDataAdd) { 78 | 79 | mCurrentPacket = sensorPacket.copy() 80 | 81 | // TODO do in periodic place mPre.add(sensorPacket) 82 | 83 | } 84 | // mModelLineChart. 85 | 86 | 87 | } 88 | 89 | fun runPeriodicTask() { 90 | 91 | mDataComputationScope.launch { 92 | while (mDataComputationScope.isActive) { 93 | // TODO should I periodic shift 94 | 95 | var items = addPreEntry() 96 | /* if(sensorType == Sensor.TYPE_GYROSCOPE){ 97 | Log.d("MpChartViewManager ", "runPeriodicTask : $sensorType") 98 | }*/ 99 | 100 | _mSensorPacketFlow.emit( 101 | ModelChartUiUpdate( 102 | // mModelLineChart 103 | sensorType, 104 | items.size, items 105 | ) 106 | ) 107 | 108 | delay(SensorsConstants.MAP_DELAY_TYPE_TO_DELAY.get(mUIRefreshDelay).toLong()) 109 | 110 | // Log.d("MpChartViewManager ", "runPeriodicTask : ") 111 | 112 | } 113 | 114 | } 115 | 116 | 117 | mDataAddScope.launch { 118 | while (mDataAddScope.isActive) { 119 | // TODO should I periodic shift 120 | 121 | synchronized(mLockDataAdd) { 122 | if(mCurrentPacket!=null){ 123 | mPre.add(mCurrentPacket!!) 124 | } 125 | } 126 | 127 | 128 | 129 | 130 | delay(SensorsConstants.MAP_DELAY_TYPE_TO_DELAY.get(mDataRefreshDelay).toLong()) 131 | 132 | // Log.d("MpChartViewManager ", "runPeriodicTask : ") 133 | 134 | } 135 | 136 | } 137 | 138 | } 139 | 140 | private fun addPreEntry(): MutableList { 141 | var preData: MutableList 142 | synchronized(mLockDataAdd) { 143 | preData = mPre 144 | mPre = mutableListOf() 145 | } 146 | 147 | val needToChangeUi = preData.size > 0 148 | 149 | for (item in preData) { 150 | 151 | for (index in mDataTypesIndexed.indices) { 152 | 153 | if (item.values != null) { 154 | if (item.values!!.size > index) { 155 | mModelLineChart.addEntry(mDataTypesIndexed[index], item.values!![index]); 156 | } 157 | } 158 | //loops all indices (performs just as well as two examples above) 159 | } 160 | } 161 | 162 | return preData; 163 | 164 | 165 | // for 166 | // preData 167 | 168 | } 169 | 170 | /*private fun shiftData(set: ILineDataSet) { 171 | if (set.entryCount > mModelLineChart!!.getSampleLength()) { 172 | set.removeEntry(0) // remove oldest 173 | // change Indexes - move to beginning by 1 174 | for (i in 1 until set.entryCount) { 175 | val entry = set.getEntryForIndex(i) 176 | entry.x = entry.x - 1 177 | } 178 | } 179 | }*/ 180 | 181 | 182 | } 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/entity/ModelChartDataSet.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart.entity 2 | 3 | import io.sensify.util.QueueFixedLength 4 | 5 | /** 6 | * Created by Niraj on 31-08-2022. 7 | */ 8 | class ModelChartDataSet(private var dataType: Int = 0, private var sampleLength: Int) { 9 | 10 | // private val TAG: String = makeLogTag(io.sensify.lib.charting.ModelChartDataSet::class.java) 11 | 12 | private var label: String? = null 13 | private var isDataHidden = false 14 | private var color = 0 15 | private var data: QueueFixedLength? = null 16 | // private var dataType = 0 17 | 18 | init { 19 | // this.dataType = dataType 20 | data = QueueFixedLength(sampleLength, null) 21 | } 22 | 23 | fun setColor(color: Int) { 24 | this.color = color 25 | } 26 | 27 | fun setDataHidden(dataHidden: Boolean) { 28 | isDataHidden = dataHidden 29 | } 30 | 31 | fun setLabel(label: String?) { 32 | this.label = label 33 | } 34 | 35 | fun setData(pData: QueueFixedLength) { 36 | data = pData 37 | } 38 | 39 | fun getDataType(): Int { 40 | return dataType 41 | } 42 | 43 | fun getColor(): Int { 44 | return color 45 | } 46 | 47 | fun getLabel(): String? { 48 | return label 49 | } 50 | 51 | fun getData(): QueueFixedLength? { 52 | return data 53 | } 54 | 55 | fun addEntry(entry: Float) { 56 | data?.add(entry) 57 | // if (data != null) { 58 | 59 | //LOGV(TAG,"addentry: type: "+ dataType + "size:"+ data.size()); 60 | // } 61 | } 62 | 63 | fun resize(size: Int) { 64 | data?.resize(size) 65 | } 66 | 67 | fun isDataHidden(): Boolean { 68 | return isDataHidden 69 | } 70 | 71 | fun setData(length_sample: Int, pData: Array?) { 72 | if (pData != null) { 73 | sampleLength = length_sample 74 | data = QueueFixedLength(length_sample, pData) 75 | } else { 76 | sampleLength = length_sample 77 | data?.resize(length_sample) 78 | } 79 | } 80 | 81 | fun getSampleLength(): Int { 82 | return sampleLength 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/entity/ModelChartUiUpdate.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart.entity 2 | 3 | import io.sensify.sensor.domains.sensors.packets.ModelSensorPacket 4 | 5 | /** 6 | * Created by Niraj on 18-09-2022. 7 | */ 8 | data class ModelChartUiUpdate( 9 | var sensorType: Int, 10 | var size: Int, 11 | var packets: List = listOf(), 12 | var timestamp: Long = System.currentTimeMillis() 13 | ) { 14 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/entity/ModelLineChart.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart.entity 2 | 3 | import io.sensify.util.QueueFixedLength 4 | 5 | /** 6 | * Created by Niraj on 31-08-2022. 7 | */ 8 | // TODO under development 9 | class ModelLineChart(var mSampleLength: Int = 100, var mVisibleNum: Int = -1) { 10 | 11 | 12 | //private SparseIntArray mMapIndex; 13 | /*private var VISIBLE_NUM = -1 14 | private var LENGTH_SAMPLE = 100*/ 15 | 16 | private var mTs = 0.1f 17 | 18 | private var mModelChartDataSets: MutableList 19 | 20 | init { 21 | 22 | mModelChartDataSets = mutableListOf() //ArrayList(5) 23 | 24 | } 25 | 26 | init { 27 | /* LENGTH_SAMPLE = mSampleLength 28 | VISIBLE_NUM = mVisibleNum*/ 29 | } 30 | 31 | 32 | fun sampleSize(sampleSize: Int) { 33 | mSampleLength = sampleSize 34 | mVisibleNum = if (mVisibleNum == -1) sampleSize else mVisibleNum 35 | for (i in mModelChartDataSets.indices) { 36 | val modelChartDataSet = mModelChartDataSets[i] 37 | modelChartDataSet.resize(mSampleLength) 38 | } 39 | 40 | } 41 | 42 | fun visibleNumber(visibleNum: Int) { 43 | mVisibleNum = if (visibleNum <= mSampleLength) visibleNum else mSampleLength 44 | } 45 | 46 | fun getSampleLength(): Int { 47 | return mSampleLength 48 | } 49 | 50 | 51 | fun addDataType( 52 | pDataType: Int, 53 | color: Int, 54 | pLabel: String?, 55 | pData: Array?, 56 | isHidden: Boolean 57 | ) { 58 | 59 | 60 | var modelChartDataSet: ModelChartDataSet? = null 61 | for (i in mModelChartDataSets.indices) { 62 | val type = mModelChartDataSets[i].getDataType() 63 | if (type == pDataType) { 64 | modelChartDataSet = mModelChartDataSets[i] 65 | } 66 | } 67 | if (modelChartDataSet == null) { 68 | modelChartDataSet = ModelChartDataSet(pDataType, mSampleLength) 69 | mModelChartDataSets.add(modelChartDataSet) 70 | } 71 | modelChartDataSet.setColor(color) 72 | if (pLabel == null) { 73 | modelChartDataSet.setLabel("data_$pDataType") 74 | } else { 75 | modelChartDataSet.setLabel(pLabel) 76 | } 77 | 78 | 79 | 80 | //LOGV(TAG, "addDta: type: "+ pDataType +" || array : "+ Arrays.toString(pData)); 81 | modelChartDataSet.setData(mSampleLength, pData) 82 | modelChartDataSet.setDataHidden(isHidden) 83 | } 84 | 85 | 86 | fun getData(index: Int): QueueFixedLength? { 87 | return if (index < mModelChartDataSets.size) { 88 | mModelChartDataSets[index].getData() 89 | } else null 90 | //return mMapData.get(dataType); 91 | } 92 | 93 | 94 | fun getLabel(index: Int): String? { 95 | return if (index < mModelChartDataSets.size) { 96 | mModelChartDataSets[index].getLabel() 97 | } else "" 98 | } 99 | 100 | 101 | fun getColor(index: Int): Int { 102 | return if (index < mModelChartDataSets.size) { 103 | mModelChartDataSets[index].getColor() 104 | } else -1 105 | } 106 | 107 | /** 108 | * 109 | * @param datatype 110 | * @return index if available otherwise -1 111 | */ 112 | fun getIndex(datatype: Int): Int { 113 | for (i in mModelChartDataSets.indices) { 114 | if (mModelChartDataSets[i].getDataType() == datatype) { 115 | return i 116 | } 117 | } 118 | return -1 119 | } 120 | 121 | fun isHidden(index: Int): Boolean { 122 | return mModelChartDataSets[index].isDataHidden() 123 | } 124 | 125 | fun addEntry(dataType: Int, `val`: Float) { 126 | //LOGV(TAG,"addentry:"); 127 | val index = getIndex(dataType) 128 | if (index >= 0) { 129 | mModelChartDataSets[index].addEntry(`val`) 130 | } 131 | 132 | } 133 | 134 | 135 | 136 | fun setHidden(index: Int, isHidden: Boolean) { 137 | if (index < mModelChartDataSets.size) { 138 | mModelChartDataSets[index].setDataHidden(isHidden) 139 | } 140 | } 141 | 142 | fun getTimePeriod(): Float { 143 | return mTs 144 | } 145 | 146 | fun setTimePeriod(millisecond: Int) { 147 | mTs = millisecond.toFloat() / 1000f 148 | mTs = Math.round(mTs * 100f) / 100f 149 | } 150 | 151 | fun getDataSets(): List { 152 | return mModelChartDataSets 153 | } 154 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/mpchart/MpChartDataManager.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart.mpchart 2 | 3 | import android.hardware.SensorManager 4 | import android.util.Log 5 | import androidx.compose.ui.graphics.toArgb 6 | import io.sensify.sensor.domains.chart.ChartDataHandler 7 | import io.sensify.sensor.domains.chart.entity.ModelChartUiUpdate 8 | import io.sensify.sensor.domains.chart.entity.ModelLineChart 9 | import io.sensify.sensor.domains.sensors.SensorsConstants 10 | import io.sensify.sensor.domains.sensors.packets.ModelSensorPacket 11 | import io.sensify.sensor.ui.resource.values.JlResColors 12 | import kotlinx.coroutines.* 13 | import kotlinx.coroutines.flow.SharedFlow 14 | 15 | /** 16 | * Created by Niraj on 13-09-2022. 17 | */ 18 | class MpChartDataManager( 19 | var sensorType: Int, 20 | var mSensorDelayType: Int = SensorManager.SENSOR_DELAY_NORMAL, 21 | var onDestroy: (type: Int) -> Unit = {} 22 | ) { 23 | 24 | private var mIsRunningPeriod: Boolean = false 25 | var mDataComputationScope = CoroutineScope(Job() + Dispatchers.Default) 26 | 27 | private var mChartDataHandler: ChartDataHandler 28 | 29 | val mSensorPacketFlow :SharedFlow 30 | 31 | init { 32 | 33 | mChartDataHandler = ChartDataHandler(sensorType) 34 | var axisCount = SensorsConstants.MAP_TYPE_TO_AXIS_COUNT.get(sensorType) 35 | 36 | if (axisCount == 1) { 37 | mChartDataHandler.addDataSet( 38 | SensorsConstants.DATA_AXIS_VALUE, 39 | JlResColors.NotePink.toArgb(), 40 | SensorsConstants.DATA_AXIS_VALUE_STRING, emptyArray(), false 41 | ) 42 | 43 | } else if (axisCount == 3) { 44 | mChartDataHandler.addDataSet( 45 | SensorsConstants.DATA_AXIS_X, 46 | JlResColors.NotePink.toArgb(), 47 | SensorsConstants.DATA_AXIS_X_STRING, emptyArray(), false 48 | ) 49 | mChartDataHandler.addDataSet( 50 | SensorsConstants.DATA_AXIS_Y, 51 | JlResColors.SensifyGreen40.toArgb(), 52 | SensorsConstants.DATA_AXIS_Y_STRING, emptyArray(), false 53 | ) 54 | mChartDataHandler.addDataSet( 55 | SensorsConstants.DATA_AXIS_Z, 56 | JlResColors.CHART_3.toArgb(), 57 | SensorsConstants.DATA_AXIS_Z_STRING, emptyArray(), false 58 | ) 59 | } 60 | 61 | mSensorPacketFlow = mChartDataHandler.mSensorPacketFlow 62 | 63 | } 64 | 65 | fun destroy(){ 66 | Log.d("MpChartDataManager","destroy $sensorType") 67 | mChartDataHandler.destroy() 68 | onDestroy.invoke(sensorType) 69 | } 70 | 71 | fun setSensorDelayType(type: Int) { 72 | mSensorDelayType = type 73 | } 74 | 75 | fun runPeriodically(){ 76 | 77 | 78 | if(mIsRunningPeriod){ 79 | return 80 | } 81 | mIsRunningPeriod = true 82 | 83 | mDataComputationScope.launch { 84 | delay(100) 85 | Log.d("MpChartViewManager ", "createChart periodic Task: $sensorType") 86 | mChartDataHandler.runPeriodicTask() 87 | } 88 | 89 | // return lineChart 90 | 91 | // return linechart 92 | 93 | } 94 | 95 | 96 | 97 | fun addEntry(sensorPacket: ModelSensorPacket) { 98 | // Log.d("MpChartViewManager ", "addEntry ") 99 | // Log.d("MpChartViewManager ", "sdsdew dsdsd ") 100 | 101 | mChartDataHandler.addEntry(sensorPacket) 102 | } 103 | 104 | 105 | /* 106 | TODO remvoe this 107 | fun updateData(lineChart: LineChart, value: ModelChartUiUpdate) { 108 | // var lineData: LineData = chart.getData() 109 | // Log.d("MpChartViewManager ", "updateData update: ${value.size} ") 110 | mMpChartViewUpdater.update(lineChart, value, mChartDataHandler.mModelLineChart) 111 | }*/ 112 | 113 | fun getModel(): ModelLineChart { 114 | return mChartDataHandler.mModelLineChart 115 | } 116 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/mpchart/MpChartViewBinder.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart.mpchart 2 | 3 | import android.content.Context 4 | import androidx.compose.ui.graphics.Color 5 | import com.github.mikephil.charting.charts.LineChart 6 | import com.github.mikephil.charting.components.YAxis 7 | import com.github.mikephil.charting.data.Entry 8 | import com.github.mikephil.charting.data.LineData 9 | import com.github.mikephil.charting.data.LineDataSet 10 | import com.github.mikephil.charting.interfaces.datasets.ILineDataSet 11 | import io.sensify.sensor.domains.chart.entity.ModelChartDataSet 12 | import io.sensify.sensor.domains.chart.entity.ModelLineChart 13 | import io.sensify.sensor.ui.components.chart.mpchart.base.IMpChartLineView 14 | import io.sensify.util.QueueFixedLength 15 | 16 | /** 17 | * Created by Niraj on 18-09-2022. 18 | */ 19 | class MpChartViewBinder (var context: Context, var chartView: IMpChartLineView, 20 | var colorSurface: Color = Color.Transparent, 21 | var colorOnSurface: Color = Color.White){ 22 | 23 | companion object{ 24 | // const val DATA_SET_TYPE_SINGLE = 1 25 | // const val DATA_SET_TYPE_3D = 3 26 | } 27 | 28 | private val chart : LineChart by lazy { 29 | chartView.create(context, colorSurface, colorOnSurface) 30 | } 31 | 32 | 33 | /*fun attachLineChart(modelLineChart: ModelLineChart){ 34 | // var dataType 35 | }*/ 36 | 37 | fun prepareDataSets(modelLineChart: ModelLineChart): MpChartViewBinder { 38 | 39 | //LOGV(TAG, "addDataSet: dataType: "+ dataType +", color: "+ color + ", label: "+ label +"index: "+ index); 40 | var lineData: LineData? = chart.data 41 | 42 | var datasets = modelLineChart.getDataSets() 43 | 44 | // Log.d("MpChartViewBinder", " prepareDataSets: ${datasets.size}") 45 | 46 | for (dataSet in datasets){ 47 | lineData = prepareDataSet(dataSet, lineData) 48 | } 49 | 50 | return this 51 | 52 | /* if(dataSetType == DATA_SET_TYPE_SINGLE){ 53 | lineData = prepareDataSet(modelLineChart, SensorsConstants.DATA_AXIS_VALUE, lineData) 54 | }else{ 55 | lineData = prepareDataSet(modelLineChart, SensorsConstants.DATA_AXIS_X, lineData) 56 | lineData = prepareDataSet(modelLineChart, SensorsConstants.DATA_AXIS_Y, lineData) 57 | lineData = prepareDataSet(modelLineChart, SensorsConstants.DATA_AXIS_Z, lineData) 58 | }*/ 59 | } 60 | 61 | private fun prepareDataSet(modelDataSet: ModelChartDataSet, pLineData: LineData?): LineData? { 62 | /* val index = modelLineChart.getIndex(dataType) 63 | 64 | 65 | 66 | if (index != -1) { 67 | chartLineData?.removeDataSet(index) 68 | }*/ 69 | // Log.d("MpChartViewBinder", " prepareDataSet: ${modelDataSet.getData()?.size()}") 70 | 71 | var chartLineData = pLineData 72 | 73 | var dataSet = createDataSet(modelDataSet) 74 | //TODO hide and show dataSet 75 | if (pLineData == null) { 76 | val sets = java.util.ArrayList() 77 | sets.add(dataSet) 78 | chartLineData = LineData(sets) 79 | chart.setData(chartLineData) 80 | // Log.d("MpChartViewBinder", " prepareDataSet 1: ") 81 | } else { 82 | val sets: MutableList = pLineData.getDataSets() 83 | sets.add(dataSet) 84 | 85 | //LOGV(TAG, "addDataSet: size:"+ sets.size()); 86 | //lineData.getDataSets().add(dataSet); 87 | chart.setData(chartLineData) 88 | //lineData.addDataSet(dataSet); 89 | //lineData.notifyDataChanged(); 90 | // Log.d("MpChartViewBinder", " prepareDataSet 2: ") 91 | } 92 | 93 | return chartLineData; 94 | 95 | } 96 | 97 | 98 | private fun createDataSet(dataSet: ModelChartDataSet): ILineDataSet { 99 | /* val index: Int = modelLineChart.getIndex(dataType)*/ 100 | val label: String? = dataSet.getLabel() 101 | val data: QueueFixedLength? = dataSet.getData() 102 | val entries: MutableList 103 | if (data == null) { 104 | entries = ArrayList(dataSet.getSampleLength()) 105 | for (i in 0 until dataSet.getSampleLength()) { 106 | entries.add(Entry(i.toFloat(), 0.0f)) 107 | } 108 | } else { 109 | var emptySize: Int = dataSet.getSampleLength() - data.size() 110 | entries = ArrayList(dataSet.getSampleLength()) 111 | for (i in 0 until emptySize) { 112 | entries.add(Entry(i.toFloat(), 0.0f)) 113 | } 114 | val iterator: Iterator = data.getValues() 115 | while (iterator.hasNext()) { 116 | if (emptySize < dataSet.getSampleLength()) { 117 | entries.add(Entry(emptySize.toFloat(), iterator.next())) 118 | emptySize++ 119 | } 120 | } 121 | } 122 | val ds = LineDataSet(entries, label) 123 | ds.lineWidth = 1.4f 124 | ds.axisDependency = YAxis.AxisDependency.RIGHT 125 | ds.setDrawCircles(false) 126 | ds.setDrawValues(true) 127 | // ds.isHighlightEnabled = true 128 | // ds.setDrawHorizontalHighlightIndicator(true) 129 | ds.color = dataSet.getColor() 130 | //LOGV(TAG, "create: dataType: "+ dataTypes+", color: "+ mModelLineChart.getColor(dataTypes)); 131 | return ds 132 | } 133 | 134 | fun invalidate(): LineChart { 135 | chart.invalidate() 136 | return chart 137 | } 138 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/mpchart/MpChartViewUpdater.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart.mpchart 2 | 3 | import android.hardware.Sensor 4 | import android.util.Log 5 | import com.github.mikephil.charting.charts.LineChart 6 | import com.github.mikephil.charting.data.Entry 7 | import com.github.mikephil.charting.data.LineData 8 | import io.sensify.sensor.domains.chart.entity.ModelChartDataSet 9 | import io.sensify.sensor.domains.chart.entity.ModelChartUiUpdate 10 | import io.sensify.sensor.domains.chart.entity.ModelLineChart 11 | 12 | /** 13 | * Created by Niraj on 20-09-2022. 14 | */ 15 | class MpChartViewUpdater { 16 | 17 | 18 | fun update(chart: LineChart, value: ModelChartUiUpdate, modelLineChart: ModelLineChart) : Boolean { 19 | if (value.size == 0) return false; 20 | var lineData: LineData? = chart.data 21 | 22 | if(lineData==null){ 23 | // Log.v("MpChartViewUpdater", "update no line data ") 24 | return false 25 | } 26 | 27 | // value.packets[0]. 28 | // modelLineChart. 29 | var datasets = modelLineChart.getDataSets() 30 | // Log.d("MpChartViewManager ", "update status 1: size: ${value.size} ${datasets.size} ") 31 | 32 | for (i in datasets.indices) { 33 | lineData = updateDataSet(i, value, datasets[i], lineData!!) 34 | 35 | } 36 | if(value.sensorType == Sensor.TYPE_GYROSCOPE){ 37 | 38 | // Log.d("MpChartViewManager ", "update status : size: ${value.size}") 39 | } 40 | 41 | notifyDataChange(chart) 42 | 43 | // 44 | 45 | /*for (dataSet in datasets){ 46 | }*/ 47 | 48 | return true 49 | 50 | } 51 | 52 | private fun updateDataSet( 53 | index: Int, value: ModelChartUiUpdate, 54 | modelDataSet: ModelChartDataSet, 55 | pLineData: LineData 56 | ): LineData { 57 | var chartLineData = pLineData 58 | 59 | if (index >= pLineData.dataSets.size) return pLineData; 60 | 61 | var dataset = pLineData.getDataSetByIndex(index) 62 | // modelDataSet 63 | 64 | var totalShift = 0 65 | 66 | var extraEntry = dataset.entryCount - modelDataSet.getSampleLength(); 67 | // Log.d("MpChartViewManager ", "updateDataSet status 1: index: ${index} entryCount: ${dataset.entryCount} ") 68 | 69 | if (extraEntry > 0) { 70 | for (j in 0 until extraEntry) { 71 | dataset.removeEntry(0) // remove oldest 72 | totalShift++; 73 | 74 | 75 | } 76 | } 77 | 78 | for (j in 0 until value.size) { 79 | dataset.removeEntry(0) // remove oldest 80 | totalShift++; 81 | 82 | } 83 | /* while(dataset.entryCount > modelDataSet.getSampleLength()){ 84 | totalShift++; 85 | }*/ 86 | 87 | // change Indexes - move to beginning by 1 88 | for (i in totalShift until dataset.entryCount) { 89 | val entry: Entry = dataset.getEntryForIndex(i) 90 | entry.x = entry.x - totalShift 91 | } 92 | 93 | var entrySize = dataset.entryCount 94 | for (j in 0 until value.size) { 95 | chartLineData.addEntry( 96 | Entry( 97 | (entrySize + j).toFloat(), 98 | value.packets[j].values?.get(index) ?: 0f 99 | ), index 100 | ); // remove oldest 101 | 102 | } 103 | // Log.d("MpChartViewManager ", "updateDataSet status 2: index: ${index} entryCount: ${dataset.entryCount} ") 104 | 105 | /*for (packet in value.packets) { 106 | dataset.addEntry() 107 | }*/ 108 | 109 | // set 110 | 111 | 112 | /* open fun shiftData(set: ILineDataSet) { 113 | if (set.entryCount > mModelLineChart.getSampleLength()) { 114 | set.removeEntry(0) // remove oldest 115 | // change Indexes - move to beginning by 1 116 | for (i in 1 until set.entryCount) { 117 | val entry = set.getEntryForIndex(i) 118 | entry.x = entry.x - 1 119 | } 120 | } 121 | }*/ 122 | 123 | 124 | /* 125 | var dataSet = createDataSet(modelDataSet) 126 | //TODO hide and show dataSet 127 | if (pLineData == null) { 128 | val sets = java.util.ArrayList() 129 | sets.add(dataSet) 130 | chartLineData = LineData(sets) 131 | chart.setData(pLineData) 132 | } else { 133 | val sets: MutableList = pLineData.getDataSets() 134 | sets.add(dataSet) 135 | 136 | //LOGV(TAG, "addDataSet: size:"+ sets.size()); 137 | //lineData.getDataSets().add(dataSet); 138 | chart.setData(pLineData) 139 | //lineData.addDataSet(dataSet); 140 | //lineData.notifyDataChanged(); 141 | }*/ 142 | 143 | return chartLineData; 144 | } 145 | 146 | 147 | fun notifyDataChange(chart: LineChart) { 148 | val data: LineData = chart.getData() 149 | // data.notifyDataChanged() 150 | chart.notifyDataSetChanged() 151 | chart.invalidate() 152 | } 153 | 154 | 155 | 156 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/chart/mpchart/axis/MpChartTimestampAxisFormatter.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.chart.mpchart.axis 2 | 3 | import android.hardware.SensorManager 4 | import com.github.mikephil.charting.components.AxisBase 5 | import com.github.mikephil.charting.formatter.ValueFormatter 6 | import io.sensify.sensor.domains.sensors.SensorsConstants.MAP_DELAY_TYPE_TO_DELAY 7 | 8 | /** 9 | * Created by Niraj on 18-09-2022. 10 | */ 11 | class MpChartTimestampAxisFormatter(var sensorDelay: Int = SensorManager.SENSOR_DELAY_NORMAL) : ValueFormatter() { 12 | 13 | 14 | 15 | fun setDelay(delay: Int){ 16 | sensorDelay = delay; 17 | } 18 | override fun getAxisLabel(value: Float, axis: AxisBase?): String { 19 | 20 | /* val LENGTH_SAMPLE: Int = mModelLineChart.getSampleLength() 21 | val labels = arrayOfNulls(LENGTH_SAMPLE) 22 | val timePreiod: Float = mModelLineChart.getTimePeriod() 23 | for (i in labels.indices) { 24 | val `val` = i.toFloat() * timePreiod 25 | labels[i] = java.lang.Float.toString(`val`) + "s" 26 | }*/ 27 | var delay = MAP_DELAY_TYPE_TO_DELAY.get(sensorDelay) 28 | var totalDelay = value*delay; 29 | return "${totalDelay/1000}s" 30 | // return super.getAxisLabel(value, axis) 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/permissions/permissionsState.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalPermissionsApi::class) 2 | 3 | package io.sensify.sensor.domains.permissions 4 | 5 | import android.util.Log 6 | import androidx.compose.runtime.derivedStateOf 7 | import androidx.compose.runtime.getValue 8 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 9 | import com.google.accompanist.permissions.MultiplePermissionsState 10 | import com.google.accompanist.permissions.isGranted 11 | 12 | /** 13 | * Created by Niraj on 12-08-2022. 14 | */ 15 | 16 | interface PermissionsState { 17 | val isGranted: Boolean 18 | fun requestManually(): Unit 19 | 20 | } 21 | 22 | class MutablePermissionState( 23 | private val permissionsRequest: PermissionsRequest, private val multiplePermissionsState: MultiplePermissionsState? 24 | 25 | ): PermissionsState { 26 | override val isGranted: Boolean by derivedStateOf { 27 | multiplePermissionsState?.allPermissionsGranted ?: false 28 | } 29 | 30 | 31 | // multiplePermissionsState?.allPermissionsGranted ?: false 32 | 33 | 34 | 35 | 36 | override fun requestManually() { 37 | if(multiplePermissionsState == null){ 38 | Log.d("MutablePermissionState: ", "requestManually 2") 39 | 40 | } 41 | multiplePermissionsState?.launchMultiplePermissionRequest() 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/sensors/SensorHelper.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.sensors 2 | 3 | import android.content.Context 4 | import android.hardware.SensorManager 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.platform.LocalContext 7 | 8 | /** 9 | * Created by Niraj on 19-08-2022. 10 | */ 11 | 12 | @Composable 13 | fun sensorManagerProvider(): SensorManager { 14 | return LocalContext.current.getSystemService(Context.SENSOR_SERVICE) as SensorManager 15 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/sensors/SensorsComposables.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.sensors 2 | 3 | import android.content.Context 4 | import android.hardware.SensorManager 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.LaunchedEffect 7 | import androidx.compose.runtime.State 8 | import androidx.compose.runtime.collectAsState 9 | import androidx.compose.ui.platform.LocalContext 10 | import io.sensify.sensor.domains.sensors.provider.ModelSensor 11 | import io.sensify.sensor.domains.sensors.provider.SensorsProvider 12 | 13 | /** 14 | * Created by Niraj on 05-10-2022. 15 | */ 16 | @Composable 17 | fun SensorsProviderComposable(): State> { 18 | 19 | val sensorsFlow = SensorsProvider.getInstance().mSensorsFlow 20 | val state1 = 21 | sensorsFlow 22 | .collectAsState(initial = listOf(ModelSensor(-1, null)) ) 23 | 24 | val context = LocalContext.current 25 | 26 | LaunchedEffect(key1 = context){ 27 | val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager 28 | 29 | SensorsProvider.getInstance().setSensorManager(sensorManager).listenSensors() 30 | 31 | } 32 | 33 | 34 | return state1 35 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/sensors/packets/ModelSensorPacket.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.sensors.packets 2 | 3 | import android.hardware.SensorEvent 4 | 5 | /** 6 | * Created by Niraj on 19-08-2022. 7 | */ 8 | data class ModelSensorPacket( 9 | /** some reference issue with this */ 10 | var sensorEvent: SensorEvent? = null, 11 | var values: FloatArray?, 12 | var type: Int, 13 | var delay: Int, 14 | var timestamp: Long) { 15 | 16 | override fun equals(other: Any?): Boolean { 17 | if (this === other) return true 18 | if (javaClass != other?.javaClass) return false 19 | 20 | other as ModelSensorPacket 21 | 22 | if (sensorEvent != other.sensorEvent) return false 23 | if (values != null) { 24 | if (other.values == null) return false 25 | if (!values.contentEquals(other.values)) return false 26 | } else if (other.values != null) return false 27 | if (type != other.type) return false 28 | if (delay != other.delay) return false 29 | if (timestamp != other.timestamp) return false 30 | 31 | return true 32 | } 33 | 34 | override fun hashCode(): Int { 35 | var result = sensorEvent?.hashCode() ?: 0 36 | result = 31 * result + (values?.contentHashCode() ?: 0) 37 | result = 31 * result + type 38 | result = 31 * result + delay 39 | result = 31 * result + timestamp.hashCode() 40 | return result 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/sensors/packets/SensorPacketConfig.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.sensors.packets 2 | 3 | import android.hardware.SensorManager 4 | 5 | /** 6 | * Created by Niraj on 19-08-2022. 7 | */ 8 | data class SensorPacketConfig( 9 | var sensorType: Int, 10 | var sensorDelay: Int = SensorManager.SENSOR_DELAY_UI 11 | ) { 12 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/sensors/packets/SensorPacketsProvider.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.sensors.packets 2 | 3 | import android.hardware.Sensor 4 | import android.hardware.SensorEvent 5 | import android.hardware.SensorEventListener 6 | import android.hardware.SensorManager 7 | import android.util.Log 8 | import android.util.SparseArray 9 | import androidx.core.util.valueIterator 10 | import kotlinx.coroutines.CoroutineScope 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.Job 13 | import kotlinx.coroutines.flow.MutableSharedFlow 14 | import kotlinx.coroutines.flow.asSharedFlow 15 | import kotlinx.coroutines.launch 16 | 17 | /** 18 | * Created by Niraj on 18-08-2022. 19 | */ 20 | class SensorPacketsProvider : SensorEventListener { 21 | 22 | 23 | companion object { 24 | private var sSensorPacketsProvider: SensorPacketsProvider? = null 25 | 26 | private val lock = Any() 27 | 28 | fun getInstance(): SensorPacketsProvider { 29 | synchronized(lock) { 30 | if (sSensorPacketsProvider == null) { 31 | sSensorPacketsProvider = SensorPacketsProvider() 32 | } 33 | return sSensorPacketsProvider!! 34 | } 35 | } 36 | 37 | } 38 | 39 | 40 | var mDefaultScope = CoroutineScope(Job() + Dispatchers.Default) 41 | 42 | private var mSensorManager: SensorManager? = null 43 | 44 | private var mSensorConfigs = SparseArray() 45 | 46 | 47 | private val _mSensorPacketFlow = MutableSharedFlow(replay = 0) 48 | val mSensorPacketFlow = _mSensorPacketFlow.asSharedFlow() 49 | 50 | 51 | 52 | // private 53 | 54 | // var keys: ds 55 | 56 | fun setSensorManager(manager: SensorManager): SensorPacketsProvider { 57 | mSensorManager = manager 58 | return this 59 | } 60 | 61 | 62 | fun attachSensor(config: SensorPacketConfig): SensorPacketsProvider { 63 | 64 | // Log.d("SensorPacketsProvider", "attachSensor") 65 | var prevConfig = mSensorConfigs.get(config.sensorType) 66 | var shouldRegister = true 67 | if (prevConfig != null) { 68 | if (prevConfig.sensorType != config.sensorType) { 69 | unregisterSensor(config) 70 | } else { 71 | shouldRegister = false 72 | } 73 | } 74 | if (shouldRegister) { 75 | // Log.d("SensorPacketsProvider", "attachSensor 2") 76 | 77 | mSensorConfigs.set(config.sensorType, config) 78 | registerSensor(config) 79 | } 80 | return this 81 | } 82 | 83 | 84 | fun detachSensor(sensorType: Int): SensorPacketsProvider { 85 | // Log.d("SensorPacketsProvider", "detachSensor 1") 86 | 87 | var sensorConfig = mSensorConfigs.get(sensorType) 88 | if (sensorConfig != null) { 89 | 90 | // Log.d("SensorPacketsProvider", "detachSensor 2") 91 | unregisterSensor(sensorConfig) 92 | mSensorConfigs.remove(sensorType) 93 | 94 | } 95 | return this 96 | } 97 | 98 | 99 | private fun unregisterSensor(config: SensorPacketConfig) { 100 | if (mSensorManager == null) return 101 | mSensorManager?.unregisterListener( 102 | this, 103 | mSensorManager!!.getDefaultSensor(config.sensorType) 104 | ) 105 | } 106 | 107 | private fun registerSensor(config: SensorPacketConfig) { 108 | if (mSensorManager == null) return 109 | mSensorManager?.registerListener( 110 | this, 111 | mSensorManager!!.getDefaultSensor(config.sensorType), 112 | config.sensorDelay 113 | ) 114 | } 115 | 116 | override fun onSensorChanged(p0: SensorEvent?) { 117 | 118 | if (p0 != null) { 119 | mDefaultScope.launch { 120 | onSensorEvent(p0) 121 | } 122 | 123 | } 124 | 125 | } 126 | 127 | override fun onAccuracyChanged(p0: Sensor?, p1: Int) { 128 | //TODO("Not yet implemented") 129 | } 130 | 131 | private suspend fun onSensorEvent(sensorEvent: SensorEvent) { 132 | 133 | synchronized(this) { 134 | val sensorType = sensorEvent.sensor.type 135 | var sensorConfig = mSensorConfigs.get(sensorType) 136 | 137 | 138 | if (sensorConfig != null) { 139 | var sensorPacket = ModelSensorPacket( 140 | sensorEvent, 141 | sensorEvent.values, 142 | sensorType, 143 | sensorConfig.sensorDelay, 144 | System.currentTimeMillis() 145 | ) 146 | // Log.d("SensorPacketsProvider", "$sensorPacket") 147 | mDefaultScope.launch { 148 | // Log.d("SensorPacketsProvider", "sa: ${Arrays.toString(sensorEvent.values)}") 149 | _mSensorPacketFlow.emit( 150 | sensorPacket 151 | ) 152 | 153 | } 154 | } 155 | 156 | 157 | } 158 | 159 | } 160 | 161 | fun clearAll() { 162 | 163 | // Log.d("SensorPacketsProvider","clearAll") 164 | if(mSensorConfigs.size() > 0 ){ 165 | for (sensorConfig in mSensorConfigs.valueIterator()){ 166 | // Log.d("SensorPacketsProvider","clearAll unregisterSensor") 167 | unregisterSensor(sensorConfig) 168 | } 169 | } 170 | mSensorConfigs.clear() 171 | mSensorManager = null 172 | } 173 | 174 | 175 | } 176 | 177 | 178 | -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/sensors/provider/ModelSensor.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.sensors.provider 2 | 3 | import android.hardware.Sensor 4 | import io.sensify.sensor.domains.sensors.SensorsConstants 5 | 6 | /** 7 | * Created by Niraj on 05-10-2022. 8 | */ 9 | class ModelSensor( 10 | var type: Int = -1, 11 | var sensor: Sensor? = null, 12 | var info: Map = mutableMapOf(), 13 | var name: String = "" 14 | 15 | ) { 16 | 17 | init { 18 | if (sensor != null) { 19 | name = SensorsConstants.MAP_TYPE_TO_NAME.get( type,sensor?.name?:"") 20 | if (info is MutableMap) { 21 | /*sensorType = sensor.getType();*/ 22 | 23 | (info as MutableMap)[SensorsConstants.DETAIL_KEY_NAME] = sensor!!.name 24 | (info as MutableMap)[SensorsConstants.DETAIL_KEY_VENDOR] = 25 | sensor!!.vendor 26 | (info as MutableMap)[SensorsConstants.DETAIL_KEY_VERSION] = 27 | sensor!!.version 28 | (info as MutableMap)[SensorsConstants.DETAIL_KEY_POWER] = 29 | sensor!!.power 30 | (info as MutableMap)[SensorsConstants.DETAIL_KEY_Resolution] = 31 | sensor!!.resolution 32 | (info as MutableMap)[SensorsConstants.DETAIL_KEY_Range] = 33 | sensor!!.maximumRange 34 | 35 | /*name= sensor.getName(); 36 | vendorName = sensor.getVendor(); 37 | version = sensor.getVersion(); 38 | power = sensor.getPower(); 39 | resolution = sensor.getResolution(); 40 | maxRange = sensor.getMaximumRange();*/ 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/domains/sensors/provider/SensorsProvider.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.domains.sensors.provider 2 | 3 | import android.hardware.Sensor 4 | import android.hardware.SensorManager 5 | import android.util.Log 6 | import androidx.core.util.keyIterator 7 | import io.sensify.sensor.domains.sensors.SensorsConstants 8 | import kotlinx.coroutines.CoroutineScope 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.Job 11 | import kotlinx.coroutines.flow.* 12 | import kotlinx.coroutines.launch 13 | 14 | 15 | /** 16 | * Created by Niraj on 05-10-2022. 17 | */ 18 | class SensorsProvider { 19 | 20 | companion object { 21 | private var sSensorsProvider: SensorsProvider? = null 22 | 23 | private val lock = Any() 24 | 25 | fun getInstance(): SensorsProvider { 26 | synchronized(lock) { 27 | if (sSensorsProvider == null) { 28 | sSensorsProvider = SensorsProvider() 29 | } 30 | return sSensorsProvider!! 31 | } 32 | } 33 | 34 | } 35 | 36 | private var mSensors: List = mutableListOf() 37 | private var mSensorManager: SensorManager? = null 38 | private val _mSensorsFlow = MutableSharedFlow>(replay = 0) 39 | 40 | val mSensorsFlow = _mSensorsFlow.asSharedFlow() 41 | 42 | var mDefaultScope = CoroutineScope(Job() + Dispatchers.Default) 43 | 44 | 45 | fun setSensorManager(manager: SensorManager): SensorsProvider { 46 | mSensorManager = manager 47 | return this 48 | } 49 | 50 | 51 | fun listenSensors(): SensorsProvider { 52 | 53 | 54 | // Log.d("SensorsProvider","listenSensors: ") 55 | if(mSensors.isEmpty()){ 56 | val sensorList = mSensorManager!!.getSensorList(Sensor.TYPE_ALL).filter { 57 | // SensorsConstants.MAP_TYPE_TO_AXIS_COUNT 58 | SensorsConstants.SENSORS.contains(it.type) 59 | 60 | }.distinctBy { it.type }.toList() 61 | // Log.d("SensorProvider", "$sensorList") 62 | mSensors = sensorList.map { ModelSensor(it.type, it) }.toList() 63 | 64 | } 65 | 66 | mDefaultScope.launch { 67 | // Log.d("SensorsProvider","listenSensors 2: ${mSensors.size}") 68 | _mSensorsFlow.emit(mSensors) 69 | 70 | } 71 | return this 72 | // sensorList[0]. 73 | } 74 | 75 | fun listenSensor(sensorType: Int): Flow { 76 | 77 | var flow= mSensorsFlow.map { sensors -> return@map sensors.singleOrNull { modelSensor -> 78 | modelSensor.type == sensorType } } 79 | 80 | 81 | 82 | return flow 83 | } 84 | 85 | fun getSensor(sensorType: Int): ModelSensor? { 86 | 87 | return mSensors.singleOrNull { modelSensor -> 88 | modelSensor.type == sensorType } 89 | } 90 | 91 | fun clearAll() { 92 | 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui 2 | 3 | import android.content.Context 4 | import android.hardware.SensorManager 5 | import android.os.Bundle 6 | import android.util.Log 7 | import android.view.View 8 | import androidx.activity.ComponentActivity 9 | import androidx.activity.compose.setContent 10 | import androidx.compose.foundation.layout.* 11 | import androidx.compose.foundation.rememberScrollState 12 | import androidx.compose.foundation.verticalScroll 13 | import androidx.compose.material3.* 14 | import androidx.compose.runtime.* 15 | import androidx.compose.ui.Alignment 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.tooling.preview.Preview 19 | import androidx.compose.ui.unit.dp 20 | import androidx.compose.ui.unit.sp 21 | import androidx.lifecycle.lifecycleScope 22 | import com.google.accompanist.pager.ExperimentalPagerApi 23 | import io.sensify.sensor.domains.sensors.packets.SensorPacketsProvider 24 | import io.sensify.sensor.domains.sensors.provider.SensorsProvider 25 | import io.sensify.sensor.ui.navigation.NavGraphApp 26 | import io.sensify.sensor.ui.resource.themes.SensifyM3Theme 27 | import kotlinx.coroutines.CoroutineScope 28 | import kotlinx.coroutines.Job 29 | import kotlinx.coroutines.coroutineScope 30 | import kotlinx.coroutines.launch 31 | import kotlin.coroutines.suspendCoroutine 32 | 33 | class MainActivity : ComponentActivity() { 34 | 35 | @OptIn(ExperimentalPagerApi::class) 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager 39 | // throw NumberFormatException("Test Crash") // Force a crash 40 | lifecycleScope.launch { 41 | SensorsProvider.getInstance().setSensorManager(sensorManager) 42 | SensorPacketsProvider.getInstance().setSensorManager(sensorManager) 43 | setContent { 44 | SensifyM3Theme { 45 | // A surface container using the 'background' color from the theme 46 | Surface( 47 | modifier = Modifier.fillMaxSize(), 48 | color = MaterialTheme.colorScheme.background 49 | ) { 50 | 51 | 52 | NavGraphApp() 53 | 54 | } 55 | } 56 | } 57 | } 58 | 59 | 60 | } 61 | 62 | 63 | override fun onDestroy() { 64 | super.onDestroy() 65 | // scope 66 | // coroutineScope { 67 | SensorsProvider.getInstance().clearAll(); 68 | 69 | 70 | SensorPacketsProvider.getInstance().clearAll(); 71 | // } 72 | lifecycleScope.launch { 73 | 74 | } 75 | CoroutineScope(Job()) 76 | 77 | 78 | //setSensorManager(sensorManager) 79 | } 80 | 81 | suspend fun dsds(){ 82 | suspendCoroutine { } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/components/chart/mpchart/MpChartLineAxis.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.components.chart.mpchart 2 | 3 | import android.content.Context 4 | import androidx.compose.ui.graphics.Color 5 | import com.github.mikephil.charting.charts.LineChart 6 | import io.sensify.sensor.ui.components.chart.mpchart.base.MpChartLineView 7 | 8 | /** 9 | * Created by Niraj on 30-09-2022. 10 | */ 11 | class MpChartLineAxis(key: Int) : MpChartLineView(key) { 12 | 13 | override fun create(context: Context, colorSurface: Color, colorOnSurface: Color): LineChart { 14 | var lineChart = super.create(context, colorSurface, colorOnSurface) 15 | 16 | lineChart.apply { 17 | axisLeft.setDrawLabels(true) 18 | // axisRight.setDrawLabels(false) 19 | axisLeft.setDrawGridLines(true) 20 | } 21 | 22 | return lineChart 23 | } 24 | 25 | 26 | 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/components/chart/mpchart/MpChartLineComet.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.components.chart.mpchart 2 | 3 | import io.sensify.sensor.ui.components.chart.mpchart.base.MpChartLineView 4 | 5 | /** 6 | * Created by Niraj on 30-09-2022. 7 | */ 8 | class MpChartLineComet() : MpChartLineView(1) { 9 | 10 | 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/components/chart/mpchart/base/IMpChartLineView.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.components.chart.mpchart.base 2 | 3 | import android.content.Context 4 | import androidx.compose.ui.graphics.Color 5 | import com.github.mikephil.charting.charts.LineChart 6 | 7 | /** 8 | * Created by Niraj on 30-09-2022. 9 | */ 10 | interface IMpChartLineView { 11 | fun create( 12 | context: Context, 13 | colorSurface: Color = Color.Transparent, 14 | colorOnSurface: Color = Color.DarkGray 15 | ): LineChart; 16 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/components/chart/mpchart/base/MpChartLineView.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.components.chart.mpchart.base 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import androidx.compose.ui.graphics.Color 6 | import androidx.compose.ui.graphics.toArgb 7 | import com.github.mikephil.charting.charts.LineChart 8 | import com.github.mikephil.charting.components.Legend 9 | import com.github.mikephil.charting.components.XAxis 10 | import io.sensify.sensor.domains.chart.mpchart.axis.MpChartTimestampAxisFormatter 11 | 12 | /** 13 | * Created by Niraj on 30-09-2022. 14 | */ 15 | open class MpChartLineView(var mKey: Int) : IMpChartLineView { 16 | var mLineChart: LineChart? = null 17 | override fun create(context: Context, colorSurface: Color, colorOnSurface: Color): LineChart { 18 | mLineChart = LineChart(context).apply { 19 | 20 | //Set shadow 21 | setLayerType(View.LAYER_TYPE_SOFTWARE, null); 22 | // renderer.paintRender.setShadowLayer(3F, 5F, 3F, Color.Gray.toArgb()); 23 | // setViewPortOffsets(0f, 0f, 0f, 0f); 24 | 25 | 26 | applyAxis(this, colorSurface, colorOnSurface) 27 | legend.isEnabled = true 28 | legend.verticalAlignment = Legend.LegendVerticalAlignment.TOP 29 | legend.horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT 30 | legend.orientation = Legend.LegendOrientation.HORIZONTAL 31 | legend.setDrawInside(false) 32 | legend.textColor = colorOnSurface.toArgb() 33 | 34 | setBackgroundColor(colorSurface.toArgb()) 35 | setDrawGridBackground(false) 36 | setDrawBorders(false) 37 | // setDrawMarkers(true) 38 | 39 | 40 | description.isEnabled = false 41 | tag = mKey 42 | 43 | } 44 | return mLineChart!! 45 | } 46 | 47 | private fun applyAxis(lineChart: LineChart, colorSurface: Color, colorOnSurface: Color) { 48 | lineChart.apply { 49 | //Formatting 50 | 51 | xAxis.valueFormatter = MpChartTimestampAxisFormatter() 52 | xAxis.granularity = 1f 53 | xAxis.setDrawAxisLine(true) 54 | xAxis.setDrawGridLines(false) 55 | xAxis.setDrawLabels(false) 56 | xAxis.position = XAxis.XAxisPosition.BOTTOM 57 | xAxis.textColor = colorOnSurface.toArgb() 58 | 59 | axisRight.setDrawZeroLine(false) 60 | axisRight.isEnabled = false 61 | axisRight.textColor = colorOnSurface.toArgb() 62 | 63 | axisLeft.setDrawZeroLine(false) 64 | axisLeft.isEnabled = true 65 | axisLeft.setDrawAxisLine(true) 66 | // axisRight.setDrawAxisLine(false) 67 | axisLeft.setDrawLabels(false) 68 | // axisRight.setDrawLabels(false) 69 | axisLeft.setDrawGridLines(false) 70 | // axisRight.setDrawGridLines(false) 71 | // axisLeft.axisMinimum = 0f 72 | axisLeft.textColor = colorOnSurface.toArgb() 73 | } 74 | 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/composables/LazyListState.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.composables 2 | 3 | import androidx.compose.foundation.lazy.LazyListState 4 | import androidx.compose.runtime.* 5 | 6 | /** 7 | * Created by Niraj on 17-10-2022. 8 | */ 9 | /** 10 | * Returns whether the lazy list is currently scrolling up. 11 | */ 12 | @Composable 13 | fun LazyListState.isScrollingUp(): Boolean { 14 | var previousIndex by remember(this) { mutableStateOf(firstVisibleItemIndex) } 15 | var previousScrollOffset by remember(this) { mutableStateOf(firstVisibleItemScrollOffset) } 16 | return remember(this) { 17 | derivedStateOf { 18 | if (previousIndex != firstVisibleItemIndex) { 19 | previousIndex > firstVisibleItemIndex 20 | } else { 21 | previousScrollOffset >= firstVisibleItemScrollOffset 22 | }.also { 23 | previousIndex = firstVisibleItemIndex 24 | previousScrollOffset = firstVisibleItemScrollOffset 25 | } 26 | } 27 | }.value 28 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/fix/material3/PagerTab.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.fix.material3 2 | 3 | import androidx.compose.material3.TabPosition 4 | import androidx.compose.ui.Modifier 5 | import androidx.compose.ui.layout.layout 6 | import com.google.accompanist.pager.ExperimentalPagerApi 7 | import com.google.accompanist.pager.PagerState 8 | import androidx.compose.ui.unit.Constraints 9 | import androidx.compose.ui.unit.lerp 10 | 11 | /** 12 | * Created by Niraj on 27-09-2022. 13 | */ 14 | 15 | /** 16 | * This indicator syncs up a [TabRow] or [ScrollableTabRow] tab indicator with a 17 | * [HorizontalPager] or [VerticalPager]. See the sample for a full demonstration. 18 | * 19 | * @sample com.google.accompanist.sample.pager.PagerWithTabs 20 | */ 21 | @ExperimentalPagerApi 22 | fun Modifier.pagerTabIndicatorOffset( 23 | pagerState: PagerState, 24 | tabPositions: List, 25 | pageIndexMapping: (Int) -> Int = { it }, 26 | ): Modifier = layout { measurable, constraints -> 27 | if (tabPositions.isEmpty()) { 28 | // If there are no pages, nothing to show 29 | layout(constraints.maxWidth, 0) {} 30 | } else { 31 | val currentPage = minOf(tabPositions.lastIndex, pageIndexMapping(pagerState.currentPage)) 32 | val currentTab = tabPositions[currentPage] 33 | val previousTab = tabPositions.getOrNull(currentPage - 1) 34 | val nextTab = tabPositions.getOrNull(currentPage + 1) 35 | val fraction = pagerState.currentPageOffset 36 | val indicatorWidth = if (fraction > 0 && nextTab != null) { 37 | lerp(currentTab.width, nextTab.width, fraction).roundToPx() 38 | } else if (fraction < 0 && previousTab != null) { 39 | lerp(currentTab.width, previousTab.width, -fraction).roundToPx() 40 | } else { 41 | currentTab.width.roundToPx() 42 | } 43 | val indicatorOffset = if (fraction > 0 && nextTab != null) { 44 | lerp(currentTab.left, nextTab.left, fraction).roundToPx() 45 | } else if (fraction < 0 && previousTab != null) { 46 | lerp(currentTab.left, previousTab.left, -fraction).roundToPx() 47 | } else { 48 | currentTab.left.roundToPx() 49 | } 50 | val placeable = measurable.measure( 51 | Constraints( 52 | minWidth = indicatorWidth, 53 | maxWidth = indicatorWidth, 54 | minHeight = 0, 55 | maxHeight = constraints.maxHeight 56 | ) 57 | ) 58 | layout(constraints.maxWidth, maxOf(placeable.height, constraints.minHeight)) { 59 | placeable.placeRelative( 60 | indicatorOffset, 61 | maxOf(constraints.minHeight - placeable.height, 0) 62 | ) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/navigation/NavDirectionsApp.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.navigation 2 | 3 | import android.hardware.Sensor 4 | import androidx.compose.runtime.Composable 5 | import androidx.navigation.NavType 6 | import androidx.navigation.compose.NavHost 7 | import androidx.navigation.compose.composable 8 | import androidx.navigation.compose.rememberNavController 9 | import androidx.navigation.navArgument 10 | import io.sensify.sensor.ui.pages.SplashPage 11 | import io.sensify.sensor.ui.pages.about.AboutPage 12 | import io.sensify.sensor.ui.pages.home.HomePage 13 | import io.sensify.sensor.ui.pages.sensor.details.SensorPage 14 | 15 | 16 | /** 17 | * Created by Niraj on 30-08-2022. 18 | */ 19 | sealed class NavDirectionsApp(val route: String) { 20 | object Root : NavDirectionsApp("root") 21 | object HomePage : NavDirectionsApp("home_page") 22 | object SensorDetailPage : NavDirectionsApp("sensor_detail_page") 23 | object AboutPage : NavDirectionsApp("about_page") 24 | object Splash : NavDirectionsApp("splash_page") 25 | } 26 | 27 | @Composable 28 | fun NavGraphApp() { 29 | val navController = rememberNavController() 30 | 31 | NavHost(navController = navController, startDestination = NavDirectionsApp.Splash.route) { 32 | // startDestination "labs1" 33 | // val viewModelHome: HomeViewModel = HomeViewModel() 34 | // val viewModelSensor: SensorViewModel = SensorViewModel() 35 | composable(NavDirectionsApp.Splash.route) { SplashPage(navController) } 36 | composable(NavDirectionsApp.HomePage.route) { 37 | HomePage( 38 | navController = navController 39 | ) 40 | } 41 | composable("${NavDirectionsApp.SensorDetailPage.route}/{type}", listOf(navArgument("type") { 42 | type = NavType.IntType 43 | })) { 44 | SensorPage( 45 | navController = navController, 46 | type = it.arguments?.getInt("type") ?: Sensor.TYPE_GYROSCOPE, 47 | // TODO might be creating bug 48 | 49 | ) 50 | } 51 | composable(NavDirectionsApp.AboutPage.route) { AboutPage(navController = navController) } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/SplashPage.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages 2 | 3 | import androidx.compose.animation.core.Animatable 4 | import androidx.compose.animation.core.AnimationVector1D 5 | import androidx.compose.foundation.Image 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.layout.* 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.graphics.Brush 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.graphics.painter.Painter 15 | import androidx.compose.material3.Text 16 | import androidx.compose.runtime.LaunchedEffect 17 | import androidx.compose.ui.draw.scale 18 | import androidx.compose.ui.geometry.Offset 19 | import androidx.compose.ui.graphics.TileMode 20 | import androidx.compose.ui.res.painterResource 21 | import androidx.compose.ui.text.ExperimentalTextApi 22 | import androidx.compose.ui.text.TextStyle 23 | import androidx.compose.ui.unit.dp 24 | import androidx.navigation.NavController 25 | import com.google.accompanist.systemuicontroller.rememberSystemUiController 26 | import io.sensify.sensor.R 27 | import io.sensify.sensor.ui.navigation.NavDirectionsApp 28 | import io.sensify.sensor.ui.resource.values.JlResColors 29 | import io.sensify.sensor.ui.resource.values.JlResShapes 30 | import io.sensify.sensor.ui.resource.values.JlResTxtStyles 31 | import kotlinx.coroutines.delay 32 | 33 | /** 34 | * Created by Niraj on 24-08-2022. 35 | */ 36 | 37 | @Composable 38 | fun SplashPage(navController: NavController) { 39 | // val systemUiController = rememberSystemUiController() 40 | //// if(darkTheme){ 41 | // systemUiController.setSystemBarsColor( 42 | // color = Color.Transparent 43 | // ) 44 | LaunchedEffect(key1 = true) { 45 | /*scaleAnimation.animateTo( 46 | targetValue = 0.5F, 47 | animationSpec = tween( 48 | durationMillis = durationMillisAnimation, 49 | easing = { 50 | OvershootInterpolator(3F).getInterpolation(it) 51 | } 52 | ) 53 | )*/ 54 | 55 | delay(timeMillis = 500) 56 | 57 | navController.popBackStack() 58 | navController.navigate(NavDirectionsApp.HomePage.route) 59 | 60 | /*navController.navigate(route = DestinationScreen.MainScreenDest.route) { 61 | popUpTo(route = DestinationScreen.SplashScreenDest.route) { 62 | inclusive = true 63 | } 64 | }*/ 65 | } 66 | SplashScreen() 67 | 68 | } 69 | 70 | @OptIn(ExperimentalTextApi::class) 71 | @Composable 72 | fun SplashScreen( 73 | modifier: Modifier = Modifier, 74 | // imagePainter: Painter, 75 | // scaleAnimation: Animatable 76 | ) { 77 | Box( 78 | modifier = modifier 79 | .fillMaxSize() 80 | .background( 81 | Brush.verticalGradient( 82 | colors = listOf( 83 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.05f), 84 | 85 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.02f), 86 | ) 87 | ) 88 | ), 89 | contentAlignment = Alignment.Center 90 | ) { 91 | Column(horizontalAlignment = Alignment.CenterHorizontally) { 92 | /*Image( 93 | painter = imagePainter, 94 | contentDescription = "Logotipo Splash Screen", 95 | modifier = modifier 96 | .size(400.dp) 97 | .scale(scale = scaleAnimation.value), 98 | )*/ 99 | Image( 100 | painter = painterResource(id = R.drawable.pic_logo), 101 | contentDescription = "Logotipo Splash Screen", 102 | modifier = modifier 103 | .size(120.dp) 104 | // .scale(scale = scaleAnimation.value), 105 | ) 106 | Spacer(modifier = JlResShapes.Space.H24) 107 | Image( 108 | painter = painterResource(id = R.drawable.pic_launcher_eye), 109 | contentDescription = "Logotipo Splash Screen", 110 | modifier = modifier 111 | .size(220.dp) 112 | // .scale(scale = scaleAnimation.value), 113 | ) 114 | Spacer(modifier = JlResShapes.Space.H56) 115 | Text( 116 | text = "Sensify", 117 | style = JlResTxtStyles.h1.merge( 118 | other = TextStyle( 119 | brush = Brush.linearGradient( 120 | colors = listOf( 121 | MaterialTheme.colorScheme.onSurface, 122 | MaterialTheme.colorScheme.onSurface.copy(0.1f) 123 | ), 124 | tileMode = TileMode.Mirror, 125 | start = Offset(0f, 0f), 126 | end = Offset(0f, Float.POSITIVE_INFINITY), 127 | ) 128 | ) 129 | ), 130 | // modifier = Modifier.(scale = scaleAnimation.value 131 | /*color = Color.White, 132 | fontSize = JlResDimens.dp40, 133 | fontWeight = FontWeight.ExtraBold, 134 | fontFamily = FontFamily.Serif, 135 | textAlign = TextAlign.Center, 136 | modifier = modifier.scale(scale = scaleAnimation.value 137 | */ 138 | ) 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/home/items/HomeSensorChartItem.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.home.items 2 | 3 | import android.hardware.Sensor 4 | import android.util.Log 5 | import androidx.compose.foundation.background 6 | import androidx.compose.foundation.layout.* 7 | import androidx.compose.material.Text 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.DisposableEffect 11 | import androidx.compose.runtime.collectAsState 12 | import androidx.compose.runtime.remember 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.graphics.Color 15 | import androidx.compose.ui.platform.LocalLifecycleOwner 16 | import androidx.compose.ui.text.style.TextAlign 17 | import androidx.compose.ui.viewinterop.AndroidView 18 | import androidx.lifecycle.LifecycleOwner 19 | import io.sensify.sensor.domains.chart.entity.ModelChartUiUpdate 20 | import io.sensify.sensor.domains.chart.mpchart.MpChartDataManager 21 | import io.sensify.sensor.domains.chart.mpchart.MpChartViewBinder 22 | import io.sensify.sensor.domains.chart.mpchart.MpChartViewUpdater 23 | import io.sensify.sensor.ui.components.chart.mpchart.base.MpChartLineView 24 | import io.sensify.sensor.ui.pages.home.model.ModelHomeSensor 25 | import io.sensify.sensor.ui.resource.values.JlResDimens 26 | import io.sensify.sensor.ui.resource.values.JlResShapes 27 | import io.sensify.sensor.ui.resource.values.JlResTxtStyles 28 | 29 | /** 30 | * Created by Niraj on 30-09-2022. 31 | */ 32 | @Composable 33 | fun HomeSensorChartItem( 34 | modelSensor: ModelHomeSensor = ModelHomeSensor( 35 | type = Sensor.TYPE_LIGHT 36 | ), 37 | mpChartDataManager : MpChartDataManager = MpChartDataManager(modelSensor.type), 38 | mpChartViewUpdater: MpChartViewUpdater = MpChartViewUpdater(), 39 | // sensorPacketFlow: SharedFlow = MutableSharedFlow(replay = 0), 40 | lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current, 41 | ) { 42 | 43 | // var sensorType = Sensor.TYPE_GRAVITY 44 | // val sensorData = rememberSensorPackets(sensorType = sensorType, sensorDelay = SensorManager.SENSOR_DELAY_NORMAL) 45 | var state = mpChartDataManager.mSensorPacketFlow.collectAsState( 46 | initial = ModelChartUiUpdate( 47 | sensorType = modelSensor.type, 48 | 0, 49 | listOf() 50 | ) 51 | ) 52 | val sensorUiUpdate = remember { 53 | state 54 | }; 55 | 56 | Log.d("HomeSensorChart", "Chart model: ${modelSensor.name} ${modelSensor.type} ${mpChartDataManager.sensorType}") 57 | // var mpChartViewManager = MpChartViewManager(modelSensor.type) 58 | /* val sensorUiUpdate = 59 | rememberChartUiUpdateEvent(mpChartDataManager, SensorManager.SENSOR_DELAY_NORMAL) 60 | */ 61 | 62 | // var counter = 0 63 | // Log.d("DefaultChartTesting", "Linechart isUpdating ${isUpdating.value}") 64 | 65 | var colorSurface = MaterialTheme.colorScheme.surface 66 | var colorOnSurface = MaterialTheme.colorScheme.onSurface 67 | Column( 68 | modifier = Modifier 69 | .background(color = Color.Transparent) 70 | .padding(horizontal = JlResDimens.dp12, vertical = JlResDimens.dp12) 71 | // .height(JlResDimens.dp168) 72 | .fillMaxSize(), 73 | ) { 74 | 75 | Text( 76 | modifier = Modifier.padding(horizontal = JlResDimens.dp12), 77 | text = "${modelSensor.name}", 78 | 79 | 80 | color = MaterialTheme.colorScheme.onSurface, 81 | textAlign = TextAlign.Start, 82 | style = JlResTxtStyles.h5, 83 | ) 84 | AndroidView( 85 | modifier = Modifier 86 | .background(color = Color.Transparent) 87 | // .height(JlResDimens.dp168) 88 | .fillMaxSize(), 89 | 90 | 91 | factory = { ctx -> 92 | 93 | Log.v("HomeSensorChart", "factory: ${mpChartDataManager.sensorType}") 94 | 95 | var view = MpChartLineView(modelSensor.type); 96 | // view 97 | val lineChart = MpChartViewBinder(ctx, view, colorOnSurface= colorOnSurface).prepareDataSets(mpChartDataManager.getModel()) 98 | .invalidate() 99 | return@AndroidView lineChart 100 | // mpChartViewManager.createChart(ctx, colorSurface, colorOnSurface) 101 | }, 102 | update = { 103 | // Log.v("HomeSensorChart", "update:${sensorUiUpdate.value.sensorType} ${it.tag} ${sensorUiUpdate.value.timestamp} ${sensorUiUpdate.value.size}") 104 | 105 | mpChartViewUpdater.update(it, sensorUiUpdate.value, mpChartDataManager.getModel()) 106 | // Log.v("HomeSensorChart", "update: ${mpChartDataManager.sensorType} ${isUpdated}") 107 | 108 | // mpChartDataManager.runPeriodically() 109 | //updateData(it, sensorUiUpdate.value) 110 | } 111 | ) 112 | Spacer(modifier = JlResShapes.Space.H18) 113 | 114 | } 115 | 116 | DisposableEffect(mpChartDataManager.sensorType) { 117 | /* val observer = LifecycleEventObserver { _, event -> 118 | if (event == Lifecycle.Event.ON_START) { 119 | // currentOnStart() 120 | } else if (event == Lifecycle.Event.ON_DESTROY) { 121 | // currentOnStop() 122 | Log.v("HomeSensorChart", "destroy: ${mpChartDataManager.sensorType}") 123 | mpChartDataManager.destroy() 124 | } 125 | } 126 | 127 | // Add the observer to the lifecycle 128 | lifecycleOwner.lifecycle.addObserver(observer)*/ 129 | onDispose { 130 | 131 | Log.v("HomeSensorChart", "dispose: ${mpChartDataManager.sensorType}") 132 | // mpChartDataManager.destroy() 133 | // lifecycleOwner.lifecycle.removeObserver(observer) 134 | 135 | 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/home/model/ModelHomeSensor.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.home.model 2 | 3 | import android.hardware.Sensor 4 | import io.sensify.sensor.domains.sensors.SensorsConstants 5 | 6 | /** 7 | * Created by Niraj on 08-10-2022. 8 | */ 9 | data class ModelHomeSensor( 10 | var type: Int = -1, 11 | var sensor: Sensor? = null, 12 | var info: Map = mutableMapOf(), 13 | var valueRms: Float? = 0.0f, 14 | var isActive: Boolean = false, 15 | var name: String = "" 16 | ) { 17 | 18 | init { 19 | 20 | name = SensorsConstants.MAP_TYPE_TO_NAME.get( type,sensor?.name?:"") 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/home/sections/HomeSensorGraphPager.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.home.sections 2 | 3 | import android.util.Log 4 | import androidx.compose.foundation.BorderStroke 5 | import androidx.compose.foundation.background 6 | import androidx.compose.foundation.layout.* 7 | import androidx.compose.foundation.shape.RoundedCornerShape 8 | import androidx.compose.material3.Card 9 | import androidx.compose.material3.CardDefaults 10 | import androidx.compose.material3.MaterialTheme 11 | import androidx.compose.runtime.* 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.geometry.Offset 14 | import androidx.compose.ui.graphics.Brush 15 | import androidx.compose.ui.graphics.Color 16 | import androidx.compose.ui.util.lerp 17 | import androidx.compose.ui.graphics.graphicsLayer 18 | import androidx.compose.ui.graphics.toArgb 19 | import androidx.compose.ui.tooling.preview.Preview 20 | import androidx.compose.ui.unit.dp 21 | import androidx.core.graphics.ColorUtils 22 | import com.google.accompanist.pager.* 23 | import io.sensify.sensor.ui.pages.home.HomeViewModel 24 | import io.sensify.sensor.ui.pages.home.items.HomeSensorChartItem 25 | import io.sensify.sensor.ui.resource.effects.drawColoredShadow 26 | import io.sensify.sensor.ui.resource.values.JlResDimens 27 | import kotlin.math.absoluteValue 28 | 29 | /** 30 | * Created by Niraj on 29-09-2022. 31 | */ 32 | @OptIn(ExperimentalPagerApi::class) 33 | @Preview(showBackground = true, backgroundColor = 0xFF041B11) 34 | @Composable 35 | fun HomeSensorGraphPager( 36 | modifier: Modifier = Modifier, 37 | viewModel: HomeViewModel = HomeViewModel(), 38 | pagerState: PagerState = rememberPagerState( 39 | // pageCount = 3, 40 | ) 41 | ) { 42 | // pagerState. 43 | // TODO uncomment val activeSensorStateList = remember { viewModel.mActiveSensorStateList } 44 | 45 | var sd = viewModel.mActiveSensorListFlow.collectAsState(initial = mutableListOf()) 46 | val activeSensorStateList = remember { sd } 47 | /*val pagerState = rememberPagerState( 48 | // pageCount = 3, 49 | )*/ 50 | 51 | LaunchedEffect(pagerState) { 52 | snapshotFlow { "${pagerState.currentPage} ${pagerState.pageCount}" }.collect { page -> 53 | Log.d("HomeSensorGraphPager", "pager 2: $page") 54 | viewModel.setActivePage(pagerState.currentPage) 55 | } 56 | } 57 | Log.d("HomeSensorGraphPager", "pager 1: $pagerState ${activeSensorStateList.value.size}") 58 | HorizontalPager( 59 | // count = 3, 60 | state = pagerState, 61 | count = activeSensorStateList.value.size, 62 | // Add 32.dp horizontal padding to 'center' the pages 63 | contentPadding = PaddingValues(horizontal = JlResDimens.dp32), 64 | key ={ 65 | if(it < activeSensorStateList.value.size){ 66 | 67 | activeSensorStateList.value[it].type 68 | }else{ 69 | -1 70 | } 71 | } , 72 | modifier = modifier 73 | .fillMaxWidth() 74 | .padding(vertical = JlResDimens.dp20), 75 | 76 | ) { page -> 77 | 78 | Card( 79 | Modifier 80 | .graphicsLayer { 81 | // Calculate the absolute offset for the current page from the 82 | // scroll position. We use the absolute value which allows us to mirror 83 | // any effects for both directions 84 | val pageOffset = calculateCurrentOffsetForPage(page).absoluteValue 85 | 86 | // We animate the scaleX + scaleY, between 85% and 100% 87 | lerp( 88 | start = 0.85f, 89 | stop = 1f, 90 | fraction = 1f - pageOffset.coerceIn(0f, 1f) 91 | ).also { scale -> 92 | Log.d("HomeSensorGraphPager", " scale: $scale") 93 | scaleX = scale 94 | scaleY = scale 95 | } 96 | 97 | 98 | // We animate the alpha, between 50% and 100% 99 | alpha = lerp( 100 | start = 0.5f, 101 | stop = 1f, 102 | fraction = 1f - pageOffset.coerceIn(0f, 1f) 103 | ) 104 | } 105 | .fillMaxWidth() 106 | .height(220.dp) 107 | .background( 108 | brush = Brush.radialGradient( 109 | 110 | listOf( 111 | Color( 112 | ColorUtils.blendARGB( 113 | MaterialTheme.colorScheme.onSurface.toArgb(), 114 | MaterialTheme.colorScheme.surface.toArgb(), 115 | 0.8f 116 | ) 117 | ), 118 | Color( 119 | ColorUtils.blendARGB( 120 | MaterialTheme.colorScheme.onSurface.toArgb(), 121 | MaterialTheme.colorScheme.surface.toArgb(), 122 | 0.97f 123 | ) 124 | ), 125 | ), 126 | center = Offset(200f, -30f), 127 | 128 | radius = 600.0f 129 | 130 | 131 | // start = Offset(0f, 0f), 132 | // end = Offset(0f, Float.POSITIVE_INFINITY) 133 | ), shape = RoundedCornerShape(JlResDimens.dp28) 134 | ) 135 | .drawColoredShadow( 136 | Color.Black, 137 | offsetY = JlResDimens.dp12, 138 | shadowRadius = JlResDimens.dp16, 139 | borderRadius = JlResDimens.dp32, 140 | alpha = 0.1f 141 | ), 142 | shape = RoundedCornerShape(JlResDimens.dp28), 143 | border = BorderStroke( 144 | brush = Brush.verticalGradient( 145 | listOf( 146 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f), 147 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.25f), 148 | ) 149 | ), 150 | width = JlResDimens.dp1, 151 | ), 152 | colors = CardDefaults.cardColors( 153 | containerColor = Color.Transparent 154 | ) 155 | ) { 156 | 157 | Box( 158 | modifier = Modifier 159 | .fillMaxSize(), 160 | 161 | ) { 162 | Log.d( 163 | "HomeSensorGraphPager", 164 | "Chart model: size: ${activeSensorStateList.value.size} ${page} ${activeSensorStateList.value[page]}" 165 | ) 166 | HomeSensorChartItem( 167 | activeSensorStateList.value[page], 168 | viewModel.getChartDataManager(activeSensorStateList.value[page].type) 169 | ) 170 | } 171 | 172 | } 173 | } 174 | 175 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/home/state/HomeUiState.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.home.state 2 | 3 | import io.sensify.sensor.ui.pages.home.model.ModelHomeSensor 4 | 5 | /** 6 | * Created by Niraj on 09-10-2022. 7 | */ 8 | data class HomeUiState(var currentSensor: ModelHomeSensor? = null, var activeSensorCounts: Int = 1) { 9 | 10 | 11 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/sensor/SensorViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.sensor 2 | 3 | import android.hardware.SensorManager 4 | import android.util.Log 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import io.sensify.sensor.domains.chart.entity.ModelChartUiUpdate 8 | import io.sensify.sensor.domains.chart.mpchart.MpChartDataManager 9 | import io.sensify.sensor.domains.sensors.packets.ModelSensorPacket 10 | import io.sensify.sensor.domains.sensors.packets.SensorPacketConfig 11 | import io.sensify.sensor.domains.sensors.packets.SensorPacketsProvider 12 | import kotlinx.coroutines.delay 13 | import kotlinx.coroutines.flow.MutableSharedFlow 14 | import kotlinx.coroutines.flow.asSharedFlow 15 | import kotlinx.coroutines.flow.filter 16 | import kotlinx.coroutines.isActive 17 | import kotlinx.coroutines.launch 18 | import java.util.* 19 | import kotlin.math.sqrt 20 | 21 | /** 22 | * Created by Niraj on 16-10-2022. 23 | */ 24 | class SensorViewModel(var mSensorType: Int) : ViewModel() { 25 | 26 | private var mIsRunningPeriod: Boolean = false 27 | private var mLatestPacket: ModelSensorPacket? = null 28 | private var mLogTimestamp: Long = 0 29 | private var mSensorPacket: ModelSensorPacket? = null 30 | private var mChartDataManager: MpChartDataManager? = null 31 | private val _mSensorPacketFlow = MutableSharedFlow(replay = 0) 32 | val mSensorPacketFlow = _mSensorPacketFlow.asSharedFlow() 33 | 34 | 35 | private val _mSensorModulus = MutableSharedFlow(replay = 0) 36 | val mSensorModulus = _mSensorModulus.asSharedFlow() 37 | 38 | /*val mSensorPacketFlow : SharedFlow 39 | get() { 40 | TODO() 41 | }*/ 42 | // private var mSensorType: Int = -1 43 | 44 | /* init { 45 | SensorsProvider.getInstance().getSensor(sensorType = ) 46 | viewModelScope.launch { 47 | // SensorPacketsProvider.getInstance().mSensorPacketFlow 48 | } 49 | }*/ 50 | 51 | /*fun setSensorType(sensorType: Int){ 52 | mSensorType = sensorType; 53 | 54 | } 55 | */ 56 | init { 57 | getChartDataManager(mSensorType) 58 | initializeFlow() 59 | } 60 | 61 | 62 | fun getChartDataManager(type: Int): MpChartDataManager { 63 | 64 | if (mChartDataManager?.sensorType == type) { 65 | return mChartDataManager!! 66 | } else { 67 | mChartDataManager = MpChartDataManager(type, onDestroy = { 68 | }) 69 | } 70 | 71 | return mChartDataManager!! 72 | } 73 | 74 | private fun initializeFlow() { 75 | 76 | var sensorPacketFlow = 77 | SensorPacketsProvider.getInstance().mSensorPacketFlow 78 | val sensorPacketFilteredFlow = 79 | sensorPacketFlow.filter { sensorPacket -> 80 | var filtered = sensorPacket.type == mSensorType 81 | // sensorPacket.sensorEvent?.values 82 | // Log.d("rememberChartUiUpdateEvent", "filtered: $filtered, ${mpChartViewManager.sensorType}") 83 | return@filter filtered 84 | } 85 | 86 | SensorPacketsProvider.getInstance().attachSensor( 87 | SensorPacketConfig(mSensorType, SensorManager.SENSOR_DELAY_NORMAL) 88 | ) 89 | viewModelScope.launch { 90 | sensorPacketFilteredFlow.collect { 91 | // Log.d("SensorViewModel", "init mSensorPacketFlow 2: ") 92 | // Log.d("SensorViewModel", "addEntry: ${it.timestamp}") 93 | 94 | if (it.timestamp - mLogTimestamp < 50) { 95 | Log.d("SensorViewModel", "addEntry: ${it.timestamp} ${it.type} ${ Arrays.toString(it.values)}") 96 | 97 | } 98 | 99 | mLogTimestamp = it.timestamp 100 | mChartDataManager?.addEntry(it) 101 | } 102 | } 103 | 104 | mChartDataManager?.runPeriodically() 105 | runPeriodicRms() 106 | 107 | viewModelScope.launch { 108 | 109 | mChartDataManager?.mSensorPacketFlow?.collect { 110 | 111 | if (it.packets.size > 0) { 112 | mLatestPacket = it.packets.last() 113 | 114 | } 115 | 116 | // it.packets 117 | // Log.d("SensorViewModel", "init mSensorPacketFlow: ${it.timestamp} ${it.size} ") 118 | _mSensorPacketFlow.emit(it) 119 | } 120 | } 121 | } 122 | 123 | fun runPeriodicRms() { 124 | 125 | 126 | if (mIsRunningPeriod) { 127 | return 128 | } 129 | mIsRunningPeriod = true 130 | 131 | viewModelScope.launch { 132 | delay(100) 133 | // Log.d("MpChartViewManager ", "createChart periodic Task: $sensorType") 134 | runPeriodicTask() 135 | } 136 | 137 | // return lineChart 138 | 139 | // return linechart 140 | 141 | } 142 | 143 | private fun runPeriodicTask() { 144 | 145 | viewModelScope.launch { 146 | while (viewModelScope.isActive) { 147 | // TODO should I periodic shift 148 | 149 | var packet = mLatestPacket?.copy(); 150 | 151 | packet?.values?.let{ 152 | 153 | // Log.d("runPeriodicTask: ", "${Arrays.toString(it)}") 154 | var output = if(it.size>1){ 155 | sqrt((it.fold(0.0f) { acc, fl -> acc + fl * fl }).toDouble()).toFloat() 156 | }else if(it.size==1){ 157 | it.first() 158 | }else { 159 | 0.0f 160 | } 161 | 162 | output = Math.round(output * 100.0f).toFloat() / 100.00f 163 | 164 | _mSensorModulus.emit(output) 165 | 166 | 167 | } 168 | 169 | 170 | // Log.d("MpChartViewManager ", "runPeriodicTask : $sensorType") 171 | /* _mSensorPacketFlow.emit( 172 | ModelChartUiUpdate( 173 | // mModelLineChart 174 | sensorType, 175 | items.size, items 176 | ) 177 | )*/ 178 | 179 | delay(400) 180 | } 181 | 182 | } 183 | 184 | } 185 | 186 | 187 | } 188 | -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/sensor/SensorViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.sensor 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | 6 | /** 7 | * Created by Niraj on 26-10-2022. 8 | */ 9 | @Suppress("UNCHECKED_CAST") 10 | class SensorViewModelFactory(private val sensorType: Int) : ViewModelProvider.Factory { 11 | 12 | override fun create(modelClass: Class): T { 13 | return SensorViewModel(sensorType) as T 14 | } 15 | 16 | /* @Suppress("UNCHECKED_CAST") 17 | override fun create(modelClass: Class): T { 18 | return SensorViewModel(sensorType) as T 19 | }*/ 20 | 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/sensor/sections/SensorCurrentValue.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.sensor.sections 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.border 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.foundation.shape.CircleShape 7 | import androidx.compose.material3.MaterialTheme 8 | import androidx.compose.material3.Text 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.graphics.Brush 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.text.buildAnnotatedString 15 | import androidx.compose.ui.text.style.TextAlign 16 | import androidx.compose.ui.text.withStyle 17 | import com.google.accompanist.pager.ExperimentalPagerApi 18 | import io.sensify.sensor.domains.sensors.SensorsConstants 19 | import io.sensify.sensor.ui.resource.effects.drawColoredShadow 20 | import io.sensify.sensor.ui.resource.values.JlResDimens 21 | import io.sensify.sensor.ui.resource.values.JlResTxtStyles 22 | 23 | /** 24 | * Created by Niraj on 27-09-2022. 25 | */ 26 | @OptIn(ExperimentalPagerApi::class) 27 | //@Preview(showBackground = true, backgroundColor = 0xFF041B11) 28 | @Composable 29 | fun SensorDetailCurrentValue(sensorType: Int = -1, value: Float= 0.0f) { 30 | var hasUnit = SensorsConstants.hasUnit(sensorType) 31 | 32 | 33 | Row( 34 | modifier = Modifier 35 | .fillMaxWidth() 36 | .padding(all = JlResDimens.dp6), 37 | verticalAlignment = Alignment.Top, 38 | 39 | ) { 40 | Box( 41 | modifier = Modifier 42 | .size(JlResDimens.dp20) 43 | .background(Color(0xFFFFCA10), shape = CircleShape) 44 | .border( 45 | width = JlResDimens.dp1, brush = Brush.verticalGradient( 46 | listOf( 47 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f), 48 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f), 49 | ) 50 | ), shape = CircleShape 51 | ) 52 | .drawColoredShadow( 53 | Color(0xFFFFCA10), offsetY = JlResDimens.dp12, 54 | shadowRadius = JlResDimens.dp8, borderRadius = JlResDimens.dp8, alpha = 0.5f 55 | ), 56 | contentAlignment = Alignment.Center 57 | 58 | ) { 59 | Box( 60 | modifier = Modifier 61 | .size(JlResDimens.dp12) 62 | .border( 63 | width = JlResDimens.dp2, 64 | color = MaterialTheme.colorScheme.onSurface, 65 | shape = CircleShape 66 | ) 67 | ) 68 | 69 | } 70 | 71 | Spacer(modifier = Modifier.width(JlResDimens.dp18)) 72 | 73 | Column( 74 | modifier = Modifier 75 | .fillMaxWidth() 76 | .padding(start = JlResDimens.dp12, end = JlResDimens.dp28) 77 | ) { 78 | 79 | Row( modifier = Modifier 80 | .fillMaxWidth() 81 | ,verticalAlignment = Alignment.CenterVertically,) { 82 | 83 | 84 | if(hasUnit){ 85 | Text( 86 | text = buildAnnotatedString { 87 | append("Current Value in ") 88 | 89 | SensorsConstants.getUnit(this, sensorType) 90 | }, 91 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), 92 | textAlign = TextAlign.Center, 93 | style = JlResTxtStyles.h4, 94 | ) 95 | }else{ 96 | Text( 97 | text = "Current Value ", 98 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), 99 | textAlign = TextAlign.Center, 100 | style = JlResTxtStyles.h4, 101 | ) 102 | } 103 | 104 | 105 | } 106 | 107 | Text( 108 | text = "${value}", 109 | fontSize = JlResDimens.sp48, 110 | color = MaterialTheme.colorScheme.onSurface, 111 | ) 112 | } 113 | 114 | Spacer(modifier = Modifier.width(JlResDimens.dp18)) 115 | 116 | 117 | } 118 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/pages/sensor/sections/SensorDetail.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.pages.sensor.sections 2 | 3 | import android.util.Log 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.border 6 | import androidx.compose.foundation.clickable 7 | import androidx.compose.foundation.layout.* 8 | import androidx.compose.foundation.shape.RoundedCornerShape 9 | import androidx.compose.material.Text 10 | import androidx.compose.material.icons.Icons 11 | import androidx.compose.material.icons.rounded.Inbox 12 | import androidx.compose.material.icons.rounded.Info 13 | import androidx.compose.material3.Icon 14 | import androidx.compose.material3.MaterialTheme 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.ui.Alignment 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.draw.clip 19 | import androidx.compose.ui.geometry.Offset 20 | import androidx.compose.ui.graphics.Brush 21 | import androidx.compose.ui.text.style.TextAlign 22 | import com.google.accompanist.pager.ExperimentalPagerApi 23 | import io.sensify.sensor.domains.sensors.provider.SensorsProvider 24 | import io.sensify.sensor.ui.resource.values.JlResDimens 25 | import io.sensify.sensor.ui.resource.values.JlResTxtStyles 26 | 27 | /** 28 | * Created by Niraj on 27-09-2022. 29 | */ 30 | @OptIn(ExperimentalPagerApi::class) 31 | //@Preview(showBackground = true, backgroundColor = 0xFF041B11) 32 | @Composable 33 | fun SensorDetail( 34 | keyValues: Map = mutableMapOf( 35 | Pair("Name", "MN26005 ALS"), 36 | Pair("Max Range", "655365 lx"), 37 | Pair("Version", "1") 38 | ) 39 | /* keyValues: List> = listOf( 40 | Pair("Name", "MN26005 ALS"), 41 | Pair("Max Range", "655365 lx"), 42 | Pair("Version", "1") 43 | )*/ 44 | ) { 45 | 46 | Log.d("SensorDetail", "1: ${keyValues}") 47 | 48 | Box( 49 | modifier = Modifier 50 | .fillMaxWidth() 51 | .clip(RoundedCornerShape(JlResDimens.dp18)) 52 | .background( 53 | brush = Brush.radialGradient( 54 | 55 | listOf( 56 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f), 57 | // MaterialTheme.colorScheme.primary, 58 | // JLThemeBase.colorPrimary30, 59 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.03f), 60 | // Color.Transparent, 61 | ), 62 | center = Offset(30f, -30f), 63 | 64 | radius = 200.0f 65 | 66 | 67 | // start = Offset(0f, 0f), 68 | // end = Offset(0f, Float.POSITIVE_INFINITY) 69 | ) 70 | ) 71 | .border( 72 | brush = Brush.verticalGradient( 73 | listOf( 74 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.0f), 75 | MaterialTheme.colorScheme.onSurface.copy(alpha = 0.15f), 76 | // Color(0x00FFFFFF), 77 | ), 78 | startY = 0f, 79 | endY = Float.POSITIVE_INFINITY, 80 | 81 | ), 82 | 83 | width = JlResDimens.dp1, 84 | shape = RoundedCornerShape(JlResDimens.dp18) 85 | ) 86 | .padding(all = JlResDimens.dp24) 87 | ) { 88 | 89 | Column() { 90 | Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { 91 | Icon(Icons.Rounded.Info, "info", 92 | modifier = Modifier.size(JlResDimens.dp12), 93 | tint = MaterialTheme.colorScheme.onSurface) 94 | Spacer(modifier = Modifier.width(JlResDimens.dp20)) 95 | Text( 96 | text = "Sensor Details", 97 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.75f), 98 | textAlign = TextAlign.Center, 99 | style = JlResTxtStyles.h4, 100 | ) 101 | } 102 | Spacer(modifier = Modifier.height(JlResDimens.dp20)) 103 | 104 | for (item in keyValues){ 105 | Row( 106 | Modifier 107 | .fillMaxWidth() 108 | .padding(start = JlResDimens.dp32)) { 109 | Text( 110 | modifier = Modifier.weight(1f), 111 | text = "${item.key}:", 112 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.34f), 113 | textAlign = TextAlign.Start, 114 | style = JlResTxtStyles.h4, 115 | ) 116 | Text( 117 | modifier = Modifier.weight(1f), 118 | text = "${item.value}", 119 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.75f), 120 | textAlign = TextAlign.Start, 121 | style = JlResTxtStyles.h4, 122 | ) 123 | } 124 | Spacer(modifier = Modifier.height(JlResDimens.dp12)) 125 | 126 | } 127 | 128 | 129 | } 130 | 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/effects/ColoredShadow.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.effects 2 | 3 | import androidx.compose.ui.Modifier 4 | import androidx.compose.ui.draw.drawBehind 5 | import androidx.compose.ui.graphics.Color 6 | import androidx.compose.ui.graphics.Paint 7 | import androidx.compose.ui.graphics.drawscope.drawIntoCanvas 8 | import androidx.compose.ui.graphics.toArgb 9 | import androidx.compose.ui.unit.Dp 10 | import androidx.compose.ui.unit.dp 11 | import androidx.core.graphics.ColorUtils 12 | 13 | /** 14 | * Created by Niraj on 27-09-2022. 15 | */ 16 | fun Modifier.drawColoredShadow( 17 | color: Color, 18 | alpha: Float = 0.2f, 19 | borderRadius: Dp = 0.dp, 20 | shadowRadius: Dp = 20.dp, 21 | offsetY: Dp = 0.dp, 22 | offsetX: Dp = 0.dp 23 | ) = this.drawBehind { 24 | // Color. 25 | val transparentColor = color.copy(alpha = 0.0f).toArgb()//android.graphics.Color.toArgb(color.copy(alpha = 0.0f).value.toLong()) 26 | val shadowColor = color.copy(alpha = alpha).toArgb()//android.graphics.Color.toArgb(color.copy(alpha = alpha).value.toLong()) 27 | this.drawIntoCanvas { 28 | val paint = Paint() 29 | val frameworkPaint = paint.asFrameworkPaint() 30 | frameworkPaint.color = transparentColor 31 | frameworkPaint.setShadowLayer( 32 | shadowRadius.toPx(), 33 | offsetX.toPx(), 34 | offsetY.toPx(), 35 | shadowColor 36 | ) 37 | it.drawRoundRect( 38 | 0f, 39 | 0f, 40 | this.size.width, 41 | this.size.height, 42 | borderRadius.toPx(), 43 | borderRadius.toPx(), 44 | paint 45 | ) 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/sensors/SensorsIcons.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.sensors 2 | 3 | import android.hardware.Sensor 4 | 5 | import android.util.SparseIntArray 6 | import io.sensify.sensor.R 7 | 8 | 9 | /** 10 | * Created by Niraj on 06-10-2022. 11 | */ 12 | object SensorsIcons { 13 | 14 | 15 | val MAP_TYPE_TO_ICON: SparseIntArray = object : SparseIntArray() { 16 | init { 17 | put(Sensor.TYPE_ACCELEROMETER, R.drawable.ic_sensor_linear_acceleration) //1 18 | put(Sensor.TYPE_MAGNETIC_FIELD, R.drawable.ic_sensor_magnet) //2 19 | put(Sensor.TYPE_ORIENTATION, R.drawable.ic_sensor_compass) //3 20 | put(Sensor.TYPE_GYROSCOPE, R.drawable.ic_sensor_gyroscope) //4 21 | put(Sensor.TYPE_LIGHT, R.drawable.ic_sensor_brightness) //5 22 | put(Sensor.TYPE_PRESSURE, R.drawable.ic_sensor_pressure) //6 23 | put(Sensor.TYPE_TEMPERATURE, R.drawable.ic_sensor_temprature) //7 24 | put(Sensor.TYPE_PROXIMITY, R.drawable.ic_sensor_proximity) //8 25 | put(Sensor.TYPE_GRAVITY, R.drawable.ic_sensor_gravity) //9 26 | put(Sensor.TYPE_LINEAR_ACCELERATION, R.drawable.ic_sensor_linear_acceleration) //10 27 | put(Sensor.TYPE_ROTATION_VECTOR, R.drawable.ic_sensor_rotation) //11 28 | put(Sensor.TYPE_RELATIVE_HUMIDITY, R.drawable.ic_sensor_humidity) //12 29 | put(Sensor.TYPE_AMBIENT_TEMPERATURE, R.drawable.ic_sensor_temprature) //13 30 | put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, R.drawable.ic_sensor_magnet) //14 31 | put(Sensor.TYPE_GAME_ROTATION_VECTOR, R.drawable.ic_sensor_rotation) //15 32 | put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, R.drawable.ic_sensor_gyroscope) //16 33 | put(Sensor.TYPE_SIGNIFICANT_MOTION, R.drawable.ic_sensor_unknown) //17 34 | put(Sensor.TYPE_STEP_DETECTOR, R.drawable.ic_sensor_unknown) //18 35 | put(Sensor.TYPE_STEP_COUNTER, R.drawable.ic_sensor_unknown) //19 36 | put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, R.drawable.ic_sensor_rotation) //20 37 | put(Sensor.TYPE_HEART_RATE, R.drawable.ic_sensor_unknown) //21 38 | // put(Sensor.TYPE_STATIONARY_DETECT, R.color.md_amber_500);//29 39 | // put(Sensor.TYPE_MOTION_DETECT, R.color.md_amber_500);//30 40 | // put(Sensor.TYPE_HEART_BEAT, R.color.md_amber_500);//31 41 | } 42 | } 43 | 44 | /*ICON_,R.drawable.ic_sensor_gyroscope 45 | ICON_,R.drawable.ic_sensor_gravity 46 | ICON_,R.drawable.ic_sensor_brightness 47 | ICON_,R.drawable.ic_sensor_magnet 48 | ICON_,R.drawable.ic_sensor_temprature 49 | ICON_,R.drawable.ic_sensor_proximity 50 | ICON_,R.drawable.ic_sensor_pressure 51 | ICON_,R.drawable.ic_sensor_humidity 52 | ICON_,R.drawable.ic_sensor_rotation 53 | ICON_,R.drawable.ic_sensor_linear_acceleration 54 | ICON_,R.drawable.ic_sensor_compass*/ 55 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/themes/JLThemeBase.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.themes 2 | 3 | import io.sensify.sensor.ui.resource.values.JlResColors 4 | 5 | /** 6 | * Created by Niraj on 09-08-2022. 7 | */ 8 | object JLThemeBase { 9 | 10 | val colorPrimary = JlResColors.SensifyGreen 11 | val colorPrimaryDark = JlResColors.SensifyGreen40 12 | val colorPrimary10 = JlResColors.SensifyGreen10 13 | val colorPrimary20 = JlResColors.SensifyGreen20 14 | val colorPrimary30 = JlResColors.SensifyGreen30 15 | val colorPrimary40 = JlResColors.SensifyGreen40 16 | val colorPrimary90 = JlResColors.SensifyGreen90 17 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/values/JlResColors.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.values 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | /** 6 | * Created by Niraj on 05-08-2022. 7 | */ 8 | object JlResColors { 9 | 10 | 11 | 12 | val gray800 = Color(0xCC333333) 13 | val gray900 = Color(0xff333333) 14 | val rust300 = Color(0xFFE1AFAF) 15 | val rust600 = Color(0xFF886363) 16 | val taupe100 = Color(0xfff0eae2) 17 | val taupe800 = Color(0xff655454) 18 | val white150 = Color(0x26FFFFFF) 19 | val white800 = Color(0xCCFFFFFF) 20 | val white850 = Color(0xD9FFFFFF) 21 | val white = Color(0xFFFFFFFF) 22 | val black = Color(0xFF000000) 23 | val yellow = Color(0xFFFFCF44) 24 | val yellow500 = Color(0xFFF5BD1C) 25 | val purple = Color(0XFF4A22A8) 26 | val darkBlue = Color(0XFF0e0f6d) 27 | val blue = Color(0XFF1753ce) 28 | val skyBlue = Color(0XFF6691cb) 29 | val sand = Color(0XFFd7975a) 30 | val snow = Color(0xFF7C706A) 31 | val darkGrey = Color(0xFF444444) 32 | 33 | val backgroundColor = Color(0xFF05150E) 34 | val backgroundColorVariant = Color(0xFF081B12) 35 | val headerCardColor = Color(0x1ADA66B2) 36 | 37 | val backgroundTop = Color(0xFF19211D) 38 | val backgroundBottom = Color(0xFF0C100E) 39 | 40 | 41 | val lightBackgroundColor = Color(0xFFfcfcfc) 42 | val lightSurfaceColor = Color(0xFFffffff) 43 | 44 | val darkBackgroundColor = Color(0xFF0C100E) 45 | val darkSurfaceColor = Color(0xFF1a1a1b) 46 | 47 | val CHART_3 = Color(0xCCCE8517) 48 | 49 | val SensifyGreen = Color(0xFF1ADA66) 50 | val SensifyGreen10 = Color(0xFF00230e) 51 | val SensifyGreen20 = Color(0xFF005120) 52 | val SensifyGreen30 = Color(0xFF00752F) 53 | val SensifyGreen40 = Color(0xFF14ae51) 54 | val SensifyGreen50 = Color(0xFF1ADA66) 55 | val SensifyGreen60 = Color(0xFF13ED6A) 56 | val SensifyGreen70 = Color(0xFF45FF90) 57 | val SensifyGreen80 = Color(0xFF7cffb1) 58 | val SensifyGreen90 = Color(0xFFa2ffc7) 59 | val SensifyGreen95 = Color(0xFFc7ffdd) 60 | val SensifyGreen99 = Color(0xFFecfff3) 61 | 62 | 63 | val SensifyYellow = Color(0xFFFFCA10) 64 | val SensifyYellow10 = Color(0xFF4c3c04) 65 | val SensifyYellow20 = Color(0xFF7f6508) 66 | val SensifyYellow30 = Color(0xFFb28d0b) 67 | val SensifyYellow40 = Color(0xFFe5b50e) 68 | val SensifyYellow50 = Color(0xFFffca10) 69 | val SensifyYellow60 = Color(0xFFffcf27) 70 | val SensifyYellow70 = Color(0xFFffd957) 71 | val SensifyYellow80 = Color(0xFFffe487) 72 | val SensifyYellow90 = Color(0xFFffefb7) 73 | val SensifyYellow95 = Color(0xFFfff4cf) 74 | val SensifyYellow99 = Color(0xFFfff9e7) 75 | 76 | 77 | 78 | val NoteYellow = Color(0xFFFFF389) 79 | val NotePink = Color(0xFFE2648C) 80 | val NoteRed = Color(0xFFE76A6A) 81 | val NoteBlue = Color(0xFF81DBDF) 82 | val NoteOrange = Color(0xFFE68049) 83 | val NoteMint = Color(0xFF3ECC89) 84 | val NoteViolet = Color(0xFFF0A2FF) 85 | val NoteIndigo = Color(0xFFA7ABE9) 86 | val NoteGreen = Color(0xFF8DDF69) 87 | val NoteWhite = Color(0xFFFFE2EB) 88 | val NoteBrown = Color(0xFFBD7857) 89 | val NoteGray = Color(0xFFA5969B) 90 | 91 | val GoalGreen = Color(0xFF86C768) 92 | val GoalYellow = Color(0xFFD8D76A) 93 | val GoalCarrot = Color(0xFFD89D53) 94 | val GoalRed = Color(0xFFD57171) 95 | 96 | 97 | 98 | 99 | val noteColors = 100 | listOf( 101 | NoteYellow, 102 | NoteGreen, 103 | NoteMint, 104 | NoteBlue, 105 | NoteIndigo, 106 | NoteViolet, 107 | NoteOrange, 108 | NoteRed, 109 | NotePink, 110 | NoteWhite, 111 | NoteGray, 112 | NoteBrown 113 | ) 114 | 115 | val goalColors = 116 | listOf( 117 | GoalGreen, 118 | GoalYellow, 119 | GoalCarrot, 120 | GoalRed 121 | ) 122 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/values/JlResDimens.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.values 2 | 3 | import androidx.compose.ui.unit.dp 4 | import androidx.compose.ui.unit.sp 5 | 6 | /** 7 | * Created by Niraj on 05-08-2022. 8 | */ 9 | object JlResDimens { 10 | val sp0_0 = 0.0.sp; 11 | val sp0_4 = 0.4.sp; 12 | val sp0_6 = 0.6.sp; 13 | val sp0_8 = 0.8.sp; 14 | val sp10 = 10.sp; 15 | val sp12 = 12.sp; 16 | val sp13 = 13.sp; 17 | val sp14 = 14.sp; 18 | val sp15 = 15.sp; 19 | val sp16 = 16.sp; 20 | val sp18 = 18.sp; 21 | val sp20 = 20.sp; 22 | val sp22 = 22.sp; 23 | val sp24 = 24.sp; 24 | val sp28 = 28.sp; 25 | val sp32 = 32.sp; 26 | val sp36 = 36.sp; 27 | val sp42 = 42.sp; 28 | val sp48 = 48.sp; 29 | 30 | val dp0 = 0.dp; 31 | val dp1 = 1.dp; 32 | val dp2 = 2.dp; 33 | val dp3 = 3.dp; 34 | val dp4 = 4.dp; 35 | val dp5 = 5.dp; 36 | val dp6 = 6.dp; 37 | val dp8 = 8.dp; 38 | val dp10 = 10.dp; 39 | val dp12 = 12.dp; 40 | val dp14 = 14.dp; 41 | val dp15 = 15.dp; 42 | val dp16 = 16.dp; 43 | val dp18 = 18.dp; 44 | val dp20 = 20.dp; 45 | val dp24 = 24.dp; 46 | val dp28 = 28.dp; 47 | val dp32 = 32.dp; 48 | val dp36 = 36.dp; 49 | val dp40 = 40.dp; 50 | val dp44 = 44.dp; 51 | val dp48 = 48.dp; 52 | val dp50 = 50.dp; 53 | val dp56 = 56.dp; 54 | val dp64 = 64.dp; 55 | val dp72 = 72.dp; 56 | val dp96 = 96.dp; 57 | val dp120 = 120.dp; 58 | val dp108 = 108.dp; 59 | val dp112 = 112.dp; 60 | val dp132 = 132.dp; 61 | val dp144 = 144.dp; 62 | val dp168 = 168.dp; 63 | val dp196 = 196.dp; 64 | val dp350 = 350.dp; 65 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/values/JlResShapes.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.values 2 | 3 | import androidx.compose.foundation.layout.Spacer 4 | import androidx.compose.foundation.layout.height 5 | import androidx.compose.foundation.layout.width 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material.Shapes 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.unit.dp 10 | 11 | /** 12 | * Created by Niraj on 05-08-2022. 13 | */ 14 | object JlResShapes { 15 | 16 | object Common{ 17 | 18 | val shapes = Shapes( 19 | small = RoundedCornerShape(8.dp), 20 | medium = RoundedCornerShape(12.dp), 21 | large = RoundedCornerShape(24.dp) 22 | ) 23 | } 24 | 25 | 26 | 27 | object Space{ 28 | val H4 = Modifier.height(JlResDimens.dp4) 29 | val H18 = Modifier.height(JlResDimens.dp18) 30 | val H20 = Modifier.height(JlResDimens.dp20) 31 | val H24 = Modifier.height(JlResDimens.dp24) 32 | val H28 = Modifier.height(JlResDimens.dp28) 33 | val H32 = Modifier.height(JlResDimens.dp32) 34 | val H48 = Modifier.height(JlResDimens.dp48) 35 | val H56 = Modifier.height(JlResDimens.dp56) 36 | val H64 = Modifier.height(JlResDimens.dp64) 37 | 38 | 39 | 40 | val W18 = Modifier.width(JlResDimens.dp18) 41 | val W20 = Modifier.width(JlResDimens.dp20) 42 | val W24 = Modifier.width(JlResDimens.dp24) 43 | val W28 = Modifier.width(JlResDimens.dp28) 44 | val W32 = Modifier.width(JlResDimens.dp32) 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/values/JlResStyles.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.values 2 | 3 | /** 4 | * Created by Niraj on 05-08-2022. 5 | */ 6 | object JlResStyles { 7 | 8 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/resource/values/JlResTxtStyles.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.resource.values 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.Font 6 | import androidx.compose.ui.text.font.FontFamily 7 | import androidx.compose.ui.text.font.FontWeight 8 | import androidx.compose.ui.unit.sp 9 | import io.sensify.sensor.R 10 | 11 | /** 12 | * Created by Niraj on 05-08-2022. 13 | */ 14 | object JlResTxtStyles { 15 | 16 | private val fontsJost = FontFamily( 17 | Font(R.font.jost_light, FontWeight.Light), 18 | Font(R.font.jost_regular), 19 | Font(R.font.jost_medium, FontWeight.Medium), 20 | Font(R.font.jost_bold, FontWeight.Bold) 21 | ) 22 | 23 | val h1 = TextStyle( 24 | fontFamily = fontsJost, 25 | fontWeight = FontWeight.Medium, 26 | fontSize = JlResDimens.sp42 27 | ) 28 | 29 | val h2 = TextStyle( 30 | fontFamily = fontsJost, 31 | fontWeight = FontWeight.Normal, 32 | fontSize = JlResDimens.sp32 33 | ) 34 | 35 | val h3 = TextStyle( 36 | fontFamily = fontsJost, 37 | fontWeight = FontWeight.Normal, 38 | fontSize = JlResDimens.sp28 39 | ) 40 | 41 | val h4 = TextStyle( 42 | fontFamily = fontsJost, 43 | fontWeight = FontWeight.Normal, 44 | fontSize = JlResDimens.sp20 45 | ) 46 | 47 | val h5 = TextStyle( 48 | fontFamily = fontsJost, 49 | fontWeight = FontWeight.Normal, 50 | fontSize = JlResDimens.sp18 51 | ) 52 | 53 | val h6 = TextStyle( 54 | fontFamily = fontsJost, 55 | fontWeight = FontWeight.Normal, 56 | fontSize = JlResDimens.sp14 57 | ) 58 | 59 | val p1 = TextStyle( 60 | fontFamily = fontsJost, 61 | fontWeight = FontWeight.Normal, 62 | fontSize = JlResDimens.sp20 63 | ) 64 | 65 | val p2 = TextStyle( 66 | fontFamily = fontsJost, 67 | fontWeight = FontWeight.Normal, 68 | fontSize = JlResDimens.sp16 69 | ) 70 | 71 | val p3 = TextStyle( 72 | fontFamily = fontsJost, 73 | fontWeight = FontWeight.Normal, 74 | fontSize = JlResDimens.sp12 75 | ) 76 | 77 | 78 | val button = TextStyle( 79 | fontFamily = fontsJost, 80 | fontWeight = FontWeight.Medium 81 | ) 82 | 83 | val caption = TextStyle( 84 | fontFamily = fontsJost, 85 | fontWeight = FontWeight.Normal, 86 | fontSize = JlResDimens.sp12 87 | ) 88 | 89 | 90 | 91 | } -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/sensor/ui/utils/MpAndridChartFormatter.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor.ui.utils 2 | 3 | import com.github.mikephil.charting.components.AxisBase 4 | import com.github.mikephil.charting.formatter.ValueFormatter 5 | 6 | /** 7 | * Created by Manish Kumar on 26/07/22. 8 | */ 9 | 10 | class MyXAxisFormatter : ValueFormatter() { 11 | private val days = arrayOf("Q1", "Q2", "Q3", "Q4") 12 | override fun getAxisLabel(value: Float, axis: AxisBase?): String { 13 | return days.getOrNull(value.toInt()) ?: value.toString() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/io/sensify/util/QueueFixedLength.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.util 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Created by Niraj on 31-08-2022. 7 | */ 8 | class QueueFixedLength(pCount: Int, pEs: Array?) { 9 | 10 | private val queue: Queue = LinkedList() 11 | private var mCount = 0 12 | 13 | 14 | init { 15 | 16 | //LOGV(TAG, "QueueFixedLength: "+ pCount); 17 | if (pCount <= 0) { 18 | mCount = 0 19 | // return 20 | } else { 21 | mCount = pCount 22 | if (pEs != null) { 23 | if (pCount <= pEs.size) { 24 | val index = pEs.size - pCount 25 | for (i in index until pEs.size) { 26 | queue.add(pEs[i]) 27 | } 28 | // return 29 | } else { 30 | if (pCount > pEs.size) { 31 | for (i in pEs.indices) { 32 | queue.add(pEs[i]) 33 | } 34 | } 35 | } 36 | } 37 | 38 | } 39 | 40 | 41 | } 42 | 43 | fun add(pE: E): Boolean { 44 | if (queue.size == mCount) queue.poll() 45 | return queue.offer(pE) 46 | } 47 | 48 | fun resize(newSize: Int): Boolean { 49 | if (newSize < 0) { 50 | return false 51 | } 52 | val gaps = mCount - newSize 53 | if (gaps > 0) { 54 | for (i in 0 until gaps) { 55 | queue.poll() 56 | } 57 | } 58 | mCount = newSize 59 | return true 60 | } 61 | 62 | fun clear() { 63 | queue.clear() 64 | mCount = 0 65 | } 66 | 67 | fun clear(pE: E) { 68 | queue.clear() 69 | if (mCount > 0) { 70 | for (i in 0 until mCount) { 71 | queue.offer(pE) 72 | } 73 | } 74 | } 75 | 76 | fun size(): Int { 77 | return queue.size 78 | } 79 | 80 | fun sizeMax(): Int { 81 | return mCount 82 | } 83 | 84 | fun toArray(data: Array, aFloat: E): Array? { 85 | val length = data.size 86 | //Iterator iterator = getValues(); 87 | val size = size() 88 | if (length >= size) { 89 | for (i in 0 until length - size) { 90 | data[i] = aFloat 91 | } 92 | var index = length - size 93 | val iterator: Iterator = getValues() 94 | while (iterator.hasNext()) { 95 | if (index < length) { 96 | data[index] = iterator.next() 97 | index++ 98 | } 99 | } 100 | } else { 101 | var index = size - length 102 | val iterator: Iterator = getValues() 103 | for (i in 0 until index) { 104 | iterator.next() 105 | } 106 | while (iterator.hasNext()) { 107 | if (index < length) { 108 | data[index] = iterator.next() 109 | index++ 110 | } 111 | } 112 | } 113 | return data 114 | } 115 | 116 | fun getValues(): Iterator { 117 | val iterator: Iterator = queue.iterator() 118 | 119 | return object : QueueIterator() { 120 | override fun hasNext(): Boolean { 121 | return iterator.hasNext() 122 | } 123 | 124 | override fun next(): E { 125 | return iterator.next() 126 | } 127 | } 128 | } 129 | 130 | fun getFirst(): E? { 131 | return queue.peek() 132 | } 133 | 134 | private abstract class QueueIterator

: 135 | MutableIterator

{ 136 | override fun remove() { 137 | throw UnsupportedOperationException() 138 | } 139 | } 140 | 141 | 142 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_github.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_gyroscope.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_night_clear.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_keyboard_arrow_left_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_keyboard_arrow_right_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_settings_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_brightness.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_compass.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_gravity.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_gyroscope.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_humidity.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_linear_acceleration.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_magnet.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_pressure.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_proximity.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_rotation.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_temprature.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sensor_unknown.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sunny.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/image_1.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/image_2.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/image_3.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/image_4.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/image_5.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/image_6.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_about_gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/pic_about_gradient.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_launcher_eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/pic_launcher_eye.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/pic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_person1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/pic_person1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_person2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/pic_person2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/pic_sensify_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/drawable/pic_sensify_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_screen_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/font/jost_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/jost_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/jost_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/jost_light.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/jost_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/jost_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/jost_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/jost_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/lato_black.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/lato_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/lato_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/lato_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/roboto_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/font/roboto_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | #05150E 11 | #FFFFFF 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #283B28 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Sensify 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/play/res/values/ccd.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/test/java/io/sensify/sensor/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package io.sensify.sensor 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | compose_version = '1.2.0' 4 | } 5 | 6 | dependencies { 7 | 8 | classpath 'com.google.gms:google-services:4.3.14' 9 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' 10 | } 11 | }// Top-level build file where you can add configuration options common to all sub-projects/modules. 12 | 13 | 14 | plugins { 15 | id 'com.android.application' version '7.3.1' apply false 16 | id 'com.android.library' version '7.3.1' apply false 17 | id 'org.jetbrains.kotlin.android' version '1.7.0' apply false 18 | } 19 | 20 | task clean(type: Delete) { 21 | delete rootProject.buildDir 22 | } -------------------------------------------------------------------------------- /foss.md: -------------------------------------------------------------------------------- 1 | 2 | # github build CD 3 | - https://proandroiddev.com/how-to-securely-build-and-sign-your-android-app-with-github-actions-ad5323452ce 4 | - https://www.kodeco.com/19407406-continuous-delivery-for-android-using-github-actions 5 | 6 | # release on playstore 7 | - https://www.freecodecamp.org/news/use-github-actions-to-automate-android-development/ 8 | 9 | # action 10 | - https://github.com/marketplace/actions/sign-android-release 11 | 12 | # CI 13 | - https://www.kodeco.com/10562143-continuous-integration-for-android?__hstc=149040233.9779d8d4ec7ab1f21c47af3c46954b7c.1677797844545.1677797844545.1677797844545.1&__hssc=149040233.1.1677797844545&__hsfp=1483251232 14 | 15 | # encode -decode 16 | - https://stackoverflow.com/questions/42592518/encode-decode-exe-into-base64 17 | 18 | # gradle not allowed 19 | - git update-index --chmod=+x gradlew 20 | - https://stackoverflow.com/questions/17668265/gradlew-permission-denied 21 | 22 | # google services json 23 | - https://stackoverflow.com/questions/70568653/google-services-json-file-for-github-actions 24 | 25 | # signing 26 | - https://github.com/r0adkll/sign-android-release/issues/34 27 | 28 | # openssl 29 | - https://adamtheautomator.com/openssl-windows-10/ 30 | 31 | # gradle 32 | - http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Product-Flavor-Configuration -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 04 21:31:09 IST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /images/sensify-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/images/sensify-all.png -------------------------------------------------------------------------------- /images/sensify-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JunkieLabs/sensify-android/d8b875f92100b63022ff7fa4360de5ba3d75dfdf/images/sensify-logo.png -------------------------------------------------------------------------------- /ref.md: -------------------------------------------------------------------------------- 1 | # h1 data 2 | 3 | # hilt 4 | 5 | - https://github.com/skydoves/DisneyCompose 6 | 7 | - https://github.com/skydoves/MovieCompose 8 | 9 | 10 | # playground 11 | 12 | - https://github.com/Foso/Jetpack-Compose-Playground 13 | 14 | 15 | # guide 16 | 17 | - https://developer.android.com/jetpack/compose 18 | - https://github.com/jetpack-compose/jetpack-compose-awesome 19 | 20 | 21 | # simple example 22 | 23 | - https://github.com/adibfara/ComposeClock 24 | 25 | - https://github.com/android/compose-samples/tree/main/Crane 26 | 27 | 28 | # moderate 29 | 30 | - https://github.com/zsoltk/compose-pokedex 31 | 32 | 33 | # samples 34 | 35 | - https://github.com/android/compose-samples 36 | 37 | 38 | # circle Image 39 | 40 | - https://howiezuo.medium.com/building-a-design-system-implementation-using-jetpack-compose-part2-d965880292b0 41 | 42 | 43 | # compose side effect 44 | - https://developer.android.com/jetpack/compose/side-effects 45 | 46 | 47 | # compose 48 | - https://github.com/Farhandroid/AndroidCleanArchitecture 49 | 50 | - https://keheira.com/nested-navigation-in-jetpack-compose/ 51 | 52 | 53 | # styling 54 | 55 | - https://github.com/enginebai/MovieHunt/tree/v2/app/src/main/java/com/enginebai/moviehunt 56 | 57 | 58 | - https://foso.github.io/Jetpack-Compose-Playground/cookbook/how_to_create_custom_shape/ 59 | - https://juliensalvi.medium.com/custom-shape-with-jetpack-compose-1cb48a991d42 60 | 61 | 62 | # permission 63 | 64 | - https://towardsdev.com/jetpack-compose-runtime-permissions-erselan-khan-75f60800b28f 65 | 66 | 67 | - https://developer.android.com/guide/topics/sensors/sensors_overview#sensors-rate-limiting 68 | 69 | 70 | - https://developer.android.com/guide/topics/sensors/sensors_motion#sensors-motion-stepcounter 71 | 72 | 73 | # flow multi listener 74 | 75 | - https://developer.android.com/kotlin/flow/stateflow-and-sharedflow#sharein 76 | 77 | - https://medium.com/androiddevelopers/things-to-know-about-flows-sharein-and-statein-operators-20e6ccb2bc74 78 | 79 | 80 | - https://developer.android.com/kotlin/flow 81 | 82 | - https://proandroiddev.com/how-to-collect-flows-lifecycle-aware-in-jetpack-compose-babd53582d0b 83 | 84 | 85 | 86 | - https://compose.academy/blog/modular_navigation_with_jetpack_compose 87 | 88 | - https://stackoverflow.com/questions/69901608/how-to-disable-simultaneous-clicks-on-multiple-items-in-jetpack-compose-list-c 89 | 90 | 91 | - https://developer.android.com/jetpack/compose/compositionlocal 92 | 93 | 94 | ## view model and state 95 | 96 | 97 | - https://stackoverflow.com/questions/72638232/view-model-with-jetpack-compose-view 98 | 99 | 100 | - https://developer.android.com/jetpack/compose/state#viewmodels-source-of-truth 101 | 102 | 103 | 104 | 105 | # sensor data 106 | 107 | - https://proandroiddev.com/parallax-effect-with-sensormanager-using-jetpack-compose-a735a2f5811b 108 | 109 | - https://stackoverflow.com/questions/66834234/access-sensors-from-within-composables-in-jetpack-compose 110 | 111 | 112 | # Splash 113 | 114 | - https://github.com/mukeshsolanki/animated-splash-screen/blob/main/app/src/main/java/com/mukeshsolanki/animatedsplashscreen/SplashScreen.kt 115 | 116 | - https://github.com/ellisonchan/ComposeBird/blob/main/app/src/main/java/com/ellison/flappybird/util/SplashScreenController.kt 117 | 118 | - https://github.com/davidorellana98/splashscreen-compose/blob/main/app/src/main/java/com/davidorellana/splashscreenconjetpackcompose/SplashScreen.kt 119 | 120 | 121 | # chart 122 | 123 | - https://github.com/pratclot/oxygentracker/blob/7cb153ca429818917f432c6a0a6ba9987a68618e/app/src/main/java/com/pratclot/oxygentracker/fragments/FragmentChart.kt 124 | 125 | 126 | - https://github.com/PhilJay/MPAndroidChart/wiki/Dynamic-&-Realtime-Data 127 | 128 | 129 | - https://betterprogramming.pub/responsive-layout-grid-363aa1fb7a8b 130 | 131 | 132 | # Tabs 133 | 134 | - https://github.com/hrishikeshpanhalkar/TabLayout-Jetpack-Compose/blob/main/app/src/main/java/com/example/tablayoutwithanimation/MainActivity.kt 135 | 136 | 137 | # pager 138 | 139 | - https://google.github.io/accompanist/pager/ 140 | 141 | 142 | # linear interpolation 143 | 144 | - https://limnu.com/sketch-lerp-unlerp-remap/ 145 | 146 | 147 | # ViewModel 148 | 149 | - https://developer.android.com/codelabs/basic-android-kotlin-compose-viewmodel-and-state#5 150 | 151 | # rememberLazyListState 152 | 153 | - https://stackoverflow.com/questions/70376897/jetpack-compose-detect-when-lazycolumns-scroll-position-is-at-the-first-index 154 | - https://stackoverflow.com/questions/67252538/jetpack-compose-update-composable-when-list-changes 155 | 156 | # conditional modifier 157 | 158 | - https://stackoverflow.com/questions/67768746/chaining-modifier-based-on-certain-conditions-in-android-compose 159 | 160 | # Readme 161 | 162 | - https://github.com/Foxpace/SensorBox 163 | - https://github.com/Ibrahim-Mushtaha/Accelerometer-Sensor 164 | - https://github.com/AkshayAshokCode/AndroidSensors 165 | - https://github.com/jemshit/SensorLogger 166 | 167 | - https://github.com/Ibrahim-Mushtaha/Step_Counter_background_service 168 | 169 | 170 | 171 | - https://github.com/kylecorry31/Trail-Sense 172 | 173 | 174 | 175 | - https://github.com/MindorksOpenSource/Jetpack-Compose-Android-Examples 176 | 177 | 178 | 179 | 180 | - https://developer.android.com/jetpack/compose/mental-model 181 | 182 | 183 | # todo 184 | - timeperiod based data pickup 185 | - aggregate sensor detail 186 | - add units in sensor 187 | - add y axis 188 | 189 | 190 | # fix 191 | - package io.sensify.sensor.ui.pages.home.items.HomeSensorChartItem 192 | - E: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 193 | at java.util.ArrayList.get(ArrayList.java:437) 194 | at com.github.mikephil.charting.data.DataSet.getEntryForIndex(DataSet.java:294) 195 | at com.github.mikephil.charting.data.BaseDataSet.removeEntry(BaseDataSet.java:513) 196 | at io.sensify.sensor.domains.chart.mpchart.MpChartViewUpdater.updateDataSet(MpChartViewUpdater.kt:75) 197 | at io.sensify.sensor.domains.chart.mpchart.MpChartViewUpdater.update(MpChartViewUpdater.kt:32) 198 | at io.sensify.sensor.ui.pages.home.items.HomeSensorChartItemKt$HomeSensorChartItem$1$2.invoke(HomeSensorChartItem.kt:109) 199 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | 7 | } 8 | } 9 | dependencyResolutionManagement { 10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 11 | repositories { 12 | google() 13 | mavenCentral() 14 | maven { url 'https://jitpack.io' } 15 | } 16 | } 17 | rootProject.name = "SensifyAndroid" 18 | include ':app' 19 | --------------------------------------------------------------------------------