From e65b506ce17ce4416c5198447fe3c0be6d481a14 Mon Sep 17 00:00:00 2001 From: "anthony@rabine.fr" Date: Tue, 28 May 2024 11:52:33 +0200 Subject: [PATCH] story-player flutter: integrated new event mask + shared preferences --- shared/story_machine.h | 15 ++++ .../android/app/src/main/AndroidManifest.xml | 5 +- story-player/android/build.gradle | 1 - story-player/android/settings.gradle | 2 +- .../lib/libstory/generated_story_machine.dart | 28 +++++++ story-player/lib/libstory/indexfile.dart | 14 +++- story-player/lib/libstory/storyvm.dart | 16 ++-- story-player/lib/main.dart | 83 +++++++++---------- story-player/pubspec.yaml | 10 ++- story-player/storyvm/CMakeLists.txt | 2 +- story-player/storyvm/storyvm.cpp | 41 +++------ 11 files changed, 131 insertions(+), 86 deletions(-) create mode 100644 shared/story_machine.h create mode 100644 story-player/lib/libstory/generated_story_machine.dart diff --git a/shared/story_machine.h b/shared/story_machine.h new file mode 100644 index 0000000..2261af6 --- /dev/null +++ b/shared/story_machine.h @@ -0,0 +1,15 @@ +#pragma once + + +#define EV_MASK_OK_BUTTON 0b1 +#define EV_MASK_PREVIOUS_BUTTON 0b10 +#define EV_MASK_NEXT_BUTTON 0b100 +#define EV_MASK_UP_BUTTON 0b1000 +#define EV_MASK_DOWN_BUTTON 0b10000 +#define EV_MASK_HOME_BUTTON 0b100000 +#define EV_MASK_SELECT_BUTTON 0b1000000 +#define EV_MASK_START_BUTTON 0b10000000 +#define EV_MASK_STOP_BUTTON 0b100000000 +#define EV_MASK_PAUSE_BUTTON 0b1000000000 +#define EV_MASK_END_OF_AUDIO 0b10000000000 +#define EV_MASK_ALL 0xFFFFFFFFUL diff --git a/story-player/android/app/src/main/AndroidManifest.xml b/story-player/android/app/src/main/AndroidManifest.xml index 4d246bc..aaa516a 100644 --- a/story-player/android/app/src/main/AndroidManifest.xml +++ b/story-player/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,9 @@ - - + + + diff --git a/story-player/android/build.gradle b/story-player/android/build.gradle index 1e73c4f..bc157bd 100644 --- a/story-player/android/build.gradle +++ b/story-player/android/build.gradle @@ -1,4 +1,3 @@ - allprojects { repositories { google() diff --git a/story-player/android/settings.gradle b/story-player/android/settings.gradle index a7fce79..a6efc8a 100644 --- a/story-player/android/settings.gradle +++ b/story-player/android/settings.gradle @@ -8,7 +8,7 @@ pluginManagement { } settings.ext.flutterSdkPath = flutterSdkPath() - // includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") repositories { google() diff --git a/story-player/lib/libstory/generated_story_machine.dart b/story-player/lib/libstory/generated_story_machine.dart new file mode 100644 index 0000000..0b4c6ef --- /dev/null +++ b/story-player/lib/libstory/generated_story_machine.dart @@ -0,0 +1,28 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint + +const int EV_MASK_OK_BUTTON = 1; + +const int EV_MASK_PREVIOUS_BUTTON = 2; + +const int EV_MASK_NEXT_BUTTON = 4; + +const int EV_MASK_UP_BUTTON = 8; + +const int EV_MASK_DOWN_BUTTON = 16; + +const int EV_MASK_HOME_BUTTON = 32; + +const int EV_MASK_SELECT_BUTTON = 64; + +const int EV_MASK_START_BUTTON = 128; + +const int EV_MASK_STOP_BUTTON = 256; + +const int EV_MASK_PAUSE_BUTTON = 512; + +const int EV_MASK_END_OF_AUDIO = 1024; + +const int EV_MASK_ALL = 4294967295; diff --git a/story-player/lib/libstory/indexfile.dart b/story-player/lib/libstory/indexfile.dart index 4b21c81..ad15f07 100644 --- a/story-player/lib/libstory/indexfile.dart +++ b/story-player/lib/libstory/indexfile.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:logger/logger.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:device_info_plus/device_info_plus.dart'; var logger = Logger(printer: PrettyPrinter(methodCount: 0)); @@ -124,11 +125,22 @@ class IndexFile { bool isGranted = false; if (Platform.isAndroid) { - if (await Permission.storage.request().isGranted) { + + AndroidDeviceInfo deviceInfo = await DeviceInfoPlugin().androidInfo; + + if (deviceInfo.version.sdkInt <= 32) { + if (await Permission.storage.request().isGranted) { + isGranted = true; + } + } else { if (await Permission.manageExternalStorage.request().isGranted) { isGranted = true; } } + if (!isGranted) { + await openAppSettings(); + } + } else { isGranted = true; } diff --git a/story-player/lib/libstory/storyvm.dart b/story-player/lib/libstory/storyvm.dart index 389397a..a0911b4 100644 --- a/story-player/lib/libstory/storyvm.dart +++ b/story-player/lib/libstory/storyvm.dart @@ -8,6 +8,7 @@ import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:logger/logger.dart'; import 'package:event_bus/event_bus.dart'; +import 'generated_story_machine.dart'; var logger = Logger(printer: PrettyPrinter(methodCount: 0)); @@ -55,8 +56,6 @@ typedef VmRun = void Function(); typedef VmSendEventType = Void Function(Int); typedef VmSendEvent = void Function(int event); -enum VmEvent { evNoEvent, evStep, evOkButton, evPreviousButton, evNextButton, evAudioFinished, evStop } - class StoryVm { static late DynamicLibrary nativeApiLib; static late VmInitialize vmInitialize; @@ -138,22 +137,27 @@ class StoryVm { static void endOfSound() { - vmSendEvent(VmEvent.evAudioFinished.index); + vmSendEvent(EV_MASK_END_OF_AUDIO); } static void okButton() { - vmSendEvent(VmEvent.evOkButton.index); + vmSendEvent(EV_MASK_OK_BUTTON); } static void previousButton() { - vmSendEvent(VmEvent.evPreviousButton.index); + vmSendEvent(EV_MASK_PREVIOUS_BUTTON); } static void nextButton() { - vmSendEvent(VmEvent.evNextButton.index); + vmSendEvent(EV_MASK_NEXT_BUTTON); + } + + static void homeButton() { + + vmSendEvent(EV_MASK_HOME_BUTTON); } static void stop() { diff --git a/story-player/lib/main.dart b/story-player/lib/main.dart index cd1304a..d1fc71e 100644 --- a/story-player/lib/main.dart +++ b/story-player/lib/main.dart @@ -14,6 +14,7 @@ import 'package:external_path/external_path.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:logger/logger.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'libstory/storyvm.dart'; import 'libstory/indexfile.dart'; @@ -97,7 +98,7 @@ class MyHomePage extends StatefulWidget { enum PlayerState { disabled, indexFile, inStory } class _MyHomePageState extends State { - String myPath = 'fffff'; + String libraryDir = 'fffff'; IndexFile indexFile = IndexFile(); String currentImage = 'assets/logo.png'; final player = AudioPlayer(); @@ -105,22 +106,42 @@ class _MyHomePageState extends State { PlayerState state = PlayerState.disabled; StreamSubscription? audioPlayerSub; + + Image img = const Image(image: AssetImage('assets/logo.png')); void initPaths() async { - Directory? dir; + - if (Platform.isAndroid) { - dir = await getExternalStorageDirectory(); + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final String? libDir = prefs.getString('library-directory'); + + if (libDir != null) { + setState(() { + libraryDir = libDir; + }); + bool success = await indexFile.loadIndexFile(libDir); + if (success) { + showCurrentStoryIndex(); + state = PlayerState.indexFile; + } } else { - dir = await getApplicationDocumentsDirectory(); + logger.d("No library directory found"); + Directory? dir; + if (Platform.isAndroid) { + dir = await getExternalStorageDirectory(); + } else { + dir = await getApplicationDocumentsDirectory(); + } + if (dir != null) { + setState(() { + libraryDir = '${dir.toString()}/stories'; + }); + } } - - setState(() { - myPath = dir.toString(); - }); - logger.d("===============+> $myPath"); + + logger.d("===============+> $libraryDir"); } _MyHomePageState() { @@ -174,9 +195,10 @@ class _MyHomePageState extends State { if (path != null) { logger.d("Selected directory: $path"); - + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setString('library-directory', path); setState(() { - myPath = path; + libraryDir = path; }); bool success = await indexFile.loadIndexFile(path); if (success) { @@ -244,10 +266,10 @@ class _MyHomePageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - myPath, - style: Theme.of(context).textTheme.bodySmall, - ), + // Text( + // libraryDir, + // style: Theme.of(context).textTheme.bodySmall, + // ), img, ], ), @@ -256,31 +278,6 @@ class _MyHomePageState extends State { color: const Color(0xFF9ab4a4), child: Row( children: [ - /* IconButton( - tooltip: 'Select library directory', - icon: const Icon( - Icons.folder, - size: 40, - ), - onPressed: () async { - // FilePickerResult? result = await FilePicker.platform.pickFiles(); - String ?path = await FilePicker.platform.getDirectoryPath(); - - if (path != null) { - logger.d("Selected directory: $path"); - - setState(() { - myPath = path; - }); - bool success = await indexFile.loadIndexFile(path); - if (success) { - showCurrentStoryIndex(); - state = PlayerState.indexFile; - } - } - }, - color: const Color(0xFFb05728), - ),*/ IconButton( tooltip: 'Previous', icon: const Icon(Icons.arrow_circle_left, size: 40), @@ -314,9 +311,7 @@ class _MyHomePageState extends State { onPressed: () { if (state == PlayerState.inStory) { - player.stop(); - showCurrentStoryIndex(); - state = PlayerState.indexFile; + StoryVm.homeButton(); } else if (state == PlayerState.indexFile) { player.stop(); } diff --git a/story-player/pubspec.yaml b/story-player/pubspec.yaml index fd016de..cd94be5 100644 --- a/story-player/pubspec.yaml +++ b/story-player/pubspec.yaml @@ -49,6 +49,9 @@ dependencies: event_bus: ^2.0.0 external_path: ^1.0.3 permission_handler: ^10.4.5 + device_info_plus: ^10.1.0 + ffigen: ^12.0.0 + shared_preferences: ^2.2.3 dev_dependencies: flutter_test: @@ -104,4 +107,9 @@ flutter: # see https://flutter.dev/custom-fonts/#from-packages assets: - assets/logo.png - \ No newline at end of file + +ffigen: + output: 'lib/libstory/generated_story_machine.dart' + headers: + entry-points: + - '../shared/story_machine.h' diff --git a/story-player/storyvm/CMakeLists.txt b/story-player/storyvm/CMakeLists.txt index 077a000..d08c54d 100644 --- a/story-player/storyvm/CMakeLists.txt +++ b/story-player/storyvm/CMakeLists.txt @@ -7,5 +7,5 @@ add_library(storyvm ../../firmware/chip32/chip32_vm.c ) -include_directories(../../firmware/chip32) +include_directories(../../firmware/chip32 ../../shared) diff --git a/story-player/storyvm/storyvm.cpp b/story-player/storyvm/storyvm.cpp index f957100..dcf8396 100644 --- a/story-player/storyvm/storyvm.cpp +++ b/story-player/storyvm/storyvm.cpp @@ -11,7 +11,7 @@ #include "chip32_vm.h" #include "dlib_export.h" - +#include "story_machine.h" static char root_dir[260]; @@ -31,12 +31,16 @@ static chip32_ctx_t chip32_ctx; static chip32_result_t run_result; - +static uint32_t event_mask = 0; static uint8_t IndexBuf[260]; static uint8_t ImageBuf[100]; static uint8_t SoundBuf[100]; +static bool IsValidEvent(uint32_t event) { + return (event_mask & event) != 0; +} + int get_filename_from_memory(chip32_ctx_t *ctx, uint32_t addr, char *filename_mem) { @@ -106,11 +110,12 @@ uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code) std::cout << "[STORYVM] No sound" << std::endl; } - retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause + retCode = SYSCALL_RET_OK; // set the VM in pause } else if (code == 2) // Wait Event { std::cout << "[STORYVM] Syscall 2 (wait for event)" << std::endl; + event_mask = ctx->registers[R0]; retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause } return retCode; @@ -160,38 +165,16 @@ extern "C" void storyvm_initialize(media_callback cb) storyvm_stop(); } - -enum VmEventType {EvNoEvent, EvStep, EvOkButton, EvPreviousButton, EvNextButton, EvAudioFinished, EvStop}; - extern "C" void storyvm_send_event(int event) { - if (event == VmEventType::EvStep) + if (IsValidEvent(event)) { + chip32_ctx.registers[R0] = event; run_result = VM_OK; } - else if (event == VmEventType::EvOkButton) + else { - chip32_ctx.registers[R0] = 0x01; - run_result = VM_OK; - } - else if (event == VmEventType::EvPreviousButton) - { - chip32_ctx.registers[R0] = 0x02; - run_result = VM_OK; - } - else if (event == VmEventType::EvNextButton) - { - chip32_ctx.registers[R0] = 0x04; - run_result = VM_OK; - } - else if (event == VmEventType::EvAudioFinished) - { - chip32_ctx.registers[R0] = 0x08; - run_result = VM_OK; - } - else if (event == VmEventType::EvStop) - { - run_result = VM_FINISHED; + std::cout << "[STORYVM] Invalid event" << std::endl; } }