├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .gitignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── flutter │ └── moum │ └── hardware_buttons │ ├── EmptyActivityLifecycleCallbacks.kt │ ├── HardwareButtonsPlugin.kt │ ├── HardwareButtonsWatcherManager.kt │ ├── HomeButtonStreamHandler.kt │ ├── LockButtonStreamHandler.kt │ └── VolumeButtonStreamHandler.kt ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── flutter │ │ │ │ │ └── moum │ │ │ │ │ └── hardware_buttons_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── HardwareButtonsPlugin.h │ ├── HardwareButtonsPlugin.m │ ├── HomeButtonStreamHandler.swift │ ├── LockButtonStreamHandler.swift │ ├── SingleHomeAndLockObserver.swift │ ├── SwiftHardwareButtonsPlugin.swift │ └── VolumeButtenStreamHandler.swift └── hardware_buttons.podspec ├── lib └── hardware_buttons.dart ├── pubspec.yaml └── test └── hardware_buttons_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea/ 4 | .vscode/ 5 | 6 | .packages 7 | .pub/ 8 | .dart_tool/ 9 | pubspec.lock 10 | flutter_export_environment.sh 11 | 12 | examples/all_plugins/pubspec.yaml 13 | 14 | Podfile 15 | Podfile.lock 16 | Pods/ 17 | .symlinks/ 18 | **/Flutter/App.framework/ 19 | **/Flutter/Flutter.framework/ 20 | **/Flutter/Generated.xcconfig 21 | **/Flutter/flutter_assets/ 22 | ServiceDefinitions.json 23 | xcuserdata/ 24 | *.xcworkspace 25 | **/DerivedData/ 26 | 27 | local.properties 28 | keystore.properties 29 | .gradle/ 30 | gradlew 31 | gradlew.bat 32 | gradle-wrapper.jar 33 | *.iml 34 | 35 | GeneratedPluginRegistrant.h 36 | GeneratedPluginRegistrant.m 37 | GeneratedPluginRegistrant.java 38 | build/ 39 | .flutter-plugins 40 | 41 | .project 42 | .classpath 43 | .settings 44 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | - Android: Migrate to AndroidX (#33) 3 | 4 | ## 0.2.4 5 | - Android: Fix issue #39 6 | 7 | ## 0.2.3 8 | - iOS: Fix Volume detection bug on iPhone 10.3.3. 9 | 10 | ## 0.2.2 11 | - iOS: Migrating to Swift 5 12 | 13 | ## 0.2.1 14 | - Update README.md 15 | 16 | ## 0.2.0 17 | 18 | - Supports lock button events subscription. 19 | - iOS: Fix bug where home button event got called when lock button was pressed. 20 | 21 | ## 0.1.1 22 | 23 | - Update README.md 24 | - Document Public APIs 25 | 26 | ## 0.1.0 27 | 28 | - Update README.md 29 | 30 | ## 0.0.1 31 | 32 | - Initial release 33 | - Supports volume button and home button detection 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 flutter-moum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | This plugin is deprecated :( We're no longer able to maintain it. 4 | 5 | # hardware_buttons 6 | 7 | [![Version](https://img.shields.io/pub/v/hardware_buttons.svg)](https://pub.dev/packages/hardware_buttons) ![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat) 8 | 9 | A Flutter plugin for iOS and Android for detecting various hardware buttons. 10 | 11 | Note: This plugin is still under development, and some APIs might not be available yet. [Feedback](https://github.com/flutter-moum/flutter_hardware_buttons/issues) and [Pull Requests](https://github.com/flutter-moum/flutter_hardware_buttons/pulls) are most welcome! 12 | 13 | ## Screenshots 14 | 15 | ![screenshots](https://user-images.githubusercontent.com/26567846/66265518-14c69900-e853-11e9-8495-8c2966be4e6c.jpg) 16 | 17 | ## Features 18 | 19 | - Detect volume buttons 20 | - Detect home button 21 | - Detect lock(power) button 22 | - To be added... 23 | 24 | ## Android specification 25 | 26 | - If you subscribe to volume button events, this plugin will inevitably request for [ACTION_MANAGER_OVERLAY_PERMISSION](https://developer.android.com/reference/android/provider/Settings.html#ACTION_MANAGE_OVERLAY_PERMISSION), since we found out this was the only way to do it well in Flutter Plugin environment. We do show permission request window for you, so there's nothing you should do other than subscribing to the event channel. However, since users may be surprised why your app needs this permission, we suggest notifying users beforehand why this permission will be requested. 27 | - **Migrated to AndroidX as of version ```1.0.0```**. To use with original support libraries, use version ```0.2.4```. Note, however, pre-AndroidX will no longer be supported. 28 | 29 | ## iOS specification 30 | 31 | - No VOLUME_DOWN events are emitted when the volume is already at its minimum. VOLUME_UP events vice versa. On the other hand, events always occur whenever user presses the button on Android. 32 | 33 | ## Usage 34 | 35 | ### Import the package 36 | 37 | To use this plugin, follow the [plugin installation instructions](https://pub.dev/packages/hardware_buttons#-installing-tab-). 38 | 39 | To use with AndroidX, install the latest version as above instructions. To use with original support libraries, install version ```0.2.4``` instead. 40 | 41 | ## Use the plugin 42 | 43 | Add the following import to your Dart code: 44 | 45 | ```dart 46 | import 'package:hardware_buttons/hardware_buttons.dart'; 47 | ``` 48 | 49 | In order to listen to volume button events, use `volumeButtonEvents.listen` as below: 50 | 51 | ```dart 52 | StreamSubscription _volumeButtonSubscription; 53 | 54 | @override 55 | void initState() { 56 | super.initState(); 57 | _volumeButtonSubscription = volumeButtonEvents.listen((VolumeButtonEvent event) { 58 | // do something 59 | // event is either VolumeButtonEvent.VOLUME_UP or VolumeButtonEvent.VOLUME_DOWN 60 | }); 61 | } 62 | 63 | @override 64 | void dispose() { 65 | super.dispose(); 66 | // be sure to cancel on dispose 67 | _volumeButtonSubscription?.cancel(); 68 | } 69 | ``` 70 | 71 | Besides volume button events, there are also: 72 | 1. Home button events, via `homeButtonEvents.listen`. 73 | 2. Lock button events, via `lockButtonEvents.listen`. 74 | 75 | ## Example 76 | 77 | Find the example wiring in the [example app](https://github.com/flutter-moum/flutter_hardware_buttons/blob/master/example/lib/main.dart). 78 | 79 | ## API details 80 | 81 | See the [hardware_buttons.dart](https://github.com/flutter-moum/flutter_hardware_buttons/blob/master/lib/hardware_buttons.dart) for more API details. 82 | 83 | ## Issues and feedback 84 | 85 | Please file [issues](https://github.com/flutter-moum/flutter_hardware_buttons/issues) to send feedback or report a bug. Thank you! 86 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'flutter.moum.hardware_buttons' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.3.60' 6 | repositories { 7 | google() 8 | jcenter() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:3.5.2' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | sourceSets { 31 | main.java.srcDirs += 'src/main/kotlin' 32 | } 33 | defaultConfig { 34 | minSdkVersion 16 35 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 36 | } 37 | lintOptions { 38 | disable 'InvalidPackage' 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 44 | } 45 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'hardware_buttons' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/kotlin/flutter/moum/hardware_buttons/EmptyActivityLifecycleCallbacks.kt: -------------------------------------------------------------------------------- 1 | package flutter.moum.hardware_buttons 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.os.Bundle 6 | 7 | 8 | open class EmptyActivityLifecycleCallbacks: Application.ActivityLifecycleCallbacks { 9 | override fun onActivityPaused(activity: Activity?) { } 10 | 11 | override fun onActivityResumed(activity: Activity?) { } 12 | 13 | override fun onActivityStarted(activity: Activity?) { } 14 | 15 | override fun onActivityDestroyed(activity: Activity?) { } 16 | 17 | override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { } 18 | 19 | override fun onActivityStopped(activity: Activity?) { } 20 | 21 | override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { } 22 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/flutter/moum/hardware_buttons/HardwareButtonsPlugin.kt: -------------------------------------------------------------------------------- 1 | package flutter.moum.hardware_buttons 2 | 3 | import io.flutter.plugin.common.EventChannel 4 | import io.flutter.plugin.common.PluginRegistry 5 | 6 | class HardwareButtonsPlugin { 7 | companion object { 8 | private const val VOLUME_BUTTON_CHANNEL_NAME = "flutter.moum.hardware_buttons.volume" 9 | private const val HOME_BUTTON_CHANNEL_NAME = "flutter.moum.hardware_buttons.home" 10 | private const val LOCK_BUTTON_CHANNEL_NAME = "flutter.moum.hardware_buttons.lock" 11 | 12 | @JvmStatic 13 | fun registerWith(registrar: PluginRegistry.Registrar) { 14 | val activity = registrar.activity() 15 | val application = activity.application 16 | 17 | registrar.addActivityResultListener(HardwareButtonsWatcherManager.getInstance(application, activity)) 18 | 19 | val volumeButtonChannel = EventChannel(registrar.messenger(), VOLUME_BUTTON_CHANNEL_NAME) 20 | volumeButtonChannel.setStreamHandler(VolumeButtonStreamHandler(activity)) 21 | 22 | val homeButtonChannel = EventChannel(registrar.messenger(), HOME_BUTTON_CHANNEL_NAME) 23 | homeButtonChannel.setStreamHandler(HomeButtonStreamHandler(activity)) 24 | 25 | val lockButtonChannel = EventChannel(registrar.messenger(), LOCK_BUTTON_CHANNEL_NAME) 26 | lockButtonChannel.setStreamHandler(LockButtonStreamHandler(activity)) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/kotlin/flutter/moum/hardware_buttons/HardwareButtonsWatcherManager.kt: -------------------------------------------------------------------------------- 1 | package flutter.moum.hardware_buttons 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.content.BroadcastReceiver 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.content.IntentFilter 9 | import android.graphics.PixelFormat 10 | import android.net.Uri 11 | import android.os.Build 12 | import android.provider.Settings 13 | import android.util.AttributeSet 14 | import android.view.KeyEvent 15 | import android.view.View 16 | import android.view.WindowManager 17 | import io.flutter.plugin.common.PluginRegistry 18 | 19 | 20 | // singleton object for managing various resources related with getting hardware button events. 21 | // those who need to listen to any hardware button events add listener to this single instance. 22 | // e.g. HardwareButtonsWatcherManager.getInstance(application, activity).addVolumeButtonListener(volumeButtonListener) 23 | class HardwareButtonsWatcherManager: PluginRegistry.ActivityResultListener { 24 | interface VolumeButtonListener { 25 | fun onVolumeButtonEvent(event: VolumeButtonEvent) 26 | } 27 | enum class VolumeButtonEvent(val value: Int) { 28 | VOLUME_UP(24), 29 | VOLUME_DOWN(25), 30 | } 31 | 32 | interface HomeButtonListener { 33 | fun onHomeButtonEvent() 34 | } 35 | 36 | interface LockButtonListener { 37 | fun onLockButtonEvent() 38 | } 39 | 40 | companion object { 41 | private const val REQUEST_CODE_OVERLAY_PERMISSION = 1000 42 | 43 | private val INSTANCE: HardwareButtonsWatcherManager by lazy { HardwareButtonsWatcherManager() } 44 | fun getInstance(application: Application, activity: Activity): HardwareButtonsWatcherManager { 45 | val instance = INSTANCE 46 | instance.application = application 47 | // set currentActivity to activity only when ActivityLifecycleCallbacks wasn't registered yet. 48 | // otherwise, currentActivity will be updated in ActivityLifecycleCallbacks. 49 | if (instance.activityLifecycleCallbacks == null) { 50 | instance.currentActivity = activity 51 | } 52 | instance.registerActivityLifecycleCallbacksIfNeeded() 53 | return instance 54 | } 55 | } 56 | 57 | private var application: Application? = null 58 | private var currentActivity: Activity? = null 59 | private var activityLifecycleCallbacks: Application.ActivityLifecycleCallbacks? = null 60 | 61 | private var keyWatcher: KeyWatcher? = null 62 | private var volumeButtonListeners: ArrayList = arrayListOf() 63 | 64 | private var homeButtonWatcher: HomeButtonWatcher? = null 65 | private var homeButtonListeners: ArrayList = arrayListOf() 66 | 67 | private var userDeniedDrawOverlaysPermission = false 68 | 69 | private var screenOffWatcher: ScreenOffWatcher? = null 70 | private var lockButtonListeners: ArrayList = arrayListOf() 71 | 72 | private fun registerActivityLifecycleCallbacksIfNeeded() { 73 | if (activityLifecycleCallbacks == null) { 74 | activityLifecycleCallbacks = object: EmptyActivityLifecycleCallbacks() { 75 | override fun onActivityStarted(activity: Activity?) { 76 | currentActivity = activity 77 | 78 | // attach necessary watchers 79 | attachKeyWatcherIfNeeded() 80 | attachHomeButtonWatcherIfNeeded() 81 | attachScreenOffWatcherIfNeeded() 82 | } 83 | 84 | override fun onActivityStopped(activity: Activity?) { 85 | if (currentActivity?.equals(activity) == true) { 86 | // detach all watchers 87 | detachKeyWatcher() 88 | detachHomeButtonWatcher() 89 | // we do NOT detach ScreenOffWatcher here, because ScreenOffWatcher callback comes in after onActivityStopped. 90 | // We'll detach it in ScreenOffWatcher's callback. 91 | } 92 | } 93 | 94 | override fun onActivityDestroyed(activity: Activity?) { 95 | if (currentActivity?.equals(activity) == true) { 96 | currentActivity = null 97 | userDeniedDrawOverlaysPermission = false 98 | 99 | // remove all listeners and detach all watchers 100 | // When flutter app finishes, it doesn't invoke StreamHandler's onCancel() callback properly, so 101 | // we should manually clean up resources (i.e. listeners) when activity state becomes invalid (in order to avoid memory leak). 102 | // related: https://github.com/flutter/plugins/pull/1992/files/04df85fef5a994d93d89b02b27bb7789ec452528#diff-efd825c710217272904545db4b2198e2 103 | volumeButtonListeners.clear() 104 | homeButtonListeners.clear() 105 | lockButtonListeners.clear() 106 | detachKeyWatcher() 107 | detachHomeButtonWatcher() 108 | detachScreenOffWatcher() 109 | } 110 | } 111 | } 112 | application?.registerActivityLifecycleCallbacks(activityLifecycleCallbacks) 113 | } 114 | } 115 | 116 | fun addVolumeButtonListener(listener: VolumeButtonListener) { 117 | if (!volumeButtonListeners.contains(listener)) { 118 | volumeButtonListeners.add(listener) 119 | } 120 | attachKeyWatcherIfNeeded() 121 | } 122 | 123 | fun addHomeButtonListener(listener: HomeButtonListener) { 124 | if (!homeButtonListeners.contains(listener)) { 125 | homeButtonListeners.add(listener) 126 | } 127 | attachHomeButtonWatcherIfNeeded() 128 | } 129 | 130 | fun addLockButtonListener(listener: LockButtonListener) { 131 | if (!lockButtonListeners.contains(listener)) { 132 | lockButtonListeners.add(listener) 133 | } 134 | attachScreenOffWatcherIfNeeded() 135 | } 136 | 137 | fun removeVolumeButtonListener(listener: VolumeButtonListener) { 138 | volumeButtonListeners.remove(listener) 139 | if (volumeButtonListeners.size == 0) { 140 | detachKeyWatcher() 141 | } 142 | } 143 | 144 | fun removeHomeButtonListener(listener: HomeButtonListener) { 145 | homeButtonListeners.remove(listener) 146 | if (homeButtonListeners.size == 0) { 147 | detachHomeButtonWatcher() 148 | } 149 | } 150 | 151 | fun removeLockButtonListener(listener: LockButtonListener) { 152 | lockButtonListeners.remove(listener) 153 | if (lockButtonListeners.size == 0) { 154 | detachScreenOffWatcher() 155 | } 156 | } 157 | 158 | private fun attachKeyWatcherIfNeeded() { 159 | val application = application ?: return 160 | val activity = currentActivity ?: return 161 | if (volumeButtonListeners.size > 0 && keyWatcher == null && !userDeniedDrawOverlaysPermission) { 162 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(application)) { 163 | val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + application.packageName)) 164 | activity.startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION) 165 | } else { 166 | keyWatcher = KeyWatcher(application.applicationContext, callback = { 167 | dispatchVolumeButtonEvent(it) 168 | currentActivity?.dispatchKeyEvent(it) 169 | }, findFocusCallback = { currentActivity?.window?.decorView?.rootView }) 170 | addOverlayWindowView(application, keyWatcher!!) 171 | } 172 | } 173 | } 174 | 175 | private fun attachHomeButtonWatcherIfNeeded() { 176 | val application = application ?: return 177 | if (homeButtonListeners.size > 0 && homeButtonWatcher == null) { 178 | homeButtonWatcher = HomeButtonWatcher { 179 | dispatchHomeButtonEvent() 180 | } 181 | val intentFilter = IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) 182 | application.registerReceiver(homeButtonWatcher, intentFilter) 183 | } 184 | } 185 | 186 | private fun attachScreenOffWatcherIfNeeded() { 187 | val application = application ?: return 188 | if (lockButtonListeners.size > 0 && screenOffWatcher == null) { 189 | screenOffWatcher = ScreenOffWatcher { 190 | if (it == ScreenOffWatcher.REASON_POWER_BUTTON) { 191 | dispatchLockButtonEvent() 192 | } 193 | detachScreenOffWatcher() 194 | } 195 | val intentFilter = IntentFilter(Intent.ACTION_SCREEN_OFF) 196 | application.registerReceiver(screenOffWatcher, intentFilter) 197 | } 198 | } 199 | 200 | private fun detachKeyWatcher() { 201 | val application = application ?: return 202 | val keyWatcher = keyWatcher ?: return 203 | removeOverlayWindowView(application, keyWatcher) 204 | this.keyWatcher = null 205 | } 206 | 207 | private fun detachHomeButtonWatcher() { 208 | val application = application ?: return 209 | val homeButtonWatcher = homeButtonWatcher ?: return 210 | application.unregisterReceiver(homeButtonWatcher) 211 | this.homeButtonWatcher = null 212 | } 213 | 214 | private fun detachScreenOffWatcher() { 215 | val application = application ?: return 216 | val screenOffWatcher = screenOffWatcher ?: return 217 | application.unregisterReceiver(screenOffWatcher) 218 | this.screenOffWatcher = null 219 | } 220 | 221 | private fun dispatchVolumeButtonEvent(keyEvent: KeyEvent) { 222 | if (keyEvent.action == KeyEvent.ACTION_DOWN) { 223 | val volumeButtonEvent = when (keyEvent.keyCode) { 224 | KeyEvent.KEYCODE_VOLUME_UP -> VolumeButtonEvent.VOLUME_UP 225 | KeyEvent.KEYCODE_VOLUME_DOWN -> VolumeButtonEvent.VOLUME_DOWN 226 | else -> null 227 | } 228 | if (volumeButtonEvent != null) { 229 | for (listener in volumeButtonListeners) { 230 | listener.onVolumeButtonEvent(volumeButtonEvent) 231 | } 232 | } 233 | } 234 | } 235 | 236 | private fun dispatchHomeButtonEvent() { 237 | for (listener in homeButtonListeners) { 238 | listener.onHomeButtonEvent() 239 | } 240 | } 241 | 242 | private fun dispatchLockButtonEvent() { 243 | for (listener in lockButtonListeners) { 244 | listener.onLockButtonEvent() 245 | } 246 | } 247 | 248 | private fun addOverlayWindowView(context: Context, view: View) { 249 | val windowType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 250 | WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 251 | else 252 | WindowManager.LayoutParams.TYPE_SYSTEM_ALERT 253 | 254 | val params = WindowManager.LayoutParams(0, 0, 255 | windowType, 256 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, 257 | PixelFormat.TRANSLUCENT) 258 | 259 | (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).addView(view, params) 260 | } 261 | 262 | private fun removeOverlayWindowView(context: Context, view: View) { 263 | (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).removeView(view) 264 | } 265 | 266 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { 267 | if (requestCode == REQUEST_CODE_OVERLAY_PERMISSION) { 268 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(application)) { 269 | userDeniedDrawOverlaysPermission = false 270 | attachKeyWatcherIfNeeded() 271 | } else { 272 | userDeniedDrawOverlaysPermission = true 273 | } 274 | return true 275 | } 276 | return false 277 | } 278 | } 279 | 280 | // simple view just to override dispatchKeyEvent 281 | private class KeyWatcher @JvmOverloads constructor( 282 | context: Context, 283 | attrs: AttributeSet? = null, 284 | defStyleAttr: Int = 0, 285 | private val callback: ((event: KeyEvent) -> Unit)? = null, 286 | private val findFocusCallback: (() -> View?)? = null 287 | ) : View(context, attrs, defStyleAttr) { 288 | override fun dispatchKeyEvent(event: KeyEvent): Boolean { 289 | callback?.invoke(event) 290 | return false 291 | } 292 | 293 | // without this, flutter app will not be able to show keyboard 294 | // because KeyWatcher view window takes all the focus.. 295 | override fun findFocus(): View? { 296 | return findFocusCallback?.invoke() 297 | } 298 | } 299 | 300 | private class HomeButtonWatcher(private val callback: () -> Unit): BroadcastReceiver() { 301 | companion object { 302 | private const val KEY_REASON = "reason" 303 | private const val REASON_HOME_KEY = "homekey" 304 | } 305 | 306 | override fun onReceive(context: Context?, intent: Intent?) { 307 | val intent = intent ?: return 308 | if (intent.action == Intent.ACTION_CLOSE_SYSTEM_DIALOGS) { 309 | if (intent.getStringExtra(KEY_REASON) == REASON_HOME_KEY) { 310 | callback() 311 | } 312 | } 313 | } 314 | } 315 | 316 | private class ScreenOffWatcher(private val callback: (reason: Int) -> Unit): BroadcastReceiver() { 317 | companion object { 318 | private const val KEY_REASON = "reason" 319 | // same value as PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, but since it's @hide, we can't access it. 320 | const val REASON_POWER_BUTTON = 4 321 | } 322 | 323 | override fun onReceive(context: Context?, intent: Intent?) { 324 | val intent = intent ?: return 325 | if (intent.action == Intent.ACTION_SCREEN_OFF) { 326 | callback(intent.getIntExtra(KEY_REASON, -1)) 327 | } 328 | } 329 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/flutter/moum/hardware_buttons/HomeButtonStreamHandler.kt: -------------------------------------------------------------------------------- 1 | package flutter.moum.hardware_buttons 2 | 3 | import android.app.Activity 4 | import io.flutter.plugin.common.EventChannel 5 | 6 | 7 | class HomeButtonStreamHandler(private val activity: Activity): EventChannel.StreamHandler { 8 | private val application = activity.application 9 | private var streamSink: EventChannel.EventSink? = null 10 | 11 | private val homeButtonListener = object: HardwareButtonsWatcherManager.HomeButtonListener { 12 | override fun onHomeButtonEvent() { 13 | streamSink?.success(0) 14 | } 15 | } 16 | 17 | override fun onListen(args: Any?, sink: EventChannel.EventSink?) { 18 | this.streamSink = sink 19 | HardwareButtonsWatcherManager.getInstance(application, activity).addHomeButtonListener(homeButtonListener) 20 | } 21 | 22 | override fun onCancel(args: Any?) { 23 | this.streamSink = null 24 | HardwareButtonsWatcherManager.getInstance(application, activity).removeHomeButtonListener(homeButtonListener) 25 | } 26 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/flutter/moum/hardware_buttons/LockButtonStreamHandler.kt: -------------------------------------------------------------------------------- 1 | package flutter.moum.hardware_buttons 2 | 3 | import android.app.Activity 4 | import io.flutter.plugin.common.EventChannel 5 | 6 | 7 | class LockButtonStreamHandler(private val activity: Activity): EventChannel.StreamHandler { 8 | private val application = activity.application 9 | private var streamSink: EventChannel.EventSink? = null 10 | 11 | private val lockButtonListener = object: HardwareButtonsWatcherManager.LockButtonListener { 12 | override fun onLockButtonEvent() { 13 | streamSink?.success(0) 14 | } 15 | } 16 | 17 | override fun onListen(args: Any?, sink: EventChannel.EventSink?) { 18 | this.streamSink = sink 19 | HardwareButtonsWatcherManager.getInstance(application, activity).addLockButtonListener(lockButtonListener) 20 | } 21 | 22 | override fun onCancel(args: Any?) { 23 | this.streamSink = null 24 | HardwareButtonsWatcherManager.getInstance(application, activity).removeLockButtonListener(lockButtonListener) 25 | } 26 | } -------------------------------------------------------------------------------- /android/src/main/kotlin/flutter/moum/hardware_buttons/VolumeButtonStreamHandler.kt: -------------------------------------------------------------------------------- 1 | package flutter.moum.hardware_buttons 2 | 3 | import android.app.Activity 4 | import io.flutter.plugin.common.EventChannel 5 | 6 | 7 | class VolumeButtonStreamHandler(private val activity: Activity): EventChannel.StreamHandler { 8 | private val application = activity.application 9 | private var streamSink: EventChannel.EventSink? = null 10 | 11 | private val volumeButtonListener = object: HardwareButtonsWatcherManager.VolumeButtonListener { 12 | override fun onVolumeButtonEvent(event: HardwareButtonsWatcherManager.VolumeButtonEvent) { 13 | streamSink?.success(event.value) 14 | } 15 | } 16 | 17 | override fun onListen(args: Any?, sink: EventChannel.EventSink?) { 18 | this.streamSink = sink 19 | HardwareButtonsWatcherManager.getInstance(application, activity).addVolumeButtonListener(volumeButtonListener) 20 | } 21 | 22 | // this function doesn't actually get called by flutter framework as of now: 2019/10/02 23 | override fun onCancel(args: Any?) { 24 | this.streamSink = null 25 | HardwareButtonsWatcherManager.getInstance(application, activity).removeVolumeButtonListener(volumeButtonListener) 26 | } 27 | } -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/Flutter/flutter_export_environment.sh 65 | **/ios/ServiceDefinitions.json 66 | **/ios/Runner/GeneratedPluginRegistrant.* 67 | 68 | # Exceptions to above rules. 69 | !**/ios/**/default.mode1v3 70 | !**/ios/**/default.mode2v3 71 | !**/ios/**/default.pbxuser 72 | !**/ios/**/default.perspectivev3 73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 74 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ```dart 4 | import 'dart:async'; 5 | 6 | import 'package:flutter/material.dart'; 7 | import 'package:hardware_buttons/hardware_buttons.dart' as HardwareButtons; 8 | 9 | void main() => runApp(MyApp()); 10 | 11 | class MyApp extends StatefulWidget { 12 | @override 13 | _MyAppState createState() => _MyAppState(); 14 | } 15 | 16 | class _MyAppState extends State { 17 | String _latestHardwareButtonEvent; 18 | 19 | StreamSubscription _volumeButtonSubscription; 20 | StreamSubscription _homeButtonSubscription; 21 | StreamSubscription _lockButtonSubscription; 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _volumeButtonSubscription = HardwareButtons.volumeButtonEvents.listen((event) { 27 | setState(() { 28 | _latestHardwareButtonEvent = event.toString(); 29 | }); 30 | }); 31 | 32 | _homeButtonSubscription = HardwareButtons.homeButtonEvents.listen((event) { 33 | setState(() { 34 | _latestHardwareButtonEvent = 'HOME_BUTTON'; 35 | }); 36 | }); 37 | 38 | _lockButtonSubscription = HardwareButtons.lockButtonEvents.listen((event) { 39 | setState(() { 40 | _latestHardwareButtonEvent = 'LOCK_BUTTON'; 41 | }); 42 | }); 43 | } 44 | 45 | @override 46 | void dispose() { 47 | super.dispose(); 48 | _volumeButtonSubscription?.cancel(); 49 | _homeButtonSubscription?.cancel(); 50 | _lockButtonSubscription?.cancel(); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return MaterialApp( 56 | home: Scaffold( 57 | appBar: AppBar( 58 | title: const Text('Plugin example app'), 59 | ), 60 | body: Center( 61 | child: Column( 62 | mainAxisSize: MainAxisSize.min, 63 | children: [ 64 | Text('Value: $_latestHardwareButtonEvent\n'), 65 | ], 66 | ), 67 | ), 68 | ), 69 | ); 70 | } 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "flutter.moum.hardware_buttons_example" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'androidx.test:runner:1.1.0' 66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 67 | } 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/flutter/moum/hardware_buttons_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package flutter.moum.hardware_buttons_example 2 | 3 | import android.os.Bundle 4 | 5 | import io.flutter.app.FlutterActivity 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | GeneratedPluginRegistrant.registerWith(this) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.2.71' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 19 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 20 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 21 | E2EF4809C27BC2CB2A77418B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F883456CDB30FFC096D3B3D9 /* Pods_Runner.framework */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXCopyFilesBuildPhase section */ 25 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 26 | isa = PBXCopyFilesBuildPhase; 27 | buildActionMask = 2147483647; 28 | dstPath = ""; 29 | dstSubfolderSpec = 10; 30 | files = ( 31 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 32 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 33 | ); 34 | name = "Embed Frameworks"; 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXCopyFilesBuildPhase section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 41 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 42 | 386E6FD45F078FB599056106 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 43 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 44 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 45 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 46 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 48 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 49 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 50 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 51 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 54 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 55 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | A2407BC8066C4FF075B2745B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 57 | F54E89F6A99B3B706E1D6EB1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 58 | F883456CDB30FFC096D3B3D9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 67 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 68 | E2EF4809C27BC2CB2A77418B /* Pods_Runner.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 25F7BA27244CCB35C4EFCC63 /* Pods */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | A2407BC8066C4FF075B2745B /* Pods-Runner.debug.xcconfig */, 79 | F54E89F6A99B3B706E1D6EB1 /* Pods-Runner.release.xcconfig */, 80 | 386E6FD45F078FB599056106 /* Pods-Runner.profile.xcconfig */, 81 | ); 82 | path = Pods; 83 | sourceTree = ""; 84 | }; 85 | 71CB126E2CA4AC4507FC3C3D /* Frameworks */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | F883456CDB30FFC096D3B3D9 /* Pods_Runner.framework */, 89 | ); 90 | name = Frameworks; 91 | sourceTree = ""; 92 | }; 93 | 9740EEB11CF90186004384FC /* Flutter */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 3B80C3931E831B6300D905FE /* App.framework */, 97 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 98 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 99 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 100 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 101 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 102 | ); 103 | name = Flutter; 104 | sourceTree = ""; 105 | }; 106 | 97C146E51CF9000F007C117D = { 107 | isa = PBXGroup; 108 | children = ( 109 | 9740EEB11CF90186004384FC /* Flutter */, 110 | 97C146F01CF9000F007C117D /* Runner */, 111 | 97C146EF1CF9000F007C117D /* Products */, 112 | 25F7BA27244CCB35C4EFCC63 /* Pods */, 113 | 71CB126E2CA4AC4507FC3C3D /* Frameworks */, 114 | ); 115 | sourceTree = ""; 116 | }; 117 | 97C146EF1CF9000F007C117D /* Products */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 97C146EE1CF9000F007C117D /* Runner.app */, 121 | ); 122 | name = Products; 123 | sourceTree = ""; 124 | }; 125 | 97C146F01CF9000F007C117D /* Runner */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 129 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 130 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 131 | 97C147021CF9000F007C117D /* Info.plist */, 132 | 97C146F11CF9000F007C117D /* Supporting Files */, 133 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 134 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 135 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 136 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 137 | ); 138 | path = Runner; 139 | sourceTree = ""; 140 | }; 141 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | /* End PBXGroup section */ 149 | 150 | /* Begin PBXNativeTarget section */ 151 | 97C146ED1CF9000F007C117D /* Runner */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 154 | buildPhases = ( 155 | BE046CFF4F8453C0D6F33C44 /* [CP] Check Pods Manifest.lock */, 156 | 9740EEB61CF901F6004384FC /* Run Script */, 157 | 97C146EA1CF9000F007C117D /* Sources */, 158 | 97C146EB1CF9000F007C117D /* Frameworks */, 159 | 97C146EC1CF9000F007C117D /* Resources */, 160 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 161 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 162 | 00D80428ABC595B8E8D05737 /* [CP] Embed Pods Frameworks */, 163 | ); 164 | buildRules = ( 165 | ); 166 | dependencies = ( 167 | ); 168 | name = Runner; 169 | productName = Runner; 170 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 171 | productType = "com.apple.product-type.application"; 172 | }; 173 | /* End PBXNativeTarget section */ 174 | 175 | /* Begin PBXProject section */ 176 | 97C146E61CF9000F007C117D /* Project object */ = { 177 | isa = PBXProject; 178 | attributes = { 179 | LastUpgradeCheck = 1020; 180 | ORGANIZATIONNAME = "The Chromium Authors"; 181 | TargetAttributes = { 182 | 97C146ED1CF9000F007C117D = { 183 | CreatedOnToolsVersion = 7.3.1; 184 | DevelopmentTeam = NF7D3MF6YF; 185 | LastSwiftMigration = 1030; 186 | }; 187 | }; 188 | }; 189 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 190 | compatibilityVersion = "Xcode 3.2"; 191 | developmentRegion = en; 192 | hasScannedForEncodings = 0; 193 | knownRegions = ( 194 | en, 195 | Base, 196 | ); 197 | mainGroup = 97C146E51CF9000F007C117D; 198 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 199 | projectDirPath = ""; 200 | projectRoot = ""; 201 | targets = ( 202 | 97C146ED1CF9000F007C117D /* Runner */, 203 | ); 204 | }; 205 | /* End PBXProject section */ 206 | 207 | /* Begin PBXResourcesBuildPhase section */ 208 | 97C146EC1CF9000F007C117D /* Resources */ = { 209 | isa = PBXResourcesBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 213 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 214 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 215 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 216 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | }; 220 | /* End PBXResourcesBuildPhase section */ 221 | 222 | /* Begin PBXShellScriptBuildPhase section */ 223 | 00D80428ABC595B8E8D05737 /* [CP] Embed Pods Frameworks */ = { 224 | isa = PBXShellScriptBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | ); 228 | inputPaths = ( 229 | ); 230 | name = "[CP] Embed Pods Frameworks"; 231 | outputPaths = ( 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | shellPath = /bin/sh; 235 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 236 | showEnvVarsInLog = 0; 237 | }; 238 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 239 | isa = PBXShellScriptBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | ); 243 | inputPaths = ( 244 | ); 245 | name = "Thin Binary"; 246 | outputPaths = ( 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | shellPath = /bin/sh; 250 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 251 | }; 252 | 9740EEB61CF901F6004384FC /* Run Script */ = { 253 | isa = PBXShellScriptBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | inputPaths = ( 258 | ); 259 | name = "Run Script"; 260 | outputPaths = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | shellPath = /bin/sh; 264 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 265 | }; 266 | BE046CFF4F8453C0D6F33C44 /* [CP] Check Pods Manifest.lock */ = { 267 | isa = PBXShellScriptBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | ); 271 | inputFileListPaths = ( 272 | ); 273 | inputPaths = ( 274 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 275 | "${PODS_ROOT}/Manifest.lock", 276 | ); 277 | name = "[CP] Check Pods Manifest.lock"; 278 | outputFileListPaths = ( 279 | ); 280 | outputPaths = ( 281 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | shellPath = /bin/sh; 285 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 286 | showEnvVarsInLog = 0; 287 | }; 288 | /* End PBXShellScriptBuildPhase section */ 289 | 290 | /* Begin PBXSourcesBuildPhase section */ 291 | 97C146EA1CF9000F007C117D /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 296 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXVariantGroup section */ 303 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | 97C146FB1CF9000F007C117D /* Base */, 307 | ); 308 | name = Main.storyboard; 309 | sourceTree = ""; 310 | }; 311 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | 97C147001CF9000F007C117D /* Base */, 315 | ); 316 | name = LaunchScreen.storyboard; 317 | sourceTree = ""; 318 | }; 319 | /* End PBXVariantGroup section */ 320 | 321 | /* Begin XCBuildConfiguration section */ 322 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 323 | isa = XCBuildConfiguration; 324 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 329 | CLANG_CXX_LIBRARY = "libc++"; 330 | CLANG_ENABLE_MODULES = YES; 331 | CLANG_ENABLE_OBJC_ARC = YES; 332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 333 | CLANG_WARN_BOOL_CONVERSION = YES; 334 | CLANG_WARN_COMMA = YES; 335 | CLANG_WARN_CONSTANT_CONVERSION = YES; 336 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 338 | CLANG_WARN_EMPTY_BODY = YES; 339 | CLANG_WARN_ENUM_CONVERSION = YES; 340 | CLANG_WARN_INFINITE_RECURSION = YES; 341 | CLANG_WARN_INT_CONVERSION = YES; 342 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 344 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 346 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 347 | CLANG_WARN_STRICT_PROTOTYPES = YES; 348 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 349 | CLANG_WARN_UNREACHABLE_CODE = YES; 350 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 351 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 352 | COPY_PHASE_STRIP = NO; 353 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 354 | ENABLE_NS_ASSERTIONS = NO; 355 | ENABLE_STRICT_OBJC_MSGSEND = YES; 356 | GCC_C_LANGUAGE_STANDARD = gnu99; 357 | GCC_NO_COMMON_BLOCKS = YES; 358 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 359 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 360 | GCC_WARN_UNDECLARED_SELECTOR = YES; 361 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 362 | GCC_WARN_UNUSED_FUNCTION = YES; 363 | GCC_WARN_UNUSED_VARIABLE = YES; 364 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 365 | MTL_ENABLE_DEBUG_INFO = NO; 366 | SDKROOT = iphoneos; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | VALIDATE_PRODUCT = YES; 369 | }; 370 | name = Profile; 371 | }; 372 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 373 | isa = XCBuildConfiguration; 374 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 375 | buildSettings = { 376 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 377 | CLANG_ENABLE_MODULES = YES; 378 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 379 | DEVELOPMENT_TEAM = NF7D3MF6YF; 380 | ENABLE_BITCODE = NO; 381 | FRAMEWORK_SEARCH_PATHS = ( 382 | "$(inherited)", 383 | "$(PROJECT_DIR)/Flutter", 384 | ); 385 | INFOPLIST_FILE = Runner/Info.plist; 386 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 387 | LIBRARY_SEARCH_PATHS = ( 388 | "$(inherited)", 389 | "$(PROJECT_DIR)/Flutter", 390 | ); 391 | PRODUCT_BUNDLE_IDENTIFIER = flutter.moum.hardwareButtonsExample; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 394 | SWIFT_VERSION = 4.2; 395 | VERSIONING_SYSTEM = "apple-generic"; 396 | }; 397 | name = Profile; 398 | }; 399 | 97C147031CF9000F007C117D /* Debug */ = { 400 | isa = XCBuildConfiguration; 401 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 402 | buildSettings = { 403 | ALWAYS_SEARCH_USER_PATHS = NO; 404 | CLANG_ANALYZER_NONNULL = YES; 405 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 406 | CLANG_CXX_LIBRARY = "libc++"; 407 | CLANG_ENABLE_MODULES = YES; 408 | CLANG_ENABLE_OBJC_ARC = YES; 409 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 410 | CLANG_WARN_BOOL_CONVERSION = YES; 411 | CLANG_WARN_COMMA = YES; 412 | CLANG_WARN_CONSTANT_CONVERSION = YES; 413 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 414 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INFINITE_RECURSION = YES; 418 | CLANG_WARN_INT_CONVERSION = YES; 419 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 420 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 421 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 422 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 423 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 424 | CLANG_WARN_STRICT_PROTOTYPES = YES; 425 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 426 | CLANG_WARN_UNREACHABLE_CODE = YES; 427 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 428 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 429 | COPY_PHASE_STRIP = NO; 430 | DEBUG_INFORMATION_FORMAT = dwarf; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | ENABLE_TESTABILITY = YES; 433 | GCC_C_LANGUAGE_STANDARD = gnu99; 434 | GCC_DYNAMIC_NO_PIC = NO; 435 | GCC_NO_COMMON_BLOCKS = YES; 436 | GCC_OPTIMIZATION_LEVEL = 0; 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 442 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 443 | GCC_WARN_UNDECLARED_SELECTOR = YES; 444 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 445 | GCC_WARN_UNUSED_FUNCTION = YES; 446 | GCC_WARN_UNUSED_VARIABLE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 448 | MTL_ENABLE_DEBUG_INFO = YES; 449 | ONLY_ACTIVE_ARCH = YES; 450 | SDKROOT = iphoneos; 451 | TARGETED_DEVICE_FAMILY = "1,2"; 452 | }; 453 | name = Debug; 454 | }; 455 | 97C147041CF9000F007C117D /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 458 | buildSettings = { 459 | ALWAYS_SEARCH_USER_PATHS = NO; 460 | CLANG_ANALYZER_NONNULL = YES; 461 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 462 | CLANG_CXX_LIBRARY = "libc++"; 463 | CLANG_ENABLE_MODULES = YES; 464 | CLANG_ENABLE_OBJC_ARC = YES; 465 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 466 | CLANG_WARN_BOOL_CONVERSION = YES; 467 | CLANG_WARN_COMMA = YES; 468 | CLANG_WARN_CONSTANT_CONVERSION = YES; 469 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 470 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 471 | CLANG_WARN_EMPTY_BODY = YES; 472 | CLANG_WARN_ENUM_CONVERSION = YES; 473 | CLANG_WARN_INFINITE_RECURSION = YES; 474 | CLANG_WARN_INT_CONVERSION = YES; 475 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 476 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 477 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 478 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 479 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 480 | CLANG_WARN_STRICT_PROTOTYPES = YES; 481 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 482 | CLANG_WARN_UNREACHABLE_CODE = YES; 483 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 484 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 485 | COPY_PHASE_STRIP = NO; 486 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 487 | ENABLE_NS_ASSERTIONS = NO; 488 | ENABLE_STRICT_OBJC_MSGSEND = YES; 489 | GCC_C_LANGUAGE_STANDARD = gnu99; 490 | GCC_NO_COMMON_BLOCKS = YES; 491 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 492 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 493 | GCC_WARN_UNDECLARED_SELECTOR = YES; 494 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 495 | GCC_WARN_UNUSED_FUNCTION = YES; 496 | GCC_WARN_UNUSED_VARIABLE = YES; 497 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 498 | MTL_ENABLE_DEBUG_INFO = NO; 499 | SDKROOT = iphoneos; 500 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | VALIDATE_PRODUCT = YES; 503 | }; 504 | name = Release; 505 | }; 506 | 97C147061CF9000F007C117D /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | CLANG_ENABLE_MODULES = YES; 512 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 513 | DEVELOPMENT_TEAM = NF7D3MF6YF; 514 | ENABLE_BITCODE = NO; 515 | FRAMEWORK_SEARCH_PATHS = ( 516 | "$(inherited)", 517 | "$(PROJECT_DIR)/Flutter", 518 | ); 519 | INFOPLIST_FILE = Runner/Info.plist; 520 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 521 | LIBRARY_SEARCH_PATHS = ( 522 | "$(inherited)", 523 | "$(PROJECT_DIR)/Flutter", 524 | ); 525 | PRODUCT_BUNDLE_IDENTIFIER = flutter.moum.hardwareButtonsExample; 526 | PRODUCT_NAME = "$(TARGET_NAME)"; 527 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 528 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 529 | SWIFT_VERSION = 4.2; 530 | VERSIONING_SYSTEM = "apple-generic"; 531 | }; 532 | name = Debug; 533 | }; 534 | 97C147071CF9000F007C117D /* Release */ = { 535 | isa = XCBuildConfiguration; 536 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 537 | buildSettings = { 538 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 539 | CLANG_ENABLE_MODULES = YES; 540 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 541 | DEVELOPMENT_TEAM = NF7D3MF6YF; 542 | ENABLE_BITCODE = NO; 543 | FRAMEWORK_SEARCH_PATHS = ( 544 | "$(inherited)", 545 | "$(PROJECT_DIR)/Flutter", 546 | ); 547 | INFOPLIST_FILE = Runner/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 549 | LIBRARY_SEARCH_PATHS = ( 550 | "$(inherited)", 551 | "$(PROJECT_DIR)/Flutter", 552 | ); 553 | PRODUCT_BUNDLE_IDENTIFIER = flutter.moum.hardwareButtonsExample; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 556 | SWIFT_VERSION = 4.2; 557 | VERSIONING_SYSTEM = "apple-generic"; 558 | }; 559 | name = Release; 560 | }; 561 | /* End XCBuildConfiguration section */ 562 | 563 | /* Begin XCConfigurationList section */ 564 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 565 | isa = XCConfigurationList; 566 | buildConfigurations = ( 567 | 97C147031CF9000F007C117D /* Debug */, 568 | 97C147041CF9000F007C117D /* Release */, 569 | 249021D3217E4FDB00AE95B9 /* Profile */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | 97C147061CF9000F007C117D /* Debug */, 578 | 97C147071CF9000F007C117D /* Release */, 579 | 249021D4217E4FDB00AE95B9 /* Profile */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | /* End XCConfigurationList section */ 585 | }; 586 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 587 | } 588 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application(_ application: UIApplication, 7 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 8 | 9 | GeneratedPluginRegistrant.register(with: self) 10 | return super.application(application, 11 | didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | hardware_buttons_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | LSApplicationCategoryType 28 | 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:hardware_buttons/hardware_buttons.dart' as HardwareButtons; 5 | 6 | void main() => runApp(MyApp()); 7 | 8 | class MyApp extends StatefulWidget { 9 | @override 10 | _MyAppState createState() => _MyAppState(); 11 | } 12 | 13 | class _MyAppState extends State { 14 | String _latestHardwareButtonEvent; 15 | 16 | StreamSubscription _volumeButtonSubscription; 17 | StreamSubscription _homeButtonSubscription; 18 | StreamSubscription _lockButtonSubscription; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _volumeButtonSubscription = HardwareButtons.volumeButtonEvents.listen((event) { 24 | setState(() { 25 | _latestHardwareButtonEvent = event.toString(); 26 | }); 27 | }); 28 | 29 | _homeButtonSubscription = HardwareButtons.homeButtonEvents.listen((event) { 30 | setState(() { 31 | _latestHardwareButtonEvent = 'HOME_BUTTON'; 32 | }); 33 | }); 34 | 35 | _lockButtonSubscription = HardwareButtons.lockButtonEvents.listen((event) { 36 | setState(() { 37 | _latestHardwareButtonEvent = 'LOCK_BUTTON'; 38 | }); 39 | }); 40 | } 41 | 42 | @override 43 | void dispose() { 44 | super.dispose(); 45 | _volumeButtonSubscription?.cancel(); 46 | _homeButtonSubscription?.cancel(); 47 | _lockButtonSubscription?.cancel(); 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | return MaterialApp( 53 | home: Scaffold( 54 | appBar: AppBar( 55 | title: const Text('Plugin example app'), 56 | ), 57 | body: Center( 58 | child: Column( 59 | mainAxisSize: MainAxisSize.min, 60 | children: [ 61 | Text('Value: $_latestHardwareButtonEvent\n'), 62 | ], 63 | ), 64 | ), 65 | ), 66 | ); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hardware_buttons_example 2 | description: Demonstrates how to use the hardware_buttons plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.1.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | # The following adds the Cupertino Icons font to your application. 13 | # Use with the CupertinoIcons class for iOS style icons. 14 | cupertino_icons: ^0.1.2 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | hardware_buttons: 21 | path: ../ 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://dart.dev/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | 29 | # The following line ensures that the Material Icons font is 30 | # included with your application, so that you can use the icons in 31 | # the material Icons class. 32 | uses-material-design: true 33 | 34 | # To add assets to your application, add an assets section, like this: 35 | # assets: 36 | # - images/a_dot_burr.jpeg 37 | # - images/a_dot_ham.jpeg 38 | 39 | # An image asset can refer to one or more resolution-specific "variants", see 40 | # https://flutter.dev/assets-and-images/#resolution-aware. 41 | 42 | # For details regarding adding assets from package dependencies, see 43 | # https://flutter.dev/assets-and-images/#from-packages 44 | 45 | # To add custom fonts to your application, add a fonts section here, 46 | # in this "flutter" section. Each entry in this list should have a 47 | # "family" key with the font family name, and a "fonts" key with a 48 | # list giving the asset and other descriptors for the font. For 49 | # example: 50 | # fonts: 51 | # - family: Schyler 52 | # fonts: 53 | # - asset: fonts/Schyler-Regular.ttf 54 | # - asset: fonts/Schyler-Italic.ttf 55 | # style: italic 56 | # - family: Trajan Pro 57 | # fonts: 58 | # - asset: fonts/TrajanPro.ttf 59 | # - asset: fonts/TrajanPro_Bold.ttf 60 | # weight: 700 61 | # 62 | # For details regarding fonts from package dependencies, 63 | # see https://flutter.dev/custom-fonts/#from-packages 64 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:hardware_buttons_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that platform version is retrieved. 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => widget is Text && 22 | widget.data.startsWith('Running on:'), 23 | ), 24 | findsOneWidget, 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flutter-moum/flutter_hardware_buttons/00d806f3c1abe79eddcfc879da011607f77cafd3/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/HardwareButtonsPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface HardwareButtonsPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/HardwareButtonsPlugin.m: -------------------------------------------------------------------------------- 1 | #import "HardwareButtonsPlugin.h" 2 | #import 3 | 4 | @implementation HardwareButtonsPlugin 5 | + (void)registerWithRegistrar:(NSObject*)registrar { 6 | [SwiftHardwareButtonsPlugin registerWithRegistrar:registrar]; 7 | } 8 | @end 9 | -------------------------------------------------------------------------------- /ios/Classes/HomeButtonStreamHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeButtonStreamHandler.swift 3 | // hardware_buttons 4 | // 5 | // Created by 양어진 on 03/10/2019. 6 | // 7 | 8 | import Foundation 9 | 10 | public class HomeButtonStreamHandler: NSObject, FlutterStreamHandler, HomeListener { 11 | 12 | private var eventSink: FlutterEventSink? 13 | 14 | public func onListen(withArguments arguments: Any?, 15 | eventSink events: @escaping FlutterEventSink) -> FlutterError? { 16 | self.eventSink = events 17 | singleHomeAndLockObserver.addHomeListener(listener: self) 18 | return nil 19 | } 20 | 21 | public func onCancel(withArguments arguments: Any?) -> FlutterError? { 22 | self.eventSink = nil 23 | singleHomeAndLockObserver.removeHomeListener(listener: self) 24 | return nil 25 | } 26 | 27 | func onEvent() { 28 | self.eventSink?(0) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ios/Classes/LockButtonStreamHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LockButtonStreamHandler.swift 3 | // hardware_buttons 4 | // 5 | // Created by 양어진 on 08/10/2019. 6 | // 7 | 8 | import Foundation 9 | 10 | public class LockButtonStreamHandler: NSObject, FlutterStreamHandler, LockListener { 11 | 12 | private var eventSink: FlutterEventSink? 13 | 14 | public func onListen(withArguments arguments: Any?, 15 | eventSink events: @escaping FlutterEventSink) -> FlutterError? { 16 | singleHomeAndLockObserver.addLockListener(listener: self) 17 | self.eventSink = events 18 | return nil 19 | } 20 | 21 | public func onCancel(withArguments arguments: Any?) -> FlutterError? { 22 | singleHomeAndLockObserver.removeLockListener(listener: self) 23 | eventSink = nil 24 | return nil 25 | } 26 | 27 | func onEvent() { 28 | self.eventSink?(0) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ios/Classes/SingleHomeAndLockObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SingleHomeAndLockObserver.swift 3 | // hardware_buttons 4 | // 5 | // Created by 양어진 on 15/10/2019. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol LockListener { 11 | func onEvent() 12 | } 13 | 14 | protocol HomeListener { 15 | func onEvent() 16 | } 17 | 18 | let singleHomeAndLockObserver = SingleHomeAndLockObserver() 19 | 20 | class SingleHomeAndLockObserver { 21 | 22 | private var lockListener: LockListener? 23 | private var homeListener: HomeListener? 24 | 25 | private let notificationCenter = NotificationCenter.default 26 | private var homeTask: DispatchWorkItem? 27 | 28 | // Add Lock Listener 29 | public func addLockListener(listener: LockListener) { 30 | if lockListener == nil { 31 | lockListener = listener 32 | if homeListener == nil { 33 | registerObserver() 34 | } 35 | } 36 | } 37 | 38 | // Remove Lock Listener 39 | public func removeLockListener(listener: LockListener) { 40 | lockListener = nil 41 | if homeListener == nil { 42 | unregisterObserver() 43 | } 44 | } 45 | 46 | // Add Home Listener 47 | public func addHomeListener(listener: HomeListener) { 48 | if homeListener == nil { 49 | homeListener = listener 50 | if lockListener == nil { 51 | registerObserver() 52 | } 53 | } 54 | } 55 | 56 | // Remove Home Listener 57 | public func removeHomeListener(listener: HomeListener) { 58 | homeListener = nil 59 | if lockListener == nil { 60 | unregisterObserver() 61 | } 62 | } 63 | 64 | private func registerObserver() { 65 | // Lock Button 66 | CFNotificationCenterAddObserver( 67 | CFNotificationCenterGetDarwinNotifyCenter(), 68 | Unmanaged.passUnretained(self).toOpaque(), 69 | displayStatusChangedCallback, 70 | "com.apple.springboard.lockcomplete" as CFString, 71 | nil, 72 | .deliverImmediately) 73 | 74 | // Home Button 75 | notificationCenter.addObserver( 76 | self, 77 | selector: #selector(applicationWillResignActive), 78 | name: UIApplication.willResignActiveNotification, 79 | object: nil) 80 | } 81 | 82 | private func unregisterObserver() { 83 | // Lock Button 84 | CFNotificationCenterRemoveObserver( 85 | CFNotificationCenterGetLocalCenter(), 86 | Unmanaged.passUnretained(self).toOpaque(), 87 | nil, 88 | nil) 89 | 90 | // Home Button 91 | notificationCenter.removeObserver(self, 92 | name: UIApplication.willResignActiveNotification, 93 | object: nil) 94 | } 95 | 96 | // Lock Button Detection 97 | private let displayStatusChangedCallback: CFNotificationCallback = { _, cfObserver, cfName, _, _ in 98 | guard let lockState = cfName?.rawValue as String? else { 99 | return 100 | } 101 | 102 | let catcher = Unmanaged 103 | .fromOpaque(UnsafeRawPointer(OpaquePointer(cfObserver)!)) 104 | .takeUnretainedValue() 105 | catcher.displayStatusChanged(lockState) 106 | } 107 | 108 | func displayStatusChanged(_ lockState: String) { 109 | if lockState == "com.apple.springboard.lockcomplete" { 110 | homeTask?.cancel() 111 | lockListener?.onEvent() 112 | } 113 | } 114 | 115 | // Home Button Detection 116 | @objc func applicationWillResignActive(){ 117 | homeTask = DispatchWorkItem { 118 | self.homeListener?.onEvent() 119 | } 120 | 121 | DispatchQueue.main.asyncAfter( 122 | deadline: DispatchTime.now() + 0.5, 123 | execute: homeTask ?? DispatchWorkItem(block: { 124 | print("error") 125 | })) 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /ios/Classes/SwiftHardwareButtonsPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | enum ChannelName { 5 | static let volume = "flutter.moum.hardware_buttons.volume" 6 | static let home = "flutter.moum.hardware_buttons.home" 7 | static let lock = "flutter.moum.hardware_buttons.lock" 8 | } 9 | 10 | public class SwiftHardwareButtonsPlugin: NSObject, FlutterPlugin { 11 | 12 | public static func register(with registrar: FlutterPluginRegistrar) { 13 | 14 | // VolumeButton 15 | let volumeInstance = VolumeButtenStreamHandler() 16 | let volumeStream = FlutterEventChannel(name: ChannelName.volume, 17 | binaryMessenger: registrar.messenger()) 18 | volumeStream.setStreamHandler(volumeInstance) 19 | 20 | // HomeButton 21 | let homeInstance = HomeButtonStreamHandler() 22 | let homeStream = FlutterEventChannel(name: ChannelName.home, 23 | binaryMessenger: registrar.messenger()) 24 | homeStream.setStreamHandler(homeInstance) 25 | 26 | // LockButton 27 | let lockInstance = LockButtonStreamHandler() 28 | let lockStream = FlutterEventChannel(name: ChannelName.lock, 29 | binaryMessenger: registrar.messenger()) 30 | lockStream.setStreamHandler(lockInstance) 31 | 32 | } 33 | 34 | public func handle(_ call: FlutterMethodCall, 35 | result: @escaping FlutterResult) { 36 | result("iOS " + UIDevice.current.systemVersion) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /ios/Classes/VolumeButtenStreamHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VolumeButtenStreamHandler.swift 3 | // hardware_buttons 4 | // 5 | // Created by 양어진 on 03/10/2019. 6 | // 7 | 8 | import Foundation 9 | import AVFoundation 10 | 11 | public class VolumeButtenStreamHandler: NSObject, FlutterStreamHandler { 12 | 13 | private var eventSink: FlutterEventSink? 14 | private var volumeLevel: Float = 0.0 15 | private var isObserving: Bool = false 16 | private let notificationCenter = NotificationCenter.default 17 | private let audioSession = AVAudioSession.sharedInstance() 18 | 19 | public func onListen(withArguments arguments: Any?, 20 | eventSink events: @escaping FlutterEventSink) -> FlutterError? { 21 | self.eventSink = events 22 | registerVolumeObserver() 23 | return nil 24 | } 25 | 26 | public func onCancel(withArguments arguments: Any?) -> FlutterError? { 27 | eventSink = nil 28 | removeVolumeObserver() 29 | return nil 30 | } 31 | 32 | // Register Volume Notification 33 | private func registerVolumeObserver() { 34 | activateAudioSession() 35 | notificationCenter.addObserver( 36 | self, 37 | selector: #selector(activateAudioSession), 38 | name: UIApplication.didBecomeActiveNotification, 39 | object: nil) 40 | } 41 | 42 | // Remove Volume Notification 43 | private func removeVolumeObserver() { 44 | audioSession.removeObserver(self, 45 | forKeyPath: "outputVolume") 46 | notificationCenter.removeObserver(self, 47 | name: UIApplication.didBecomeActiveNotification, 48 | object: nil) 49 | } 50 | 51 | @objc func activateAudioSession(){ 52 | do { 53 | try audioSession.setCategory(AVAudioSession.Category.ambient) 54 | try audioSession.setActive(true) 55 | if !isObserving { 56 | audioSession.addObserver(self, 57 | forKeyPath: "outputVolume", 58 | options: .new, 59 | context: nil) 60 | isObserving = true 61 | } 62 | volumeLevel = audioSession.outputVolume 63 | } catch { 64 | print("error") 65 | } 66 | } 67 | 68 | override public func observeValue(forKeyPath keyPath: String?, 69 | of object: Any?, 70 | change: [NSKeyValueChangeKey: Any]?, 71 | context: UnsafeMutableRawPointer?) { 72 | if keyPath == "outputVolume" { 73 | if audioSession.outputVolume > volumeLevel { 74 | eventSink?(24) 75 | } 76 | if audioSession.outputVolume < volumeLevel { 77 | eventSink?(25) 78 | } 79 | volumeLevel = audioSession.outputVolume 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ios/hardware_buttons.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 3 | # 4 | Pod::Spec.new do |s| 5 | s.name = 'hardware_buttons' 6 | s.version = '0.0.1' 7 | s.summary = 'A new flutter plugin project.' 8 | s.description = <<-DESC 9 | A new flutter plugin project. 10 | DESC 11 | s.homepage = 'http://example.com' 12 | s.license = { :file => '../LICENSE' } 13 | s.author = { 'Your Company' => 'email@example.com' } 14 | s.source = { :path => '.' } 15 | s.source_files = 'Classes/**/*' 16 | s.public_header_files = 'Classes/**/*.h' 17 | s.dependency 'Flutter' 18 | 19 | s.ios.deployment_target = '8.0' 20 | end 21 | 22 | -------------------------------------------------------------------------------- /lib/hardware_buttons.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | const _VOLUME_BUTTON_CHANNEL_NAME = 'flutter.moum.hardware_buttons.volume'; 6 | const _HOME_BUTTON_CHANNEL_NAME = 'flutter.moum.hardware_buttons.home'; 7 | const _LOCK_BUTTON_CHANNEL_NAME = 'flutter.moum.hardware_buttons.lock'; 8 | 9 | const EventChannel _volumeButtonEventChannel = 10 | EventChannel(_VOLUME_BUTTON_CHANNEL_NAME); 11 | const EventChannel _homeButtonEventChannel = 12 | EventChannel(_HOME_BUTTON_CHANNEL_NAME); 13 | const EventChannel _lockButtonEventChannel = 14 | EventChannel(_LOCK_BUTTON_CHANNEL_NAME); 15 | 16 | Stream _volumeButtonEvents; 17 | 18 | /// A broadcast stream of volume button events 19 | Stream get volumeButtonEvents { 20 | if (_volumeButtonEvents == null) { 21 | _volumeButtonEvents = _volumeButtonEventChannel 22 | .receiveBroadcastStream() 23 | .map((dynamic event) => _eventToVolumeButtonEvent(event)); 24 | } 25 | return _volumeButtonEvents; 26 | } 27 | 28 | Stream _homeButtonEvents; 29 | 30 | /// A broadcast stream of home button events 31 | Stream get homeButtonEvents { 32 | if (_homeButtonEvents == null) { 33 | _homeButtonEvents = _homeButtonEventChannel 34 | .receiveBroadcastStream() 35 | .map((dynamic event) => HomeButtonEvent.INSTANCE); 36 | } 37 | return _homeButtonEvents; 38 | } 39 | 40 | Stream _lockButtonEvents; 41 | 42 | /// A broadcast stream of lock button events 43 | Stream get lockButtonEvents { 44 | if (_lockButtonEvents == null) { 45 | _lockButtonEvents = _lockButtonEventChannel 46 | .receiveBroadcastStream() 47 | .map((dynamic event) => LockButtonEvent.INSTANCE); 48 | } 49 | return _lockButtonEvents; 50 | } 51 | 52 | /// Volume button events 53 | /// Applies both to device and earphone buttons 54 | enum VolumeButtonEvent { 55 | /// Volume Up button event 56 | VOLUME_UP, 57 | 58 | /// Volume Down button event 59 | VOLUME_DOWN, 60 | } 61 | 62 | VolumeButtonEvent _eventToVolumeButtonEvent(dynamic event) { 63 | if (event == 24) { 64 | return VolumeButtonEvent.VOLUME_UP; 65 | } else if (event == 25) { 66 | return VolumeButtonEvent.VOLUME_DOWN; 67 | } else { 68 | throw Exception('Invalid volume button event'); 69 | } 70 | } 71 | 72 | /// Home button event 73 | /// On Android, this gets called immediately after user presses the home button. 74 | /// On iOS, this gets called when user presses the home button and returns to the app. 75 | class HomeButtonEvent { 76 | static const INSTANCE = HomeButtonEvent(); 77 | 78 | const HomeButtonEvent(); 79 | } 80 | 81 | /// Lock button event 82 | class LockButtonEvent { 83 | static const INSTANCE = LockButtonEvent(); 84 | 85 | const LockButtonEvent(); 86 | } 87 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hardware_buttons 2 | description: A Flutter plugin for iOS and Android for detecting various hardware buttons such as volume and home button. 3 | version: 1.0.0 4 | authors: 5 | - Hansol Lee 6 | - Eojin Yang 7 | - FlutterMoum Group 8 | 9 | homepage: https://github.com/flutter-moum/flutter_hardware_buttons 10 | 11 | environment: 12 | sdk: ">=2.1.0 <3.0.0" 13 | 14 | dependencies: 15 | flutter: 16 | sdk: flutter 17 | 18 | dev_dependencies: 19 | flutter_test: 20 | sdk: flutter 21 | 22 | flutter: 23 | plugin: 24 | androidPackage: flutter.moum.hardware_buttons 25 | pluginClass: HardwareButtonsPlugin 26 | -------------------------------------------------------------------------------- /test/hardware_buttons_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | const MethodChannel channel = MethodChannel('hardware_buttons'); 6 | 7 | setUp(() { 8 | channel.setMockMethodCallHandler((MethodCall methodCall) async { 9 | return '42'; 10 | }); 11 | }); 12 | 13 | tearDown(() { 14 | channel.setMockMethodCallHandler(null); 15 | }); 16 | 17 | // test('getPlatformVersion', () async { 18 | // expect(await HardwareButtons.platformVersion, '42'); 19 | // }); 20 | } 21 | --------------------------------------------------------------------------------