├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ ├── keyboard_fragment.xml │ │ │ ├── d_pad_fragment.xml │ │ │ ├── configuration_fragment.xml │ │ │ ├── mouse_fragment.xml │ │ │ └── consumer_fragment.xml │ │ ├── drawable │ │ │ ├── border.xml │ │ │ ├── ic_stop_black_24dp.xml │ │ │ ├── ic_eject_black_24dp.xml │ │ │ ├── ic_skip_next_black_24dp.xml │ │ │ ├── ic_skip_previous_black_24dp.xml │ │ │ ├── ic_pause_play_black_24dp.xml │ │ │ ├── ic_arrow_back_black_24dp.xml │ │ │ ├── ic_arrow_forward_black_24dp.xml │ │ │ ├── ic_arrow_upward_black_24dp.xml │ │ │ ├── ic_arrow_downward_black_24dp.xml │ │ │ ├── ic_volume_down_black_24dp.xml │ │ │ ├── ic_hotel_black_24dp.xml │ │ │ ├── ic_looks_one_black_24dp.xml │ │ │ ├── ic_looks_4_black_24dp.xml │ │ │ ├── ic_brightness_3_black_24dp.xml │ │ │ ├── ic_looks_5_black_24dp.xml │ │ │ ├── ic_brightness_low_black_24dp.xml │ │ │ ├── ic_looks_two_black_24dp.xml │ │ │ ├── ic_volume_up_black_24dp.xml │ │ │ ├── ic_refresh_black_24dp.xml │ │ │ ├── ic_power_settings_new_black_24dp.xml │ │ │ ├── ic_looks_3_black_24dp.xml │ │ │ ├── ic_brightness_high_black_24dp.xml │ │ │ ├── ic_usb_black_24dp.xml │ │ │ ├── ic_photo_camera_black_24dp.xml │ │ │ └── ic_volume_off_black_24dp.xml │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── styles.xml │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── alek │ │ └── ble_hid_example │ │ ├── CustomViewPager.java │ │ ├── UUIDs.java │ │ ├── ApplicationConfiguration.java │ │ ├── DPadFragment.java │ │ ├── ConsumerControlUsage.java │ │ ├── ConfigurationFragment.java │ │ ├── KeyboardFragment.java │ │ ├── ApplicationLaunchButtonsUsage.java │ │ ├── MouseFragment.java │ │ ├── ConsumerFragment.java │ │ ├── ApplicationControlUsage.java │ │ ├── MainActivity.java │ │ ├── KeyboardUsage.java │ │ └── HidBleService.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── screenshots ├── d_pad.png ├── mouse.png ├── consumer.png ├── keyboard.png └── configuration.png ├── .idea ├── copyright │ └── profiles_settings.xml ├── modules.xml ├── runConfigurations.xml ├── compiler.xml └── misc.xml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /screenshots/d_pad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aleksander-Drewnicki/BLE_HID_EXAMPLE/HEAD/screenshots/d_pad.png -------------------------------------------------------------------------------- /screenshots/mouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aleksander-Drewnicki/BLE_HID_EXAMPLE/HEAD/screenshots/mouse.png -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /screenshots/consumer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aleksander-Drewnicki/BLE_HID_EXAMPLE/HEAD/screenshots/consumer.png -------------------------------------------------------------------------------- /screenshots/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aleksander-Drewnicki/BLE_HID_EXAMPLE/HEAD/screenshots/keyboard.png -------------------------------------------------------------------------------- /screenshots/configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aleksander-Drewnicki/BLE_HID_EXAMPLE/HEAD/screenshots/configuration.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aleksander-Drewnicki/BLE_HID_EXAMPLE/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 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-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stop_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_eject_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #101010 7 | #888888 8 | #FFFFFF 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_next_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_previous_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pause_play_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_forward_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_upward_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_downward_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_down_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hotel_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_looks_one_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_looks_4_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_brightness_3_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_looks_5_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4dp 4 | 16dp 5 | 6 | 16dp 7 | 24dp 8 | 9 | 10 | 24sp 11 | 32sp 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_brightness_low_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_looks_two_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_up_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_power_settings_new_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_looks_3_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_brightness_high_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_usb_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_photo_camera_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/alek/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_off_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.example.alek.hid2" 8 | minSdkVersion 21 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | void 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 27 | exclude group: 'com.android.support', module: 'support-annotations' 28 | }) 29 | compile 'com.android.support:appcompat-v7:25.2.0' 30 | testCompile 'junit:junit:4.12' 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | 45 | # Keystore files 46 | # Uncomment the following line if you do not want to check your keystore files in. 47 | #*.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/alek/ble_hid_example/CustomViewPager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2019 Aleksander Drewnicki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.alek.ble_hid_example; 17 | 18 | import android.content.Context; 19 | import android.support.v4.view.ViewPager; 20 | import android.util.AttributeSet; 21 | import android.view.MotionEvent; 22 | 23 | public class CustomViewPager extends ViewPager { 24 | private boolean swipeEnable = true; 25 | 26 | public CustomViewPager(Context context) { 27 | super(context); 28 | } 29 | 30 | public CustomViewPager(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | } 33 | 34 | public void swipePossible(boolean enable) { 35 | swipeEnable = enable; 36 | } 37 | 38 | @Override 39 | public boolean onInterceptTouchEvent(MotionEvent ev) { 40 | return swipeEnable && super.onInterceptTouchEvent(ev); 41 | } 42 | 43 | @Override 44 | public boolean onTouchEvent(MotionEvent ev) { 45 | return swipeEnable && super.onTouchEvent(ev); 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/alek/ble_hid_example/UUIDs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2019 Aleksander Drewnicki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.alek.ble_hid_example; 17 | 18 | final class UUIDs { 19 | // HID related UUIDs 20 | public static final String SERVICE_HID = "00001812-0000-1000-8000-00805F9B34FB"; 21 | public static final String CHAR_REPORT = "00002A4D-0000-1000-8000-00805F9B34FB"; 22 | public static final String CHAR_REPORT_MAP = "00002A4B-0000-1000-8000-00805F9B34FB"; 23 | public static final String CHAR_HID_INFORMATION = "00002A4A-0000-1000-8000-00805F9B34FB"; 24 | public static final String CHAR_HID_CONTROL_POINT = "00002A4C-0000-1000-8000-00805F9B34FB"; 25 | public static final String DESC_REPORT_REFERENCE = "00002908-0000-1000-8000-00805F9B34FB"; 26 | public static final String DESC_CCC = "00002902-0000-1000-8000-00805F9B34FB"; 27 | 28 | // DIS related UUIDs 29 | public static final String SERVICE_DIS = "0000180A-0000-1000-8000-00805F9B34FB"; 30 | public static final String CHAR_PNP_ID = "00002A50-0000-1000-8000-00805F9B34FB"; 31 | 32 | // BAS related UUIDs 33 | public static final String SERVICE_BAS = "0000180F-0000-1000-8000-00805F9B34FB"; 34 | public static final String CHAR_BATTERY_LEVEL = "00002A19-0000-1000-8000-00805F9B34FB"; 35 | 36 | private UUIDs() { 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BLE_HID_EXAMPLE 3 | Saved_Force_Bond 4 | Saved_Enable_Consumer 5 | Saved_Enable_Mouse 6 | Saved_Enable_Keyboard 7 | Saved_Basic_Mode 8 | BLE_HID_EXAMPLE_prefs 9 | Configuration 10 | D-Pad 11 | Mouse 12 | Consumer control 13 | Force pairing 14 | Enter text 15 | Music/Player control 16 | Basic mode 17 | Send text 18 | Send Single Character 19 | Consumer 20 | Keyboard On 21 | Keyboard Off 22 | Reboot application 23 | Device not connected 24 | Volume control 25 | List of connected devices 26 | Keyboard 27 | Brightness control 28 | Miscellaneous 29 | System Control 30 | Application Launch 31 | Application Control 32 | 33 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BLE_HID_EXAMPLE 2 | Example of HID over GATT (Bluetooth Low Energy) Android Application. Application emulates the peripheral, server BLE device like mouse or keyboard, which could be connected by some central device like PC or smartphone. 3 | It is working in 2 modes: basic - which could be handled by various operating systems and advanced - which communicates with Linux based and Android operating systems only. 4 | Application is made of 2 (basic mode) or 5 (advance mode) full screen fragments. 5 | When more than one device is connected to the application then the same notifications are sent to the all devices. 6 | 7 | ## Configuration fragment 8 | ![](screenshots/configuration.png) 9 | 10 | - Force pairing switch - if enabled the device tries to start pairing with remote device, usable when the central doesn't start the pairing. 11 | - Basic mode switch - if enabled only consumer control feature is supported by application, otherwise consumer control, mouse, keyboard and the D-Pad 12 | - Reboot application button - it is recommended to reboot the application after switching the mode 13 | - List of currently connected devices - device name and its address is shown 14 | 15 | ## Consumer control fragment 16 | ![](screenshots/consumer.png) 17 | 18 | - Music/player control buttons - previous track, stop playing, play/pause, next track 19 | - Volume control buttons - decrease volume, mute, increase volume 20 | - Brightness control buttons - decrease brightness, increase brightness 21 | - Miscellaneous buttons - eject (e.g. CD/DVD), take a snapshot 22 | - System control buttons (advance mode only) - perform: hibernation, sleep. shut down, restart 23 | - Application launch spinner (advance mode only) - start selected application e.g. calculator or WEB browser 24 | - Application control spinner (advance mode only) - perform selected action on currently focused application e.g. show help 25 | 26 | ## Mouse fragment (advance mode only) 27 | ![](screenshots/mouse.png) 28 | 29 | - Buttons (1, 2, 3, 4, 5) - emulates mouse's buttons click 30 | - Touchpad area - move cursor, double click emulates a click by first (left) mouse button 31 | - Device's volume up and volume down button - emulates the scroll wheel 32 | 33 | ## Keyboard fragment (advance mode only) 34 | ![](screenshots/keyboard.png) 35 | 36 | - Send phrase text view & 'send text button' - written text will be sent after clicking the button 37 | - Send single character spinner - selected character will be sent 38 | - Keyboard On/Off button - enables/disables the software keyboard, on each click selected character will be sent immediately 39 | 40 | ## D-Pad fragment (advance mode only) 41 | ![](screenshots/d_pad.png) 42 | 43 | - Arrow button - send up/down/left/right arrow character 44 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/alek/ble_hid_example/ApplicationConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2019 Aleksander Drewnicki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.alek.ble_hid_example; 17 | 18 | import android.content.Context; 19 | import android.content.SharedPreferences; 20 | 21 | class ApplicationConfiguration { 22 | final static public int FORCE_BOND_FEAT = 0x01; 23 | final static public int CONSUMER_FEAT = 0x02; 24 | final static public int MOUSE_FEAT = 0x03; 25 | final static public int KEYBOARD_FEAT = 0x04; 26 | final static public int BASIC_FEAT = 0x05; 27 | private final static int TRUE = 1; 28 | private final static int FALSE = 0; 29 | final static private int[][] FEAT_ID_ARRAY = { 30 | /* ID, string ID, default value */ 31 | {FORCE_BOND_FEAT, R.string.saved_force_bond, FALSE}, 32 | {CONSUMER_FEAT, R.string.saved_enable_consumer, TRUE}, 33 | {MOUSE_FEAT, R.string.saved_enable_mouse, FALSE}, 34 | {KEYBOARD_FEAT, R.string.saved_enable_keyboard, FALSE}, 35 | {BASIC_FEAT, R.string.saved_basic_mode, TRUE}, 36 | }; 37 | 38 | static void initializeConfiguration(Context context) { 39 | // Get values if exist - set them otherwise, all are true by defaults 40 | SharedPreferences sharedPref = context.getSharedPreferences( 41 | context.getString(R.string.app_name_prefs), Context.MODE_PRIVATE); 42 | SharedPreferences.Editor editor = sharedPref.edit(); 43 | 44 | for (int[] elem : FEAT_ID_ARRAY) { 45 | int id = elem[1]; 46 | int def_val = elem[2]; 47 | 48 | if (!sharedPref.contains(context.getString(id))) { 49 | editor.putInt(context.getString(id), def_val); 50 | } 51 | } 52 | 53 | editor.apply(); 54 | } 55 | 56 | static void setConfigurationField(Context context, int feature, boolean value) { 57 | SharedPreferences sharedPref = context.getSharedPreferences( 58 | context.getString(R.string.app_name_prefs), Context.MODE_PRIVATE); 59 | SharedPreferences.Editor editor = sharedPref.edit(); 60 | 61 | for (int[] pair : FEAT_ID_ARRAY) { 62 | int feat = pair[0]; 63 | int id = pair[1]; 64 | 65 | if (feat == feature) { 66 | editor.putInt(context.getString(id), value ? TRUE : FALSE); 67 | editor.apply(); 68 | return; 69 | } 70 | } 71 | } 72 | 73 | static boolean getConfigurationField(Context context, int feature) { 74 | SharedPreferences sharedPref = context.getSharedPreferences( 75 | context.getString(R.string.app_name_prefs), Context.MODE_PRIVATE); 76 | 77 | for (int[] pair : FEAT_ID_ARRAY) { 78 | int feat = pair[0]; 79 | int id = pair[1]; 80 | 81 | if (feat == feature) { 82 | return (sharedPref.contains(context.getString(id))) && 83 | (sharedPref.getInt(context.getString(id), FALSE) == TRUE); 84 | } 85 | } 86 | 87 | return false; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/alek/ble_hid_example/DPadFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2019 Aleksander Drewnicki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.example.alek.ble_hid_example; 17 | 18 | import android.os.Bundle; 19 | import android.support.annotation.Nullable; 20 | import android.support.v4.app.Fragment; 21 | import android.view.LayoutInflater; 22 | import android.view.MotionEvent; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.view.ViewTreeObserver; 26 | 27 | public class DPadFragment extends Fragment implements View.OnTouchListener { 28 | 29 | public static DPadFragment newInstance() { 30 | return new DPadFragment(); 31 | } 32 | 33 | @Override 34 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 35 | return inflater.inflate(R.layout.d_pad_fragment, container, false); 36 | } 37 | 38 | @Override 39 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 40 | final View final_view = view; 41 | ViewTreeObserver observer = final_view.getViewTreeObserver(); 42 | 43 | getActivity().findViewById(R.id.d_pad_up).setOnTouchListener(this); 44 | getActivity().findViewById(R.id.d_pad_left).setOnTouchListener(this); 45 | getActivity().findViewById(R.id.d_pad_right).setOnTouchListener(this); 46 | getActivity().findViewById(R.id.d_pad_down).setOnTouchListener(this); 47 | 48 | observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 49 | @Override 50 | public void onGlobalLayout() { 51 | int ids[] = {R.id.d_pad_up, R.id.d_pad_left, R.id.d_pad_right, R.id.d_pad_down}; 52 | 53 | for (int id : ids) { 54 | View v = getActivity().findViewById(id); 55 | v.setMinimumHeight(v.getWidth()); 56 | } 57 | final_view.getViewTreeObserver().removeOnGlobalLayoutListener(this); 58 | } 59 | }); 60 | } 61 | 62 | @Override 63 | public boolean onTouch(View v, MotionEvent event) { 64 | MainActivity activity = (MainActivity) getActivity(); 65 | byte toSend = 0; 66 | byte value; 67 | 68 | switch (v.getId()) { 69 | /* D-Pad keys */ 70 | case R.id.d_pad_up: 71 | value = KeyboardUsage.getUsage("Up"); 72 | break; 73 | case R.id.d_pad_left: 74 | value = KeyboardUsage.getUsage("Left"); 75 | break; 76 | case R.id.d_pad_right: 77 | value = KeyboardUsage.getUsage("Right"); 78 | break; 79 | case R.id.d_pad_down: 80 | value = KeyboardUsage.getUsage("Down"); 81 | break; 82 | default: 83 | return false; 84 | } 85 | 86 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 87 | toSend |= value; 88 | } else if (event.getAction() == MotionEvent.ACTION_UP) { 89 | toSend = 0; 90 | } else { 91 | return false; 92 | } 93 | 94 | activity.sendNotification(ReportField.REPORT_FIELD_KEYBOARD_KEYS, toSend); 95 | 96 | return true; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/res/layout/keyboard_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 19 | 20 | 28 | 29 | 37 | 38 | 47 | 48 |