From 94f1e791ea3c18416df96d3d69595154d148c4da Mon Sep 17 00:00:00 2001 From: Paul Maanen Date: Wed, 9 Apr 2025 11:50:25 +0200 Subject: [PATCH 01/55] Fix typo --- .github/workflows/android_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android_build.yml b/.github/workflows/android_build.yml index e7e3e6d..d24a1ad 100644 --- a/.github/workflows/android_build.yml +++ b/.github/workflows/android_build.yml @@ -57,7 +57,7 @@ jobs: java-version: '17' # Change this to the required Java version for your Android project - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 7.4.2 From bc33b5a344d3db2713f1a2fd901cfa4846389ab2 Mon Sep 17 00:00:00 2001 From: Paul Maanen Date: Thu, 10 Apr 2025 11:45:08 +0200 Subject: [PATCH 02/55] Improve spelling and grammar in README --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8fbba55..77ce6fa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ --- # SENDA Android Application to stream out sensor data directly from the Phone -SENDA(Sensor Data Streamer) is an android application to stream real time sensor reading using LSL (Lab Streaming Layer). Along with the sensors it streams real time audio as well. This is a fork from the original author's Git [Ali Ayub Khan's SENDA](https://github.com/AliAyub007/SENDA), which contains additional features and support for Samsung phones. +SENDA (Sensor Data Streamer) is an android application to stream real-time sensor reading using LSL (Lab Streaming Layer). Along with the sensors it streams real-time audio as well. This is a fork from the original author's Git [Ali Ayub Khan's SENDA](https://github.com/AliAyub007/SENDA), which contains additional features and support for Samsung phones. ## Features The following sensors are included: @@ -35,10 +35,10 @@ For details about the data format and the orientation of the axes, see the Movel ## Getting Started -#### Installation -Download the [latest release](https://github.com/NeuropsyOL/SENDA/releases/latest) and install the apk on your smartphone or tablet running Android 11 or higher. +### Installation +Download the [latest release](https://github.com/NeuropsyOL/SENDA/releases/latest) and install the APK on your smartphone or tablet running Android 11 or higher. -#### Usage: +### Usage Upon launching the app, the user is presented with a main screen displaying a list of available device sensors. Each sensor can be individually selected using check buttons, allowing the user to choose which data to stream. The list of sensors can be refreshed with a swipe-down gesture. Some sensors—such as audio classification and recording, GPS, and Movella sensors—require special permissions to function properly. When necessary, a permission request dialog is shown when a sensor is selected. GPS requires access to precise location at all times in order to operate correctly. If a permission is repeatedly denied, Android may block further requests. In such cases, the app opens the system settings screen, allowing the user to grant the required permission manually. @@ -47,25 +47,25 @@ To start streaming, the user must press the **START LSL** button. To stop stream The LSL streams transmitted by SENDA can be recorded using any LSL-compatible application, such as the [LabRecorder](https://github.com/labstreaminglayer/App-LabRecorder) on PC or [RECORDA](https://github.com/NeuropsyOL/RECORDA) on Android. -On newer versions of Android, it may be necessary to prevent the system from limiting the app's processing time to conserve battery. This can be done by navigating to **Settings → Battery optimization ** and disabling battery optimization for SENDA. +On newer versions of Android, it may be necessary to prevent the system from limiting the app's processing time to conserve battery. This can be done by navigating to **Settings → Battery optimization** and disabling battery optimization for SENDA. ## Development -#### Prerequisites: +### Prerequisites - Android Studio Giraffe | 2022.3.1 Patch 2 - Android API 34 SDK platform - Android NDK 25.2.9519653 - CMake 3.22.1 -Other version may work, the above listed versions are the ones used for development and testing. +Other versions may work, the above listed versions are the ones used for development and testing. -#### Development: +### Development In order to start with development you need to follow these steps: -- Clone this repository -- Open project with Android Studio -- Import the project +- - Clone the repository +- - Open the project with Android Studio +- - Import the project ## Contributing Please feel free to contribute to this project by creating an issue first and then sending a pull request respectively. @@ -76,12 +76,12 @@ Please feel free to contribute to this project by creating an issue first and th * **Paul Maanen** - [pmaanen](https://github.com/pmaanen) ## License -This project is licensed under GNU General Public License License - see the [LICENSE.md](LICENSE.md) file for details +This project is licensed under GNU General Public License - see the [LICENSE.md](LICENSE.md) file for details ## Known Issues -- Sometimes the app complains about mission bluetooth permissions but does the scan for Movella sensors anyway. -- On a new installation multiple restarts of the app might be necessary until it asks for and notices newly granted permissions -- Some phones don't respect the foreground service and wakelock. A possible workaround is to set the app to unlimited background power usage in the app settings. +- Sometimes the app reports missing Bluetooth permissions but does the scan for Movella sensors anyway. +- After a fresh installation, multiple restarts may be required before the app detects newly granted permissions. +- Some devices may not properly honor foreground service or wakelock settings. A potential workaround is to allow unlimited background power usage for the app in the system settings. ## Acknowledgments @@ -90,4 +90,4 @@ This project is licensed under GNU General Public License License - see the [LIC [Google Mediapipe](https://developers.google.com/mediapipe), Apache v2.0 license. ## Cite As: - +- Blum S, Hölle D, Bleichner MG, Debener S. Pocketable Labs for Everyone: Synchronized Multi-Sensor Data Streaming and Recording on Smartphones with the Lab Streaming Layer. Sensors. 2021; 21(23):8135. https://doi.org/10.3390/s21238135 From 044e838743bb9478368ce13bf87b277740ed38cd Mon Sep 17 00:00:00 2001 From: Paul Maanen Date: Wed, 16 Apr 2025 14:58:18 +0200 Subject: [PATCH 03/55] Add in-app tutorial --- app/src/main/AndroidManifest.xml | 9 +++ .../de/uol/neuropsy/senda/MainActivity.java | 14 ++++ .../de/uol/neuropsy/senda/TutorialActivity.kt | 40 ++++++++++++ .../neuropsy/senda/TutorialPageFragment.kt | 61 ++++++++++++++++++ app/src/main/res/drawable/senda_1.png | Bin 0 -> 608797 bytes app/src/main/res/drawable/senda_2.png | Bin 0 -> 617245 bytes app/src/main/res/drawable/senda_3.png | Bin 0 -> 597326 bytes app/src/main/res/drawable/senda_4.png | Bin 0 -> 595565 bytes app/src/main/res/drawable/senda_refresh.gif | Bin 0 -> 124769 bytes app/src/main/res/layout/activity_main.xml | 15 +++++ app/src/main/res/layout/activity_tutorial.xml | 17 +++++ .../main/res/layout/close_tutorial_button.xml | 10 +++ app/src/main/res/layout/fragment_tutorial.xml | 15 +++++ app/src/main/res/layout/tutorial_image.xml | 7 ++ .../main/res/layout/tutorial_page_default.xml | 12 ++++ .../main/res/layout/tutorial_page_five.xml | 23 +++++++ .../main/res/layout/tutorial_page_four.xml | 28 ++++++++ .../main/res/layout/tutorial_page_last.xml | 52 +++++++++++++++ app/src/main/res/layout/tutorial_page_one.xml | 39 +++++++++++ .../main/res/layout/tutorial_page_three.xml | 28 ++++++++ app/src/main/res/layout/tutorial_page_two.xml | 28 ++++++++ app/src/main/res/values/strings.xml | 8 +++ 22 files changed, 406 insertions(+) create mode 100644 app/src/main/java/de/uol/neuropsy/senda/TutorialActivity.kt create mode 100644 app/src/main/java/de/uol/neuropsy/senda/TutorialPageFragment.kt create mode 100644 app/src/main/res/drawable/senda_1.png create mode 100644 app/src/main/res/drawable/senda_2.png create mode 100644 app/src/main/res/drawable/senda_3.png create mode 100644 app/src/main/res/drawable/senda_4.png create mode 100644 app/src/main/res/drawable/senda_refresh.gif create mode 100644 app/src/main/res/layout/activity_tutorial.xml create mode 100644 app/src/main/res/layout/close_tutorial_button.xml create mode 100644 app/src/main/res/layout/fragment_tutorial.xml create mode 100644 app/src/main/res/layout/tutorial_image.xml create mode 100644 app/src/main/res/layout/tutorial_page_default.xml create mode 100644 app/src/main/res/layout/tutorial_page_five.xml create mode 100644 app/src/main/res/layout/tutorial_page_four.xml create mode 100644 app/src/main/res/layout/tutorial_page_last.xml create mode 100644 app/src/main/res/layout/tutorial_page_one.xml create mode 100644 app/src/main/res/layout/tutorial_page_three.xml create mode 100644 app/src/main/res/layout/tutorial_page_two.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f214972..b7be22e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,15 @@ + + + + diff --git a/app/src/main/java/de/uol/neuropsy/senda/MainActivity.java b/app/src/main/java/de/uol/neuropsy/senda/MainActivity.java index 8e0b393..04a5aab 100644 --- a/app/src/main/java/de/uol/neuropsy/senda/MainActivity.java +++ b/app/src/main/java/de/uol/neuropsy/senda/MainActivity.java @@ -147,6 +147,7 @@ private void initDotSdk() { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initDotSdk(); + setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); @@ -193,6 +194,19 @@ public void onItemClick(AdapterView parent, View view, int position, long id) } } }); + + SharedPreferences sharedPref = getApplicationContext().getSharedPreferences("MyPref",0); + SharedPreferences.Editor editor = sharedPref.edit(); + if(sharedPref.getBoolean("firstStart",true)) { + Intent tutorialIntent=new Intent(this,TutorialActivity.class); + startActivity(tutorialIntent); + } + + Button tutorialButton = findViewById(R.id.tutorialButton); + tutorialButton.setOnClickListener(v->{ + Intent tutorialIntent=new Intent(this,TutorialActivity.class); + startActivity(tutorialIntent); + }); } // end onCreate public Boolean isActivated(String s) { diff --git a/app/src/main/java/de/uol/neuropsy/senda/TutorialActivity.kt b/app/src/main/java/de/uol/neuropsy/senda/TutorialActivity.kt new file mode 100644 index 0000000..4a801aa --- /dev/null +++ b/app/src/main/java/de/uol/neuropsy/senda/TutorialActivity.kt @@ -0,0 +1,40 @@ +package de.uol.neuropsy.senda + +import android.os.Bundle +import android.widget.Button +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 +import de.uol.neuropsy.senda.R + +/** + * The number of pages (wizard steps) to show in this demo. + */ +private const val NUM_PAGES = 5 + + +class TutorialActivity : FragmentActivity() { + protected lateinit var viewPager: ViewPager2 + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_tutorial) + + // Instantiate a ViewPager2 and a PagerAdapter. + viewPager = findViewById(R.id.pager) + // The pager adapter, which provides the pages to the view pager widget. + val pagerAdapter = ScreenSlidePagerAdapter(this) + viewPager.adapter = pagerAdapter + } + + /** + * A simple pager adapter that represents ScreenSlidePageFragment objects, in + * sequence. + */ + private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { + override fun getItemCount(): Int = NUM_PAGES + override fun createFragment(position: Int): Fragment { + return TutorialPageFragment.newInstance(position) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/uol/neuropsy/senda/TutorialPageFragment.kt b/app/src/main/java/de/uol/neuropsy/senda/TutorialPageFragment.kt new file mode 100644 index 0000000..9d5a4e2 --- /dev/null +++ b/app/src/main/java/de/uol/neuropsy/senda/TutorialPageFragment.kt @@ -0,0 +1,61 @@ +package de.uol.neuropsy.senda + +import android.content.SharedPreferences +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView +import androidx.fragment.app.Fragment +import coil.ImageLoader +import coil.load + + +class TutorialPageFragment() : Fragment() { + + companion object { + // Factory method that creates a new instance of TutorialPageFragment with position as an argument. + fun newInstance(position: Int): TutorialPageFragment { + val fragment = TutorialPageFragment() + val args = Bundle() + args.putInt("position", position) + fragment.arguments = args + return fragment + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Get the position value. Default to 0 if not provided. + val position = arguments?.getInt("position") ?: 0 + + // Choose a layout based on the page position. + val layoutResId = when (position) { + 0 -> R.layout.tutorial_page_one // For example, a layout for the first tutorial page. + 1 -> R.layout.tutorial_page_two + 2 -> R.layout.tutorial_page_three + 3 -> R.layout.tutorial_page_four + 4 -> R.layout.tutorial_page_last + else -> R.layout.tutorial_page_default // A default layout if needed. + } + + var view = inflater.inflate(layoutResId, container, false) + view.findViewById