├── settings.gradle
├── .gitignore
├── screenshots
├── 1.png
├── ads.png
├── desclock.png
└── ic_launcher-web.png
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── gradle.iml
├── Application
├── src
│ └── main
│ │ ├── ic_launcher-web.png
│ │ ├── assets
│ │ ├── beep_4_times.mp3
│ │ └── ClearSans-Light.ttf
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── template-dimens.xml
│ │ │ └── strings.xml
│ │ ├── layout
│ │ │ ├── activity_main.xml
│ │ │ ├── listitem_device.xml
│ │ │ ├── actionbar_indeterminate_progress.xml
│ │ │ ├── applist_item.xml
│ │ │ └── main_fragment.xml
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── values-sw600dp
│ │ │ └── template-dimens.xml
│ │ ├── menu
│ │ │ └── main.xml
│ │ ├── xml
│ │ │ └── pref_general.xml
│ │ ├── values-es
│ │ │ └── strings.xml
│ │ └── values-ru
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── ru
│ │ │ └── wilix
│ │ │ └── device
│ │ │ └── geekbracelet
│ │ │ ├── model
│ │ │ ├── DeviceClockAlarm.java
│ │ │ ├── AppNotification.java
│ │ │ ├── DeviceInfo.java
│ │ │ └── Sport.java
│ │ │ ├── MainActivity.java
│ │ │ ├── receiver
│ │ │ ├── DeskClockAlarm.java
│ │ │ ├── CallReceiver.java
│ │ │ ├── NotificationMonitor.java
│ │ │ └── Receiver.java
│ │ │ ├── AppListFragment.java
│ │ │ ├── BroadcastConstants.java
│ │ │ ├── App.java
│ │ │ ├── i5
│ │ │ ├── Utils.java
│ │ │ ├── PebbleBitmap.java
│ │ │ ├── Constants.java
│ │ │ ├── Communication.java
│ │ │ └── Device.java
│ │ │ ├── AppsAdapter.java
│ │ │ ├── DeviceSettingsActivity.java
│ │ │ ├── DeviceScanActivity.java
│ │ │ ├── BLEService.java
│ │ │ ├── GoogleFitConnector.java
│ │ │ └── MainFragment.java
│ │ └── AndroidManifest.xml
├── build.gradle
└── Application.iml
├── packaging.yaml
├── iWownController.iml
├── README.md
├── gradlew.bat
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'Application'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .google
2 | .gradle
3 | .idea
4 | local.properties
5 | build
6 |
--------------------------------------------------------------------------------
/screenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/screenshots/1.png
--------------------------------------------------------------------------------
/screenshots/ads.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/screenshots/ads.png
--------------------------------------------------------------------------------
/screenshots/desclock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/screenshots/desclock.png
--------------------------------------------------------------------------------
/screenshots/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/screenshots/ic_launcher-web.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Application/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/Application/src/main/assets/beep_4_times.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/assets/beep_4_times.mp3
--------------------------------------------------------------------------------
/Application/src/main/assets/ClearSans-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/assets/ClearSans-Light.ttf
--------------------------------------------------------------------------------
/Application/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Application/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Application/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Application/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WilixLead/iWownController/HEAD/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Application/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Feb 18 00:14:41 ICT 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-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/Application/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/model/DeviceClockAlarm.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.model;
2 |
3 | /**
4 | * Created by Aloyan Dmitry on 02.09.2015
5 | * TODO: This class not done
6 | */
7 | public class DeviceClockAlarm {
8 | public int week = 0;
9 | public int hour = 0;
10 | public int minute = 0;
11 | public boolean isOpen = false;
12 | }
13 |
--------------------------------------------------------------------------------
/Application/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/packaging.yaml:
--------------------------------------------------------------------------------
1 | # GOOGLE SAMPLE PACKAGING DATA
2 | #
3 | # This file is used by Google as part of our samples packaging process.
4 | # End users may safely ignore this file. It has no relevance to other systems.
5 | ---
6 |
7 | status: PUBLISHED
8 | technologies: [Android]
9 | categories: [Connectivity]
10 | languages: [Java]
11 | solutions: [Mobile]
12 | github: googlesamples/android-BluetoothLeGatt
13 | level: BEGINNER
14 | icon: BluetoothLeGattSample/src/main/res/drawable-xxhdpi/ic_launcher.png
15 | license: apache2-android
16 |
--------------------------------------------------------------------------------
/gradle/gradle.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Application/src/main/res/layout/listitem_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
14 |
--------------------------------------------------------------------------------
/Application/src/main/res/values-sw600dp/template-dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 | @dimen/margin_huge
22 | @dimen/margin_medium
23 |
24 |
25 |
--------------------------------------------------------------------------------
/iWownController.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/model/AppNotification.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.model;
2 |
3 | import android.content.SharedPreferences;
4 |
5 | import ru.wilix.device.geekbracelet.App;
6 |
7 | /**
8 | * Created by Aloyan Dmitry on 16.09.2015
9 | */
10 | public class AppNotification {
11 | public static Integer canNotice(String packageName){
12 | if( App.sPref.contains("appnotif_" + packageName) )
13 | return App.sPref.getInt("appnotif_" + packageName, 0);
14 | return 0;
15 | }
16 |
17 | public static void enableApp(String packageName, Integer type){
18 | SharedPreferences.Editor ed = App.sPref.edit();
19 | ed.putInt("appnotif_" + packageName, type);
20 | ed.apply();
21 | }
22 |
23 | public static void disableApp(String packageName){
24 | if( !App.sPref.contains("appnotif_" + packageName) )
25 | return;
26 | SharedPreferences.Editor ed = App.sPref.edit();
27 | ed.remove("appnotif_" + packageName);
28 | ed.apply();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Application/src/main/res/layout/actionbar_indeterminate_progress.xml:
--------------------------------------------------------------------------------
1 |
16 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/Application/src/main/res/values/template-dimens.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 | 4dp
22 | 8dp
23 | 16dp
24 | 32dp
25 | 64dp
26 |
27 |
28 |
29 | @dimen/margin_medium
30 | @dimen/margin_medium
31 |
32 |
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iWownController
2 | Alternative application for Zeroner, for work with fitness-bracelet iWown i5, i5+, i6.
3 |
4 | This application under development and testing stage.
5 | Follow me in Twitter https://twitter.com/wilixlead
6 |
7 | You can just install this app from Google Play
8 | https://play.google.com/store/apps/details?id=ru.wilix.device.geekbracelet
9 |
10 | Now supported stock cyanogenmod and google default alarms. You can find alarm by link:
11 | https://play.google.com/store/apps/details?id=com.google.android.deskclock
12 |
13 | ## Features:
14 |
15 | * Support i5/i5+ and may be i6
16 | * Vibrate by incoming call
17 | * You can choice any applications to send notifications
18 | * Android DescClock (has in screenshoot)
19 | * Google Fit sync
20 | * Find you mobile by long click to device button
21 | * Configure device settings (blink, gesture, body params, etc.)
22 |
23 | ## Contribute
24 |
25 | Contributions are welcome, be it feedback, bugreports, documentation, translation, research or code. Feel free to work
26 | on any of the open [issues](https://github.com/WilixLead/iWownController/issues?q=is%3Aopen+is%3Aissue);
27 | just leave a comment that you're working on one to avoid duplicated work.
28 |
29 | If you has any suggestions and comments, just send email or use GitHub.
30 |
31 | Checked with iWown i5 / i5+ (Firmware 1.1.0.9/1.1.0.21)
32 |
--------------------------------------------------------------------------------
/Application/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
30 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/MainActivity.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.app.Activity;
4 | import android.app.FragmentManager;
5 | import android.os.Bundle;
6 |
7 | public class MainActivity extends Activity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 | getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
13 | @Override
14 | public void onBackStackChanged() {
15 | if(getFragmentManager().getBackStackEntryCount() <= 0)
16 | finish();
17 | }
18 | });
19 | }
20 |
21 | @Override
22 | public void onResume(){
23 | super.onResume();
24 | if( getFragmentManager().getBackStackEntryCount() <= 0 )
25 | getFragmentManager().beginTransaction()
26 | .replace(R.id.container, new MainFragment())
27 | .addToBackStack("main")
28 | .commit();
29 | }
30 |
31 | @Override
32 | protected void onRestoreInstanceState(Bundle savedInstanceState) {
33 | super.onRestoreInstanceState(savedInstanceState);
34 |
35 | }
36 |
37 | @Override
38 | protected void onSaveInstanceState(Bundle outState) {
39 | super.onSaveInstanceState(outState);
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Application/src/main/res/layout/applist_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
13 |
14 |
20 |
21 |
27 |
28 |
34 |
35 |
40 |
41 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/receiver/DeskClockAlarm.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.receiver;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import ru.wilix.device.geekbracelet.App;
8 | import ru.wilix.device.geekbracelet.BLEService;
9 |
10 | /**
11 | * Created by Aloyan Dmitry on 29.08.2015
12 | */
13 | public class DeskClockAlarm extends BroadcastReceiver {
14 | public static boolean isAlarm = false;
15 |
16 | public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT";
17 | public static final String ALARM_SNOOZE_ACTION = "com.android.deskclock.ALARM_SNOOZE";
18 | public static final String ALARM_DISMISS_ACTION = "com.android.deskclock.ALARM_DISMISS";
19 | public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE";
20 |
21 | public void onReceive(Context context, Intent intent){
22 | if( !App.sPref.getBoolean("cbx_notice_deskclock", false) )
23 | return;
24 |
25 | if( BLEService.getSelf() == null || BLEService.getSelf().getDevice() == null )
26 | return;
27 |
28 | String action = intent.getAction();
29 | switch (action){
30 | case ALARM_ALERT_ACTION:
31 | isAlarm = true;
32 | BLEService.getSelf().getDevice().sendCall("UP!UP!");
33 | break;
34 | case ALARM_SNOOZE_ACTION:
35 | case ALARM_DISMISS_ACTION:
36 | case ALARM_DONE_ACTION:
37 | isAlarm = false;
38 | BLEService.getSelf().getDevice().sendCallEnd();
39 | break;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Application/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.3'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.application'
12 |
13 | repositories {
14 | jcenter()
15 | }
16 |
17 | dependencies {
18 | compile 'com.android.support:appcompat-v7:23.4.0'
19 | compile 'ch.acra:acra:4.9.2'
20 | compile 'com.google.android.gms:play-services-fitness:10.2.0'
21 | compile 'com.google.guava:guava:20.0'
22 | }
23 |
24 | // The sample build uses multiple directories to
25 | // keep boilerplate and common code separate from
26 | // the main sample code.
27 | List dirs = [
28 | 'main', // main sample code; look here for the interesting stuff.
29 | 'common', // components that are reused by multiple samples
30 | 'template'] // boilerplate code that is generated by the sample template process
31 |
32 | android {
33 | signingConfigs {
34 | deploy_key {
35 | keyAlias 'wilix'
36 | keyPassword 'azsxdc'
37 | storeFile file('G:/Android/crt.keystore')
38 | storePassword 'azsxdc'
39 | }
40 | }
41 | compileSdkVersion 23
42 | buildToolsVersion '23.0.3'
43 | defaultConfig {
44 | minSdkVersion 19
45 | targetSdkVersion 23
46 | signingConfig signingConfigs.deploy_key
47 | }
48 | compileOptions {
49 | sourceCompatibility JavaVersion.VERSION_1_7
50 | targetCompatibility JavaVersion.VERSION_1_7
51 | }
52 | sourceSets {
53 | main {
54 | dirs.each { dir ->
55 | java.srcDirs "src/${dir}/java"
56 | res.srcDirs "src/${dir}/res"
57 | }
58 | }
59 | androidTest.setRoot('tests')
60 | androidTest.java.srcDirs = ['tests/src']
61 |
62 | }
63 | productFlavors {
64 | }
65 | buildTypes {
66 | debug {
67 | signingConfig signingConfigs.deploy_key
68 | }
69 | release {
70 | signingConfig signingConfigs.deploy_key
71 | }
72 | }
73 | }
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/AppListFragment.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.app.ListFragment;
4 | import android.app.ProgressDialog;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageManager;
7 | import android.os.AsyncTask;
8 | import android.os.Bundle;
9 |
10 | import java.util.List;
11 |
12 | import ru.wilix.device.geekbracelet.model.AppNotification;
13 |
14 | public class AppListFragment extends ListFragment{
15 | private PackageManager packageManager = null;
16 | private List applist = null;
17 | private AppsAdapter listadaptor = null;
18 |
19 | @Override
20 | public void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | packageManager = App.mContext.getPackageManager();
23 | new LoadApplications().execute();
24 | }
25 |
26 | private class LoadApplications extends AsyncTask {
27 | private ProgressDialog progress = null;
28 |
29 | @Override
30 | protected Void doInBackground(Void... params) {
31 | applist = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
32 | for( ApplicationInfo app : applist )
33 | if(AppNotification.canNotice(app.packageName) > 0)
34 | app.enabled = true;
35 | else
36 | app.enabled = false;
37 | listadaptor = new AppsAdapter(getActivity(), R.layout.applist_item, applist);
38 | return null;
39 | }
40 |
41 | @Override
42 | protected void onCancelled() {
43 | super.onCancelled();
44 | }
45 |
46 | @Override
47 | protected void onPostExecute(Void result) {
48 | setListAdapter(listadaptor);
49 | progress.dismiss();
50 | super.onPostExecute(result);
51 | }
52 |
53 | @Override
54 | protected void onPreExecute() {
55 | progress = ProgressDialog.show(getActivity(), null,
56 | "Loading application info...");
57 | super.onPreExecute();
58 | }
59 |
60 | @Override
61 | protected void onProgressUpdate(Void... values) {
62 | super.onProgressUpdate(values);
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/BroadcastConstants.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | /**
4 | * Created by Aloyan Dmitry on 30.08.2015
5 | */
6 | public class BroadcastConstants {
7 | public static final String ACTION_DEVICE_INFO = "ru.wilix.device.geekbracelet.ACTION_DEVICE_INFO";
8 | public static final String ACTION_DEVICE_POWER = "ru.wilix.device.geekbracelet.ACTION_DEVICE_POWER";
9 | public static final String ACTION_SPORT_DATA = "ru.wilix.device.geekbracelet.ACTION_SPORT_DATA";
10 | public static final String ACTION_DAILY_DATA = "ru.wilix.device.geekbracelet.ACTION_DAILY_DATA";
11 | public static final String ACTION_DATE_DATA = "ru.wilix.device.geekbracelet.ACTION_DATE_DATA";
12 | public static final String ACTION_SEDENTARY_DATA = "ru.wilix.device.geekbracelet.ACTION_SEDENTARY_DATA";
13 | public static final String ACTION_ALARM_DATA = "ru.wilix.device.geekbracelet.ACTION_ALARM_DATA";
14 | public static final String ACTION_PAIR_DATA = "ru.wilix.device.geekbracelet.ACTION_PAIR_DATA";
15 | public static final String ACTION_SELFIE = "ru.wilix.device.geekbracelet.ACTION_SELFIE";
16 | public static final String ACTION_PLAYPAUSE = "ru.wilix.device.geekbracelet.ACTION_PLAYPAUSE";
17 | public static final String ACTION_INCOMING_CALL = "ru.wilix.device.geekbracelet.ACTION_INCOMING_CALL";
18 | public static final String ACTION_END_CALL = "ru.wilix.device.geekbracelet.ACTION_END_CALL";
19 | public static final String ACTION_NEW_NOTIFICATION_RECEIVED = "ru.wilix.device.geekbracelet.ACTION_NEW_NOTIFICATION_RECEIVED";
20 |
21 | public static final String ACTION_BLE_DATA = "ru.wilix.device.geekbracelet.ACTION_BLE_DATA";
22 | public static final String ACTION_USER_BODY_DATA = "ru.wilix.device.geekbracelet.ACTION_USER_BODY_DATA";
23 | public static final String ACTION_DEVICE_CONF_DATA = "ru.wilix.device.geekbracelet.ACTION_DEVICE_CONF_DATA";
24 |
25 | public static final String ACTION_CONNECT_TO_GFIT = "ru.wilix.device.geekbracelet.ACTION_CONNECT_TO_GFIT";
26 |
27 | public final static String ACTION_GATT_CONNECTED = "ru.wilix.device.geekbracelet.ACTION_GATT_CONNECTED";
28 | public final static String ACTION_GATT_DISCONNECTED = "ru.wilix.device.geekbracelet.ACTION_GATT_DISCONNECTED";
29 | public final static String ACTION_GATT_SERVICES_DISCOVERED = "ru.wilix.device.geekbracelet.ACTION_GATT_SERVICES_DISCOVERED";
30 | }
31 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/model/DeviceInfo.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.model;
2 |
3 | import java.io.Serializable;
4 | import java.lang.reflect.Field;
5 | import java.lang.reflect.Modifier;
6 | import java.util.Arrays;
7 |
8 | import ru.wilix.device.geekbracelet.i5.Utils;
9 |
10 | /**
11 | * Created by Dmitry on 30.08.2015.
12 | */
13 | public class DeviceInfo implements Serializable {
14 | private String model;
15 | private int oadmode;
16 | private String swversion;
17 | private String bleAddr;
18 | private int displayWidthFont;
19 |
20 | public static DeviceInfo fromData(byte[] data){
21 | DeviceInfo info = new DeviceInfo();
22 | info.setModel(Utils.ascii2String(Arrays.copyOfRange(data, 6, 10)));
23 | info.setOadmode((data[10] * 255) + data[11]);
24 | info.setSwversion(data[12] + "." + data[13] + "." + data[14] + "." + data[15]);
25 | info.setBleAddr(Utils.byteArrayToString(Arrays.copyOfRange(data, 16, 22)));
26 | if (data.length == 29)
27 | info.setDisplayWidthFont(Utils.bytesToInt(Arrays.copyOfRange(data, 28, 29)));
28 | else if (data.length == 28)
29 | info.setDisplayWidthFont(Utils.bytesToInt(Arrays.copyOfRange(data, 27, 28)));
30 | return info;
31 | }
32 |
33 | public String toString(){
34 | String buff = "";
35 | Field[] fields = this.getClass().getDeclaredFields();
36 | for (Field field : fields) {
37 | if ( (field.getModifiers() & Modifier.FINAL) == Modifier.FINAL )
38 | continue;
39 | try {
40 | buff += field.getName() + ": " + field.get(this) + " ";
41 | } catch (Exception e) {
42 | }
43 | }
44 | return buff;
45 | }
46 |
47 | public void setModel(String value){
48 | this.model = value;
49 | }
50 |
51 | public String getModel(){
52 | return this.model;
53 | }
54 |
55 | public void setOadmode(int value){
56 | this.oadmode = value;
57 | }
58 |
59 | public int getOadmode(){
60 | return this.oadmode;
61 | }
62 |
63 | public void setSwversion(String value){
64 | this.swversion = value;
65 | }
66 |
67 | public String getSwversion(){
68 | return this.swversion;
69 | }
70 |
71 | public void setBleAddr(String value){
72 | this.bleAddr = value;
73 | }
74 |
75 | public String getBleAddr(){
76 | return this.bleAddr;
77 | }
78 |
79 | public void setDisplayWidthFont(int value){
80 | this.displayWidthFont = value;
81 | }
82 |
83 | public int getDisplayWidthFont(){
84 | return this.displayWidthFont;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/App.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.app.Application;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.graphics.BitmapFactory;
11 | import android.preference.PreferenceManager;
12 | import android.support.v4.app.NotificationCompat;
13 | import android.widget.Toast;
14 |
15 | import org.acra.ACRA;
16 | import org.acra.annotation.ReportsCrashes;
17 |
18 | import ru.wilix.device.geekbracelet.receiver.NotificationMonitor;
19 |
20 | /**
21 | * Created by Aloyan Dmitry on 29.08.2015
22 | */
23 | @ReportsCrashes(formUri = "http://acra-server.wilix.ru/logs/iWownController")
24 | public class App extends Application {
25 | public static Context mContext;
26 | public static SharedPreferences sPref;
27 |
28 | @Override
29 | public void onCreate() {
30 | ACRA.init(this);
31 | super.onCreate();
32 | App.mContext = getApplicationContext();
33 | App.sPref = PreferenceManager.getDefaultSharedPreferences(App.mContext);
34 |
35 | if (BLEService.isBluetoothAvailable()) {
36 | // Create service
37 | Intent gattServiceIntent = new Intent(this, BLEService.class);
38 | startService(gattServiceIntent);
39 |
40 | PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
41 | new Intent(this, MainActivity.class), 0);
42 |
43 | Notification notification = new NotificationCompat.Builder(this)
44 | .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher))
45 | .setSmallIcon(R.drawable.ic_launcher)
46 | .setContentText("WiliX Controller")
47 | .setContentTitle("WiliX Controller")
48 | .setTicker("WiliX Controller")
49 | .setPriority(Notification.PRIORITY_LOW)
50 | .setAutoCancel(false)
51 | .setContentIntent(pi)
52 | .build();
53 |
54 | notification.flags |= Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_INSISTENT;// Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT |
55 | ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(0, notification);
56 | loadProperties();
57 | } else {
58 | Toast.makeText(App.mContext, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
59 | android.os.Process.killProcess(android.os.Process.myPid());
60 | }
61 | }
62 |
63 | public static void loadProperties(){
64 | SharedPreferences sp = App.sPref;
65 | if( sp.getBoolean("fit_connected", false) )
66 | GoogleFitConnector.connect(App.mContext);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/receiver/CallReceiver.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.receiver;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.database.Cursor;
7 | import android.net.Uri;
8 | import android.provider.ContactsContract;
9 | import android.telephony.TelephonyManager;
10 |
11 | import java.lang.reflect.Method;
12 |
13 | import ru.wilix.device.geekbracelet.App;
14 | import ru.wilix.device.geekbracelet.BLEService;
15 | import ru.wilix.device.geekbracelet.BroadcastConstants;
16 |
17 | /**
18 | * Created by Aloyan Dmitry on 29.08.2015
19 | */
20 | public class CallReceiver extends BroadcastReceiver {
21 | public void onReceive(Context context, Intent intent){
22 | if( !App.sPref.getBoolean("cbx_notice_call", false) )
23 | return;
24 |
25 | if( BLEService.getSelf() == null || BLEService.getSelf().getDevice() == null )
26 | return;
27 |
28 | Intent in;
29 | switch (((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getCallState()){
30 | case TelephonyManager.CALL_STATE_OFFHOOK:
31 | case TelephonyManager.CALL_STATE_IDLE:
32 | in = new Intent(BroadcastConstants.ACTION_END_CALL);
33 | context.sendBroadcast(in);
34 | break;
35 | case TelephonyManager.CALL_STATE_RINGING:
36 | String number = intent.getStringExtra("incoming_number");
37 |
38 | in = new Intent(BroadcastConstants.ACTION_INCOMING_CALL);
39 | in.putExtra("data", getContact(context, number));
40 | context.sendBroadcast(in);
41 | break;
42 | default:
43 | in = new Intent(BroadcastConstants.ACTION_END_CALL);
44 | context.sendBroadcast(in);
45 | break;
46 | }
47 | }
48 |
49 | public static String getContact(Context context, String phoneNumber) {
50 | if ( phoneNumber == null || phoneNumber.length() <= 0 )
51 | return "Unknown";
52 |
53 | String name = "No name";
54 | Cursor cursor = null;
55 | try {
56 | Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
57 | cursor = context.getContentResolver().query(uri, new String[]{"display_name", "type", "label"}, null, null, "display_name LIMIT 1");
58 | if (cursor.moveToNext())
59 | name = cursor.getString(cursor.getColumnIndex("display_name"));
60 | if (cursor != null)
61 | cursor.close();
62 | } catch (Exception e) {
63 | name = phoneNumber;
64 | if (cursor != null) {
65 | cursor.close();
66 | }
67 | } catch (Throwable th) {
68 | if (cursor != null) {
69 | cursor.close();
70 | }
71 | }
72 | return name;
73 | }
74 |
75 | public static void rejectCall(Context context, int mode){
76 | try{
77 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
78 | Class c = Class.forName(tm.getClass().getName());
79 | Method m = c.getDeclaredMethod("getITelephony");
80 | m.setAccessible(true);
81 | Object telephonyService = m.invoke(tm); // Get the internal ITelephony object
82 | c = Class.forName(telephonyService.getClass().getName()); // Get its class
83 | m = c.getDeclaredMethod( (mode == 0) ? "endCall" : "silenceRinger");
84 | m.setAccessible(true); // Make it accessible
85 | m.invoke(telephonyService); // invoke endCall()
86 | }catch( Exception e ){ e.printStackTrace(); }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Application/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
69 |
70 |
73 |
77 |
78 |
79 |
80 |
81 |
82 |
85 |
86 |
87 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/i5/Utils.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.i5;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | import java.util.ArrayList;
6 | import java.util.GregorianCalendar;
7 |
8 | /**
9 | * Created by Dmitry on 30.08.2015.
10 | */
11 | public class Utils {
12 | /**
13 | * Parse characteristic and return date in seconds
14 | * @param chr
15 | * @return
16 | */
17 | public static long parseDateCharacteristic(BluetoothGattCharacteristic chr){
18 | int year = chr.getIntValue(17, 0) + 2000;
19 | int month = chr.getIntValue(17, 1) + 1;
20 | int day = chr.getIntValue(17, 2) + 1;
21 | int hour = chr.getIntValue(17, 3);
22 | int minute = chr.getIntValue(17, 4);
23 | int second = chr.getIntValue(17, 5);
24 | return new GregorianCalendar(year, month, day, hour, minute, second).getTime().getTime() / 1000;
25 | }
26 |
27 | /**
28 | * Format header for device packet
29 | * @param grp - Group of commands
30 | * @param cmd - Command
31 | * @return - formated byte
32 | */
33 | public static byte form_Header(int grp, int cmd) {
34 | return (byte) (((((byte) grp) & 15) << 4) | (((byte) cmd) & 15));
35 | }
36 |
37 | /**
38 | * Convert incomig data to wristband packet
39 | * @param prefix - unknow now
40 | * @param header - still unknow
41 | * @param datas - ByteArray to create packet
42 | * @return - byte code for device
43 | */
44 | public static byte[] getDataByte(boolean prefix, byte header, ArrayList datas) {
45 | byte[] commonData = new byte[4];
46 | if (prefix) {
47 | commonData[0] = (byte) 33;
48 | } else {
49 | commonData[0] = (byte) 34;
50 | }
51 | commonData[1] = (byte) -1;
52 | commonData[2] = header;
53 | if (datas != null) {
54 | commonData[3] = (byte) datas.size();
55 | byte[] data = new byte[datas.size()];
56 | for (int i = 0; i < datas.size(); i++) {
57 | data[i] = (Byte) datas.get(i);
58 | }
59 | return concat(commonData, data);
60 | }
61 | commonData[3] = (byte) 0;
62 | return commonData;
63 | }
64 |
65 | public static String ascii2String(byte[] bytes2) {
66 | StringBuffer sb = new StringBuffer();
67 | for (int i = 0; i < bytes2.length; i++) {
68 | if ((char)(bytes2[i]) != '\u0000') {
69 | sb.append((char)(bytes2[i]));
70 | }
71 | }
72 | return sb.toString();
73 | }
74 |
75 | public static String bytesToString(byte[] bytes) {
76 | StringBuilder stringBuilder = new StringBuilder(bytes.length);
77 | int length = bytes.length;
78 | for (byte aByte : bytes) {
79 | stringBuilder.append(String.format("%02X", new Object[]{Byte.valueOf(aByte)}));
80 | }
81 | return stringBuilder.toString();
82 | }
83 |
84 | public static String byteArrayToString(byte[] copyOfRange) {
85 | StringBuilder sb = new StringBuilder();
86 | for (byte b : copyOfRange) {
87 | String i = Integer.toHexString(b & 255);
88 | if (i.length() == 1) {
89 | i = "0" + i;
90 | }
91 | sb.append(i);
92 | }
93 | return sb.toString();
94 | }
95 |
96 | public static int bytesToInt(byte[] bytes) {
97 | if (bytes.length == 1) {
98 | return bytes[0] & 255;
99 | }
100 | if (bytes.length == 4) {
101 | return (((bytes[0] & 255) | ((bytes[1] << 8) & 65280)) | ((bytes[2] << 16) & 16711680)) | ((bytes[3] << 24) & -16777216);
102 | }
103 | if (bytes.length == 2) {
104 | return (bytes[0] & 255) | ((bytes[1] << 8) & 65280);
105 | }
106 | if (bytes.length == 3) {
107 | return ((bytes[0] & 255) | ((bytes[1] << 8) & 65280)) | ((bytes[2] << 16) & 16711680);
108 | }
109 | return 0;
110 | }
111 |
112 | /**
113 | * Concat helper. TODO check other concat functions for replace this
114 | * @param a - argument A
115 | * @param b - argument B
116 | * @return - summ of A and B
117 | // */
118 | public static byte[] concat(byte[] a, byte[] b) {
119 | if (a == null) {
120 | return b;
121 | }
122 | if (b == null) {
123 | return a;
124 | }
125 | byte[] c = new byte[(a.length + b.length)];
126 | System.arraycopy(a, 0, c, 0, a.length);
127 | System.arraycopy(b, 0, c, a.length, b.length);
128 | return c;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Application/src/main/res/xml/pref_general.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
16 |
17 |
27 |
28 |
38 |
39 |
49 |
50 |
58 |
59 |
60 |
63 |
68 |
69 |
73 |
74 |
78 |
79 |
83 |
84 |
88 |
89 |
94 |
95 |
96 |
99 |
100 |
105 |
106 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/i5/PebbleBitmap.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.i5;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Bitmap.Config;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint.Align;
8 | import android.graphics.Typeface;
9 | import android.text.Layout.Alignment;
10 | import android.text.StaticLayout;
11 | import android.text.TextPaint;
12 | import android.util.Log;
13 |
14 | import com.google.common.primitives.UnsignedInteger;
15 |
16 | import java.io.IOException;
17 | import java.io.InputStream;
18 | import java.math.BigInteger;
19 | import java.nio.ByteBuffer;
20 | import java.nio.ByteOrder;
21 |
22 | import ru.wilix.device.geekbracelet.App;
23 | import ru.wilix.device.geekbracelet.BuildConfig;
24 |
25 | public class PebbleBitmap {
26 | public final byte[] data;
27 | public final UnsignedInteger flags;
28 | public final short height;
29 | public int index;
30 | public int offset;
31 | public final UnsignedInteger rowLengthBytes;
32 | public final short width;
33 | public final short f1286x;
34 | public final short f1287y;
35 | public static Typeface unifont = Typeface.createFromAsset(App.mContext.getAssets(), "ClearSans-Light.ttf");
36 |
37 | public static final String PIXEL_OFF = "0";
38 | public static final String PIXEL_ON = "1";
39 | public static final int SETTINGS_DEFAULT_MBR_SIZE = 4096;
40 |
41 | private PebbleBitmap(UnsignedInteger _rowLengthBytes, UnsignedInteger _flags, short _x, short _y, short _width, short _height, byte[] _data) {
42 | this.offset = 0;
43 | this.index = 0;
44 | this.rowLengthBytes = _rowLengthBytes;
45 | this.flags = _flags;
46 | this.f1286x = _x;
47 | this.f1287y = _y;
48 | this.width = _width;
49 | this.height = _height;
50 | this.data = _data;
51 | }
52 |
53 | public static PebbleBitmap fromString(String text, int w, int len) {
54 | TextPaint textPaint = new TextPaint();
55 | textPaint.setAntiAlias(true);
56 | textPaint.setTextSize(14.0f);
57 | if (w == 32)
58 | textPaint.setTextAlign(Align.CENTER);
59 | textPaint.setTypeface(unifont);
60 | float spacingmult = 1.0f;
61 | float spacingadd = 0.49f;
62 | boolean includepad = false;
63 | StaticLayout sl = new StaticLayout(text, textPaint, w, Alignment.ALIGN_CENTER, spacingmult, spacingadd, includepad);
64 | int h = sl.getHeight();
65 | if (h > len * 16) {
66 | h = len * 16;
67 | }
68 | Bitmap newBitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
69 | sl.draw(new Canvas(newBitmap));
70 | return fromAndroidBitmap(newBitmap);
71 | }
72 |
73 | public static PebbleBitmap fromAndroidBitmap(Bitmap bitmap) {
74 | int width = bitmap.getWidth();
75 | int height = bitmap.getHeight();
76 | int rowLengthBytes = width / 8;
77 | ByteBuffer data = ByteBuffer.allocate(rowLengthBytes * height);
78 | data.order(ByteOrder.LITTLE_ENDIAN);
79 | StringBuffer stringBuffer;
80 | StringBuilder stringBuffer1 = new StringBuilder("");
81 | for (int y = 0; y < height; y++) {
82 | int[] pixels = new int[width];
83 | bitmap.getPixels(pixels, 0, width * 2, 0, y, width, 1);
84 | stringBuffer = new StringBuffer("");
85 |
86 | for (int x = 0; x < width; x++) {
87 | if (pixels[x] == 0) {
88 | stringBuffer.append(PIXEL_OFF);
89 | if (BuildConfig.DEBUG)
90 | stringBuffer1.append("-");
91 | } else {
92 | stringBuffer.append(PIXEL_ON);
93 | if (BuildConfig.DEBUG)
94 | stringBuffer1.append("#");
95 | }
96 | }
97 | for (int k = 0; k < rowLengthBytes * 8; k += 8) {
98 | data.put((byte) new BigInteger(stringBuffer.substring(k, k + 8), 2).intValue());
99 | }
100 | if (BuildConfig.DEBUG) {
101 | stringBuffer1.append("\n");
102 | Log.i("info", stringBuffer.toString());
103 | }
104 | }
105 | if (BuildConfig.DEBUG)
106 | System.out.println(stringBuffer1.toString());
107 |
108 | if (!bitmap.isRecycled())
109 | bitmap.recycle();
110 | System.gc();
111 | return new PebbleBitmap(UnsignedInteger.fromIntBits(rowLengthBytes), UnsignedInteger.fromIntBits(SETTINGS_DEFAULT_MBR_SIZE), (short) 0, (short) 0, (short) width, (short) height, data.array());
112 | }
113 |
114 | public static PebbleBitmap fromPng(InputStream paramInputStream) throws IOException {
115 | return fromAndroidBitmap(BitmapFactory.decodeStream(paramInputStream));
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/Application/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | GeekBracelet
4 | BLE is not supported
5 | Connected
6 | Connectiong to Google Fit
7 | Not connected
8 | Device not connected
9 | Retriving data from device
10 | For enable retrieve notifications from your mobile to bracelet you should enable access to system notifications. Open settings?
11 | yes
12 | no
13 | Warning
14 | Disconnected
15 | Bluetooth not supported
16 | Google Fit connected, sport data will be stored to Fit
17 | Can\'t connect to Google Fit
18 | Scan
19 | Stop
20 | Auto sleep
21 | BLE enable
22 | Unknown parameter. Use for own risk
23 | Age
24 | Gender
25 |
26 | - Male
27 | - Female
28 |
29 |
30 | - 0
31 | - 1
32 |
33 | Goal
34 | Your height
35 | Your weight
36 | Use English units
37 | Unable gestures
38 | Show time when you try to look at watch
39 | Blink blue led when bluetooth connected
40 | Use 12h format
41 | Device Settings
42 | Device Scan
43 | User Personal Options
44 | Device Settings
45 | Notification Settings
46 | Keep Special Characters
47 | Can cause some problems in some languages or some devices.
48 | Add Delay
49 | To ignore repeated or cleared notifications in a second
50 |
51 | Message
52 | Clouds
53 | Error
54 | Click on call for mute ringer
55 | Long click for search phone
56 | Long click on call for reject
57 | Vibrate when alarm ring (use only with Android DeskClock)
58 | Choise notification icon
59 | Connect to Google Fit
60 | Connect
61 | Device
62 | Model:
63 | Power level:
64 | Device time:
65 | Firmware:
66 |
67 | Wrong time!
68 |
69 | Settings
70 | Unknown
71 | Don\t notice
72 | Reconnect to Google Fit
73 | Vibrate on incoming call
74 | Choice apps for receive notifications
75 | Data:
76 | Device address:
77 | State:
78 | Connect
79 | Disconnect
80 | No data
81 | Unknown characteristic
82 | Unknown device
83 | Unknown service
84 |
85 | Warning
86 | You should grant next permissions for normal work
87 | Some Permission is Denied. I can\'t work
88 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/receiver/NotificationMonitor.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.receiver;
2 |
3 | import android.app.Notification;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.IBinder;
7 | import android.service.notification.NotificationListenerService;
8 | import android.service.notification.StatusBarNotification;
9 | import android.util.Log;
10 |
11 | import java.io.Serializable;
12 |
13 | import ru.wilix.device.geekbracelet.BroadcastConstants;
14 | import ru.wilix.device.geekbracelet.i5.Constants;
15 | import ru.wilix.device.geekbracelet.model.AppNotification;
16 |
17 | /**
18 | * Created by Aloyan Dmitry on 30.08.2015
19 | */
20 | public class NotificationMonitor extends NotificationListenerService {
21 | public static StatusBarNotification lastSbn;
22 |
23 | public static boolean settingsKeepForeign = false;
24 | public static int settingsDelay = 0;
25 |
26 | @Override
27 | public void onNotificationPosted(StatusBarNotification sbn) {
28 | Log.i("NOTIFICATION", "From package: " + sbn.getPackageName());
29 | lastSbn = sbn;
30 | new Thread(new Runnable() {
31 | @Override
32 | public void run() {
33 | try {
34 | if(settingsDelay != 0) {
35 | StatusBarNotification notif = lastSbn.clone();
36 | Thread.sleep(settingsDelay * 1000);
37 | if(lastSbn == null || lastSbn.getPostTime() != notif.getPostTime()) return;
38 | }
39 | int canNotice = AppNotification.canNotice(lastSbn.getPackageName());
40 | if(canNotice > 0 && lastSbn.isClearable()){
41 | Intent intent = new Intent(BroadcastConstants.ACTION_NEW_NOTIFICATION_RECEIVED);
42 | intent.putExtra("data", Notif.fromSbn(lastSbn, canNotice));
43 | sendBroadcast(intent);
44 | return;
45 | }
46 | }catch (Exception e){ e.printStackTrace(); }
47 | if(lastSbn != null) Log.i("NOTIFICATION", "Package: " + lastSbn.getPackageName() + " skipped");
48 | }
49 | }).start();
50 | }
51 |
52 | @Override
53 | public void onNotificationRemoved(StatusBarNotification sbn) {
54 | // Log.i("NOTIF!", "On remove");
55 | if(settingsDelay != 0) {
56 | if(lastSbn != null && sbn.getPackageName().equals(lastSbn.getPackageName())) {
57 | lastSbn = null;
58 | }
59 | }
60 | }
61 |
62 | public IBinder onBind(Intent intent){
63 | return super.onBind(intent);
64 | }
65 |
66 | public static class Notif implements Serializable{
67 | public String appName = "";
68 | public String fromName = "";
69 | public String msgText = "";
70 | public Integer noticeType = 0;
71 |
72 | public static Notif fromSbn(StatusBarNotification sbn, int notice_type){
73 | Notif nf = new Notif();
74 | //String ticker = "";
75 |
76 | if( sbn == null || sbn.getPackageName() == null )
77 | return nf;
78 |
79 | Log.i("Package", sbn.getPackageName());
80 |
81 | if( sbn.getNotification() == null )
82 | return nf;
83 |
84 | // if( sbn.getNotification().tickerText != null ) {
85 | // ticker = sbn.getNotification().tickerText.toString();
86 | // }
87 | if( sbn.getNotification().extras != null ) {
88 | Bundle extras = sbn.getNotification().extras;
89 | if( extras.containsKey(Notification.EXTRA_TITLE) )
90 | nf.fromName = extras.get(Notification.EXTRA_TITLE).toString();
91 | if( extras.containsKey("android.text") )
92 | nf.msgText = extras.get("android.text").toString();
93 | }
94 |
95 | // Log.i("Ticker", ticker);
96 | if( nf.fromName != null )
97 | Log.i("Title", nf.fromName);
98 | // if( nf.msgText != null )
99 | // Log.i("Text", nf.msgText);
100 | nf.noticeType = notice_type;
101 |
102 | return nf;
103 | }
104 |
105 | public Integer getDeviceNoticeType(){
106 | switch (this.noticeType){
107 | case 1:return Constants.ALERT_TYPE_MESSAGE;
108 | case 2:return Constants.ALERT_TYPE_CLOUD;
109 | case 3:return Constants.ALERT_TYPE_ERROR;
110 | default:return Constants.ALERT_TYPE_MESSAGE;
111 | }
112 | }
113 |
114 | public void setAppName(String value){
115 | this.appName = value;
116 | }
117 | public String getAppName(){
118 | return this.appName;
119 | }
120 | public void setFromName(String value){
121 | this.fromName = value;
122 | }
123 | public String getFromName(){
124 | return this.fromName;
125 | }
126 | public void setMsgText(String value){
127 | this.msgText = value;
128 | }
129 | public String getMsgText(){
130 | return this.msgText;
131 | }
132 | public void setNoticeType(Integer type){
133 | this.noticeType = type;
134 | }
135 | public Integer getNoticeType(){
136 | return this.noticeType;
137 | }
138 | }
139 | }
--------------------------------------------------------------------------------
/Application/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | GeekBracelet
4 | BLE no es compatible
5 | Conectado
6 | Conectarse a Google Fit
7 | No conectado
8 | Dispositivo no conectado
9 | Recibiendo datos del dispositivo
10 | Aviso
11 | Desconectado
12 | Bluetooth no soportado
13 | Google Fit conectado, los datos deportivos se almacenaran en Fit
14 | Imposible conectar a Google Fit
15 | Buscar
16 | Stop
17 | Auto sleep
18 | BLE activado
19 | Parametro desconocido. Use bajo su propio riesgo
20 | Edad
21 | Gйnero
22 |
23 | - Hombre
24 | - Mujer
25 |
26 |
27 | - 0
28 | - 1
29 |
30 | Meta
31 | Tu altura
32 | Tu peso
33 | Unidad Inglesa
34 | Inhabilitar gestos
35 | Mostrar hora al mirar el reloj
36 | Parpadear led azul cuando conecte bluetooth
37 | Usar formato 12h
38 | Ajustes del dispositivo
39 | Buscar dispositivo
40 | Opciones de usuario
41 | Ajustes del dispositivo
42 | Notification Settings
43 | Keep Special Characters
44 | Can cause some problems in some languages or some devices.
45 | Add Delay
46 | To ignore repeated or cleared notifications in a second
47 |
48 |
49 | Mensaje
50 | Nube
51 | Error
52 | Pulse en la llamada para silenciar el timbre
53 | Pulsaciуn larga para buscar telйfono
54 | Pulsaciуn larga para rechazar llamada
55 | Vibrar cuando suene la alarma (Compatible solo con Android DeskClock)
56 | Elija icono de notificaciуn
57 | Conectado a Google Fit
58 | Conectado
59 | Dispositivo
60 | Modelo:
61 | Nivel de bateria:
62 | Hora del dispositivo:
63 | Firmware:
64 | Ajustes
65 | Desconocido
66 | No molestar
67 | Reconectar a Google Fit
68 | Titra á incomaing símtal
69 | Choice apps for receive notifications
70 | Data:
71 | Device address:
72 | State:
73 | Wrong time!
74 | Connect
75 | Disconnect
76 | No data
77 | no
78 | For enable retrieve notifications from your mobile to bracelet you should enable access to system notifications. Open settings?
79 | Unknown characteristic
80 | Unknown device
81 | Unknown service
82 | yes
83 |
84 | Advertencia
85 | Debe conceder los siguientes permisos para el trabajo normal
86 | Algunos Permiso denegado. Puedo \'t trabajar
87 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/AppsAdapter.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.content.pm.ApplicationInfo;
8 | import android.content.pm.PackageManager;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.ArrayAdapter;
13 | import android.widget.ImageView;
14 | import android.widget.ListView;
15 | import android.widget.TextView;
16 |
17 | import java.util.List;
18 |
19 | import ru.wilix.device.geekbracelet.model.AppNotification;
20 |
21 | /**
22 | * Created by Aloyan Dmitry on 16.09.2015
23 | */
24 | public class AppsAdapter extends ArrayAdapter {
25 | private List appsList = null;
26 | private Context context;
27 | private PackageManager packageManager;
28 | private String noticeTypes[] = {
29 | getContext().getResources().getString(R.string.notice_type_dont_notice),
30 | getContext().getResources().getString(R.string.notice_type_message),
31 | getContext().getResources().getString(R.string.notice_type_cloud),
32 | getContext().getResources().getString(R.string.notice_type_error),
33 | };
34 |
35 | private AlertDialog.Builder noticeTypeDialog = new AlertDialog.Builder(getContext());
36 | private DialogInterface.OnClickListener dialogItemListener;
37 | private View.OnClickListener bodyListener;
38 | private ApplicationInfo selectedItem;
39 | private View selectedView;
40 |
41 | public AppsAdapter(Context context, int textViewResourceId, List appsList) {
42 | super(context, textViewResourceId, appsList);
43 | this.context = context;
44 | this.appsList = appsList;
45 | packageManager = context.getPackageManager();
46 |
47 | noticeTypeDialog.setTitle(R.string.choise_notice_type);
48 |
49 | dialogItemListener = new DialogInterface.OnClickListener() {
50 | @Override
51 | public void onClick(DialogInterface dialog, int which) {
52 | if (which == Dialog.BUTTON_NEGATIVE)
53 | return;
54 | ListView lv = ((AlertDialog) dialog).getListView();
55 | if( lv.getCheckedItemPosition() <= 0 ) {
56 | AppNotification.disableApp(selectedItem.packageName);
57 | selectedItem.enabled = false;
58 | }else {
59 | AppNotification.enableApp(selectedItem.packageName, lv.getCheckedItemPosition());
60 | selectedItem.enabled = true;
61 | }
62 |
63 | selectedView.post(new Runnable() {
64 | @Override
65 | public void run() {
66 | if( selectedItem.enabled )
67 | ((ImageView) selectedView.findViewById(R.id.app_used))
68 | .setImageResource(R.drawable.abc_btn_check_to_on_mtrl_015);
69 | else
70 | ((ImageView) selectedView.findViewById(R.id.app_used))
71 | .setImageResource(R.drawable.abc_btn_check_to_on_mtrl_000);
72 | }
73 | });
74 | dialog.dismiss();
75 | }
76 | };
77 |
78 | bodyListener = new View.OnClickListener() {
79 | @Override
80 | public void onClick(View v) {
81 | selectedItem = getItem((Integer)v.getTag());
82 | selectedView = v;
83 | noticeTypeDialog.setSingleChoiceItems(noticeTypes,
84 | AppNotification.canNotice(selectedItem.packageName),
85 | dialogItemListener);
86 | noticeTypeDialog.create().show();
87 | }
88 | };
89 | }
90 |
91 | @Override
92 | public int getCount() {
93 | return ((null != appsList) ? appsList.size() : 0);
94 | }
95 |
96 | @Override
97 | public ApplicationInfo getItem(int position) {
98 | return ((null != appsList) ? appsList.get(position) : null);
99 | }
100 |
101 | @Override
102 | public long getItemId(int position) {
103 | return position;
104 | }
105 |
106 | @Override
107 | public View getView(int position, View convertView, ViewGroup parent) {
108 | View view = convertView;
109 | if (null == view) {
110 | LayoutInflater layoutInflater = (LayoutInflater) context
111 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
112 | view = layoutInflater.inflate(R.layout.applist_item, null);
113 | view.setOnClickListener(bodyListener);
114 | }
115 |
116 | ApplicationInfo data = appsList.get(position);
117 | if (null != data) {
118 | ImageView usedView = (ImageView) view.findViewById(R.id.app_used);
119 |
120 | ((TextView) view.findViewById(R.id.app_name)).setText(data.loadLabel(packageManager));
121 | ((TextView) view.findViewById(R.id.app_package)).setText(data.packageName);
122 | ((ImageView) view.findViewById(R.id.app_icon))
123 | .setImageDrawable(data.loadIcon(packageManager));
124 |
125 | if( data.enabled )
126 | usedView.setImageResource(R.drawable.abc_btn_check_to_on_mtrl_015);
127 | else
128 | usedView.setImageResource(R.drawable.abc_btn_check_to_on_mtrl_000);
129 | view.setTag(position);
130 | }
131 | return view;
132 | }
133 |
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/Application/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Bluetooth LE не поддерживается
5 | Данные:
6 | Device address:
7 | State:
8 | Нет данных
9 | Подключено
10 | Отключено
11 | Поиск устройства
12 | Bluetooth не поддерживается
13 |
14 | Для отображения уведомлений на браслете необходимо разрешить доступ к уведомлениям. Открыть настройки?
15 | Да
16 | Нет
17 |
18 | Unknown device
19 | Unknown characteristic
20 | Unknown service
21 |
22 |
23 | Устройство
24 | Модель:
25 | Прошивка:
26 | Время устройства:
27 | Уровень заряда:
28 |
29 | Неверное время!
30 |
31 | Подключиться
32 | Неизвестно
33 | Настройки
34 |
35 | Клик при входящем звонке выключает звук
36 | Долгий клик при входящем звонке сбрасывает вызов
37 | Долгий клик - поиск телефона
38 | Вибрировать когда звонит будильник (работает только с Android DeskClock)
39 |
40 | Не подключено
41 | Google Fit подключен, данные выгружаются в него
42 | Неудалось подключиться к Google Fit
43 | Подключение к Google Fit
44 | Подключиться к Google Fit
45 | Переподключиться к Google Fit
46 |
47 |
48 | Подключить
49 | Отключить
50 | Искать
51 | Остановить
52 |
53 |
54 | Устройство не подключено
55 | Получение данных от устройства
56 | Внимание
57 | Настройка пользователя
58 | Настройка устройства
59 |
60 |
61 | Настройки устройства
62 |
63 | Ваша цель
64 | Ваш вес
65 | Ваш рост
66 | Ваш возраст
67 | Указать пол
68 | Notification Settings
69 | Keep Special Characters
70 | Can cause some problems in some languages or some devices.
71 | Add Delay
72 | To ignore repeated or cleared notifications in a second
73 | - Мужик
74 | - Женщина
75 |
76 |
77 | - 0
78 | - 1
79 |
80 |
81 | Разрешить жесты
82 | Показывать время когда Вы смотрите на браслет как на часы
83 |
84 | Автосон
85 | Мигать синим светодиодом
86 | Время в 12 формате
87 | Использовать английскую систему
88 |
89 | Включить BLE
90 | Неизвестный параметр. Использовать на свой страх и риск
91 |
92 | Тип значка
93 | Не уведомлять
94 | Конвертик
95 | Облачка
96 | Ошибка
97 | Вибрировать при входяшем вызове
98 | Выбрать приложения для уведомления
99 | GeekBracelet
100 |
101 | Внимание!
102 | Вы должны разрешить доступ к некоторым функциям, для нормально работы приложения
103 | Некоторые разрешения не получены. Работать дальше не могу
104 |
105 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/i5/Constants.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.i5;
2 |
3 | /**
4 | * Created by Dmitry on 29.08.2015.
5 | */
6 | public class Constants {
7 | public static final String BAND_SERVICE_BATTERY = "0000180f-0000-1000-8000-00805f9b34fb";
8 | public static final String BAND_SERVICE_INFO = "00001800-0000-1000-8000-00805f9b34fb";
9 | public static final String BAND_SERVICE_MAIN = "f000ff00-0451-4000-b000-000000000000";
10 | public static final String BAND_SERVICE_MAIN_NEW = "0000ff20-0000-1000-8000-00805f9b34fb";
11 | public static final String BAND_SERVICE_PHONE_ALERT = "f000ff10-0451-4000-b000-000000000000";
12 | public static final String UPDATE_SERVICE_MAIN = "00001530-0000-1000-8000-00805f9b34fb";
13 | public static final String HZ = "00001801-0000-1000-8000-00805f9b34fb";
14 |
15 | public static final String BAND_CHARACTERISTIC_ALARM = "f000ff01-0451-4000-b000-000000000000";
16 | public static final String BAND_CHARACTERISTIC_BATTERY = "00002a19-0000-1000-8000-00805f9b34fb";
17 | public static final String BAND_CHARACTERISTIC_DAILY = "f000ff07-0451-4000-b000-000000000000";
18 | public static final String BAND_CHARACTERISTIC_DATE = "f000ff05-0451-4000-b000-000000000000";
19 | public static final String BAND_CHARACTERISTIC_INFO = "00002a00-0000-1000-8000-00805f9b34fb";
20 | public static final String BAND_CHARACTERISTIC_LED = "f000ff04-0451-4000-b000-000000000000";
21 | public static final String BAND_CHARACTERISTIC_NEW_NOTIFY = "0000ff22-0000-1000-8000-00805f9b34fb";
22 | public static final String BAND_CHARACTERISTIC_NEW_WRITE = "0000ff21-0000-1000-8000-00805f9b34fb";
23 | public static final String BAND_CHARACTERISTIC_NEW_INDICATE = "0000ff23-0000-1000-8000-00805f9b34fb";
24 | public static final String BAND_CHARACTERISTIC_PAIR = "f000ff06-0451-4000-b000-000000000000";
25 | public static final String BAND_CHARACTERISTIC_PHONE_ALERT = "f000ff11-0451-4000-b000-000000000000";
26 | public static final String BAND_CHARACTERISTIC_POWER_SAVING = "f000ff09-0451-4000-b000-000000000000";
27 | public static final String BAND_CHARACTERISTIC_SCALE_DATA = "0000fff4-0000-1000-8000-00805f9b34fb";
28 | public static final String BAND_CHARACTERISTIC_SCALE_SERVICE_MAIN = "0000fff0-0000-1000-8000-00805f9b34fb";
29 | public static final String BAND_CHARACTERISTIC_SCALE_SETTING = "0000fff1-0000-1000-8000-00805f9b34fb";
30 | public static final String BAND_CHARACTERISTIC_SEDENTARY = "f000ff08-0451-4000-b000-000000000000";
31 | public static final String BAND_CHARACTERISTIC_SPORT = "f000ff03-0451-4000-b000-000000000000";
32 | public static final String BAND_CHARACTERISTIC_USER = "f000ff02-0451-4000-b000-000000000000";
33 | public static final String CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb";
34 |
35 | public static final String CHR1 = "00002a01-0000-1000-8000-00805f9b34fb";
36 | public static final String CHR2 = "00002a02-0000-1000-8000-00805f9b34fb";
37 | public static final String CHR3 = "00002a03-0000-1000-8000-00805f9b34fb";
38 | public static final String CHR4 = "00002a04-0000-1000-8000-00805f9b34fb";
39 | public static final String CHR5 = "00002a05-0000-1000-8000-00805f9b34fb";
40 |
41 | public static final int ALERT_TYPE_CALL = 1;
42 | public static final int ALERT_TYPE_MESSAGE = 2;
43 | public static final int ALERT_TYPE_CLOUD = 3;
44 | public static final int ALERT_TYPE_ERROR = 4;
45 |
46 | /**
47 | * // HEADER GROUPS //
48 | * DEVICE = 0
49 | * CONFIG = 1
50 | * DATALOG = 2
51 | * MSG = 3
52 | * PHONE_MSG = 4 ???
53 | *
54 | * // CONFIG ///
55 | * CMD_ID_CONFIG_GET_AC = 5
56 | * CMD_ID_CONFIG_GET_BLE = 3
57 | * CMD_ID_CONFIG_GET_HW_OPTION = 9
58 | * CMD_ID_CONFIG_GET_NMA = 7
59 | * CMD_ID_CONFIG_GET_TIME = 1
60 | *
61 | * CMD_ID_CONFIG_SET_AC = 4
62 | * CMD_ID_CONFIG_SET_BLE = 2
63 | * CMD_ID_CONFIG_SET_HW_OPTION = 8
64 | * CMD_ID_CONFIG_SET_NMA = 6
65 | * CMD_ID_CONFIG_SET_TIME = 0
66 | *
67 | * // DATALOG //
68 | * CMD_ID_DATALOG_CLEAR_ALL = 2
69 | * CMD_ID_DATALOG_GET_BODY_PARAM = 1
70 | * CMD_ID_DATALOG_SET_BODY_PARAM = 0
71 | *
72 | * CMD_ID_DATALOG_GET_CUR_DAY_DATA = 7
73 | *
74 | * CMD_ID_DATALOG_START_GET_DAY_DATA = 3
75 | * CMD_ID_DATALOG_START_GET_MINUTE_DATA = 5
76 | *
77 | * CMD_ID_DATALOG_STOP_GET_DAY_DATA = 4
78 | * CMD_ID_DATALOG_STOP_GET_MINUTE_DATA = 6
79 | *
80 | * // DEVICE //
81 | * CMD_ID_DEVICE_GET_BATTERY = 1
82 | * CMD_ID_DEVICE_GET_INFORMATION = 0
83 | * CMD_ID_DEVICE_RESE = 2
84 | * CMD_ID_DEVICE_UPDATE = 3
85 | *
86 | * // MSG //
87 | * CMD_ID_MSG_DOWNLOAD = 1
88 | * CMD_ID_MSG_MULTI_DOWNLOAD_CONTINUE = 3
89 | * CMD_ID_MSG_MULTI_DOWNLOAD_END = 4
90 | * CMD_ID_MSG_MULTI_DOWNLOAD_START = 2
91 | * CMD_ID_MSG_UPLOAD = 0
92 | *
93 | * // PHONE_MSG //
94 | * CMD_ID_PHONE_ALERT = 1
95 | * CMD_ID_PHONE_PRESSKEY = 0
96 | */
97 |
98 | public static final byte APIv1_DATA_DEVICE_INFO = 0;
99 | public static final byte APIv1_DATA_DEVICE_POWER = 1;
100 | public static final byte APIv1_SET_DEVICE_DATE = 16;
101 | public static final byte APIv1_DATA_DEVICE_DATE = 17;
102 | public static final byte APIv1_SET_DEVICE_BLE = 18;
103 | public static final byte APIv1_DATA_DEVICE_BLE = 19;
104 | public static final byte APIv1_SET_DEVICE_CLOCK_ALARM = 20;
105 | public static final byte APIv1_SET_DEVICE_CONFIG = 24;
106 | public static final byte APIv1_DATA_DEVICE_CONFIG = 25;
107 | public static final byte APIv1_SET_USER_PARAMS = 32;
108 | public static final byte APIv1_DATA_USER_PARAMS = 33;
109 | public static final byte APIv1_DATA_SUBSCRIBE_FOR_SPORT = 35;
110 | public static final byte APIv1_DATA_LOCAL_SPORT = 37;
111 | public static final byte APIv1_DATA_DAILY_SPORT2 = 39;
112 | public static final byte APIv1_SET_ALERT_DATA = 49;
113 | public static final byte APIv1_SET_ALERT_DATA2 = 50;
114 | public static final byte APIv1_SET_ALERT_DATA3 = 51;
115 | public static final byte APIv1_SET_ALERT_DATA4 = 52;
116 | public static final byte APIv1_DATA_SELFIE = 64;
117 | public static final byte APIv1_SET_END_CALL = 65;
118 | }
119 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/receiver/Receiver.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.receiver;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.res.AssetFileDescriptor;
7 | import android.media.AudioManager;
8 | import android.media.MediaPlayer;
9 |
10 | import ru.wilix.device.geekbracelet.App;
11 | import ru.wilix.device.geekbracelet.BLEService;
12 | import ru.wilix.device.geekbracelet.BroadcastConstants;
13 | import ru.wilix.device.geekbracelet.GoogleFitConnector;
14 | import ru.wilix.device.geekbracelet.model.Sport;
15 |
16 | /**
17 | * Created by Aloyan Dmitry on 30.08.2015
18 | */
19 | public class Receiver extends BroadcastReceiver {
20 | private static boolean isIncomingCallMuted = false;
21 | private static boolean hasIncomingCall = false;
22 |
23 | public void onReceive(Context context, Intent intent){
24 | String action = intent.getAction();
25 | if( action != "android.intent.action.BOOT_COMPLETED" &&
26 | (BLEService.getSelf() == null || BLEService.getSelf().getDevice()== null) )
27 | return;
28 |
29 | switch (action){
30 | case "android.intent.action.BOOT_COMPLETED":
31 | // App should init
32 | break;
33 | case BroadcastConstants.ACTION_NEW_NOTIFICATION_RECEIVED:
34 | NotificationMonitor.Notif nf = (NotificationMonitor.Notif)intent.getSerializableExtra("data");
35 | String message = nf.getFromName() + ": " + nf.getMsgText();
36 | BLEService.getSelf().getDevice().sendAlert(message, nf.getDeviceNoticeType());
37 | break;
38 | case BroadcastConstants.ACTION_INCOMING_CALL:
39 | hasIncomingCall = true;
40 | stopLocatior(context); // If we receive call and locate the phone, need end locator
41 |
42 | if( App.sPref.getBoolean("cbx_action_mute_onclick", false) )
43 | BLEService.getSelf().getDevice().setSelfieMode(true); // Set one click mode for mute
44 |
45 | String callid = intent.getStringExtra("data");
46 | BLEService.getSelf().getDevice().sendCall(callid); // Show call in device
47 | break;
48 | case BroadcastConstants.ACTION_END_CALL:
49 | hasIncomingCall = false;
50 | BLEService.getSelf().getDevice().sendCallEnd();
51 | if( isIncomingCallMuted ) { // If we mute call, we need restore it
52 | ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE)).setRingerMode(AudioManager.RINGER_MODE_NORMAL);
53 | BLEService.getSelf().getDevice().setSelfieMode(false);
54 | }
55 | break;
56 | case BroadcastConstants.ACTION_SELFIE:
57 | // If one click and we have ringing, need to mute
58 | if( hasIncomingCall && App.sPref.getBoolean("cbx_action_mute_onclick", false) ){
59 | AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
60 | am.adjustVolume(AudioManager.ADJUST_LOWER, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
61 | CallReceiver.rejectCall(context, 1);
62 | isIncomingCallMuted = true;
63 | BLEService.getSelf().getDevice().setSelfieMode(false);
64 | return;
65 | }
66 | stopLocatior(context);
67 | BLEService.getSelf().getDevice().setSelfieMode(false);
68 | break;
69 | case BroadcastConstants.ACTION_PLAYPAUSE:
70 | // Call reject
71 | if( hasIncomingCall && App.sPref.getBoolean("cbx_action_reject_on_long", false) ){
72 | CallReceiver.rejectCall(context, 0);
73 | return;
74 | }
75 |
76 | // Locator service
77 | if( !hasIncomingCall && App.sPref.getBoolean("cbx_action_locator_on_long", false) ){
78 | startLocator(context);
79 | BLEService.getSelf().getDevice().setSelfieMode(true);
80 | return;
81 | }
82 | break;
83 |
84 | case BroadcastConstants.ACTION_SPORT_DATA:
85 | Sport sport = (Sport)intent.getSerializableExtra("data");
86 | if( App.sPref.getBoolean("fit_connected", false) )
87 | GoogleFitConnector.publish(sport);
88 | break;
89 |
90 | case BroadcastConstants.ACTION_CONNECT_TO_GFIT:
91 | if( App.sPref.getBoolean("fit_connected", false) )
92 | if( BLEService.getSelf() != null && BLEService.getSelf().getDevice() != null )
93 | BLEService.getSelf().getDevice().subscribeForSportUpdates();
94 | break;
95 | case BroadcastConstants.ACTION_GATT_CONNECTED:
96 | if( App.sPref.getBoolean("fit_connected", false) )
97 | BLEService.getSelf().getDevice().subscribeForSportUpdates();
98 | break;
99 | }
100 | }
101 |
102 | private static MediaPlayer player = new MediaPlayer();
103 | private static int lastVolume;
104 |
105 | private void startLocator(Context context){
106 | try {
107 | AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
108 | lastVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
109 | am.setStreamVolume(AudioManager.STREAM_MUSIC, am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
110 |
111 | AssetFileDescriptor afd = context.getAssets().openFd("beep_4_times.mp3");
112 | player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
113 | player.setLooping(true);
114 | player.prepare();
115 | player.start();
116 | autoOffLocator();
117 | }catch (Exception e){}
118 | }
119 |
120 | private void stopLocatior(Context context){
121 | try {
122 | if (player.isPlaying()) {
123 | player.stop();
124 | player.release();
125 |
126 | AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
127 | am.setStreamVolume(AudioManager.STREAM_MUSIC, lastVolume, 0);
128 | }
129 | }catch (Exception e){}
130 | }
131 |
132 | private void autoOffLocator(){
133 | new Thread(new Runnable() {
134 | @Override
135 | public void run() {
136 | try {
137 | Thread.sleep(120000); // 2 min
138 | if( player.isPlaying() ) {
139 | player.stop();
140 | player.release();
141 | }
142 | }catch (Exception e){}
143 | }
144 | }).start();
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/i5/Communication.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.i5;
2 |
3 | import android.bluetooth.BluetoothGatt;
4 | import android.bluetooth.BluetoothGattCallback;
5 | import android.bluetooth.BluetoothGattCharacteristic;
6 | import android.bluetooth.BluetoothGattDescriptor;
7 | import android.bluetooth.BluetoothGattService;
8 | import android.bluetooth.BluetoothProfile;
9 | import android.content.Intent;
10 | import android.util.Log;
11 |
12 | import java.util.ArrayList;
13 | import java.util.Date;
14 | import java.util.UUID;
15 |
16 | import ru.wilix.device.geekbracelet.App;
17 | import ru.wilix.device.geekbracelet.BLEService;
18 | import ru.wilix.device.geekbracelet.BroadcastConstants;
19 |
20 | /**
21 | * Created by Dmitry on 29.08.2015.
22 | */
23 | public class Communication extends BluetoothGattCallback {
24 | private static final String TAG = "i5Communication";
25 | private static BLEService bleService;
26 | public static int apiVersion = 1;
27 | public static long lastDataReceived = 0;
28 |
29 | private Device device;
30 |
31 | public Communication(BLEService bleService, Device device){
32 | this.bleService = bleService;
33 | this.device = device;
34 | }
35 |
36 | @Override
37 | public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
38 | Intent intent;
39 | if (newState == BluetoothProfile.STATE_CONNECTED) {
40 | BLEService.getSelf().mConnectionState = BLEService.STATE_CONNECTED;
41 | intent = new Intent(BroadcastConstants.ACTION_GATT_CONNECTED);
42 | App.mContext.sendBroadcast(intent);
43 |
44 | Log.i(TAG, "Connected to GATT server.");
45 | Log.i(TAG, "Attempting to start service discovery.");
46 | BLEService.getSelf().getmBluetoothGatt().discoverServices();
47 | } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
48 | intent = new Intent(BroadcastConstants.ACTION_GATT_DISCONNECTED);
49 | App.mContext.sendBroadcast(intent);
50 |
51 | Log.i(TAG, "Disconnected from GATT server.");
52 | BLEService.getSelf().mConnectionState = BLEService.STATE_DISCONNECTED;
53 | }
54 | }
55 |
56 | @Override
57 | public void onServicesDiscovered(BluetoothGatt gatt, int status) {
58 | if (status == BluetoothGatt.GATT_SUCCESS) {
59 | Log.w(TAG, "onServicesDiscovered received: " + status);
60 | for (BluetoothGattService gattService : gatt.getServices()) {
61 | BLEService.services.add(gattService);
62 | for(BluetoothGattCharacteristic chr : gattService.getCharacteristics()){
63 | BLEService.characteristics.add(chr);
64 | }
65 | }
66 | BluetoothGattCharacteristic notify_chr =
67 | bleService.getCharacteristic(UUID.fromString(Constants.BAND_CHARACTERISTIC_NEW_NOTIFY));
68 |
69 | //If FW version 2.x.x.x
70 | if(notify_chr == null)
71 | {
72 | notify_chr = bleService.getCharacteristic(UUID.fromString(Constants.BAND_CHARACTERISTIC_NEW_INDICATE));
73 | apiVersion = 2;
74 | }
75 |
76 | gatt.setCharacteristicNotification(notify_chr, true);
77 |
78 | // Set Descriptor for receive notices
79 | BluetoothGattDescriptor descriptor = notify_chr.getDescriptor(
80 | UUID.fromString(Constants.CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID));
81 |
82 | if ((notify_chr.getProperties() & 32) != 0)
83 | descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
84 | else if ((notify_chr.getProperties() & 16) != 0)
85 | descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
86 | else
87 | descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
88 |
89 | if( gatt.writeDescriptor(descriptor) )
90 | Log.d(TAG, "Descriptor is set");
91 | else
92 | Log.e(TAG, "Can't set descriptor!");
93 |
94 | Intent intent = new Intent(BroadcastConstants.ACTION_GATT_SERVICES_DISCOVERED);
95 | App.mContext.sendBroadcast(intent);
96 | } else {
97 | Log.w(TAG, "onServicesDiscovered received: " + status);
98 | }
99 | }
100 |
101 | @Override
102 | public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
103 | Log.i(TAG, "CHR Read");
104 | if (status == BluetoothGatt.GATT_SUCCESS) {
105 | parseCharacteristic(characteristic);
106 | //broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
107 | }
108 | }
109 |
110 | @Override
111 | public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
112 | Log.i(TAG, "CHR Changed");
113 | parseCharacteristic(characteristic);
114 | //broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
115 | }
116 |
117 | public void parseCharacteristic(BluetoothGattCharacteristic chr){
118 | lastDataReceived = new Date().getTime();
119 |
120 | if( apiVersion >= 1 )
121 | this.device.parserAPIv1(chr);
122 | else
123 | this.device.parserAPIv0(chr);
124 | }
125 |
126 | public void WriteDataPacket(WriteDataTask task){
127 | ArrayList tasks = new ArrayList<>();
128 | tasks.add(task);
129 | WriteDataPacket(tasks);
130 | }
131 |
132 | public void WriteDataPacket(ArrayList tasks){
133 | final ArrayList ptasks = tasks;
134 | new Thread(new Runnable() {
135 | @Override
136 | public void run() {
137 | try {
138 | for(WriteDataTask task : ptasks) {
139 | Thread.sleep(240);
140 | task.run();
141 | }
142 | Thread.sleep(1000);
143 | } catch (InterruptedException e) {
144 | e.printStackTrace();
145 | }
146 | }
147 | }).start();
148 | }
149 |
150 | public static class WriteDataTask implements Runnable{
151 | private byte[] data;
152 | private UUID uuid;
153 |
154 | /**
155 | * Create write task for packet manager
156 | * @param uuid - Write to characteristic
157 | * @param data - data for write
158 | */
159 | public WriteDataTask(UUID uuid, byte[] data){
160 | this.data = data;
161 | this.uuid = uuid;
162 | }
163 |
164 | @Override
165 | public void run() {
166 | try {
167 | BluetoothGattCharacteristic characteristic = bleService.getCharacteristic(uuid);
168 | if (characteristic != null && BLEService.getSelf().getmBluetoothGatt() != null) {
169 | characteristic.setValue(data);
170 | BLEService.getSelf().getmBluetoothGatt().writeCharacteristic(characteristic);
171 | }
172 | } catch (Exception e) {
173 | e.printStackTrace();
174 | }
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/DeviceSettingsActivity.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.app.Activity;
4 | import android.app.ProgressDialog;
5 | import android.content.BroadcastReceiver;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.content.SharedPreferences;
11 | import android.os.Bundle;
12 | import android.preference.PreferenceFragment;
13 | import android.preference.PreferenceManager;
14 | import android.widget.Toast;
15 |
16 | import java.util.HashMap;
17 |
18 | import ru.wilix.device.geekbracelet.receiver.NotificationMonitor;
19 |
20 | public class DeviceSettingsActivity extends Activity {
21 | ProgressDialog dialog;
22 | IntentFilter inFilter;
23 |
24 | public void onCreate(Bundle savedInstanceState){
25 | super.onCreate(savedInstanceState);
26 |
27 | if( BLEService.getSelf() == null || BLEService.getSelf().getDevice() == null ){
28 | Toast.makeText(this, R.string.device_settings_device_not_connected, Toast.LENGTH_LONG).show();
29 | finish();
30 | return;
31 | }
32 | }
33 |
34 | private int packetIterator = 0;
35 | private final BroadcastReceiver resultReceiver = new BroadcastReceiver() {
36 | @Override
37 | public void onReceive(Context context, Intent intent) {
38 | packetIterator++;
39 | SharedPreferences.Editor ed = App.sPref.edit();
40 | final String action = intent.getAction();
41 | switch (action){
42 | case BroadcastConstants.ACTION_BLE_DATA:
43 | ed.putBoolean("dev_conf_ble", intent.getIntExtra("data", 0) > 0);
44 | break;
45 | case BroadcastConstants.ACTION_USER_BODY_DATA:
46 | HashMap userData = (HashMap)intent.getSerializableExtra("data");
47 |
48 | ed.putString("dev_conf_goal", Integer.toString(userData.get("goal_high")));
49 | ed.putString("dev_conf_weight", Integer.toString(userData.get("weight")));
50 | ed.putString("dev_conf_height", Integer.toString(userData.get("height")));
51 | ed.putString("dev_conf_age", Integer.toString(userData.get("age")));
52 | ed.putString("dev_conf_gender", Integer.toString(userData.get("gender")));
53 | break;
54 | case BroadcastConstants.ACTION_DEVICE_CONF_DATA:
55 | HashMap confData = (HashMap)intent.getSerializableExtra("data");
56 |
57 | ed.putBoolean("dev_conf_light", confData.get("light") > 0);
58 | ed.putBoolean("dev_conf_gesture", confData.get("gesture") > 0);
59 | ed.putBoolean("dev_conf_englishunits", confData.get("englishUnits") > 0);
60 | ed.putBoolean("dev_conf_use24hours", confData.get("use24hour") > 0);
61 | ed.putBoolean("dev_conf_autosleep", confData.get("autoSleep") > 0);
62 | break;
63 | }
64 | ed.apply();
65 | if( packetIterator >= 3 ){
66 | if( dialog != null && dialog.isShowing() ) {
67 | dialog.dismiss();
68 | PreferenceManager.setDefaultValues(DeviceSettingsActivity.this, R.xml.pref_general, true);
69 | getFragmentManager().beginTransaction()
70 | .replace(android.R.id.content, new prefGeneral())
71 | .commit();
72 | }
73 | }
74 | }
75 | };
76 |
77 | @Override
78 | public void onResume(){
79 | super.onResume();
80 |
81 | inFilter = new IntentFilter();
82 | inFilter.addAction(BroadcastConstants.ACTION_DEVICE_CONF_DATA);
83 | inFilter.addAction(BroadcastConstants.ACTION_BLE_DATA);
84 | inFilter.addAction(BroadcastConstants.ACTION_USER_BODY_DATA);
85 |
86 | dialog = new ProgressDialog(this);
87 | dialog.setIndeterminate(true);
88 | dialog.setCanceledOnTouchOutside(false);
89 | dialog.setMessage(getResources().getString(R.string.device_settings_wait_device_alert));
90 | dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
91 | @Override
92 | public void onCancel(DialogInterface dialog) {
93 | finish();
94 | }
95 | });
96 |
97 | new Thread(new Runnable() {
98 | @Override
99 | public void run() {
100 | try {
101 | BLEService.getSelf().getDevice().askBle();
102 | Thread.sleep(500);
103 | BLEService.getSelf().getDevice().askConfig();
104 | Thread.sleep(500);
105 | BLEService.getSelf().getDevice().askUserParams();
106 | }catch (Exception e){}
107 | }
108 | }).start();
109 | dialog.show();
110 |
111 | registerReceiver(resultReceiver, inFilter);
112 | }
113 |
114 | @Override
115 | public void onPause(){
116 | super.onPause();
117 | new Saver().run();
118 | unregisterReceiver(resultReceiver);
119 | }
120 |
121 | @Override
122 | public void onDestroy(){
123 | super.onDestroy();
124 | new Saver().run();
125 |
126 | if( BLEService.getSelf() == null || BLEService.getSelf().getDevice() == null )
127 | return;
128 |
129 | }
130 |
131 | public static class prefGeneral extends PreferenceFragment {
132 | public void onCreate(Bundle savedInstanceState) {
133 | super.onCreate(savedInstanceState);
134 | addPreferencesFromResource(R.xml.pref_general);
135 | }
136 | }
137 |
138 | private class Saver implements Runnable {
139 | @Override
140 | public void run() {
141 | int goal_high = Integer.parseInt(App.sPref.getString("dev_conf_goal", "1000"));
142 | int weight = Integer.parseInt(App.sPref.getString("dev_conf_weight", "70"));
143 | int height = Integer.parseInt(App.sPref.getString("dev_conf_height", "180"));
144 | int age = Integer.parseInt(App.sPref.getString("dev_conf_age", "26"));
145 | int gender = Integer.parseInt(App.sPref.getString("dev_conf_gender", "0"));
146 | BLEService.getSelf().getDevice().setUserParams(height, weight, gender > 0, age, goal_high);
147 |
148 | boolean light = App.sPref.getBoolean("dev_conf_light", false);
149 | boolean gesture = App.sPref.getBoolean("dev_conf_gesture", false);
150 | boolean englishunits = App.sPref.getBoolean("dev_conf_englishunits", false);
151 | boolean use24hours = App.sPref.getBoolean("dev_conf_use24hours", false);
152 | boolean autosleep = App.sPref.getBoolean("dev_conf_autosleep", false);
153 | BLEService.getSelf().getDevice().setConfig(light, gesture, englishunits,
154 | use24hours, autosleep);
155 |
156 | BLEService.getSelf().getDevice().setBle(App.sPref.getBoolean("dev_conf_ble", false));
157 |
158 | NotificationMonitor.settingsKeepForeign = App.sPref.getBoolean("notif_foreign", false);
159 | NotificationMonitor.settingsDelay = Integer.parseInt(App.sPref.getString("notif_delay", "0"));
160 |
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/model/Sport.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.model;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 |
5 | import java.io.Serializable;
6 | import java.lang.reflect.Field;
7 | import java.lang.reflect.Modifier;
8 | import java.util.Arrays;
9 | import java.util.Date;
10 | import java.util.GregorianCalendar;
11 |
12 | import ru.wilix.device.geekbracelet.i5.Utils;
13 |
14 | /**
15 | * Created by Dmitry on 30.08.2015.
16 | */
17 | public class Sport implements Serializable {
18 | private int bcc = 0;
19 | private int minute = 0;
20 | private int hour = 0;
21 | private int day = 0;
22 | private int month = 0;
23 | private int year = 0;
24 | private int flag = 0;
25 | private int steps = 0;
26 | private float distance = 0;
27 | private float calorie = 0;
28 | private int type = 0;
29 |
30 | public static final int TYPE_LOCAL_SPORT = 0;
31 | public static final int TYPE_DAILY_A = 1;
32 | public static final int TYPE_DAILY_B = 2;
33 |
34 | public static Sport fromBytes(byte[] data, int type){
35 | try {
36 | Sport sport = new Sport();
37 | if( type != TYPE_LOCAL_SPORT ) {
38 | sport.setBcc(Utils.bytesToInt(Arrays.copyOfRange(data, 4, 5)));
39 | sport.setYear(Utils.bytesToInt(Arrays.copyOfRange(data, 7, 8)));
40 | sport.setMonth(Utils.bytesToInt(Arrays.copyOfRange(data, 6, 7)));
41 | sport.setDay(Utils.bytesToInt(Arrays.copyOfRange(data, 5, 6)));
42 | sport.setSteps(Utils.bytesToInt(Arrays.copyOfRange(data, 8, 12)));
43 | sport.setDistance(((float) Utils.bytesToInt(Arrays.copyOfRange(data, 12, 16))) * 0.1f);
44 | sport.setCalorie(((float) Utils.bytesToInt(Arrays.copyOfRange(data, 16, 20))) * 0.1f);
45 | sport.setType(type);
46 | }else{
47 | sport.setBcc(Utils.bytesToInt(Arrays.copyOfRange(data, 4, 5)));
48 | sport.setMinute(Utils.bytesToInt(Arrays.copyOfRange(data, 5, 6)));
49 | sport.setHour(Utils.bytesToInt(Arrays.copyOfRange(data, 6, 7)));
50 | sport.setDay(Utils.bytesToInt(Arrays.copyOfRange(data, 7, 8)) + 1);
51 | sport.setMonth(Utils.bytesToInt(Arrays.copyOfRange(data, 8, 9)) + 1);
52 | sport.setYear(Utils.bytesToInt(Arrays.copyOfRange(data, 9, 10)) + 2000);
53 | sport.setFlag(Utils.bytesToInt(Arrays.copyOfRange(data, 10, 11)));
54 | sport.setSteps(Utils.bytesToInt(Arrays.copyOfRange(data, 11, 13)));
55 | sport.setDistance(((float) Utils.bytesToInt(Arrays.copyOfRange(data, 13, 15))) * 0.1f);
56 | sport.setCalorie(((float) Utils.bytesToInt(Arrays.copyOfRange(data, 15, 17))) * 0.1f);
57 | sport.setType(TYPE_LOCAL_SPORT);
58 | }
59 | return sport;
60 | } catch (Exception e) {
61 | e.printStackTrace();
62 | return null;
63 | }
64 | }
65 |
66 | /**
67 | * Create new Sport model from Characteristic
68 | * @param chr Characteristic
69 | * @return
70 | */
71 | public static Sport fromCharacteristic(BluetoothGattCharacteristic chr, boolean daily){
72 | Sport sp = new Sport();
73 | if( !daily ) {
74 | sp.setBcc(chr.getIntValue(17, 0));
75 | sp.setMinute(chr.getIntValue(17, 1));
76 | sp.setHour(chr.getIntValue(17, 2));
77 | sp.setDay(chr.getIntValue(17, 3) + 1);
78 | sp.setMonth(chr.getIntValue(17, 4) + 1);
79 | sp.setYear(chr.getIntValue(17, 5) + 2000);
80 | sp.setFlag(chr.getIntValue(17, 6));
81 | sp.setSteps(chr.getIntValue(18, 7));
82 | sp.setDistance(((float) chr.getIntValue(18, 9)) * 0.1f);
83 | sp.setCalorie(((float) chr.getIntValue(17, 11)) * 0.1f);
84 | sp.setType(TYPE_LOCAL_SPORT);
85 | }else{
86 | if( chr.getValue().length == 12 ){
87 | sp.setSteps(chr.getIntValue(20, 0));
88 | sp.setDistance(((float) chr.getIntValue(20, 4)) * 0.1f);
89 | sp.setCalorie(((float) chr.getIntValue(10, 8)) * 0.1f);
90 | sp.setType(TYPE_DAILY_A);
91 | }else{
92 | sp.setBcc(chr.getIntValue(17, 0));
93 | sp.setDay(chr.getIntValue(17, 1) + 1);
94 | sp.setMonth(chr.getIntValue(17, 2) + 1);
95 | sp.setYear(chr.getIntValue(17, 3) + 2000);
96 | sp.setSteps(chr.getIntValue(36, 4));
97 | sp.setDistance(((float) chr.getIntValue(20, 8)) * 0.1f);
98 | sp.setCalorie(((float) chr.getIntValue(20, 12)) * 0.1f);
99 | sp.setType(TYPE_DAILY_B);
100 | }
101 | }
102 | return sp;
103 | }
104 |
105 | /**
106 | * Return timestamp for this sport point
107 | * @return
108 | */
109 | public long getTimestamp(){
110 | if( this.year == 0 )
111 | return new GregorianCalendar().getTimeInMillis();
112 | long tmp = new GregorianCalendar(this.year, this.month, this.day, this.hour, this.minute).getTimeInMillis();
113 | if( tmp <= 0 )
114 | return new Date().getTime();
115 | return tmp;
116 | }
117 |
118 | public String toString(){
119 | String buff = "";
120 | Field[] fields = this.getClass().getDeclaredFields();
121 | for (Field field : fields) {
122 | if ( (field.getModifiers() & Modifier.FINAL) == Modifier.FINAL )
123 | continue;
124 | try {
125 | buff += field.getName() + ": " + field.get(this) + " ";
126 | } catch (Exception e) {
127 | }
128 | }
129 | return buff;
130 | }
131 |
132 | public void setBcc(int value){
133 | this.bcc = value;
134 | }
135 |
136 | public int getBcc(){
137 | return this.bcc;
138 | }
139 |
140 | public void setMinute(int value){
141 | this.minute = value;
142 | }
143 |
144 | public int getMinute(){
145 | return this.minute;
146 | }
147 |
148 | public void setHour(int value){
149 | this.hour = value;
150 | }
151 |
152 | public int getHour(){
153 | return this.hour;
154 | }
155 |
156 | public void setDay(int value){
157 | this.day = value;
158 | }
159 |
160 | public int getDay(){
161 | return this.day;
162 | }
163 |
164 | public void setMonth(int value){
165 | this.month = value;
166 | }
167 |
168 | public int getMonth(){
169 | return this.month;
170 | }
171 |
172 | public void setYear(int value){
173 | this.year = value;
174 | }
175 |
176 | public int getYear(){
177 | return this.year;
178 | }
179 |
180 | public void setFlag(int value){
181 | this.flag = value;
182 | }
183 |
184 | public int getFlag(){
185 | return this.flag;
186 | }
187 |
188 | public void setSteps(int value){
189 | this.steps = value;
190 | }
191 |
192 | public int getSteps(){
193 | return this.steps;
194 | }
195 |
196 | public void setDistance(float value){
197 | this.distance = value;
198 | }
199 |
200 | public float getDistance(){
201 | return this.distance;
202 | }
203 |
204 | public void setCalorie(float value){
205 | this.calorie = value;
206 | }
207 |
208 | public float getCalorie(){
209 | return this.calorie;
210 | }
211 |
212 | public void setType(int value){
213 | this.type = value;
214 | }
215 |
216 | public int getType(){
217 | return this.type;
218 | }
219 | }
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/DeviceScanActivity.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.app.Activity;
4 | import android.app.ListActivity;
5 | import android.bluetooth.BluetoothAdapter;
6 | import android.bluetooth.BluetoothDevice;
7 | import android.bluetooth.BluetoothManager;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.content.SharedPreferences;
11 | import android.content.pm.PackageManager;
12 | import android.os.Bundle;
13 | import android.os.Handler;
14 | import android.view.LayoutInflater;
15 | import android.view.Menu;
16 | import android.view.MenuItem;
17 | import android.view.View;
18 | import android.view.ViewGroup;
19 | import android.widget.BaseAdapter;
20 | import android.widget.ListView;
21 | import android.widget.TextView;
22 | import android.widget.Toast;
23 |
24 | import java.util.ArrayList;
25 |
26 | public class DeviceScanActivity extends ListActivity {
27 | private LeDeviceListAdapter mLeDeviceListAdapter;
28 | private BluetoothAdapter mBluetoothAdapter;
29 | private boolean mScanning;
30 | private Handler mHandler;
31 |
32 | private static final int REQUEST_ENABLE_BT = 1;
33 | // Stops scanning after 10 seconds.
34 | private static final long SCAN_PERIOD = 10000;
35 |
36 | @Override
37 | public void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | getActionBar().setTitle(R.string.title_devices);
40 | mHandler = new Handler();
41 |
42 | // Use this check to determine whether BLE is supported on the device. Then you can
43 | // selectively disable BLE-related features.
44 | if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
45 | Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
46 | finish();
47 | }
48 |
49 | // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
50 | // BluetoothAdapter through BluetoothManager.
51 | final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
52 | mBluetoothAdapter = bluetoothManager.getAdapter();
53 |
54 | // Checks if Bluetooth is supported on the device.
55 | if (mBluetoothAdapter == null) {
56 | Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
57 | finish();
58 | return;
59 | }
60 | }
61 |
62 | @Override
63 | public boolean onCreateOptionsMenu(Menu menu) {
64 | getMenuInflater().inflate(R.menu.main, menu);
65 | if (!mScanning) {
66 | menu.findItem(R.id.menu_stop).setVisible(false);
67 | menu.findItem(R.id.menu_scan).setVisible(true);
68 | menu.findItem(R.id.menu_refresh).setActionView(null);
69 | } else {
70 | menu.findItem(R.id.menu_stop).setVisible(true);
71 | menu.findItem(R.id.menu_scan).setVisible(false);
72 | menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress);
73 | }
74 | return true;
75 | }
76 |
77 | @Override
78 | public boolean onOptionsItemSelected(MenuItem item) {
79 | switch (item.getItemId()) {
80 | case R.id.menu_scan:
81 | mLeDeviceListAdapter.clear();
82 | scanLeDevice(true);
83 | break;
84 | case R.id.menu_stop:
85 | scanLeDevice(false);
86 | break;
87 | }
88 | return true;
89 | }
90 |
91 | @Override
92 | protected void onResume() {
93 | super.onResume();
94 |
95 | // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
96 | // fire an intent to display a dialog asking the user to grant permission to enable it.
97 | if (!mBluetoothAdapter.isEnabled()) {
98 | if (!mBluetoothAdapter.isEnabled()) {
99 | Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
100 | startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
101 | }
102 | }
103 | // Initializes list view adapter.
104 | mLeDeviceListAdapter = new LeDeviceListAdapter();
105 | setListAdapter(mLeDeviceListAdapter);
106 | scanLeDevice(true);
107 | }
108 |
109 | @Override
110 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
111 | // User chose not to enable Bluetooth.
112 | if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
113 | finish();
114 | return;
115 | }
116 | super.onActivityResult(requestCode, resultCode, data);
117 | }
118 |
119 | @Override
120 | protected void onPause() {
121 | super.onPause();
122 | scanLeDevice(false);
123 | mLeDeviceListAdapter.clear();
124 | }
125 |
126 | @Override
127 | protected void onListItemClick(ListView l, View v, int position, long id) {
128 | final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
129 | if (device == null) return;
130 |
131 | if (mScanning) {
132 | mBluetoothAdapter.stopLeScan(mLeScanCallback);
133 | mScanning = false;
134 | }
135 |
136 | SharedPreferences.Editor ed = App.sPref.edit();
137 | ed.putString("DEVICE_ADDR", device.getAddress());
138 | ed.putString("DEVICE_NAME", device.getName());
139 | ed.apply();
140 |
141 | BLEService.getSelf().connect(device.getAddress(), true);
142 |
143 | finish();
144 | }
145 |
146 | private void scanLeDevice(final boolean enable) {
147 | if (enable) {
148 | // Stops scanning after a pre-defined scan period.
149 | mHandler.postDelayed(new Runnable() {
150 | @Override
151 | public void run() {
152 | mScanning = false;
153 | mBluetoothAdapter.stopLeScan(mLeScanCallback);
154 | invalidateOptionsMenu();
155 | }
156 | }, SCAN_PERIOD);
157 |
158 | mScanning = true;
159 | mBluetoothAdapter.startLeScan(mLeScanCallback);
160 | } else {
161 | mScanning = false;
162 | mBluetoothAdapter.stopLeScan(mLeScanCallback);
163 | }
164 | invalidateOptionsMenu();
165 | }
166 |
167 | // Adapter for holding devices found through scanning.
168 | private class LeDeviceListAdapter extends BaseAdapter {
169 | private ArrayList mLeDevices;
170 | private LayoutInflater mInflator;
171 |
172 | public LeDeviceListAdapter() {
173 | super();
174 | mLeDevices = new ArrayList();
175 | mInflator = DeviceScanActivity.this.getLayoutInflater();
176 | }
177 |
178 | public void addDevice(BluetoothDevice device) {
179 | if(!mLeDevices.contains(device)) {
180 | mLeDevices.add(device);
181 | }
182 | }
183 |
184 | public BluetoothDevice getDevice(int position) {
185 | return mLeDevices.get(position);
186 | }
187 |
188 | public void clear() {
189 | mLeDevices.clear();
190 | }
191 |
192 | @Override
193 | public int getCount() {
194 | return mLeDevices.size();
195 | }
196 |
197 | @Override
198 | public Object getItem(int i) {
199 | return mLeDevices.get(i);
200 | }
201 |
202 | @Override
203 | public long getItemId(int i) {
204 | return i;
205 | }
206 |
207 | @Override
208 | public View getView(int i, View view, ViewGroup viewGroup) {
209 | ViewHolder viewHolder;
210 | // General ListView optimization code.
211 | if (view == null) {
212 | view = mInflator.inflate(R.layout.listitem_device, null);
213 | viewHolder = new ViewHolder();
214 | viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
215 | viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
216 | view.setTag(viewHolder);
217 | } else {
218 | viewHolder = (ViewHolder) view.getTag();
219 | }
220 |
221 | BluetoothDevice device = mLeDevices.get(i);
222 | final String deviceName = device.getName();
223 | if (deviceName != null && deviceName.length() > 0)
224 | viewHolder.deviceName.setText(deviceName);
225 | else
226 | viewHolder.deviceName.setText(R.string.unknown_device);
227 | viewHolder.deviceAddress.setText(device.getAddress());
228 |
229 | return view;
230 | }
231 | }
232 |
233 | // Device scan callback.
234 | private BluetoothAdapter.LeScanCallback mLeScanCallback =
235 | new BluetoothAdapter.LeScanCallback() {
236 |
237 | @Override
238 | public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
239 | runOnUiThread(new Runnable() {
240 | @Override
241 | public void run() {
242 | mLeDeviceListAdapter.addDevice(device);
243 | mLeDeviceListAdapter.notifyDataSetChanged();
244 | }
245 | });
246 | }
247 | };
248 |
249 | static class ViewHolder {
250 | TextView deviceName;
251 | TextView deviceAddress;
252 | }
253 | }
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/BLEService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
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 |
17 | package ru.wilix.device.geekbracelet;
18 |
19 | import android.app.Service;
20 | import android.bluetooth.BluetoothAdapter;
21 | import android.bluetooth.BluetoothDevice;
22 | import android.bluetooth.BluetoothGatt;
23 | import android.bluetooth.BluetoothGattCharacteristic;
24 | import android.bluetooth.BluetoothGattService;
25 | import android.bluetooth.BluetoothManager;
26 | import android.content.Context;
27 | import android.content.Intent;
28 | import android.content.pm.PackageManager;
29 | import android.os.IBinder;
30 | import android.util.Log;
31 |
32 | import java.util.ArrayList;
33 | import java.util.Date;
34 | import java.util.UUID;
35 |
36 | import ru.wilix.device.geekbracelet.i5.Device;
37 |
38 | /**
39 | * Service for managing connection and data communication with a GATT server hosted on a
40 | * given Bluetooth LE device.
41 | */
42 | public class BLEService extends Service {
43 | private final static String TAG = BLEService.class.getSimpleName();
44 |
45 | private BluetoothManager mBluetoothManager;
46 | private BluetoothAdapter mBluetoothAdapter;
47 |
48 | public static ArrayList services = new ArrayList();
49 | public static ArrayList characteristics = new ArrayList();
50 | public static BLEService self;
51 |
52 | private Device device;
53 | private BluetoothGatt mBluetoothGatt;
54 |
55 | public int mConnectionState = STATE_DISCONNECTED;
56 | public static final int STATE_DISCONNECTED = 0;
57 | public static final int STATE_CONNECTING = 1;
58 | public static final int STATE_CONNECTED = 2;
59 |
60 | public void onCreate() {
61 | super.onCreate();
62 | self = this;
63 |
64 | this.device = new Device(this);
65 | if (!initialize()) {
66 | Log.e(TAG, "Unable to initialize Bluetooth");
67 | return;
68 | }
69 | this.connect(App.sPref.getString("DEVICE_ADDR", ""), true);
70 | }
71 |
72 | public int onStartCommand(Intent intent, int flags, int startId) {
73 | return super.onStartCommand(intent, flags, startId);
74 | }
75 |
76 | public static BLEService getSelf(){
77 | return self;
78 | }
79 |
80 | public Device getDevice(){
81 | return this.device;
82 | }
83 |
84 | public BluetoothGatt getmBluetoothGatt(){
85 | return this.mBluetoothGatt;
86 | }
87 |
88 | /**
89 | * Initializes a reference to the local Bluetooth adapter.
90 | * @return Return true if the initialization is successful.
91 | */
92 | public boolean initialize() {
93 | // For API level 18 and above, get a reference to BluetoothAdapter through
94 | // BluetoothManager.
95 | if (mBluetoothManager == null) {
96 | mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
97 | if (mBluetoothManager == null) {
98 | Log.e(TAG, "Unable to initialize BluetoothManager.");
99 | return false;
100 | }
101 | }
102 |
103 | mBluetoothAdapter = mBluetoothManager.getAdapter();
104 | if (mBluetoothAdapter == null) {
105 | Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
106 | return false;
107 | }
108 |
109 | return true;
110 | }
111 |
112 | /**
113 | * Connects to the GATT server hosted on the Bluetooth LE device.
114 | * @param address The device address of the destination device.
115 | * @return Return true if the connection is initiated successfully. The connection result
116 | * is reported asynchronously through the
117 | * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
118 | * callback.
119 | */
120 | public boolean connect(final String address, boolean forceConnect) {
121 | if (mBluetoothAdapter == null || address == null ||
122 | (App.sPref.getString("DEVICE_ADDR", "").length() <= 0) && address.length() <= 0 ) {
123 | Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
124 | return false;
125 | }
126 |
127 | // Previously connected device. Try to reconnect.
128 | if ( !forceConnect && App.sPref.getString("DEVICE_ADDR", "").length() > 0 &&
129 | address.equals(App.sPref.getString("DEVICE_ADDR", "")) && mBluetoothGatt != null) {
130 | Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
131 | //this.autoReconnectOnTimeout();
132 | if (mBluetoothGatt.connect()) {
133 | mConnectionState = STATE_CONNECTING;
134 | Log.d(TAG, "Reconnecting");
135 | return true;
136 | } else {
137 | Log.d(TAG, "Connection problem");
138 | return false;
139 | }
140 | }
141 |
142 | if( forceConnect && mBluetoothGatt != null )
143 | disconnect();
144 |
145 | final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
146 | if (device == null) {
147 | Log.w(TAG, "Device not found. Unable to connect.");
148 | return false;
149 | }
150 | // We want to directly connect to the device
151 | mBluetoothGatt = device.connectGatt(this, true, this.device.comm);
152 | Log.d(TAG, "Trying to create a new connection.");
153 | mConnectionState = STATE_CONNECTING;
154 | //this.autoReconnectOnTimeout();
155 | this.checkConnection();
156 | return true;
157 | }
158 |
159 | /**
160 | * Disconnects an existing connection or cancel a pending connection. The disconnection result
161 | * is reported asynchronously through the
162 | * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
163 | * callback.
164 | */
165 | public void disconnect() {
166 | if (mBluetoothAdapter == null || mBluetoothGatt == null) {
167 | Log.w(TAG, "BluetoothAdapter not initialized");
168 | return;
169 | }
170 | try {
171 | mBluetoothGatt.disconnect();
172 | close();
173 | }catch (Exception e){
174 |
175 | }
176 | }
177 |
178 | /**
179 | * After using a given BLE device, the app must call this method to ensure resources are
180 | * released properly.
181 | */
182 | public void close() {
183 | if (mBluetoothGatt == null)
184 | return;
185 | mBluetoothGatt.close();
186 | mBluetoothGatt = null;
187 | }
188 |
189 | public BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
190 | try {
191 | for (BluetoothGattCharacteristic characteristic : BLEService.characteristics) {
192 | if (characteristic.getUuid().equals(uuid)) {
193 | return characteristic;
194 | }
195 | }
196 | return null;
197 | } catch (Exception e) {
198 | e.printStackTrace();
199 | return null;
200 | }
201 | }
202 |
203 | public static boolean isBluetoothAvailable(){
204 | if (!App.mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
205 | return false;
206 |
207 | // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
208 | // BluetoothAdapter through BluetoothManager.
209 | final BluetoothManager bluetoothManager = (BluetoothManager) App.mContext.getSystemService(Context.BLUETOOTH_SERVICE);
210 | // Checks if Bluetooth is supported on the device.
211 | if (bluetoothManager.getAdapter() == null)
212 | return false;
213 | return true;
214 | }
215 |
216 | boolean alreadyChecking = false;
217 |
218 | public void checkConnection(){
219 | alreadyChecking = false;
220 | new Thread(new Runnable() {
221 | @Override
222 | public void run() {
223 | try {
224 | if( alreadyChecking )
225 | return;
226 | alreadyChecking = true;
227 | Thread.sleep(60000);
228 | if( App.sPref.getString("DEVICE_ADDR", "").length() > 0 ) {
229 | if( BLEService.getSelf() != null && BLEService.getSelf().getDevice() != null ){
230 | // FIXME bad practice use comm object
231 | if (BLEService.getSelf().getDevice().comm.lastDataReceived - (new Date().getTime()) >= 120000 ) {
232 | BLEService.getSelf().disconnect();
233 | BLEService.getSelf().connect(App.sPref.getString("DEVICE_ADDR", ""), true);
234 | checkConnection();
235 | return;
236 | }
237 | BLEService.getSelf().getDevice().askPower();
238 | BLEService.getSelf().getDevice().askDailyData();
239 | }
240 | }
241 | checkConnection();
242 | }catch (Exception e){}
243 | }
244 | }).start();
245 | }
246 |
247 | public IBinder onBind(Intent intent) {
248 | return null;
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/GoogleFitConnector.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentSender;
7 | import android.content.SharedPreferences;
8 | import android.os.AsyncTask;
9 | import android.os.Bundle;
10 | import android.util.Log;
11 |
12 | import com.google.android.gms.common.ConnectionResult;
13 | import com.google.android.gms.common.GooglePlayServicesUtil;
14 | import com.google.android.gms.common.Scopes;
15 | import com.google.android.gms.common.api.GoogleApiClient;
16 | import com.google.android.gms.common.api.Scope;
17 | import com.google.android.gms.fitness.Fitness;
18 | import com.google.android.gms.fitness.data.DataPoint;
19 | import com.google.android.gms.fitness.data.DataSet;
20 | import com.google.android.gms.fitness.data.DataSource;
21 | import com.google.android.gms.fitness.data.DataType;
22 | import com.google.android.gms.fitness.data.Field;
23 |
24 | import java.util.concurrent.TimeUnit;
25 |
26 | import ru.wilix.device.geekbracelet.model.Sport;
27 |
28 | public class GoogleFitConnector {
29 | private static final String TAG = "FitnessService";
30 | private static GoogleApiClient mClient;
31 | private static boolean authInProgress = false;
32 |
33 | private static Sport lastSport;
34 |
35 | public static void buildClient(final Context context){
36 | // Create the Google API Client
37 | mClient = new GoogleApiClient.Builder(context)
38 | .addApi(Fitness.HISTORY_API)
39 | .addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ_WRITE))
40 | .addScope(new Scope(Scopes.FITNESS_LOCATION_READ_WRITE))
41 | .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
42 | @Override
43 | public void onConnected(Bundle bundle) {
44 | Log.i(TAG, "Connected!!!");
45 | // Now you can make calls to the Fitness APIs. What to do?
46 | // Look at some data!!
47 | SharedPreferences.Editor ed = App.sPref.edit();
48 | ed.putBoolean("fit_connected", true);
49 | ed.apply();
50 |
51 | Intent intent = new Intent(BroadcastConstants.ACTION_CONNECT_TO_GFIT);
52 | context.sendBroadcast(intent);
53 | }
54 |
55 | @Override
56 | public void onConnectionSuspended(int i) {
57 | // If your connection to the sensor gets lost at some point,
58 | // you'll be able to determine the reason and react to it here.
59 | if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
60 | Log.i(TAG, "Connection lost. Cause: Network Lost.");
61 | } else if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
62 | Log.i(TAG, "Connection lost. Reason: Service Disconnected");
63 | }
64 | SharedPreferences.Editor ed = App.sPref.edit();
65 | ed.putBoolean("fit_connected", false);
66 | ed.apply();
67 |
68 | Intent intent = new Intent(BroadcastConstants.ACTION_CONNECT_TO_GFIT);
69 | context.sendBroadcast(intent);
70 | }
71 | })
72 | .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
73 | // Called whenever the API client fails to connect.
74 | @Override
75 | public void onConnectionFailed(ConnectionResult result) {
76 | Log.i(TAG, "Connection failed. Cause: " + result.toString());
77 | if (!result.hasResolution()) {
78 | SharedPreferences.Editor ed = App.sPref.edit();
79 | ed.putBoolean("fit_connected", false);
80 | ed.apply();
81 | // Show the localized error dialog
82 | if( context instanceof Activity )
83 | GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(),
84 | (Activity)context, 0).show();
85 |
86 | Intent intent = new Intent(BroadcastConstants.ACTION_CONNECT_TO_GFIT);
87 | context.sendBroadcast(intent);
88 | return;
89 | }
90 | // The failure has a resolution. Resolve it.
91 | // Called typically when the app is not yet authorized, and an
92 | // authorization dialog is displayed to the user.
93 | if (!authInProgress) {
94 | try {
95 | Log.i(TAG, "Attempting to resolve failed connection");
96 | authInProgress = true;
97 | connect(context);
98 | if( context instanceof Activity )
99 | result.startResolutionForResult((Activity)context, 1);
100 | } catch (IntentSender.SendIntentException e) {
101 | Log.e(TAG, "Exception while starting resolution activity", e);
102 | }
103 | }
104 | }
105 | }).build();
106 | }
107 |
108 | public static void connect(final Context context){
109 | if( mClient == null )
110 | buildClient(context);
111 | mClient.connect();
112 | }
113 |
114 | public static void publish(Sport sport){
115 | Log.i(TAG, "Creating a new data insert request");
116 |
117 | if( mClient == null || !mClient.isConnected() ){
118 | Log.i(TAG, "Fit client not connected. Try connect");
119 | connect(App.mContext);
120 | return;
121 | }
122 |
123 | new InsertTask().execute(sport);
124 | }
125 |
126 | private static class InsertTask extends AsyncTask {
127 | protected Void doInBackground(Sport... sports) {
128 | Sport sport = sports[0];
129 | DataSource DSTEP_SOURCE = new DataSource.Builder()
130 | .setAppPackageName(App.mContext.getPackageName())
131 | .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
132 | .setName("GeekFit Steps")
133 | .setStreamName("GeekFit Steps")
134 | .setType(DataSource.TYPE_RAW)
135 | .build();
136 |
137 | DataSource DDISTANCE_SOURCE = new DataSource.Builder()
138 | .setAppPackageName(App.mContext.getPackageName())
139 | .setDataType(DataType.TYPE_DISTANCE_DELTA)
140 | .setName("GeekFit Distance")
141 | .setStreamName("GeekFit Distance")
142 | .setType(DataSource.TYPE_RAW)
143 | .build();
144 |
145 | DataSource DCALORIE_SOURCE = new DataSource.Builder()
146 | .setAppPackageName(App.mContext.getPackageName())
147 | .setDataType(DataType.TYPE_CALORIES_EXPENDED)
148 | .setName("GeekFit Calorie")
149 | .setStreamName("GeekFit Calorie")
150 | .setType(DataSource.TYPE_RAW)
151 | .build();
152 |
153 | if( lastSport == null )
154 | lastSport = new Sport();
155 |
156 | // FIXME make only one request for insert data
157 | // Steps data
158 | DataSet dataSet = DataSet.create(DSTEP_SOURCE);
159 | DataPoint dataPoint = dataSet.createDataPoint();
160 | dataPoint.setTimestamp(sport.getTimestamp(), TimeUnit.MILLISECONDS);
161 | dataPoint.getValue(Field.FIELD_STEPS).setInt(sport.getSteps() - lastSport.getSteps());
162 | dataSet.add(dataPoint);
163 |
164 | Log.i(TAG, "Inserting the dataset in the History API");
165 | com.google.android.gms.common.api.Status insertStatus =
166 | Fitness.HistoryApi.insertData(mClient, dataSet).await(1, TimeUnit.MINUTES);
167 |
168 | // Before querying the data, check to see if the insertion succeeded.
169 | if (!insertStatus.isSuccess()) {
170 | Log.i(TAG, "There was a problem inserting the dataset.");
171 | return null;
172 | }
173 |
174 | // Distance data
175 | dataSet = DataSet.create(DDISTANCE_SOURCE);
176 | dataPoint = dataSet.createDataPoint();
177 | dataPoint.setTimestamp(sport.getTimestamp(), TimeUnit.MILLISECONDS);
178 | dataPoint.getValue(Field.FIELD_DISTANCE).setFloat(sport.getDistance() - lastSport.getDistance());
179 | dataSet.add(dataPoint);
180 |
181 | Log.i(TAG, "Inserting the dataset in the History API");
182 | insertStatus = Fitness.HistoryApi.insertData(mClient, dataSet).await(1, TimeUnit.MINUTES);
183 |
184 | // Before querying the data, check to see if the insertion succeeded.
185 | if (!insertStatus.isSuccess()) {
186 | Log.i(TAG, "There was a problem inserting the dataset.");
187 | return null;
188 | }
189 |
190 | // Calorie data
191 | dataSet = DataSet.create(DCALORIE_SOURCE);
192 | dataPoint = dataSet.createDataPoint();
193 | dataPoint.setTimestamp(sport.getTimestamp(), TimeUnit.MILLISECONDS);
194 | dataPoint.getValue(Field.FIELD_CALORIES).setFloat(sport.getCalorie() - lastSport.getCalorie());
195 | dataSet.add(dataPoint);
196 |
197 | Log.i(TAG, "Inserting the dataset in the History API");
198 | insertStatus = Fitness.HistoryApi.insertData(mClient, dataSet).await(1, TimeUnit.MINUTES);
199 |
200 | // Before querying the data, check to see if the insertion succeeded.
201 | if (!insertStatus.isSuccess()) {
202 | Log.i(TAG, "There was a problem inserting the dataset.");
203 | return null;
204 | }
205 |
206 | lastSport = sport;
207 | Log.i(TAG, "Data insert was successful!");
208 | return null;
209 | }
210 | }
211 | }
--------------------------------------------------------------------------------
/Application/src/main/res/layout/main_fragment.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
13 |
17 |
23 |
24 |
34 |
35 |
44 |
45 |
48 |
57 |
58 |
68 |
69 |
78 |
79 |
89 |
90 |
99 |
100 |
110 |
111 |
120 |
121 |
131 |
132 |
133 |
134 |
138 |
139 |
148 |
149 |
156 |
157 |
164 |
165 |
172 |
173 |
180 |
181 |
189 |
190 |
191 |
197 |
198 |
204 |
205 |
211 |
212 |
213 |
214 |
--------------------------------------------------------------------------------
/Application/Application.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | generateDebugAndroidTestSources
19 | generateDebugSources
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/MainFragment.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet;
2 |
3 | import android.Manifest;
4 | import android.app.AlertDialog;
5 | import android.app.Fragment;
6 | import android.content.BroadcastReceiver;
7 | import android.content.ContentResolver;
8 | import android.content.Context;
9 | import android.content.DialogInterface;
10 | import android.content.Intent;
11 | import android.content.IntentFilter;
12 | import android.content.SharedPreferences;
13 | import android.content.pm.PackageManager;
14 | import android.os.Build;
15 | import android.os.Bundle;
16 | import android.provider.Settings;
17 | import android.view.LayoutInflater;
18 | import android.view.View;
19 | import android.view.ViewGroup;
20 | import android.widget.Button;
21 | import android.widget.CheckBox;
22 | import android.widget.TextView;
23 | import android.widget.Toast;
24 |
25 | import java.util.ArrayList;
26 | import java.util.Date;
27 | import java.util.HashMap;
28 | import java.util.List;
29 | import java.util.Map;
30 |
31 | import ru.wilix.device.geekbracelet.model.DeviceInfo;
32 | import ru.wilix.device.geekbracelet.receiver.NotificationMonitor;
33 |
34 | /**
35 | * Created by Aloyan Dmitry on 16.09.2015
36 | */
37 | public class MainFragment extends Fragment {
38 | private Button connectBtn;
39 | private View container;
40 |
41 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
42 | View inView = inflater.inflate(R.layout.main_fragment, null);
43 | this.container = inView;
44 | initViews();
45 | return inView;
46 | }
47 |
48 | private void initViews(){
49 | connectBtn = (Button)container.findViewById(R.id.connectBtn);
50 | connectBtn.setOnClickListener(new View.OnClickListener() {
51 | @Override
52 | public void onClick(View view) {
53 | Intent intent = new Intent(getActivity(), DeviceScanActivity.class);
54 | startActivity(intent);
55 | }
56 | });
57 |
58 | View.OnClickListener listener = new View.OnClickListener() {
59 | @Override
60 | public void onClick(View view) {
61 | SharedPreferences.Editor ed = App.sPref.edit();
62 |
63 | ed.putBoolean("cbx_notice_call",
64 | ((CheckBox)container.findViewById(R.id.cbx_notice_call)).isChecked());
65 | ed.putBoolean("cbx_action_locator_on_long",
66 | ((CheckBox)container.findViewById(R.id.cbx_action_locator_on_long)).isChecked());
67 | ed.putBoolean("cbx_action_mute_onclick",
68 | ((CheckBox)container.findViewById(R.id.cbx_action_mute_onclick)).isChecked());
69 | ed.putBoolean("cbx_action_reject_on_long",
70 | ((CheckBox)container.findViewById(R.id.cbx_action_reject_on_long)).isChecked());
71 | ed.putBoolean("cbx_notice_deskclock",
72 | ((CheckBox)container.findViewById(R.id.cbx_notice_deskclock)).isChecked());
73 | ed.apply();
74 | App.loadProperties();
75 | }
76 | };
77 | ((CheckBox)container.findViewById(R.id.cbx_notice_call)).setOnClickListener(listener);
78 | ((CheckBox)container.findViewById(R.id.cbx_action_locator_on_long)).setOnClickListener(listener);
79 | ((CheckBox)container.findViewById(R.id.cbx_action_mute_onclick)).setOnClickListener(listener);
80 | ((CheckBox)container.findViewById(R.id.cbx_action_reject_on_long)).setOnClickListener(listener);
81 | ((CheckBox)container.findViewById(R.id.cbx_notice_deskclock)).setOnClickListener(listener);
82 |
83 | ((Button)container.findViewById(R.id.settingsBtn)).setOnClickListener(new View.OnClickListener() {
84 | @Override
85 | public void onClick(View v) {
86 | Intent in = new Intent(getActivity(), DeviceSettingsActivity.class);
87 | startActivity(in);
88 | }
89 | });
90 |
91 | ((Button)container.findViewById(R.id.connectToFitBtn)).setOnClickListener(new View.OnClickListener() {
92 | @Override
93 | public void onClick(View v) {
94 | GoogleFitConnector.connect(getActivity());
95 | }
96 | });
97 |
98 | ((Button)container.findViewById(R.id.show_applist_btn)).setOnClickListener(new View.OnClickListener() {
99 | @Override
100 | public void onClick(View v) {
101 | getFragmentManager().beginTransaction()
102 | .replace(R.id.container, new AppListFragment())
103 | .addToBackStack("applist")
104 | .commit();
105 | }
106 | });
107 |
108 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
109 | checkPermissions();
110 | }
111 |
112 | if( !checkNotificationListenEnabled() ) {
113 | askNotificationAccess();
114 | }
115 | }
116 |
117 | private void loadProperties(){
118 | getActivity().runOnUiThread(new Runnable() {
119 | @Override
120 | public void run() {
121 | SharedPreferences sp = App.sPref;
122 | ((CheckBox) container.findViewById(R.id.cbx_notice_call)).setChecked(sp.getBoolean("cbx_notice_call", false));
123 | ((CheckBox) container.findViewById(R.id.cbx_action_locator_on_long)).setChecked(sp.getBoolean("cbx_action_locator_on_long", false));
124 | ((CheckBox) container.findViewById(R.id.cbx_action_mute_onclick)).setChecked(sp.getBoolean("cbx_action_mute_onclick", false));
125 | ((CheckBox) container.findViewById(R.id.cbx_action_reject_on_long)).setChecked(sp.getBoolean("cbx_action_reject_on_long", false));
126 | ((CheckBox) container.findViewById(R.id.cbx_notice_deskclock)).setChecked(sp.getBoolean("cbx_notice_deskclock", false));
127 |
128 | NotificationMonitor.settingsKeepForeign = sp.getBoolean("notif_foreign", false);
129 | NotificationMonitor.settingsDelay = Integer.parseInt(App.sPref.getString("notif_delay", "0"));
130 | }
131 | });
132 | }
133 |
134 | private boolean checkNotificationListenEnabled(){
135 | ContentResolver contentResolver = getActivity().getContentResolver();
136 | String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
137 | String packageName = getActivity().getPackageName();
138 |
139 | // check to see if the enabledNotificationListeners String contains our package name
140 | if (enabledNotificationListeners == null || !enabledNotificationListeners.contains(packageName))
141 | // in this situation we know that the user has not granted the app the Notification access permission
142 | return false;
143 | else
144 | return true;
145 | }
146 |
147 | private void askNotificationAccess() {
148 | AlertDialog alert = new AlertDialog.Builder(getActivity())
149 | .setTitle(getResources().getText(R.string.dialog_warning))
150 | .setMessage(getResources().getText(R.string.system_notice_need_notification_access))
151 | .setPositiveButton(getResources().getText(R.string.yes), new DialogInterface.OnClickListener() {
152 | @Override
153 | public void onClick(DialogInterface dialogInterface, int i) {
154 | Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
155 | startActivity(intent);
156 | }
157 | })
158 | .setNegativeButton(getResources().getText(R.string.no), null)
159 | .create();
160 | alert.show();
161 | }
162 |
163 | private void requestDeviceInfo(){
164 | new Thread(new Runnable() {
165 | @Override
166 | public void run() {
167 | try {
168 | BLEService.getSelf().getDevice().askFmVersionInfo();
169 | Thread.sleep(500);
170 | BLEService.getSelf().getDevice().askPower();
171 | Thread.sleep(500);
172 | BLEService.getSelf().getDevice().askDate();
173 | }catch (Exception e){}
174 | }
175 | }).start();
176 | }
177 |
178 | @Override
179 | public void onResume() {
180 | super.onResume();
181 | loadProperties();
182 |
183 | getActivity().registerReceiver(mGattUpdateReceiver, gattIntentFilter());
184 |
185 | if( BLEService.getSelf() != null &&
186 | BLEService.getSelf().getmBluetoothGatt() != null &&
187 | BLEService.getSelf().getmBluetoothGatt().getDevice() != null ) {
188 | ((Button)container.findViewById(R.id.connectBtn)).setText(BLEService.getSelf().getmBluetoothGatt().getDevice().getName());
189 | requestDeviceInfo();
190 | }else{
191 | ((Button)container.findViewById(R.id.connectBtn)).setText(getResources().getString(R.string.device_not_connected));
192 | if( BLEService.getSelf() != null )
193 | BLEService.getSelf().connect(App.sPref.getString("DEVICE_ADDR", ""), true);
194 | }
195 | }
196 |
197 | @Override
198 | public void onPause(){
199 | super.onPause();
200 | try {
201 | getActivity().unregisterReceiver(mGattUpdateReceiver);
202 | }catch (Exception e){}
203 | }
204 |
205 | public void onDestroy(){
206 | super.onDestroy();
207 | //getActivity().finish();
208 | }
209 |
210 | // Handles various events fired by the Service.
211 | // ACTION_GATT_CONNECTED: connected to a GATT server.
212 | // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
213 | // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
214 | private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
215 | @Override
216 | public void onReceive(final Context context, Intent intent) {
217 | final Intent in = intent;
218 | getActivity().runOnUiThread(new Runnable() {
219 | @Override
220 | public void run() {
221 | final String action = in.getAction();
222 | switch (action) {
223 | case BroadcastConstants.ACTION_GATT_CONNECTED:
224 | ((Button) container.findViewById(R.id.connectBtn))
225 | .setText(BLEService.getSelf().getmBluetoothGatt().getDevice().getName());
226 | break;
227 | case BroadcastConstants.ACTION_GATT_DISCONNECTED:
228 | ((Button) container.findViewById(R.id.connectBtn)).setText(getResources().getText(R.string.device_not_connected));
229 | break;
230 | case BroadcastConstants.ACTION_GATT_SERVICES_DISCOVERED:
231 | requestDeviceInfo();
232 | break;
233 | case BroadcastConstants.ACTION_DEVICE_INFO:
234 | final DeviceInfo info = (DeviceInfo) in.getSerializableExtra("data");
235 | ((TextView) container.findViewById(R.id.label_device_model)).setText(info.getModel());
236 | ((TextView) container.findViewById(R.id.label_device_sw)).setText(info.getSwversion());
237 | break;
238 | case BroadcastConstants.ACTION_DEVICE_POWER:
239 | ((TextView) container.findViewById(R.id.label_device_power)).setText(in.getIntExtra("data", 0) + "%");
240 | break;
241 | case BroadcastConstants.ACTION_DATE_DATA:
242 | long timestamp = in.getLongExtra("data", 0);
243 | if (timestamp <= 0)
244 | ((TextView) container.findViewById(R.id.label_device_time)).setText(getResources().getText(R.string.label_wrong_time));
245 | else {
246 | CharSequence dt = android.text.format.DateFormat.format("dd/MM/yyyy HH:mm", new Date(timestamp));
247 | ((TextView) container.findViewById(R.id.label_device_time)).setText(dt);
248 | }
249 | BLEService.getSelf().getDevice().setDate();
250 | break;
251 | case BroadcastConstants.ACTION_CONNECT_TO_GFIT:
252 | if (App.sPref.getBoolean("fit_connected", false) == false) {
253 | Toast.makeText(getActivity(), getResources().getString(R.string.google_fit_not_connected),
254 | Toast.LENGTH_SHORT).show();
255 | ((Button) container.findViewById(R.id.connectToFitBtn))
256 | .setText(getResources().getString(R.string.connect_to_fit));
257 | return;
258 | }
259 |
260 | Toast.makeText(getActivity(), getResources().getString(R.string.google_fit_connected),
261 | Toast.LENGTH_SHORT).show();
262 | ((Button) container.findViewById(R.id.connectToFitBtn))
263 | .setText(getResources().getString(R.string.reconnect_to_fit));
264 | if (BLEService.getSelf() == null || BLEService.getSelf().getDevice() == null)
265 | return;
266 |
267 | // BLEService.getSelf().getDevice().askDailyData();
268 | BLEService.getSelf().getDevice().subscribeForSportUpdates();
269 | break;
270 | }
271 | }
272 | });
273 | }
274 | };
275 |
276 | public static IntentFilter gattIntentFilter(){
277 | final IntentFilter intentFilter = new IntentFilter();
278 | intentFilter.addAction(BroadcastConstants.ACTION_GATT_CONNECTED);
279 | intentFilter.addAction(BroadcastConstants.ACTION_GATT_DISCONNECTED);
280 | intentFilter.addAction(BroadcastConstants.ACTION_GATT_SERVICES_DISCOVERED);
281 |
282 | intentFilter.addAction(BroadcastConstants.ACTION_DEVICE_INFO);
283 | intentFilter.addAction(BroadcastConstants.ACTION_DEVICE_POWER);
284 | intentFilter.addAction(BroadcastConstants.ACTION_DATE_DATA);
285 | intentFilter.addAction(BroadcastConstants.ACTION_CONNECT_TO_GFIT);
286 |
287 | return intentFilter;
288 | }
289 |
290 | // API 23 - Marshmallow permissions
291 | private void checkPermissions() {
292 | boolean needGrantMessage = false;
293 | List permissionsNeeded = new ArrayList<>();
294 |
295 | final List permissionsList = new ArrayList();
296 | if (!addPermission(permissionsList, Manifest.permission.ACCESS_COARSE_LOCATION)) {
297 | needGrantMessage = true;
298 | }
299 | if (!addPermission(permissionsList, Manifest.permission.READ_PHONE_STATE)) {
300 | needGrantMessage = true;
301 | }
302 | // if (!addPermission(permissionsList, Manifest.permission.MODIFY_PHONE_STATE)) {
303 | // needGrantMessage = true;
304 | // }
305 | if (!addPermission(permissionsList, Manifest.permission.CALL_PHONE)) {
306 | needGrantMessage = true;
307 | }
308 | if (!addPermission(permissionsList, Manifest.permission.PROCESS_OUTGOING_CALLS)) {
309 | needGrantMessage = true;
310 | }
311 | if (!addPermission(permissionsList, Manifest.permission.MODIFY_AUDIO_SETTINGS)) {
312 | needGrantMessage = true;
313 | }
314 | if (!addPermission(permissionsList, Manifest.permission.READ_CONTACTS)) {
315 | needGrantMessage = true;
316 | }
317 | if (!addPermission(permissionsList, Manifest.permission.INTERNET)) {
318 | needGrantMessage = true;
319 | }
320 | if (!addPermission(permissionsList, Manifest.permission.ACCESS_NETWORK_STATE)) {
321 | needGrantMessage = true;
322 | }
323 |
324 | if (needGrantMessage) {
325 | new AlertDialog.Builder(getActivity())
326 | .setTitle(getResources().getText(R.string.need_permissions_dialog))
327 | .setMessage(R.string.need_permissions_dialog_text)
328 | .setNeutralButton("Ok", new DialogInterface.OnClickListener() {
329 | @Override
330 | public void onClick(DialogInterface dialog, int which) {
331 | requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
332 | 557);
333 | }
334 | }).show();
335 | return;
336 | }
337 | }
338 |
339 | private boolean addPermission(List permissionsList, String permission) {
340 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
341 | {
342 | if (getActivity().checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
343 | permissionsList.add(permission);
344 | // Check for Rationale Option
345 | if (!shouldShowRequestPermissionRationale(permission))
346 | return false;
347 | }
348 | }
349 | return true;
350 | }
351 |
352 | @Override
353 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
354 | switch (requestCode) {
355 | case 557: {
356 | boolean allGrunded = true;
357 | for( int state : grantResults ) {
358 | if( state != PackageManager.PERMISSION_GRANTED ) {
359 | allGrunded = false;
360 | }
361 | }
362 | if ( !allGrunded ) {
363 | // Permission Denied
364 | Toast.makeText(getActivity(), R.string.need_permissions_denied, Toast.LENGTH_SHORT)
365 | .show();
366 | getActivity().finish();
367 | }
368 | }
369 | break;
370 | default:
371 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
372 | }
373 | }
374 | }
375 |
--------------------------------------------------------------------------------
/Application/src/main/java/ru/wilix/device/geekbracelet/i5/Device.java:
--------------------------------------------------------------------------------
1 | package ru.wilix.device.geekbracelet.i5;
2 |
3 | import android.bluetooth.BluetoothGattCharacteristic;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
7 | import android.util.Log;
8 |
9 | import java.io.UnsupportedEncodingException;
10 | import java.util.ArrayList;
11 | import java.util.Arrays;
12 | import java.util.Calendar;
13 | import java.util.Date;
14 | import java.util.GregorianCalendar;
15 | import java.util.HashMap;
16 | import java.util.UUID;
17 |
18 | import ru.wilix.device.geekbracelet.App;
19 | import ru.wilix.device.geekbracelet.BLEService;
20 | import ru.wilix.device.geekbracelet.BroadcastConstants;
21 | import ru.wilix.device.geekbracelet.model.DeviceClockAlarm;
22 | import ru.wilix.device.geekbracelet.model.DeviceInfo;
23 | import ru.wilix.device.geekbracelet.model.Sport;
24 | import ru.wilix.device.geekbracelet.receiver.NotificationMonitor;
25 |
26 | /**
27 | * Created by Dmitry on 29.08.2015.
28 | * My swversion: 1.1.0.9 I5
29 | * DEVICE_POWER﹕ bleAddr: 4d2b2a84bec4 displayWidthFont: 0 model: I5 oadmode: 0 swversion: 1.1.0.9
30 | */
31 | public class Device {
32 | private static final String TAG = "Device_iWown_i5";
33 | public Communication comm;
34 | public DeviceInfo deviceInfo;
35 |
36 | public Device(BLEService bleService){
37 | this.comm = new Communication(bleService, this);
38 | }
39 |
40 | /**
41 | * Return Firmware version
42 | */
43 | public void askFmVersionInfo(){
44 | writePacket(Utils.getDataByte(true, Utils.form_Header(0, 0), null));
45 | }
46 |
47 | /**
48 | * Return battery power
49 | */
50 | public void askPower(){
51 | writePacket(Utils.getDataByte(true, Utils.form_Header(0, 1), null));
52 | }
53 |
54 | /**
55 | * Return device configuration
56 | */
57 | public void askConfig(){
58 | writePacket(Utils.getDataByte(true, Utils.form_Header(1, 9), null));
59 | }
60 |
61 | /**
62 | * Return User Body parameters
63 | */
64 | public void askUserParams(){
65 | writePacket(Utils.getDataByte(true, Utils.form_Header(2, 1), null));
66 | }
67 |
68 | /**
69 | * Return BLE state
70 | * TODO need to understand what is this
71 | */
72 | public void askBle(){
73 | writePacket(Utils.getDataByte(true, Utils.form_Header(1, 3), null));
74 | }
75 |
76 | // TODO ask Alarams. Need to test. If send this command, device return all alrms or need
77 | // specified alarm ID in ask
78 | // public void askAlarm(){
79 | // writePacket(Utils.getDataByte(true, Utils.form_Header(1, 3), null));
80 | // }
81 |
82 | /**
83 | * Return Daily Sport entries. This entries automatically clear in device on ask
84 | */
85 | public void askDailyData(){
86 | writePacket(Utils.getDataByte(true, Utils.form_Header(2, 7), null));
87 | }
88 |
89 | /**
90 | * Return device data and time
91 | */
92 | public void askDate(){
93 | writePacket(Utils.getDataByte(true, Utils.form_Header(1, 1), null));
94 | }
95 |
96 | /**
97 | * Return data of local sport. May be it sleep data...
98 | * TODO Check what is this data means
99 | */
100 | public void askLocalSport(){
101 | writePacket(Utils.getDataByte(true, Utils.form_Header(2, 5), null));
102 | }
103 |
104 | /**
105 | * After subscribe, device will send Sport object on each activity
106 | * and once in minute if activity don't register
107 | */
108 | public void subscribeForSportUpdates(){
109 | writePacket(Utils.getDataByte(true, Utils.form_Header(2, 3), null));
110 | }
111 |
112 | /**
113 | * Set date and time to device internal clock
114 | */
115 | public void setDate() {
116 | GregorianCalendar date = new GregorianCalendar();
117 | ArrayList data = new ArrayList<>();
118 | data.add( ((byte)(date.get(Calendar.YEAR) - 2000)) );
119 | if(App.sPref.getString("device_model", "i5").contains("+"))
120 | data.add( ((byte)(date.get(Calendar.MONTH))) );
121 | else
122 | data.add( ((byte)(date.get(Calendar.MONTH) - 1)) );
123 | data.add( ((byte)(date.get(Calendar.DAY_OF_MONTH) - 1)) );
124 | data.add( ((byte)date.get(Calendar.HOUR_OF_DAY)) );
125 | data.add( ((byte)date.get(Calendar.MINUTE)) );
126 | data.add( ((byte)date.get(Calendar.SECOND)) );
127 | writePacket(Utils.getDataByte(true, Utils.form_Header(1, 0), data));
128 | }
129 |
130 | /**
131 | * Sel alarm to device internal memory.
132 | * @param alarm - alarm to write
133 | * @param sectionId - Available 7 alarms. IDS: 0,1,2,3,4,5,6
134 | */
135 | public void setClockAlarm(DeviceClockAlarm alarm, int sectionId){
136 | ArrayList data = new ArrayList<>();
137 | data.add((byte) sectionId);
138 | data.add((byte) 0);
139 | data.add((byte) (alarm.isOpen ? alarm.week : 0));
140 | data.add((byte) alarm.hour);
141 | data.add((byte) alarm.minute);
142 | writePacket(Utils.getDataByte(true, Utils.form_Header(1, 4), data));
143 | }
144 |
145 | /**
146 | * Unknown command. Call on send configuration params to device
147 | */
148 | public void setBle(boolean enabled){
149 | ArrayList data = new ArrayList<>();
150 | data.add((byte)0);
151 | data.add((byte) (enabled ? 1 : 0));
152 | writePacket(Utils.getDataByte(true, Utils.form_Header(1, 2), data));
153 | }
154 |
155 | /**
156 | * Set selfie mode. Rise selfie event on one click to button
157 | * @param enable
158 | */
159 | public void setSelfieMode(boolean enable){
160 | ArrayList data = new ArrayList<>();
161 | data.add(enable ? (byte) 1 : (byte) 0);
162 | writePacket(Utils.getDataByte(true, Utils.form_Header(4, 0), data));
163 | }
164 |
165 | /**
166 | * Send user body and goal params. This params important for calculating steps and distantion
167 | * FIXME NOT TESTED
168 | *
169 | * @param height - Body height. Like 180
170 | * @param weight - Body weight. Like 79
171 | * @param gender false = male, true = female
172 | * @param age - age in years. Like 26 years
173 | * @param goal - in steps. For example 10000 steps per day
174 | */
175 | public void setUserParams(int height, int weight, boolean gender, int age, int goal){
176 | ArrayList datas = new ArrayList<>();
177 | datas.add((byte)height);
178 | datas.add((byte)weight);
179 | datas.add((byte)( !gender ? 0 : 1));
180 | datas.add((byte)age);
181 | int goal_low = goal % AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
182 | int goal_high = (goal - goal_low) / AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
183 | datas.add((byte) goal_low);
184 | datas.add((byte) goal_high);
185 |
186 | byte[] data = Utils.getDataByte(true, Utils.form_Header(2, 0), datas);
187 | writePacket(data);
188 | }
189 |
190 | /**
191 | * Send config to device
192 | * FIXME NOT TESTED
193 | *
194 | * @param light - Enable blue light blinking
195 | * @param gesture - Enable gesture. Turn display on if you try to look at the device
196 | * @param englishUnits - Use English Units (Miles, Foots and etc.)
197 | * @param use24hour - Use 24 hour time format, if false used 12 hour time
198 | * @param autoSleep - Enable auto sleep mode when you go to sleep
199 | */
200 | public void setConfig(boolean light, boolean gesture, boolean englishUnits,
201 | boolean use24hour, boolean autoSleep) {
202 | ArrayList datas = new ArrayList<>();
203 |
204 | datas.add((byte) (light ? 1 : 0));
205 | datas.add((byte) (gesture ? 1 : 0));
206 | datas.add((byte) (englishUnits ? 1 : 0));
207 | datas.add((byte) (use24hour ? 1 : 0));
208 | datas.add((byte) (autoSleep ? 1 : 0));
209 |
210 | if(Communication.apiVersion == 2)
211 | {
212 | datas.add((byte) 1);
213 | datas.add((byte) 8); // Light Start Time (Whatever that means); Default
214 | datas.add((byte) 20); // Light End Time (Whatever that means); Default
215 |
216 | datas.add((byte) 0); // Inverse Colors
217 | datas.add((byte) 0); // Is English
218 | datas.add((byte) 0); // Disconnect Tip
219 | }
220 | else
221 | {
222 | datas.add((byte) 0);
223 | datas.add((byte) 0);
224 | }
225 |
226 | byte[] data = Utils.getDataByte(true, Utils.form_Header(1, 8), datas);
227 | writePacket(data);
228 | }
229 |
230 | /**
231 | * Show message alert in display
232 | * @param msg - message to show
233 | */
234 | public void sendMessage(String msg){
235 | sendAlert(msg, Constants.ALERT_TYPE_MESSAGE);
236 | }
237 |
238 | /**
239 | * Show call message and vibrate while not receive sendCallEnd command
240 | * @param msg - mesage to show
241 | */
242 | public void sendCall(String msg){
243 | sendAlert(msg, Constants.ALERT_TYPE_CALL);
244 | }
245 |
246 | /**
247 | * End vibration and call event started after sendCall command
248 | */
249 | public void sendCallEnd() {
250 | ArrayList datas = new ArrayList<>();
251 | datas.add((byte) 0);
252 | writePacket(Utils.getDataByte(true, Utils.form_Header(4, 1), datas));
253 | }
254 |
255 | /**
256 | * Send alert to device. Device shows your message and icon (call for type 1, msg for type 2)
257 | * @param msg - Text message
258 | * @param type - Type of alert. 1 - Call type, 2 - Message type
259 | */
260 | public void sendAlert(String msg, int type){
261 | if( msg == null )
262 | return ;
263 | ArrayList datas = new ArrayList<>();
264 | if(Communication.apiVersion == 2)
265 | {
266 | datas.add((byte) type);
267 | datas.add((byte) -1);
268 |
269 | byte[] buffer = new byte[0];
270 |
271 | try {
272 | if(NotificationMonitor.settingsKeepForeign) {
273 | buffer = msg.getBytes("utf-8");
274 | } else {
275 | buffer = msg.replaceAll("[^\u0020-\u0079]", "#").getBytes("utf-8");
276 | }
277 | } catch (UnsupportedEncodingException ex) {
278 | ex.printStackTrace();
279 | }
280 | for(byte b : buffer)
281 | datas.add(b);
282 | }
283 | else
284 | {
285 | datas.add((byte) type);
286 | int i = 0;
287 | if(msg.length() < 6)
288 | {
289 | while(msg.length() < 6)
290 | msg += " ";
291 | }
292 | else if(msg.length() >= 6)
293 | {
294 | msg = msg.substring(0, 6);
295 | }
296 |
297 | while(i < msg.length())
298 | {
299 | // if (msg.charAt(i) < '@' || (msg.charAt(i) < '\u0080' && msg.charAt(i) > '`')) {
300 | // char e = msg.charAt(i);
301 | // datas.add(Byte.valueOf((byte) 0));
302 | // for (byte valueOf : PebbleBitmap.fromString(String.valueOf(e), 8, 1).data) {
303 | // datas.add(Byte.valueOf(valueOf));
304 | // }
305 | // } else {
306 | char c = msg.charAt(i);
307 | datas.add((byte) 1);
308 | for(byte valueOf2 : PebbleBitmap.fromString(String.valueOf(c), 16, 1).data)
309 | {
310 | datas.add(valueOf2);
311 | }
312 | // }
313 | i++;
314 | }
315 | }
316 | byte[] data = Utils.getDataByte(true, Utils.form_Header(3, 1), datas);
317 | ArrayList tasks = new ArrayList<>();
318 | for (int i = 0; i < data.length; i += 20) {
319 | byte[] writeData;
320 | if (i + 20 > data.length) {
321 | writeData = Arrays.copyOfRange(data, i, data.length);
322 | } else {
323 | writeData = Arrays.copyOfRange(data, i, i + 20);
324 | }
325 | tasks.add(new Communication.WriteDataTask(UUID.fromString(Constants.BAND_CHARACTERISTIC_NEW_WRITE), writeData));
326 | }
327 | comm.WriteDataPacket(tasks);
328 | }
329 |
330 | private byte[] receiveBuffer;
331 | private int receiveBufferLength = 0;
332 | private boolean isDataOver = true;
333 |
334 | public void parserAPIv1(BluetoothGattCharacteristic chr){
335 | String uuid = chr.getUuid().toString();
336 | Log.i(TAG, "Parse APIv1 data. UUID:" + uuid);
337 | if (Constants.BAND_CHARACTERISTIC_NEW_NOTIFY.equals(uuid) || Constants.BAND_CHARACTERISTIC_NEW_INDICATE.equals(uuid)) {
338 | byte[] data = chr.getValue();
339 | if (data != null && data.length != 0) {
340 | if (this.isDataOver) {
341 | if (data[0] == 34 || (Communication.apiVersion == 2 && data[0] == 35)) {
342 | this.receiveBufferLength = data[3];
343 | //Log.i(TAG, "Received length --->" + this.receiveBufferLength);
344 | //Log.i(TAG, "Received data --->" + Utils.bytesToString(data));
345 | } else {
346 | return;
347 | }
348 | }
349 | this.receiveBuffer = Utils.concat(this.receiveBuffer, data);
350 | if (this.receiveBuffer.length - 4 >= this.receiveBufferLength) {
351 | this.isDataOver = true;
352 | //Log.i(TAG, "Received length--->" + (this.receiveBuffer.length - 4));
353 | //Log.i(TAG, "Received data--->" + Utils.bytesToString(this.receiveBuffer));
354 | // Data ready for parse
355 | if (this.receiveBuffer.length >= 3) {
356 | Intent intent;
357 | switch (this.receiveBuffer[2]){
358 | case Constants.APIv1_DATA_DEVICE_INFO:
359 | DeviceInfo info = DeviceInfo.fromData(this.receiveBuffer);
360 | deviceInfo = info;
361 |
362 | SharedPreferences.Editor ed = App.sPref.edit();
363 | ed.putString("device_model", info.getModel());
364 | ed.putString("device_sw", info.getSwversion());
365 | ed.apply();
366 |
367 | Log.d(TAG, "DEVICE_INFO: " + info.toString());
368 | intent = new Intent(BroadcastConstants.ACTION_DEVICE_INFO);
369 | intent.putExtra("data", DeviceInfo.fromData(this.receiveBuffer));
370 | App.mContext.sendBroadcast(intent);
371 | break;
372 | case Constants.APIv1_DATA_DEVICE_POWER:
373 | int power = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 4, 5));
374 | Log.d(TAG, "DEVICE_POWER: " + power + "%");
375 | intent = new Intent(BroadcastConstants.ACTION_DEVICE_POWER);
376 | intent.putExtra("data", power);
377 | App.mContext.sendBroadcast(intent);
378 | break;
379 | case Constants.APIv1_DATA_DEVICE_BLE:
380 | int ble = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 4, 5));
381 | Log.d(TAG, "DEVICE_BLE: " + (ble > 0 ? "enabled" : "disabled"));
382 | intent = new Intent(BroadcastConstants.ACTION_BLE_DATA);
383 | intent.putExtra("data", ble);
384 | App.mContext.sendBroadcast(intent);
385 | break;
386 | case Constants.APIv1_DATA_USER_PARAMS:
387 | HashMap userData = new HashMap<>();
388 | userData.put("height", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 4, 5)));
389 | userData.put("weight", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 5, 6)));
390 | userData.put("gender", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 6, 7)));
391 | userData.put("age", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 7, 8)));
392 | userData.put("goal_low", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 8, 9)));
393 | userData.put("goal_high", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 9, 10)));
394 | Log.d(TAG, "DEVICE_USER_PARAMS: " +
395 | " Height: " + userData.get("height") +
396 | " Weight: " + userData.get("weight") +
397 | " Gender: " + (userData.get("gender") > 0 ? "female" : "male") +
398 | " Age: " + userData.get("age") +
399 | " Goal: " + (userData.get("goal_high") + "." + userData.get("goal_low")));
400 | intent = new Intent(BroadcastConstants.ACTION_USER_BODY_DATA);
401 | intent.putExtra("data", userData);
402 | App.mContext.sendBroadcast(intent);
403 | break;
404 | case Constants.APIv1_DATA_DEVICE_CONFIG:
405 | HashMap configData = new HashMap<>();
406 | configData.put("light", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 4, 5)));
407 | configData.put("gesture", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 5, 6)));
408 | configData.put("englishUnits", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 6, 7)));
409 | configData.put("use24hour", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 7, 8)));
410 | configData.put("autoSleep", Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 8, 9)));
411 |
412 | Log.d(TAG, "DEVICE_DEVICE_CONFIG: " +
413 | " light: " + configData.get("light") +
414 | " gesture: " + configData.get("gesture") +
415 | " englishUnits: " + configData.get("englishUnits") +
416 | " use24hour: " + configData.get("use24hour") +
417 | " autoSleep: " + configData.get("autoSleep"));
418 | intent = new Intent(BroadcastConstants.ACTION_DEVICE_CONF_DATA);
419 | intent.putExtra("data", configData);
420 | App.mContext.sendBroadcast(intent);
421 | break;
422 | case Constants.APIv1_DATA_DEVICE_DATE:
423 | int year = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 4, 5)) + 2000;
424 | int month;
425 | if(App.sPref.getString("device_model", "i5").contains("+"))
426 | month = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 5, 6));
427 | else
428 | month = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 5, 6)) + 1;
429 | int day = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 6, 7)) + 1;
430 | int hour = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 7, 8));
431 | int minute = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 8, 9));
432 | int second = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 9, 10));
433 | long timestamp = new GregorianCalendar(year, month, day, hour, minute, second).getTimeInMillis();
434 | Log.d(TAG, "DEVICE_DATE: " + new Date(timestamp).toString());
435 | intent = new Intent(BroadcastConstants.ACTION_DATE_DATA);
436 | intent.putExtra("data", timestamp);
437 | App.mContext.sendBroadcast(intent);
438 | break;
439 | case Constants.APIv1_DATA_SUBSCRIBE_FOR_SPORT:
440 | Sport dailySport = Sport.fromBytes(this.receiveBuffer, Sport.TYPE_DAILY_A);
441 | Log.d(TAG, "DAILY_SPORT: " + dailySport.toString());
442 | intent = new Intent(BroadcastConstants.ACTION_SPORT_DATA);
443 | intent.putExtra("data", dailySport);
444 | App.mContext.sendBroadcast(intent);
445 | break;
446 | case Constants.APIv1_DATA_LOCAL_SPORT:
447 | Sport localSport = Sport.fromBytes(this.receiveBuffer, Sport.TYPE_LOCAL_SPORT);
448 | Log.d(TAG, "LOCAL_SPORT: " + localSport.toString());
449 | intent = new Intent(BroadcastConstants.ACTION_SPORT_DATA);
450 | intent.putExtra("data", localSport);
451 | App.mContext.sendBroadcast(intent);
452 | break;
453 | case Constants.APIv1_DATA_DAILY_SPORT2:
454 | Sport dailySport2 = Sport.fromBytes(this.receiveBuffer, Sport.TYPE_DAILY_B);
455 | Log.d(TAG, "DAILY_SPORT2: " + dailySport2.toString());
456 | intent = new Intent(BroadcastConstants.ACTION_SPORT_DATA);
457 | intent.putExtra("data", dailySport2);
458 | App.mContext.sendBroadcast(intent);
459 | break;
460 | case Constants.APIv1_DATA_SELFIE:
461 | int code = Utils.bytesToInt(Arrays.copyOfRange(this.receiveBuffer, 4, 5));
462 | Log.d(TAG, "SELFIE_DATA: " + Integer.toString(code));
463 | intent = new Intent( (code == 1) ? BroadcastConstants.ACTION_SELFIE : BroadcastConstants.ACTION_PLAYPAUSE );
464 | intent.putExtra("data", code);
465 | App.mContext.sendBroadcast(intent);
466 | default:
467 | String buff = "";
468 | Log.i(TAG, "Receiver unknown APIv1 command");
469 | for (Byte b : this.receiveBuffer )
470 | buff += b + " ";
471 | Log.i(TAG, "Buffer: " + buff);
472 | }
473 | }
474 | // Clear buffer for new data
475 | this.receiveBuffer = new byte[0];
476 | return;
477 | }
478 | this.isDataOver = false;
479 | }
480 | }
481 | }
482 |
483 | public void parserAPIv0(BluetoothGattCharacteristic chr){
484 | String uuid = chr.getUuid().toString();
485 | Log.i(TAG, "Parse APIv0 data. UUID:" + uuid);
486 | Intent intent;
487 | Sport sp;
488 | switch (uuid){
489 | case Constants.BAND_CHARACTERISTIC_SPORT:
490 | sp = Sport.fromCharacteristic(chr, false);
491 | intent = new Intent(BroadcastConstants.ACTION_SPORT_DATA);
492 | intent.putExtra("data", sp);
493 | App.mContext.sendBroadcast(intent);
494 | break;
495 | case Constants.BAND_CHARACTERISTIC_DAILY:
496 | sp = Sport.fromCharacteristic(chr, true);
497 | intent = new Intent(BroadcastConstants.ACTION_DAILY_DATA);
498 | intent.putExtra("data", sp);
499 | App.mContext.sendBroadcast(intent);
500 | break;
501 | case Constants.BAND_CHARACTERISTIC_DATE:
502 | long time = Utils.parseDateCharacteristic(chr);
503 | intent = new Intent(BroadcastConstants.ACTION_DATE_DATA);
504 | intent.putExtra("data", time);
505 | App.mContext.sendBroadcast(intent);
506 | break;
507 | case Constants.BAND_CHARACTERISTIC_SEDENTARY:
508 | intent = new Intent(BroadcastConstants.ACTION_SEDENTARY_DATA);
509 | intent.putExtra("data", chr.getValue());
510 | App.mContext.sendBroadcast(intent);
511 | break;
512 | case Constants.BAND_CHARACTERISTIC_ALARM:
513 | intent = new Intent(BroadcastConstants.ACTION_ALARM_DATA);
514 | intent.putExtra("data", chr.getValue());
515 | App.mContext.sendBroadcast(intent);
516 | break;
517 | case Constants.BAND_CHARACTERISTIC_PAIR:
518 | intent = new Intent(BroadcastConstants.ACTION_PAIR_DATA);
519 | intent.putExtra("data", chr.getValue());
520 | App.mContext.sendBroadcast(intent);
521 | break;
522 | }
523 | }
524 |
525 | /**
526 | * Send data to device
527 | * @param data
528 | */
529 | public void writePacket(byte[] data){
530 | comm.WriteDataPacket(new Communication.WriteDataTask(UUID.fromString(Constants.BAND_CHARACTERISTIC_NEW_WRITE), data));
531 | }
532 | }
533 |
--------------------------------------------------------------------------------