diff --git a/android/app/build.gradle b/android/app/build.gradle
index 389ec87..40de5e6 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -32,7 +32,7 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
- compileSdkVersion 30
+ compileSdkVersion 31
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@@ -43,7 +43,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "org.bytedream.yamete_kudasai"
minSdkVersion 16
- targetSdkVersion 30
+ targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 681b907..68e1d81 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,9 +1,9 @@
-
-
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
+ android:name="io.flutter.embedding.android.SplashScreenDrawable"
+ android:resource="@drawable/launch_background"
+ />
@@ -38,22 +38,15 @@
android:name="flutterEmbedding"
android:value="2" />
+
+
+
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/background.dart b/lib/background.dart
index 04b5ebb..c4cf5d5 100644
--- a/lib/background.dart
+++ b/lib/background.dart
@@ -38,25 +38,13 @@ void initBackground() {
);
Map? data;
- int running = 0;
StreamSubscription? sub = _portUpdate.stream.listen((event) async {
data ??= (jsonDecode(generateEventData(await SharedPreferences.getInstance())) as Map)
.map((key, value) => MapEntry(UpdateAction.values.elementAt(int.parse(key)), value as String));
if (data!.containsKey(event!)) {
final player = await _player.play(data![event]!);
- running++;
- FlutterBackgroundService().setNotificationInfo(
- title: 'Yamete Kudasai',
- content: 'Dispatching ${actions.values.elementAt(event.index).toLowerCase()} event'
- );
await player.onPlayerCompletion.first;
- if (--running == 0) {
- FlutterBackgroundService().setNotificationInfo(
- title: 'Yamete Kudasai',
- content: 'Running'
- );
- }
}
});
diff --git a/lib/main.dart b/lib/main.dart
index a70ef9c..0146fc7 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,11 +1,15 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
+import 'package:future_progress_dialog/future_progress_dialog.dart';
import 'package:http/http.dart' as http;
+import 'package:open_file/open_file.dart';
import 'package:package_info_plus/package_info_plus.dart';
+import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:yamete_kudasai/background.dart';
@@ -15,7 +19,20 @@ import 'choose_audio.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
- runApp(YameteKudasai());
+ runApp(MaterialApp(
+ title: "Yamete Kudasai",
+ theme: ThemeData.from(
+ colorScheme: const ColorScheme.highContrastDark(
+ primary: Color(0xFFFF0000),
+ primaryVariant: Color(0xFFC20000),
+ secondary: Colors.purple,
+ surface: Colors.black,
+ background: Colors.black12,
+ onPrimary: Colors.white,
+ ),
+ ),
+ home: YameteKudasai(),
+ ));
}
class YameteKudasai extends StatefulWidget {
@@ -26,97 +43,89 @@ class YameteKudasai extends StatefulWidget {
class _YameteKudasaiState extends State {
@override
Widget build(BuildContext context) {
- http.get(Uri.https('api.github.com', 'repos/ByteDream/yamete_kudasai/releases/latest'))
- .timeout(const Duration(seconds: 5), onTimeout: () => http.Response.bytes([], 504)).then((response) async {
- if (response.statusCode == 200) {
- final packageInfo = await PackageInfo.fromPlatform();
- final tag = (jsonDecode(response.body) as Map)['tag_name'] as String;
- if (int.parse(tag.substring(1).replaceAll('.', '')) > int.parse(packageInfo.version.replaceAll('.', ''))) {
- showDialog(
- context: context,
- builder: (BuildContext context) => _buildUpdateNotification(context, tag)
- );
- }
- }
- });
FlutterBackgroundService.initialize(initBackground, foreground: false);
+ checkUpdate(context);
- return MaterialApp(
- title: "Yamete Kudasai",
- theme: ThemeData.from(
- colorScheme: const ColorScheme.highContrastDark(
- primary: Color(0xFFFF0000),
- primaryVariant: Color(0xFFC20000),
- secondary: Colors.purple,
- surface: Colors.black,
- background: Colors.black26,
- onPrimary: Colors.white,
- ),
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Yamete Kudasai'),
),
- home: Scaffold(
- appBar: AppBar(
- title: const Text('Yamete Kudasai'),
- ),
- body: Center(
- child: Column(
- children: [
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 20),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceEvenly,
- children: [
- ElevatedButton(
- child: Row(
- children: const [
- Text("Start"),
- Icon(Icons.play_arrow_outlined)
- ],
- ),
- onPressed: () async {
- WidgetsFlutterBinding.ensureInitialized();
- FlutterBackgroundService().sendData({'action': 'stop'});
- while (await isRunning()) {}
- FlutterBackgroundService.initialize(initBackground);
- },
+ body: Center(
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 20),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ ElevatedButton(
+ child: Row(
+ children: const [
+ Text("Start"),
+ Icon(Icons.play_arrow_outlined)
+ ],
),
- ElevatedButton(
- child: Row(
- children: const [
- Text("Stop"),
- Icon(Icons.stop_outlined)
- ],
- ),
- onPressed: () {
- FlutterBackgroundService().sendData({'action': 'stop'});
- },
- )
- ],
- ),
+ onPressed: () async {
+ WidgetsFlutterBinding.ensureInitialized();
+ FlutterBackgroundService().sendData({'action': 'stop'});
+ while (await isRunning()) {}
+ FlutterBackgroundService.initialize(initBackground);
+ },
+ ),
+ ElevatedButton(
+ child: Row(
+ children: const [
+ Text("Stop"),
+ Icon(Icons.stop_outlined)
+ ],
+ ),
+ onPressed: () {
+ FlutterBackgroundService().sendData({'action': 'stop'});
+ },
+ )
+ ],
),
- const Divider(color: Colors.white),
- _buildAudioSettings()
- ],
- ),
+ ),
+ const Divider(color: Colors.white),
+ _buildAudioSettings()
+ ],
),
),
);
}
- Widget _buildUpdateNotification(BuildContext context, String tag) {
+ Widget _buildUpdateNotification(BuildContext context, String tag, String apkUrl) {
return AlertDialog(
+ backgroundColor: Colors.black,
title: Text('Newer version is available ($tag)'),
actions: [
TextButton(
- onPressed: () async {
- if (await canLaunch('https://github.com/ByteDream/yamete_kudasai/releases/tag/$tag')) {
- await launch('https://github.com/ByteDream/yamete_kudasai/releases/tag/$tag');
- }
- },
- child: const Text('Show new release')
+ onPressed: () async {
+ await updateAPK(context, apkUrl);
+ await showDialog(
+ context: context,
+ builder: (BuildContext context) => FutureProgressDialog(
+ updateAPK(context, apkUrl),
+ decoration: const BoxDecoration(
+ color: Colors.transparent
+ ),
+ message: const Text('Downloading update...'),
+ )
+ );
+ },
+ child: const Text('Update')
),
TextButton(
- onPressed: () => Navigator.of(context).pop(),
- child: const Text('No thanks :)')
+ onPressed: () async {
+ if (await canLaunch('https://github.com/ByteDream/Yamete-Kudasai/releases/tag/$tag')) {
+ await launch('https://github.com/ByteDream/Yamete-Kudasai/releases/tag/$tag');
+ }
+ },
+ child: const Text('Show new release')
+ ),
+ TextButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: const Text('No thanks :)')
)
],
);
@@ -134,56 +143,119 @@ class _YameteKudasaiState extends State {
final entries = actions.entries;
return ListView.builder(
- shrinkWrap: true,
- itemCount: actions.length,
- itemBuilder: (BuildContext context, int index) {
- final item = entries.elementAt(index);
+ shrinkWrap: true,
+ itemCount: actions.length,
+ itemBuilder: (BuildContext context, int index) {
+ final item = entries.elementAt(index);
- final activatedKey = "${item.key.index}.activated";
- final targetKey = "${item.key.index}.target";
+ final activatedKey = "${item.key.index}.activated";
+ final targetKey = "${item.key.index}.target";
- final activatedAudio = prefs.getBool(activatedKey) ?? true;
- final targetAudio = prefs.getString(targetKey) ?? "assets/audio/yamete_kudasai.mp3";
+ final activatedAudio = prefs.getBool(activatedKey) ?? true;
+ final targetAudio = prefs.getString(targetKey) ?? "assets/audio/yamete_kudasai.mp3";
- return ListTile(
- title: Text(item.value),
- subtitle: Text(audio.entries.firstWhere((element) => element.value == targetAudio).key),
- trailing: Switch(
- activeColor: Theme.of(context).colorScheme.secondary,
- value: prefs.getBool(activatedKey) ?? true,
- onChanged: (bool newValue) async {
- SharedPreferences prefs = await SharedPreferences.getInstance();
- prefs.setBool(activatedKey, !activatedAudio);
- FlutterBackgroundService().sendData({'action': 'data', 'value': generateEventData(prefs)});
- setState(() {});
- }),
- onTap: () async {
- final audioFile = await Navigator.push(
- context,
- MaterialPageRoute(
- builder: (BuildContext context) => ChooseAudio(targetAudio)
- )
- );
- if (audioFile != null && audioFile != targetAudio) {
- SharedPreferences prefs = await SharedPreferences.getInstance();
- prefs.setString(targetKey, audioFile);
- FlutterBackgroundService().sendData({'action': 'data', 'value': generateEventData(prefs)});
- setState(() {});
- }
- },
- );
- }
+ return ListTile(
+ title: Text(item.value),
+ subtitle: Text(audio.entries.firstWhere((element) => element.value == targetAudio).key),
+ trailing: Switch(
+ activeColor: Theme.of(context).colorScheme.secondary,
+ value: prefs.getBool(activatedKey) ?? true,
+ onChanged: (bool newValue) async {
+ SharedPreferences prefs = await SharedPreferences.getInstance();
+ prefs.setBool(activatedKey, !activatedAudio);
+ FlutterBackgroundService().sendData({'action': 'data', 'value': generateEventData(prefs)});
+ setState(() {});
+ }),
+ onTap: () async {
+ final audioFile = await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (BuildContext context) => ChooseAudio(targetAudio)
+ )
+ );
+ if (audioFile != null && audioFile != targetAudio) {
+ SharedPreferences prefs = await SharedPreferences.getInstance();
+ prefs.setString(targetKey, audioFile);
+ FlutterBackgroundService().sendData({'action': 'data', 'value': generateEventData(prefs)});
+ setState(() {});
+ }
+ },
+ );
+ }
);
}
);
}
+ Future checkUpdate(BuildContext context) async {
+ final response = await http.get(Uri.https('api.github.com', 'repos/ByteDream/Yamete-Kudasai/releases/latest'))
+ .timeout(const Duration(seconds: 5), onTimeout: () => http.Response.bytes([], 504));
+ if (response.statusCode == 200) {
+ final packageInfo = await PackageInfo.fromPlatform();
+ final json = (jsonDecode(response.body) as Map);
+ final tag = json['tag_name'] as String;
+ final apkUrl = json['assets'][0]['browser_download_url'] as String;
+ if (int.parse(tag.substring(1).replaceAll('.', '')) > int.parse(packageInfo.version.replaceAll('.', ''))) {
+ await showDialog(
+ context: context,
+ builder: (BuildContext context) => _buildUpdateNotification(context, tag, apkUrl)
+ );
+ }
+ }
+ }
+
+ Future updateAPK(BuildContext context, String apkUrl) async {
+ ResultType result;
+ if ((await Permission.storage.request()).isGranted) {
+ final file = File('/storage/emulated/0/Download/${apkUrl.split('/').last}');
+ final completer = Completer();
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return FutureProgressDialog(
+ completer.future,
+ decoration: const BoxDecoration(
+ color: Colors.transparent
+ ),
+ message: const Text('Downloading update...'),
+ );
+ }
+ );
+ if (!(await file.exists())) {
+ final resp = await http.get(Uri.parse(apkUrl));
+ await file.writeAsBytes(resp.bodyBytes);
+ }
+ result = (await OpenFile.open(file.path)).type;
+ completer.complete();
+ } else {
+ result = ResultType.error;
+ }
+ if (result != ResultType.done) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: const Text('Failed to install update'),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: const Text('Ok'),
+ )
+ ],
+ );
+ }
+ );
+ }
+ Navigator.pop(context);
+ // await file.delete();
+ }
+
Future isRunning() async {
try {
FlutterBackgroundService().sendData({'action': 'ping'});
await FlutterBackgroundService().onDataReceived.first.timeout(const Duration(milliseconds: 500));
return true;
- } on Exception catch (e) {
+ } on Exception {
return false;
}
}
diff --git a/plugin/port_update/android/src/main/java/org/bytedream/port_update/PortUpdatePlugin.java b/plugin/port_update/android/src/main/java/org/bytedream/port_update/PortUpdatePlugin.java
index 592304e..d3266b3 100644
--- a/plugin/port_update/android/src/main/java/org/bytedream/port_update/PortUpdatePlugin.java
+++ b/plugin/port_update/android/src/main/java/org/bytedream/port_update/PortUpdatePlugin.java
@@ -45,11 +45,11 @@ public class PortUpdatePlugin implements FlutterPlugin, StreamHandler {
batteryReceiver = createBatteryReceiver(eventSink);
headphoneReceiver = createHeadphoneReceiver(eventSink);
- context.registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- context.registerReceiver(headphoneReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
+ Intent batteryIntent = context.registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ Intent headphoneIntent = context.registerReceiver(headphoneReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
- // lastBatteryStatus = getBatteryStatus(intent);
- // lastHeadphoneStatus = getHeadphoneStatus(intent);
+ if (batteryIntent != null) lastBatteryStatus = getBatteryStatus(batteryIntent);
+ if (headphoneIntent != null) lastHeadphoneStatus = getHeadphoneStatus(headphoneIntent);
}
@Override
diff --git a/pubspec.yaml b/pubspec.yaml
index 93fc0ac..1cb2461 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 1.0.0
+version: 1.1.0
environment:
sdk: ">=2.12.0 <3.0.0"
@@ -36,8 +36,12 @@ dependencies:
audioplayers: ^0.20.1
cupertino_icons: ^1.0.2
flutter_background_service: ^0.1.5
+ future_progress_dialog: ^0.2.0
http: ^0.13.4
+ open_file: ^3.2.1
package_info_plus: ^1.3.0
+ path_provider: ^2.0.7
+ permission_handler: ^8.3.0
shared_preferences: ^2.0.8
url_launcher: ^6.0.13