mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
story-player flutter: integrated new event mask + shared preferences
This commit is contained in:
parent
26c917fe27
commit
e65b506ce1
11 changed files with 131 additions and 86 deletions
15
shared/story_machine.h
Normal file
15
shared/story_machine.h
Normal file
|
|
@ -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
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
28
story-player/lib/libstory/generated_story_machine.dart
Normal file
28
story-player/lib/libstory/generated_story_machine.dart
Normal file
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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<MyHomePage> {
|
||||
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<MyHomePage> {
|
|||
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<MyHomePage> {
|
|||
|
||||
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<MyHomePage> {
|
|||
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
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<MyHomePage> {
|
|||
color: const Color(0xFF9ab4a4),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
/* 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<MyHomePage> {
|
|||
onPressed: () {
|
||||
|
||||
if (state == PlayerState.inStory) {
|
||||
player.stop();
|
||||
showCurrentStoryIndex();
|
||||
state = PlayerState.indexFile;
|
||||
StoryVm.homeButton();
|
||||
} else if (state == PlayerState.indexFile) {
|
||||
player.stop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
ffigen:
|
||||
output: 'lib/libstory/generated_story_machine.dart'
|
||||
headers:
|
||||
entry-points:
|
||||
- '../shared/story_machine.h'
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ add_library(storyvm
|
|||
../../firmware/chip32/chip32_vm.c
|
||||
)
|
||||
|
||||
include_directories(../../firmware/chip32)
|
||||
include_directories(../../firmware/chip32 ../../shared)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue