commit bd221bb030bb78566905a8da988c2ce9ff6a6698 Author: bytedream Date: Tue Nov 16 22:29:44 2021 +0100 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d349dfc --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..756027e --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# Yamete Kudasai + +Cute anime girl moaning when something is plugged in. + +## Getting Started + +> Android only + +I think the sentence above says all about the app... + +When you enter the app, just click start and whenever an event happens one of the precisely chosen sounds gets played. +If you want to turn off the sound playing for a specific event, just toggle off the button behind it. +For playing other sounds than the standard "Yamete Kudasai", click on the entry for the event you want to change +and a new window pops up where you can set the sound which should be played on the event. + +Supported events: +- **Battery** + - Battery charging + - Battery discharging + - Battery full +- **Headphone** + - Headphone connected + - Headphone disconnected + +## License + +This project is licensed under the Do What The F*ck You Want To Public License (WTFPL) - see the [LICENSE](LICENSE) file for more details. + diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..389ec87 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,70 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +// load keystore to sign apk (obviously not included in this repo) +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 30 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + // 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 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + multiDexEnabled true + } + + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + + buildTypes { + release { + signingConfig signingConfigs.release + } + } +} + +flutter { + source '../..' +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..bf5e55f --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..681b907 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/org/bytedream/yamete_kudasai/MainActivity.java b/android/app/src/main/java/org/bytedream/yamete_kudasai/MainActivity.java new file mode 100644 index 0000000..520b378 --- /dev/null +++ b/android/app/src/main/java/org/bytedream/yamete_kudasai/MainActivity.java @@ -0,0 +1,9 @@ +package org.bytedream.yamete_kudasai; + +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugin.common.EventChannel; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..7c8f7d3 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..46bae06 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..35b440b Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..33f1de8 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..bd1d362 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..449a9f9 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d74aa35 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..bf5e55f --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..8b987c1 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,28 @@ +buildscript { + ext.kotlin_version = '1.4.0' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bc6a58a --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/audio/airis_first_tutoring_lesson.mp3 b/assets/audio/airis_first_tutoring_lesson.mp3 new file mode 100644 index 0000000..69deacf Binary files /dev/null and b/assets/audio/airis_first_tutoring_lesson.mp3 differ diff --git a/assets/audio/the_helpful_pharmacist.mp3 b/assets/audio/the_helpful_pharmacist.mp3 new file mode 100644 index 0000000..74e2c88 Binary files /dev/null and b/assets/audio/the_helpful_pharmacist.mp3 differ diff --git a/assets/audio/yamete_kudasai.mp3 b/assets/audio/yamete_kudasai.mp3 new file mode 100644 index 0000000..be2730a Binary files /dev/null and b/assets/audio/yamete_kudasai.mp3 differ diff --git a/assets/icon/icon.png b/assets/icon/icon.png new file mode 100644 index 0000000..dd0cea7 Binary files /dev/null and b/assets/icon/icon.png differ diff --git a/ext/preview_1.png b/ext/preview_1.png new file mode 100644 index 0000000..b29bda7 Binary files /dev/null and b/ext/preview_1.png differ diff --git a/ext/preview_2.png b/ext/preview_2.png new file mode 100644 index 0000000..1c8076c Binary files /dev/null and b/ext/preview_2.png differ diff --git a/ext/preview_3.png b/ext/preview_3.png new file mode 100644 index 0000000..32f838b Binary files /dev/null and b/ext/preview_3.png differ diff --git a/lib/background.dart b/lib/background.dart new file mode 100644 index 0000000..04b5ebb --- /dev/null +++ b/lib/background.dart @@ -0,0 +1,84 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_background_service/flutter_background_service.dart'; +import 'package:port_update/port_update.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +final actions = { + UpdateAction.batteryCharging: "Battery charging", + UpdateAction.batteryDischarging: "Battery discharging", + UpdateAction.batteryFull: "Battery full", + UpdateAction.headphoneConnected: "Headphone connected", + UpdateAction.headphoneDisconnected: "Headphone disconnected", +}; + +final _player = AudioCache(prefix: ''); +final _portUpdate = PortUpdate(); + +String generateEventData(SharedPreferences sharedPreferences) { + Map data = {}; + + for (var action in UpdateAction.values) { + if (sharedPreferences.getBool("${action.index}.activated") ?? true) { + data[action.index.toString()] = sharedPreferences.getString("${action.index}.target") ?? "assets/audio/yamete_kudasai.mp3"; + } + } + return jsonEncode(data); +} + +void initBackground() { + WidgetsFlutterBinding.ensureInitialized(); + + FlutterBackgroundService().setNotificationInfo( + title: "Yamete Kudasai", + content: "Preparing", + ); + + 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' + ); + } + } + }); + + FlutterBackgroundService().setNotificationInfo( + title: "Yamete Kudasai", + content: "Running", + ); + + FlutterBackgroundService().onDataReceived.listen((event) async { + switch (event!['action']) { + case 'stop': + await sub.cancel(); + FlutterBackgroundService().stopBackgroundService(); + break; + case 'data': + data = (jsonDecode(event['value']) as Map).map((key, value) { + return MapEntry(UpdateAction.values.elementAt(int.parse(key)), value.toString()); + }); + break; + case 'ping': + FlutterBackgroundService().sendData({'action': 'pong'}); + break; + } + }); +} diff --git a/lib/choose_audio.dart b/lib/choose_audio.dart new file mode 100644 index 0000000..02a43dc --- /dev/null +++ b/lib/choose_audio.dart @@ -0,0 +1,87 @@ +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/material.dart'; + +final audio = { + 'Airi\'s first tutoring lesson': 'assets/audio/airis_first_tutoring_lesson.mp3', + 'The helpful pharmacist': 'assets/audio/the_helpful_pharmacist.mp3', + 'Yamete Kudasai': 'assets/audio/yamete_kudasai.mp3' +}; + +class ChooseAudio extends StatefulWidget { + String _before; + + ChooseAudio(this._before, {Key? key}) : super(key: key); + + @override + State createState() => _ChooseAudioState(); +} + +class _ChooseAudioState extends State { + final _player = AudioCache(prefix: ''); + int _playIndex = -1; + AudioPlayer? _playing; + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + Navigator.pop(context, widget._before); + return true; + }, + child: Scaffold( + appBar: AppBar( + title: const Text('Choose audio') + ), + body: ListView.separated( + itemBuilder: (BuildContext context, int index) { + MapEntry item = audio.entries.elementAt(index); + if (index == _playIndex) { + if (_playing == null) { + play(item.value); + } else { + _playing!.stop().then((value) => play(item.value)); + } + } + return ListTile( + leading: Radio( + activeColor: Theme.of(context).colorScheme.secondary, + value: item.value, + groupValue: widget._before, + onChanged: (String? value) { + setState(() { + widget._before = value!; + }); + }), + title: Text(item.key), + trailing: Icon(_playIndex == index ? Icons.stop_outlined : Icons.play_arrow_outlined), + onTap: () { + setState(() { + _playIndex = index; + }); + }, + ); + }, + separatorBuilder: (BuildContext context, int index) => const Divider(), + itemCount: audio.length + ), + ), + ); + } + + @override + void dispose() { + if (_playing != null) { + _playing!.stop(); + } + super.dispose(); + } + + void play(String path) async { + _playing = await _player.play(path); + _playing!.onPlayerCompletion.listen((event) { + setState(() { + _playIndex = -1; + }); + }); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..a70ef9c --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,190 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_background_service/flutter_background_service.dart'; +import 'package:http/http.dart' as http; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:yamete_kudasai/background.dart'; + +import 'choose_audio.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); + runApp(YameteKudasai()); +} + +class YameteKudasai extends StatefulWidget { + @override + _YameteKudasaiState createState() => _YameteKudasaiState(); +} + +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); + + 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, + ), + ), + 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); + }, + ), + ElevatedButton( + child: Row( + children: const [ + Text("Stop"), + Icon(Icons.stop_outlined) + ], + ), + onPressed: () { + FlutterBackgroundService().sendData({'action': 'stop'}); + }, + ) + ], + ), + ), + const Divider(color: Colors.white), + _buildAudioSettings() + ], + ), + ), + ), + ); + } + + Widget _buildUpdateNotification(BuildContext context, String tag) { + return AlertDialog( + 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') + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('No thanks :)') + ) + ], + ); + } + + Widget _buildAudioSettings() { + return FutureBuilder( + future: SharedPreferences.getInstance(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (!snapshot.hasData) { + return const SizedBox.shrink(); + } + + final prefs = snapshot.data!; + final entries = actions.entries; + + return ListView.builder( + 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 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(() {}); + } + }, + ); + } + ); + } + ); + } + + Future isRunning() async { + try { + FlutterBackgroundService().sendData({'action': 'ping'}); + await FlutterBackgroundService().onDataReceived.first.timeout(const Duration(milliseconds: 500)); + return true; + } on Exception catch (e) { + return false; + } + } +} diff --git a/plugin/port_update/.gitignore b/plugin/port_update/.gitignore new file mode 100644 index 0000000..e9dc58d --- /dev/null +++ b/plugin/port_update/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ diff --git a/plugin/port_update/.metadata b/plugin/port_update/.metadata new file mode 100644 index 0000000..5bed526 --- /dev/null +++ b/plugin/port_update/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 18116933e77adc82f80866c928266a5b4f1ed645 + channel: stable + +project_type: plugin diff --git a/plugin/port_update/CHANGELOG.md b/plugin/port_update/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/plugin/port_update/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/plugin/port_update/LICENSE b/plugin/port_update/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/plugin/port_update/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/plugin/port_update/README.md b/plugin/port_update/README.md new file mode 100644 index 0000000..99b8686 --- /dev/null +++ b/plugin/port_update/README.md @@ -0,0 +1,15 @@ +# port_update + +Bruh I need a library to call native code from foreground task + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/plugin/port_update/analysis_options.yaml b/plugin/port_update/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/plugin/port_update/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/plugin/port_update/android/.gitignore b/plugin/port_update/android/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/plugin/port_update/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/plugin/port_update/android/build.gradle b/plugin/port_update/android/build.gradle new file mode 100644 index 0000000..dc5b94e --- /dev/null +++ b/plugin/port_update/android/build.gradle @@ -0,0 +1,35 @@ +group 'org.bytedream.port_update' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 30 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 16 + } +} diff --git a/plugin/port_update/android/gradle.properties b/plugin/port_update/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/plugin/port_update/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/plugin/port_update/android/gradle/wrapper/gradle-wrapper.properties b/plugin/port_update/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3c9d085 --- /dev/null +++ b/plugin/port_update/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/plugin/port_update/android/settings.gradle b/plugin/port_update/android/settings.gradle new file mode 100644 index 0000000..df02681 --- /dev/null +++ b/plugin/port_update/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'port_update' diff --git a/plugin/port_update/android/src/main/AndroidManifest.xml b/plugin/port_update/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..25f4458 --- /dev/null +++ b/plugin/port_update/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + 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 new file mode 100644 index 0000000..592304e --- /dev/null +++ b/plugin/port_update/android/src/main/java/org/bytedream/port_update/PortUpdatePlugin.java @@ -0,0 +1,136 @@ +package org.bytedream.port_update; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; + +import io.flutter.Log; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.EventChannel; +import io.flutter.plugin.common.EventChannel.StreamHandler; + +/** PortUpdatePlugin */ +public class PortUpdatePlugin implements FlutterPlugin, StreamHandler { + public static final String TAG = "PortUpdate"; + public static final String CHANNEL = "port/stream"; + + private Context context; + private EventChannel channel; + + private BroadcastReceiver batteryReceiver; + private BroadcastReceiver headphoneReceiver; + + private int lastBatteryStatus; + private int lastHeadphoneStatus; + + @Override + public void onAttachedToEngine(FlutterPluginBinding flutterPluginBinding) { + context = flutterPluginBinding.getApplicationContext(); + channel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL); + channel.setStreamHandler(this); + } + + @Override + public void onDetachedFromEngine(FlutterPluginBinding binding) { + context = null; + channel.setStreamHandler(null); + } + + @Override + public void onListen(Object o, EventChannel.EventSink eventSink) { + Log.w(TAG, "adding listener"); + + batteryReceiver = createBatteryReceiver(eventSink); + headphoneReceiver = createHeadphoneReceiver(eventSink); + + context.registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + context.registerReceiver(headphoneReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); + + // lastBatteryStatus = getBatteryStatus(intent); + // lastHeadphoneStatus = getHeadphoneStatus(intent); + } + + @Override + public void onCancel(Object o) { + Log.w(TAG, "canceling listener"); + context.unregisterReceiver(batteryReceiver); + context.unregisterReceiver(headphoneReceiver); + } + + private BroadcastReceiver createBatteryReceiver(EventChannel.EventSink eventSink) { + return new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int batteryStatus = getBatteryStatus(intent); + if (batteryStatus != lastBatteryStatus || batteryStatus == -1) { + lastBatteryStatus = batteryStatus; + eventSink.success(getBatteryAction(batteryStatus).value); + } + } + }; + } + + private int getBatteryStatus(Intent intent) { + return intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); + } + + private Action getBatteryAction(int batteryStatus) { + switch (batteryStatus) { + case BatteryManager.BATTERY_STATUS_CHARGING: + return Action.BATTERY_CHARGING; + case BatteryManager.BATTERY_STATUS_DISCHARGING: + return Action.BATTERY_DISCHARGING; + case BatteryManager.BATTERY_STATUS_FULL: + return Action.BATTERY_FULL; + default: + return Action.UNKNOWN; + } + } + + private BroadcastReceiver createHeadphoneReceiver(EventChannel.EventSink eventSink) { + return new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int headphoneStatus = intent.getIntExtra("state", -1); + if (headphoneStatus != lastHeadphoneStatus || headphoneStatus == -1) { + lastHeadphoneStatus = headphoneStatus; + eventSink.success(getHeadphoneAction(headphoneStatus).value); + } + } + }; + } + + private int getHeadphoneStatus(Intent intent) { + return intent.getIntExtra("state", -1); + } + + private Action getHeadphoneAction(int headphoneStatus) { + switch (headphoneStatus) { + // unplugged + case 0: + return Action.HEADPHONE_DISCONNECTED; + // plugged in + case 1: + return Action.HEADPHONE_CONNECTED; + default: + return Action.UNKNOWN; + } + } + + private enum Action { + UNKNOWN(0), + BATTERY_CHARGING(1), + BATTERY_DISCHARGING(2), + BATTERY_FULL(3), + HEADPHONE_CONNECTED(4), + HEADPHONE_DISCONNECTED(5); + + public final int value; + + Action(int value) { + this.value = value; + } + } +} diff --git a/plugin/port_update/lib/port_update.dart b/plugin/port_update/lib/port_update.dart new file mode 100644 index 0000000..5ce28de --- /dev/null +++ b/plugin/port_update/lib/port_update.dart @@ -0,0 +1,3 @@ +library port_update; + +export 'src/port_update.dart' show PortUpdate, UpdateAction; \ No newline at end of file diff --git a/plugin/port_update/lib/src/port_update.dart b/plugin/port_update/lib/src/port_update.dart new file mode 100644 index 0000000..df811a6 --- /dev/null +++ b/plugin/port_update/lib/src/port_update.dart @@ -0,0 +1,18 @@ +import 'package:flutter/services.dart'; + +enum UpdateAction { + unknown, + batteryCharging, + batteryDischarging, + batteryFull, + headphoneConnected, + headphoneDisconnected +} + +class PortUpdate { + static const channel = "port/stream"; + + final _channel = const EventChannel(channel); + + Stream get stream => _channel.receiveBroadcastStream().map((event) => UpdateAction.values.elementAt(event)); +} diff --git a/plugin/port_update/pubspec.lock b/plugin/port_update/pubspec.lock new file mode 100644 index 0000000..00e6c32 --- /dev/null +++ b/plugin/port_update/pubspec.lock @@ -0,0 +1,161 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" diff --git a/plugin/port_update/pubspec.yaml b/plugin/port_update/pubspec.yaml new file mode 100644 index 0000000..4fa9613 --- /dev/null +++ b/plugin/port_update/pubspec.yaml @@ -0,0 +1,63 @@ +name: port_update +description: Bruh I need a library to call native code from foreground task +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' and Android 'package' identifiers should not ordinarily + # be modified. They are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: org.bytedream.port_update + pluginClass: PortUpdatePlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..93fc0ac --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,97 @@ +name: yamete_kudasai +description: Cute anime girl moaning when something is plugged in. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# 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 + +environment: + sdk: ">=2.12.0 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + audioplayers: ^0.20.1 + cupertino_icons: ^1.0.2 + flutter_background_service: ^0.1.5 + http: ^0.13.4 + package_info_plus: ^1.3.0 + shared_preferences: ^2.0.8 + url_launcher: ^6.0.13 + + port_update: + path: ./plugin/port_update + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^1.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/audio/ + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages