├── api
├── proguard-rules.pro
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── io
│ │ └── thinger
│ │ └── api
│ │ ├── ThingerAPIInterface.java
│ │ └── ThingerAPI.java
└── build.gradle
├── mobile
├── .gitignore
├── src
│ └── main
│ │ ├── ic_launcher-web.png
│ │ ├── ic_launcher_run-web.png
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ ├── ic_drawer.png
│ │ │ ├── drawer_shadow.9.png
│ │ │ ├── ic_action_play.png
│ │ │ ├── ic_action_refresh.png
│ │ │ ├── ic_action_remove.png
│ │ │ ├── ic_add_white_24dp.png
│ │ │ ├── ic_action_refreshing.png
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── ic_action_play.png
│ │ │ │ ├── ic_action_refresh.png
│ │ │ │ └── ic_action_refreshing.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── ic_action_play.png
│ │ │ │ ├── ic_action_refresh.png
│ │ │ │ └── ic_action_refreshing.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_action_play.png
│ │ │ │ ├── ic_action_refresh.png
│ │ │ │ └── ic_action_refreshing.png
│ │ │ └── drawable-xxhdpi
│ │ │ │ ├── ic_action_play.png
│ │ │ │ ├── ic_action_refresh.png
│ │ │ │ └── ic_action_refreshing.png
│ │ ├── drawable-mdpi
│ │ │ ├── ic_drawer.png
│ │ │ ├── drawer_shadow.9.png
│ │ │ ├── ic_action_play.png
│ │ │ ├── ic_action_refresh.png
│ │ │ ├── ic_action_remove.png
│ │ │ ├── ic_add_white_24dp.png
│ │ │ └── ic_action_refreshing.png
│ │ ├── drawable-xhdpi
│ │ │ ├── ic_drawer.png
│ │ │ ├── ic_action_play.png
│ │ │ ├── drawer_shadow.9.png
│ │ │ ├── ic_action_refresh.png
│ │ │ ├── ic_action_remove.png
│ │ │ ├── ic_add_white_24dp.png
│ │ │ └── ic_action_refreshing.png
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-ldpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xxhdpi
│ │ │ ├── ic_drawer.png
│ │ │ ├── drawer_shadow.9.png
│ │ │ ├── ic_action_play.png
│ │ │ ├── ic_action_remove.png
│ │ │ ├── ic_action_refresh.png
│ │ │ ├── ic_add_white_24dp.png
│ │ │ └── ic_action_refreshing.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xxxhdpi
│ │ │ └── ic_add_white_24dp.png
│ │ ├── menu
│ │ │ ├── global.xml
│ │ │ ├── devices_options.xml
│ │ │ ├── menu_scanner.xml
│ │ │ ├── menu_devices.xml
│ │ │ └── menu_thinger_device.xml
│ │ ├── layout
│ │ │ ├── card_resource_param_name.xml
│ │ │ ├── card_resource_io_category.xml
│ │ │ ├── content_main.xml
│ │ │ ├── activity_nfc.xml
│ │ │ ├── card_device.xml
│ │ │ ├── card_resource_param_switch.xml
│ │ │ ├── activity_thinger_device.xml
│ │ │ ├── card_resource_param_value.xml
│ │ │ ├── activity_devices.xml
│ │ │ ├── activity_main.xml
│ │ │ └── card_resource.xml
│ │ ├── values-v21
│ │ │ └── styles.xml
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── anim
│ │ │ └── rotate.xml
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── xml
│ │ │ └── nfc_tech_filter.xml
│ │ ├── java
│ │ └── io
│ │ │ └── thinger
│ │ │ └── thinger
│ │ │ ├── model
│ │ │ ├── Device.java
│ │ │ ├── DeviceToken.java
│ │ │ └── DeviceFromToken.java
│ │ │ ├── views
│ │ │ ├── ArrayValue.java
│ │ │ ├── NumberValue.java
│ │ │ ├── StringValue.java
│ │ │ ├── ObjectValue.java
│ │ │ ├── BoolValue.java
│ │ │ ├── Element.java
│ │ │ └── DeviceResource.java
│ │ │ ├── database
│ │ │ └── DBDeviceToken.java
│ │ │ ├── MainActivity.java
│ │ │ ├── DeviceResourceAdapter.java
│ │ │ ├── ThingerApplication.java
│ │ │ ├── QRScannerActivity.java
│ │ │ ├── DeviceListAdapter.java
│ │ │ ├── services
│ │ │ └── WearListCallListenerService.java
│ │ │ ├── NFCActivity.java
│ │ │ ├── DevicesActivity.java
│ │ │ └── ThingerDevice.java
│ │ └── AndroidManifest.xml
├── build.gradle
└── proguard-rules.pro
├── wear
├── .gitignore
├── src
│ └── main
│ │ ├── ic_run-web.png
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_run.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_run.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_run.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_run.png
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_run.png
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ └── strings.xml
│ │ ├── anim
│ │ │ └── slidein_top.xml
│ │ └── layout
│ │ │ ├── resource_number_view.xml
│ │ │ ├── resource_run_view.xml
│ │ │ ├── activity_device.xml
│ │ │ ├── activity_device_list.xml
│ │ │ ├── activity_resource_control.xml
│ │ │ ├── row_device_resource.xml
│ │ │ ├── row_device.xml
│ │ │ ├── rect_activity_resource_control.xml
│ │ │ ├── rect_activity_device_list.xml
│ │ │ ├── rect_activity_device.xml
│ │ │ ├── round_activity_device.xml
│ │ │ ├── round_activity_resource_control.xml
│ │ │ └── round_activity_device_list.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── io
│ │ └── thinger
│ │ └── thinger
│ │ ├── DeviceResourceDescription.java
│ │ ├── DeviceListActivity.java
│ │ ├── MessageActivity.java
│ │ ├── DeviceActivity.java
│ │ └── ResourceControlActivity.java
├── build.gradle
└── proguard-rules.pro
├── wearinterface
├── proguard-rules.pro
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── io
│ │ └── thinger
│ │ └── wearinterface
│ │ ├── WearMessages.java
│ │ ├── WearDeviceInfo.java
│ │ └── WearDeviceResource.java
└── build.gradle
├── settings.gradle
├── .gitignore
├── gradle.properties
└── README.md
/api/proguard-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mobile/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/wear/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/wearinterface/proguard-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/wearinterface/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':mobile', ':wear', ':api', ':wearinterface'
2 |
--------------------------------------------------------------------------------
/wear/src/main/ic_run-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/ic_run-web.png
--------------------------------------------------------------------------------
/api/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ThingerAPI
3 |
4 |
--------------------------------------------------------------------------------
/wear/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/mobile/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/mobile/src/main/ic_launcher_run-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/ic_launcher_run-web.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-hdpi/ic_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-hdpi/ic_run.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-mdpi/ic_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-mdpi/ic_run.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-xhdpi/ic_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-xhdpi/ic_run.png
--------------------------------------------------------------------------------
/wearinterface/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MobileWearInterface
3 |
4 |
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-xxhdpi/ic_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-xxhdpi/ic_run.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-xxxhdpi/ic_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-xxxhdpi/ic_run.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/ic_drawer.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-mdpi/ic_drawer.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xhdpi/ic_drawer.png
--------------------------------------------------------------------------------
/mobile/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/mipmap-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/mipmap-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/wear/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/wear/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_drawer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxhdpi/ic_drawer.png
--------------------------------------------------------------------------------
/mobile/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-mdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-mdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xhdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_action_remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/ic_action_remove.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-mdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_action_remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-mdpi/ic_action_remove.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-mdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xhdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xhdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_action_remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xhdpi/ic_action_remove.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xhdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/drawer_shadow.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxhdpi/drawer_shadow.9.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxhdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_action_remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxhdpi/ic_action_remove.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-mdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-mdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxhdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xhdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xhdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-xxhdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-xxhdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-hdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-hdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-mdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-mdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-xhdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-xhdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-xxhdpi/ic_action_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-xxhdpi/ic_action_play.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-hdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-hdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-mdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-mdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-xhdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-xhdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-xxhdpi/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-xxhdpi/ic_action_refresh.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-hdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-hdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-mdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-mdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-xhdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-xhdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/mobile/src/main/res/drawable-hdpi/drawable-xxhdpi/ic_action_refreshing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinger-io/Android-Client/HEAD/mobile/src/main/res/drawable-hdpi/drawable-xxhdpi/ic_action_refreshing.png
--------------------------------------------------------------------------------
/wear/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Thinger.io
3 | Device
4 | Device Resource
5 |
6 |
--------------------------------------------------------------------------------
/api/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/wear/src/main/res/anim/slidein_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/mobile/src/main/res/menu/global.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/card_resource_param_name.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/resource_number_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/card_resource_io_category.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/wearinterface/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/mobile/src/main/res/menu/devices_options.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/mobile/src/main/res/anim/rotate.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/mobile/src/main/res/menu/menu_scanner.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/mobile/src/main/res/menu/menu_devices.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/mobile/src/main/res/menu/menu_thinger_device.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
8 | 240dp
9 | 16dp
10 |
11 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Thinger.io
3 | Settings
4 | Scan Device QR Code
5 | Device
6 | DevicesActivity
7 | MainActivity
8 | Camera is required to scan the QR code of the device
9 | This device doesn\'t support NFC.
10 |
11 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/resource_run_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/wearinterface/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 24
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | consumerProguardFiles 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile 'com.google.code.gson:gson:2.5'
24 | }
25 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/activity_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/activity_device_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/activity_resource_control.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/api/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 24
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | consumerProguardFiles 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
24 | compile 'com.squareup.retrofit2:converter-gson:2.1.0'
25 | compile 'com.google.code.gson:gson:2.5'
26 | }
27 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/wear/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "23.0.3"
6 | defaultConfig {
7 | applicationId "io.thinger.thinger"
8 | minSdkVersion 21
9 | targetSdkVersion 24
10 | versionCode 3
11 | versionName "1.2"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled true
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile 'com.google.android.support:wearable:1.4.0'
23 | compile 'com.google.android.gms:play-services-wearable:9.2.1'
24 | compile 'com.google.code.gson:gson:2.5'
25 | compile project(':wearinterface')
26 | }
27 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/activity_nfc.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/card_device.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | /*/build/
3 | /build
4 | /captures
5 |
6 | # Crashlytics configuations
7 | com_crashlytics_export_strings.xml
8 |
9 | # Local configuration file (sdk path, etc)
10 | local.properties
11 |
12 | # Gradle generated files
13 | .gradle
14 | .gradle/
15 | /*/*.iml
16 |
17 | .signing/
18 | .navigation/
19 | build/
20 | projectFilesBackup/
21 | *.jar
22 | *.apk
23 |
24 | # User-specific configurations
25 | .idea/*
26 | .idea/libraries/
27 | .idea/workspace.xml
28 | .idea/tasks.xml
29 | .idea/.name
30 | .idea/compiler.xml
31 | .idea/copyright/profiles_settings.xml
32 | .idea/encodings.xml
33 | .idea/misc.xml
34 | .idea/modules.xml
35 | .idea/scopes/scope_settings.xml
36 | .idea/vcs.xml
37 | *.iml
38 |
39 | # OS-specific files
40 | .DS_Store
41 | .DS_Store?
42 | ._*
43 | .Spotlight-V100
44 | .Trashes
45 | ehthumbs.db
46 | Thumbs.db
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/card_resource_param_switch.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
18 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/activity_thinger_device.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/mobile/src/main/res/xml/nfc_tech_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | android.nfc.tech.Ndef
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/wear/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | ## GSON 2.2.4 specific rules ##
2 |
3 | # Gson uses generic type information stored in a class file when working with fields. Proguard
4 | # removes such information by default, so configure it to keep all of it.
5 | -keepattributes Signature
6 |
7 | # For using GSON @Expose annotation
8 | -keepattributes *Annotation*
9 |
10 | -keepattributes EnclosingMethod
11 |
12 | # Gson specific classes
13 | -keep class sun.misc.Unsafe { *; }
14 | -keep class com.google.gson.stream.** { *; }
15 |
16 | # Application classes that will be serialized/deserialized over Gson
17 | -keep class io.thinger.wearinterface.WearDeviceInfo { *; }
18 | -keep class io.thinger.wearinterface.WearDeviceResource { *; }
19 | -keep class io.thinger.thinger.model.DeviceToken { *; }
20 |
21 | -assumenosideeffects class android.util.Log {
22 | public static *** d(...);
23 | public static *** w(...);
24 | public static *** v(...);
25 | public static *** i(...);
26 | }
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/card_resource_param_value.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
18 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/row_device_resource.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
25 |
26 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/row_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
25 |
26 |
--------------------------------------------------------------------------------
/mobile/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'realm-android'
3 |
4 | android {
5 | compileSdkVersion 24
6 | buildToolsVersion "23.0.3"
7 | defaultConfig {
8 | applicationId "io.thinger.thinger"
9 | minSdkVersion 15
10 | targetSdkVersion 24
11 | versionCode 3
12 | versionName "1.2"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled true
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | wearApp project(':wear')
24 | compile project(':api')
25 | compile project(':wearinterface')
26 | compile 'com.android.support:appcompat-v7:24.1.1'
27 | compile 'com.android.support:cardview-v7:24.1.1'
28 | compile 'com.android.support:design:24.1.1'
29 | compile 'com.android.support:recyclerview-v7:24.1.1'
30 | compile 'me.dm7.barcodescanner:zxing:1.8.2'
31 | compile 'com.google.android.gms:play-services-wearable:9.2.1'
32 | compile 'com.google.android.gms:play-services-analytics:9.2.1'
33 | }
34 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/activity_devices.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
14 |
15 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/rect_activity_resource_control.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
25 |
26 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/rect_activity_device_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
21 |
22 |
27 |
28 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/api/src/main/java/io/thinger/api/ThingerAPIInterface.java:
--------------------------------------------------------------------------------
1 | package io.thinger.api;
2 |
3 | import com.google.gson.JsonElement;
4 | import com.google.gson.JsonObject;
5 |
6 | import retrofit2.Call;
7 | import retrofit2.http.Body;
8 | import retrofit2.http.GET;
9 | import retrofit2.http.Header;
10 | import retrofit2.http.POST;
11 | import retrofit2.http.Path;
12 |
13 | public interface ThingerAPIInterface {
14 | @GET("/v2/users/{user}/devices/{device}/api")
15 | Call deviceAPI(@Header("Authorization") String authorization, @Path("user") String user, @Path("device") String device);
16 |
17 | @GET("/v2/users/{user}/devices/{device}/{resource}/api")
18 | Call resourceAPI(@Header("Authorization") String authorization, @Path("user") String user, @Path("device") String device, @Path("resource") String resource);
19 |
20 | @GET("/v2/users/{user}/devices/{device}/{resource}")
21 | Call getResource(@Header("Authorization") String authorization, @Path("user") String user, @Path("device") String device, @Path("resource") String resource);
22 |
23 | @POST("/v2/users/{user}/devices/{device}/{resource}")
24 | Call postResource(@Header("Authorization") String authorization, @Path("user") String user, @Path("device") String device, @Path("resource") String resource, @Body JsonElement jsonContent);
25 | }
26 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/rect_activity_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
25 |
26 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/round_activity_device.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
25 |
26 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/wear/src/main/res/layout/round_activity_resource_control.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
25 |
26 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/mobile/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Retrofit 2.X
2 | ## https://square.github.io/retrofit/ ##
3 |
4 | -dontwarn retrofit2.**
5 | -keep class retrofit2.** { *; }
6 | -keepattributes Signature
7 | -keepattributes Exceptions
8 |
9 | -keepclasseswithmembers class * {
10 | @retrofit2.http.* ;
11 | }
12 |
13 | -dontwarn okio.**
14 | -dontwarn com.squareup.okhttp.**
15 | -keep class com.squareup.okhttp.** { *; }
16 | -keep interface com.squareup.okhttp.** { *; }
17 |
18 | ## GSON 2.2.4 specific rules ##
19 |
20 | # Gson uses generic type information stored in a class file when working with fields. Proguard
21 | # removes such information by default, so configure it to keep all of it.
22 | -keepattributes Signature
23 |
24 | # For using GSON @Expose annotation
25 | -keepattributes *Annotation*
26 |
27 | -keepattributes EnclosingMethod
28 |
29 | # Gson specific classes
30 | -keep class sun.misc.Unsafe { *; }
31 | -keep class com.google.gson.stream.** { *; }
32 |
33 | # Application classes that will be serialized/deserialized over Gson
34 | -keep class io.thinger.wearinterface.WearDeviceInfo { *; }
35 | -keep class io.thinger.wearinterface.WearDeviceResource { *; }
36 | -keep class io.thinger.thinger.model.DeviceToken { *; }
37 |
38 | # Diable all application logs
39 | -assumenosideeffects class android.util.Log {
40 | public static *** d(...);
41 | public static *** w(...);
42 | public static *** v(...);
43 | public static *** i(...);
44 | }
--------------------------------------------------------------------------------
/wear/src/main/res/layout/round_activity_device_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
22 |
23 |
28 |
29 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/model/Device.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.model;
27 |
28 | public abstract class Device {
29 | public abstract void remove();
30 | public abstract String getDeviceId();
31 | public abstract String getDeviceOwner();
32 | }
33 |
--------------------------------------------------------------------------------
/wear/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/views/ArrayValue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.views;
27 |
28 | import com.google.gson.JsonElement;
29 |
30 | public class ArrayValue extends Element {
31 |
32 | public ArrayValue() {
33 | super(Type.ARRAY);
34 | }
35 |
36 | @Override
37 | public void refreshContent(JsonElement element) {
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/database/DBDeviceToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.database;
27 |
28 | import io.realm.RealmObject;
29 |
30 | public class DBDeviceToken extends RealmObject{
31 | private String token;
32 | public String getToken() {
33 | return token;
34 | }
35 | public void setToken(String token) {
36 | this.token = token;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/mobile/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #2196F3
5 | #1976D2
6 | #BBDEFB
7 | #FF4081
8 | #212121
9 | #727272
10 | #FFFFFF
11 | #B6B6B6
12 |
13 |
19 |
20 |
21 |
36 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/api/src/main/java/io/thinger/api/ThingerAPI.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.api;
27 |
28 | import retrofit2.Retrofit;
29 | import retrofit2.converter.gson.GsonConverterFactory;
30 |
31 | public class ThingerAPI {
32 |
33 | public ThingerAPI() {
34 |
35 | }
36 |
37 | private static ThingerAPIInterface instance;
38 |
39 | static public ThingerAPIInterface getInstance(){
40 | if(instance==null){
41 | GsonConverterFactory gsonFactory = GsonConverterFactory.create();
42 | Retrofit retrofit = new Retrofit.Builder()
43 | .baseUrl("https://api.thinger.io")
44 | .addConverterFactory(gsonFactory)
45 | .build();
46 |
47 | instance = retrofit.create(ThingerAPIInterface.class);
48 | }
49 | return instance;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.os.Bundle;
29 | import android.support.design.widget.FloatingActionButton;
30 | import android.support.design.widget.Snackbar;
31 | import android.support.v7.app.AppCompatActivity;
32 | import android.support.v7.widget.Toolbar;
33 | import android.view.View;
34 |
35 | public class MainActivity extends AppCompatActivity {
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_main);
41 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
42 | setSupportActionBar(toolbar);
43 |
44 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
45 | fab.setOnClickListener(new View.OnClickListener() {
46 | @Override
47 | public void onClick(View view) {
48 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
49 | .setAction("Action", null).show();
50 | }
51 | });
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/wearinterface/src/main/java/io/thinger/wearinterface/WearMessages.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.wearinterface;
27 |
28 | public class WearMessages {
29 | public static String LIST_DEVICES = "/list_devices";
30 | public static String LIST_DEVICES_OK = "/list_devices/ok";
31 | public static String LIST_DEVICES_ERROR = "/list_devices/error";
32 |
33 | public static String GET_DEVICE_API = "/get_device_api";
34 | public static String GET_DEVICE_API_OK = "/get_device_api/ok";
35 | public static String GET_DEVICE_API_ERROR = "/get_device_api/error";
36 |
37 | public static String GET_RESOURCE_API = "/get_resource_api";
38 | public static String GET_RESOURCE_API_OK = "/get_resource_api/ok";
39 | public static String GET_RESOURCE_API_ERROR = "/get_resource_api/error";
40 |
41 | public static String POST_DEVICE_RESOURCE = "/post_device_resource";
42 | public static String POST_DEVICE_RESOURCE_OK = "/post_device_resource/ok";
43 | public static String POST_DEVICE_RESOURCE_ERROR = "/post_device_resource/error";
44 |
45 | public static String GET_DEVICE_RESOURCE = "/get_device_resource";
46 | public static String GET_DEVICE_RESOURCE_OK = "/get_device_resource/ok";
47 | public static String GET_DEVICE_RESOURCE_ERROR = "/get_device_resource/error";
48 | }
49 |
--------------------------------------------------------------------------------
/wearinterface/src/main/java/io/thinger/wearinterface/WearDeviceInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.wearinterface;
27 |
28 | import com.google.gson.Gson;
29 |
30 | import java.io.Serializable;
31 |
32 | public class WearDeviceInfo implements Serializable {
33 | private String user;
34 | private String device;
35 | private String token;
36 |
37 | public WearDeviceInfo(String user, String device, String token) {
38 | this.user = user;
39 | this.device = device;
40 | this.token = token;
41 | }
42 |
43 | public String getUser() {
44 | return user;
45 | }
46 |
47 | public void setUser(String user) {
48 | this.user = user;
49 | }
50 |
51 | public String getDevice() {
52 | return device;
53 | }
54 |
55 | public void setDevice(String device) {
56 | this.device = device;
57 | }
58 |
59 | public String getToken() {
60 | return token;
61 | }
62 |
63 | public void setToken(String token) {
64 | this.token = token;
65 | }
66 |
67 | public byte[] serializeToJson() {
68 | Gson gson = new Gson();
69 | String json = gson.toJson(this);
70 | return json.getBytes();
71 | }
72 |
73 | public static WearDeviceInfo fromJson(byte[] json){
74 | Gson gson = new Gson();
75 | WearDeviceInfo device = gson.fromJson(new String(json), WearDeviceInfo.class);
76 | return device;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/mobile/src/main/res/layout/card_resource.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
18 |
25 |
26 |
34 |
35 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
55 |
56 |
64 |
65 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/model/DeviceToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.model;
27 |
28 | import android.util.Base64;
29 |
30 | import com.google.gson.Gson;
31 |
32 | import java.util.List;
33 |
34 | public class DeviceToken {
35 | private Long iat;
36 | private Long exp;
37 | private String usr;
38 | private String dev;
39 | private List res;
40 | private String jwtToken;
41 |
42 | @Override
43 | public String toString() {
44 | return "DeviceTokens{" +
45 | "iat=" + iat +
46 | ", exp=" + exp +
47 | ", usr='" + usr + '\'' +
48 | ", dev='" + dev + '\'' +
49 | ", res=" + res +
50 | '}';
51 | }
52 |
53 | public String getAuthorizationHeader(){
54 | return "Bearer " + jwtToken;
55 | }
56 |
57 | public String getJwtToken(){
58 | return jwtToken;
59 | }
60 |
61 | public String getUser(){
62 | return usr;
63 | }
64 |
65 | public String getDevice(){
66 | return dev;
67 | }
68 |
69 | public List getResources(){
70 | return res;
71 | }
72 |
73 | public static DeviceToken parse(String jwt){
74 | String[] parts = jwt.split("\\.");
75 | if(parts.length==3){
76 | String payload = new String(Base64.decode(parts[1], Base64.DEFAULT));
77 | Gson gson = new Gson();
78 | DeviceToken deviceToken = gson.fromJson(payload, DeviceToken.class);
79 | deviceToken.jwtToken = jwt;
80 | return deviceToken;
81 | }
82 | return null;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/mobile/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
21 |
22 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
40 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/views/NumberValue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.views;
27 |
28 | import android.text.InputType;
29 | import android.view.LayoutInflater;
30 | import android.view.View;
31 | import android.widget.EditText;
32 | import android.widget.LinearLayout;
33 | import android.widget.TextView;
34 |
35 | import com.google.gson.JsonElement;
36 |
37 | import io.thinger.thinger.R;
38 |
39 | public class NumberValue extends Element {
40 | private EditText editText;
41 |
42 | public NumberValue(LinearLayout layout, String name, Number number, boolean output) {
43 | super(Type.NUMBER);
44 |
45 | LayoutInflater inflater = LayoutInflater.from(layout.getContext());
46 | View value = inflater.inflate(R.layout.card_resource_param_value, null, false);
47 |
48 | TextView nameView = (TextView) value.findViewById(R.id.param_name);
49 | nameView.setText(name);
50 |
51 | editText = (EditText) value.findViewById(R.id.param_value);
52 | editText.setText(number.toString());
53 | editText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL);
54 | editText.setMaxLines(1);
55 |
56 | // disable EditText for output
57 | if(output){
58 | editText.setInputType(InputType.TYPE_NULL);
59 | editText.setTextIsSelectable(true);
60 | }
61 |
62 | layout.addView(value);
63 | }
64 |
65 | @Override
66 | public String toString(){
67 | return editText.getText().toString();
68 | }
69 |
70 | @Override
71 | public void refreshContent(JsonElement element) {
72 | if(element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber()){
73 | editText.setText(element.getAsNumber().toString());
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/DeviceResourceAdapter.java:
--------------------------------------------------------------------------------
1 | package io.thinger.thinger;
2 |
3 | import android.support.v7.widget.CardView;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.ViewGroup;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | import io.thinger.thinger.views.DeviceResource;
12 |
13 | public class DeviceResourceAdapter extends RecyclerView.Adapter {
14 | private List devices;
15 | private OnDeviceResourceClicked deviceResourceClickListener;
16 |
17 | public void clear() {
18 | int size = devices.size();
19 | if(size>0){
20 | devices.clear();
21 | notifyDataSetChanged();
22 | }
23 | }
24 |
25 | public interface OnDeviceResourceClicked{
26 | public void onDeviceTokenClick(DeviceResource token);
27 | }
28 |
29 | // Provide a reference to the views for each data item
30 | // Complex data items may need more than one view per item, and
31 | // you provide access to all the views for a data item in a view holder
32 | public static class ViewHolder extends RecyclerView.ViewHolder {
33 | // each data item is just a string in this case
34 | public CardView cardView;
35 | public ViewHolder(CardView v) {
36 | super(v);
37 | cardView = v;
38 | }
39 | }
40 |
41 | public DeviceResourceAdapter(OnDeviceResourceClicked listener) {
42 | this.deviceResourceClickListener = listener;
43 | this.devices = new ArrayList<>();
44 | }
45 |
46 | // Create new views (invoked by the layout manager)
47 | @Override
48 | public DeviceResourceAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
49 | // create a new view
50 | CardView cardView = (CardView) LayoutInflater.from(parent.getContext())
51 | .inflate(R.layout.card_resource, parent, false);
52 | // set the view's size, margins, paddings and layout parameters
53 |
54 | /*
55 | cardView.setOnClickListener(new View.OnClickListener() {
56 | @Override
57 | public void onClick(View v) {
58 | deviceResourceClickListener.onDeviceTokenClick((DeviceResource) v.getTag());
59 | }
60 | });
61 | */
62 |
63 | ViewHolder vh = new ViewHolder(cardView);
64 | return vh;
65 | }
66 |
67 | // Replace the contents of a view (invoked by the layout manager)
68 | @Override
69 | public void onBindViewHolder(ViewHolder holder, int position) {
70 | final DeviceResource resource = devices.get(position);
71 | resource.fillView(holder.cardView);
72 |
73 | holder.cardView.setTag(resource);
74 | }
75 |
76 | // Return the size of your dataset (invoked by the layout manager)
77 | @Override
78 | public int getItemCount() {
79 | return devices.size();
80 | }
81 |
82 | public void addResource(DeviceResource resource){
83 | devices.add(resource);
84 | notifyItemInserted(devices.size()-1);
85 | }
86 | }
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/views/StringValue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.views;
27 |
28 | import android.text.InputType;
29 | import android.view.LayoutInflater;
30 | import android.view.View;
31 | import android.widget.EditText;
32 | import android.widget.LinearLayout;
33 | import android.widget.TextView;
34 |
35 | import com.google.gson.JsonElement;
36 |
37 | import io.thinger.thinger.R;
38 |
39 | public class StringValue extends Element {
40 | private EditText editText;
41 |
42 | public StringValue(LinearLayout layout, String paramName, String paramValue, boolean output) {
43 | super(Type.STRING);
44 |
45 | // inflate resource to view
46 | LayoutInflater inflater = LayoutInflater.from(layout.getContext());
47 | View value = inflater.inflate(R.layout.card_resource_param_value, null, false);
48 |
49 | // set parameter name
50 | TextView nameView = (TextView) value.findViewById(R.id.param_name);
51 | if(paramName.isEmpty()){
52 | nameView.setVisibility(View.GONE);
53 | }else{
54 | nameView.setText(paramName);
55 | }
56 |
57 | // set parameter text
58 | editText = (EditText) value.findViewById(R.id.param_value);
59 | editText.setText(paramValue);
60 | editText.setMaxLines(1);
61 |
62 | // disable EditText for output
63 | if(output){
64 | editText.setInputType(InputType.TYPE_NULL);
65 | editText.setTextIsSelectable(true);
66 | }
67 |
68 | layout.addView(value);
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | return "\"" + editText.getText().toString() + "\"";
74 | }
75 |
76 | @Override
77 | public void refreshContent(JsonElement element) {
78 | if(element.isJsonPrimitive() && element.getAsJsonPrimitive().isString()){
79 | editText.setText(element.getAsString());
80 | }else{
81 | editText.setText("");
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/ThingerApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.app.Application;
29 | import android.content.SharedPreferences;
30 |
31 | import com.google.android.gms.analytics.GoogleAnalytics;
32 | import com.google.android.gms.analytics.Tracker;
33 |
34 | import java.math.BigInteger;
35 | import java.security.SecureRandom;
36 |
37 | import io.realm.Realm;
38 | import io.realm.RealmConfiguration;
39 |
40 | public class ThingerApplication extends Application{
41 |
42 | private static final String PREFERENCES = "preferences";
43 | public static GoogleAnalytics analytics;
44 | public static Tracker tracker;
45 |
46 | @Override
47 | public void onCreate() {
48 | super.onCreate();
49 |
50 | SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
51 |
52 | String dbKey;
53 | if(preferences.contains("db")){
54 | dbKey = preferences.getString("db", "");
55 | }else{
56 | dbKey = getRandomKey();
57 | SharedPreferences.Editor prefs = preferences.edit();
58 | prefs.putString("db", dbKey);
59 | prefs.apply();
60 | }
61 |
62 | RealmConfiguration config = new RealmConfiguration.Builder(this)
63 | .name("thinger.realm")
64 | .encryptionKey(dbKey.getBytes())
65 | .schemaVersion(1)
66 | .build();
67 | Realm.setDefaultConfiguration(config);
68 |
69 |
70 | analytics = GoogleAnalytics.getInstance(this);
71 | analytics.setLocalDispatchPeriod(1800);
72 |
73 | tracker = analytics.newTracker("UA-59416166-1");
74 | tracker.enableExceptionReporting(true);
75 | tracker.enableAdvertisingIdCollection(true);
76 | tracker.enableAutoActivityTracking(true);
77 | }
78 |
79 | public String getRandomKey() {
80 | SecureRandom random = new SecureRandom();
81 | return new BigInteger(320, random).toString(32);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/QRScannerActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.content.Intent;
29 | import android.os.Bundle;
30 | import android.support.v7.app.AppCompatActivity;
31 | import android.util.Log;
32 |
33 | import com.google.zxing.BarcodeFormat;
34 | import com.google.zxing.Result;
35 |
36 | import java.util.ArrayList;
37 | import java.util.List;
38 |
39 | import me.dm7.barcodescanner.zxing.ZXingScannerView;
40 |
41 | public class QRScannerActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {
42 | private ZXingScannerView mScannerView;
43 |
44 | @Override
45 | protected void onCreate(Bundle savedInstanceState) {
46 | super.onCreate(savedInstanceState);
47 |
48 | // initialize the scanner view
49 | mScannerView = new ZXingScannerView(this);
50 |
51 | // set only to read QR Codes
52 | List formats = new ArrayList<>();
53 | formats.add(BarcodeFormat.QR_CODE);
54 | mScannerView.setFormats(formats);
55 |
56 | // set content view to scanner
57 | setContentView(mScannerView);
58 | }
59 |
60 | @Override
61 | public void onResume() {
62 | super.onResume();
63 | mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
64 | mScannerView.startCamera(); // Start camera on resume
65 | }
66 |
67 | @Override
68 | public void onPause() {
69 | super.onPause();
70 | mScannerView.stopCamera(); // Stop camera on pause
71 | }
72 |
73 | @Override
74 | public void handleResult(Result rawResult) {
75 | // Do something with the result here
76 | Log.v("QR", rawResult.getText()); // Prints scan results
77 | Log.v("QR", rawResult.getBarcodeFormat().toString()); // Prints the scan format (qrcode, pdf417 etc.)
78 | Intent data = new Intent();
79 | data.putExtra("token", rawResult.getText());
80 | setResult(RESULT_OK, data);
81 | finish();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/views/ObjectValue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.views;
27 |
28 | import android.widget.LinearLayout;
29 |
30 | import com.google.gson.JsonElement;
31 | import com.google.gson.JsonObject;
32 |
33 | import java.util.HashMap;
34 | import java.util.Map;
35 |
36 | public class ObjectValue extends Element {
37 | Map entries;
38 |
39 | public ObjectValue(LinearLayout layout, JsonObject object, boolean output) {
40 | super(Type.OBJECT);
41 | this.entries = new HashMap<>();
42 | for (Map.Entry entry : object.entrySet()) {
43 | //addResourceName(entry.getKey(), layout);
44 | if(entry.getValue().isJsonPrimitive()){
45 | entries.put(entry.getKey(), createPrimitiveElement(entry.getKey(), entry.getValue().getAsJsonPrimitive(), layout, output));
46 | }else if(entry.getValue().isJsonObject()){
47 | entries.put(entry.getKey(), new ObjectValue(layout, entry.getValue().getAsJsonObject(), output));
48 | }
49 | }
50 | }
51 |
52 | @Override
53 | public String toString(){
54 | StringBuffer buffer = new StringBuffer();
55 | int count = 0;
56 | buffer.append("{");
57 | for(Map.Entry entry : entries.entrySet()){
58 | if(count>0) buffer.append(',');
59 | buffer.append("\"");
60 | buffer.append(entry.getKey());
61 | buffer.append("\":");
62 | buffer.append(entry.getValue().toString());
63 | count++;
64 | }
65 | buffer.append("}");
66 | return buffer.toString();
67 | }
68 |
69 | @Override
70 | public void refreshContent(JsonElement element) {
71 | if(element.isJsonObject()){
72 | for (Map.Entry entry : element.getAsJsonObject().entrySet()) {
73 | Element currentElement = entries.get(entry.getKey());
74 | if(currentElement!=null){
75 | currentElement.refreshContent(entry.getValue());
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/views/BoolValue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.views;
27 |
28 | import android.view.LayoutInflater;
29 | import android.view.View;
30 | import android.widget.CompoundButton;
31 | import android.widget.LinearLayout;
32 | import android.widget.Switch;
33 | import android.widget.TextView;
34 |
35 | import com.google.gson.JsonElement;
36 |
37 | import io.thinger.thinger.R;
38 |
39 | public class BoolValue extends Element {
40 | private Switch button;
41 |
42 | public BoolValue(LinearLayout layout, String paramName, boolean paramValue, boolean output) {
43 | super(Type.BOOLEAN);
44 |
45 | // inflate resource to view
46 | LayoutInflater inflater = LayoutInflater.from(layout.getContext());
47 | View value = inflater.inflate(R.layout.card_resource_param_switch, null, false);
48 |
49 | // set parameter name
50 | TextView nameView = (TextView) value.findViewById(R.id.param_name);
51 | if(paramName.isEmpty()){
52 | nameView.setVisibility(View.GONE);
53 | }else{
54 | nameView.setText(paramName);
55 | }
56 |
57 | // set button content and listener
58 | button = (Switch) value.findViewById(R.id.param_value);
59 | button.setChecked(paramValue);
60 | button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
61 | @Override
62 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
63 | if (listener != null) {
64 | listener.onElementChanged();
65 | }
66 | }
67 | });
68 |
69 | // disable EditText for output
70 | if(output){
71 | button.setEnabled(false);
72 | }
73 |
74 | layout.addView(value);
75 | }
76 |
77 | @Override
78 | public String toString(){
79 | return button.isChecked() ? "true" : "false";
80 | }
81 |
82 | @Override
83 | public void refreshContent(JsonElement element) {
84 | if(element.isJsonPrimitive() && element.getAsJsonPrimitive().isBoolean()){
85 | button.setChecked(element.getAsBoolean());
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/wear/src/main/java/io/thinger/thinger/DeviceResourceDescription.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import com.google.gson.JsonElement;
29 | import com.google.gson.JsonObject;
30 | import com.google.gson.JsonPrimitive;
31 |
32 | public class DeviceResourceDescription {
33 | private ResourceType resourceType;
34 | private String resourceName;
35 |
36 | public enum ResourceType {
37 | UNKNOWN(-1), NONE(0), RUN(1), PSON_IN(2), PSON_OUT(3), PSON_IN_OUT(4);
38 |
39 | private final int number;
40 |
41 | private ResourceType(int number) {
42 | this.number = number;
43 | }
44 |
45 | public int getNumber() {
46 | return number;
47 | }
48 |
49 | public static ResourceType get(int value){
50 | switch (value){
51 | case -1:
52 | return UNKNOWN;
53 | case 0:
54 | return NONE;
55 | case 1:
56 | return RUN;
57 | case 2:
58 | return PSON_IN;
59 | case 3:
60 | return PSON_OUT;
61 | case 4:
62 | return PSON_IN_OUT;
63 | default:
64 | return UNKNOWN;
65 | }
66 | }
67 | }
68 |
69 | public DeviceResourceDescription(String resourceName, JsonElement resourceDescription) {
70 | this.resourceName = resourceName;
71 | this.resourceType = ResourceType.NONE;
72 | if(resourceDescription.isJsonObject()){
73 | JsonObject object = resourceDescription.getAsJsonObject();
74 | if(object.has("fn")){
75 | JsonElement function = object.get("fn");
76 | if(function.isJsonPrimitive()){
77 | JsonPrimitive value = function.getAsJsonPrimitive();
78 | if(value.isNumber()){
79 | resourceType = ResourceType.get(value.getAsInt());
80 | }
81 | }
82 | }
83 | }
84 | }
85 |
86 | public String getResourceName() {
87 | return resourceName;
88 | }
89 |
90 | public ResourceType getResourceType(){
91 | return resourceType;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Thinger.io Android Client
2 |
3 | Android source code for the [Thinger.io](https://thinger.io) Internet of Things Platform. With wear support.
4 |
5 | ## Overview
6 |
7 | The current version of the Android APP does not require any kind of login to interact with your IoT devices. Simply scan your device token as a QR code and your phone will be able to interact with your device for reading sensor values, changing led or relays states, and so on.
8 |
9 |
10 |
11 |
12 |
13 | Download at Google Play: [Thinger.io Android Client](https://play.google.com/store/apps/details?id=io.thinger.thinger)
14 |
15 | ## Wear Support
16 | The Android APP supports Android Wear devices for controlling and reading values from your sensors and actuators! The current version is still under testing and therefore it can still have some errors. At this moment the Wear version allows reading values (not composed values at this moment), execute resources, and control boolean resources. It is so cool to turn on and off things from the SmartWatch! To get the devices available in the SmartWatch, it is not required to do anything special. As usually, just scan the QR code from the platform with the mobile phone.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ## Features under development
25 |
26 | Some of the planned featues for the Thinger.io Android App are the following:
27 |
28 | * Writing device tokens to NFC Tags and reading them back without using QR.
29 | * Improve Android Wear support.
30 | * Improve device discovery screen.
31 | * Add settings.
32 | * Session login for accounts and all the underlying stuff like listing devices, showing connections, dashboards?, etc.
33 |
34 | ## Contributors
35 |
36 | Any help is always welcome! If you feel confident about some feature to implement, or want to fix a bug, or improve the existing code, please, submit a pull request! :)
37 |
38 | ## License
39 |
40 |
41 |
42 | The class is licensed under the [MIT License](http://opensource.org/licenses/MIT):
43 |
44 | Copyright © 2015 [THINGER LTD](http://thinger.io)
45 |
46 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
47 |
48 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
49 |
50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/views/Element.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.views;
27 |
28 | import android.view.LayoutInflater;
29 | import android.widget.LinearLayout;
30 | import android.widget.TextView;
31 |
32 | import com.google.gson.JsonElement;
33 | import com.google.gson.JsonPrimitive;
34 |
35 | import io.thinger.thinger.R;
36 |
37 | public abstract class Element {
38 | public enum Type{
39 | BOOLEAN,
40 | NUMBER,
41 | STRING,
42 | OBJECT,
43 | ARRAY
44 | }
45 |
46 | public interface ElementListener{
47 | public void onElementChanged();
48 | }
49 |
50 | private Type type;
51 |
52 | public Type getType()
53 | {
54 | return this.type;
55 | }
56 |
57 | public Element(Type type) {
58 | this.type = type;
59 | }
60 |
61 | protected ElementListener listener;
62 |
63 | public static Element createPrimitiveElement(String name, JsonPrimitive primitive, LinearLayout layout, boolean output){
64 | if(primitive.isBoolean()){
65 | return new BoolValue(layout, name, primitive.getAsBoolean(), output);
66 | }else if(primitive.isNumber()){
67 | return new NumberValue(layout, name, primitive.getAsNumber(), output);
68 | }else if(primitive.isString()){
69 | return new StringValue(layout, name, primitive.getAsString(), output);
70 | }
71 | return null;
72 | }
73 |
74 | public void addResourceName(String title, LinearLayout layout){
75 | LayoutInflater inflater = LayoutInflater.from(layout.getContext());
76 | TextView categoryLayout = (TextView) inflater.inflate(R.layout.card_resource_param_name, null, false);
77 | categoryLayout.setText(title);
78 | layout.addView(categoryLayout);
79 | }
80 |
81 | public void setListener(ElementListener listener){
82 | this.listener = listener;
83 | }
84 |
85 | public static Element createElement(LinearLayout layout, JsonElement element, boolean output){
86 | if(element.isJsonPrimitive()){
87 | return createPrimitiveElement("", element.getAsJsonPrimitive(), layout, output);
88 | } else if (element.isJsonObject()){
89 | return new ObjectValue(layout, element.getAsJsonObject(), output);
90 | }
91 | return null;
92 | }
93 |
94 | public abstract void refreshContent(JsonElement element);
95 | }
--------------------------------------------------------------------------------
/wearinterface/src/main/java/io/thinger/wearinterface/WearDeviceResource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.wearinterface;
27 |
28 | import com.google.gson.Gson;
29 | import com.google.gson.JsonElement;
30 | import com.google.gson.JsonObject;
31 | import com.google.gson.JsonParser;
32 |
33 | import java.io.Serializable;
34 |
35 | public class WearDeviceResource implements Serializable {
36 | private WearDeviceInfo device;
37 | private String resource;
38 | private String value;
39 | private int type;
40 |
41 | public WearDeviceResource(WearDeviceInfo deviceInfo, String resource, int type) {
42 | this.device = deviceInfo;
43 | this.resource = resource;
44 | this.type = type;
45 | }
46 |
47 | public WearDeviceInfo getDeviceInfo() {
48 | return device;
49 | }
50 |
51 | public int getType() {
52 | return type;
53 | }
54 |
55 | public void setType(int type) {
56 | this.type = type;
57 | }
58 |
59 | public void setDevice(WearDeviceInfo deviceInfo) {
60 | this.device = deviceInfo;
61 | }
62 |
63 | public String getResource() {
64 | return resource;
65 | }
66 |
67 | public void setResource(String resource) {
68 | this.resource = resource;
69 | }
70 |
71 | public byte[] serializeToJson() {
72 | Gson gson = new Gson();
73 | String json = gson.toJson(this);
74 | return json.getBytes();
75 | }
76 |
77 | public static WearDeviceResource fromJson(byte[] json){
78 | Gson gson = new Gson();
79 | WearDeviceResource resource = gson.fromJson(new String(json), WearDeviceResource.class);
80 | return resource;
81 | }
82 |
83 | public JsonElement getJsonElement() {
84 | if (value != null && !value.isEmpty()) {
85 | return new JsonParser().parse(value);
86 | } else {
87 | return null;
88 | }
89 | }
90 |
91 | public JsonElement getInput(){
92 | if(this.value==null || this.value.isEmpty()) return null;
93 | JsonObject value = getJsonElement().getAsJsonObject();
94 | if(value.has("in")){
95 | return value.get("in");
96 | }
97 | return null;
98 | }
99 |
100 | public JsonElement getOutput(){
101 | if(this.value==null || this.value.isEmpty()) return null;
102 | JsonObject value = getJsonElement().getAsJsonObject();
103 | if(value!=null && value.has("out")){
104 | return value.get("out");
105 | }
106 | return null;
107 | }
108 |
109 | public String getValue() {
110 | return value;
111 | }
112 |
113 | public void setValue(String value) {
114 | this.value = value;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/model/DeviceFromToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.model;
27 |
28 | import java.util.ArrayList;
29 | import java.util.List;
30 |
31 | import io.realm.Realm;
32 | import io.realm.RealmResults;
33 | import io.thinger.thinger.database.DBDeviceToken;
34 | import io.thinger.wearinterface.WearDeviceInfo;
35 |
36 | public class DeviceFromToken extends Device {
37 | private DBDeviceToken databaseToken;
38 | private DeviceToken deviceToken;
39 |
40 | @Override
41 | public void remove() {
42 | Realm realm = Realm.getDefaultInstance();
43 | realm.beginTransaction();
44 | databaseToken.deleteFromRealm();
45 | realm.commitTransaction();
46 | }
47 |
48 | @Override
49 | public String getDeviceId() {
50 | return deviceToken.getDevice();
51 | }
52 |
53 | @Override
54 | public String getDeviceOwner() {
55 | return deviceToken.getUser();
56 | }
57 |
58 | public String getJWTToken(){
59 | return deviceToken.getJwtToken();
60 | }
61 |
62 | public static List getDeviceTokens(){
63 | List devices = new ArrayList<>();
64 | Realm realm = Realm.getDefaultInstance();
65 | RealmResults deviceTokens = realm.where(DBDeviceToken.class).findAll();
66 | for(DBDeviceToken token : deviceTokens){
67 | DeviceFromToken deviceFromToken = new DeviceFromToken();
68 | deviceFromToken.databaseToken = token;
69 | deviceFromToken.deviceToken = DeviceToken.parse(token.getToken());
70 | devices.add(deviceFromToken);
71 | }
72 | return devices;
73 | }
74 |
75 |
76 | public static List getWearDevices(){
77 | List devices = new ArrayList<>();
78 | Realm realm = Realm.getDefaultInstance();
79 | RealmResults deviceTokens = realm.where(DBDeviceToken.class).findAll();
80 | for(DBDeviceToken token : deviceTokens){
81 | DeviceToken deviceToken = DeviceToken.parse(token.getToken());
82 | devices.add(new WearDeviceInfo(deviceToken.getUser(), deviceToken.getDevice(), deviceToken.getJwtToken()));
83 | }
84 | return devices;
85 | }
86 |
87 | public static Device createDeviceFromToken(DeviceToken deviceToken){
88 | DeviceFromToken deviceFromToken = new DeviceFromToken();
89 | deviceFromToken.databaseToken = saveDeviceToken(deviceToken);
90 | deviceFromToken.deviceToken = deviceToken;
91 | return deviceFromToken;
92 | }
93 |
94 | public static DBDeviceToken saveDeviceToken(DeviceToken deviceToken){
95 | Realm realm = Realm.getDefaultInstance();
96 | realm.beginTransaction();
97 | DBDeviceToken dbToken = realm.createObject(DBDeviceToken.class);
98 | dbToken.setToken(deviceToken.getJwtToken());
99 | realm.commitTransaction();
100 | return dbToken;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/DeviceListAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.support.v7.widget.CardView;
29 | import android.support.v7.widget.RecyclerView;
30 | import android.view.LayoutInflater;
31 | import android.view.View;
32 | import android.view.ViewGroup;
33 | import android.widget.TextView;
34 |
35 | import java.util.ArrayList;
36 | import java.util.List;
37 |
38 | import io.thinger.thinger.model.Device;
39 |
40 | public class DeviceListAdapter extends RecyclerView.Adapter {
41 | private List devices;
42 | private OnDeviceTokenClicked deviceTokenClickListener;
43 |
44 | public void remove(Device device) {
45 | int index = devices.indexOf(device);
46 | if(index>=0){
47 | devices.remove(index);
48 | notifyItemRemoved(index);
49 | device.remove();
50 | }
51 | }
52 |
53 | public interface OnDeviceTokenClicked{
54 | public void onDeviceTokenClick(Device token);
55 | public void onDeviceTokenLongClick(Device token);
56 | }
57 |
58 | // Provide a reference to the views for each data item
59 | // Complex data items may need more than one view per item, and
60 | // you provide access to all the views for a data item in a view holder
61 | public static class ViewHolder extends RecyclerView.ViewHolder {
62 | // each data item is just a string in this case
63 | public CardView cardView;
64 | public ViewHolder(CardView v) {
65 | super(v);
66 | cardView = v;
67 | }
68 | }
69 |
70 | public DeviceListAdapter(OnDeviceTokenClicked listener) {
71 | this.deviceTokenClickListener = listener;
72 | this.devices = new ArrayList<>();
73 | }
74 |
75 | public void addDevices(List devices){
76 | this.devices.addAll(devices);
77 | notifyDataSetChanged();
78 | }
79 |
80 | // Create new views (invoked by the layout manager)
81 | @Override
82 | public DeviceListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
83 | // create a new view
84 | CardView cardView = (CardView) LayoutInflater.from(parent.getContext())
85 | .inflate(R.layout.card_device, parent, false);
86 | // set the view's size, margins, paddings and layout parameters
87 |
88 | cardView.setOnClickListener(new View.OnClickListener() {
89 | @Override
90 | public void onClick(View v) {
91 | deviceTokenClickListener.onDeviceTokenClick((Device) v.getTag());
92 | }
93 | });
94 |
95 | cardView.setOnLongClickListener(new View.OnLongClickListener() {
96 | @Override
97 | public boolean onLongClick(View v) {
98 | deviceTokenClickListener.onDeviceTokenLongClick((Device) v.getTag());
99 | return true;
100 | }
101 | });
102 |
103 | ViewHolder vh = new ViewHolder(cardView);
104 | return vh;
105 | }
106 |
107 | // Replace the contents of a view (invoked by the layout manager)
108 | @Override
109 | public void onBindViewHolder(ViewHolder holder, int position) {
110 | // - get element from your dataset at this position
111 | // - replace the contents of the view with that element
112 | TextView text = (TextView) holder.cardView.findViewById(R.id.info_text);
113 | text.setText(devices.get(position).getDeviceId());
114 | holder.cardView.setTag(devices.get(position));
115 | }
116 |
117 | // Return the size of your dataset (invoked by the layout manager)
118 | @Override
119 | public int getItemCount() {
120 | return devices.size();
121 | }
122 |
123 | public void addDevice(Device device){
124 | devices.add(device);
125 | notifyItemInserted(devices.size()-1);
126 | }
127 | }
--------------------------------------------------------------------------------
/wear/src/main/java/io/thinger/thinger/DeviceListActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.content.Context;
29 | import android.content.Intent;
30 | import android.os.Bundle;
31 | import android.support.wearable.activity.ConfirmationActivity;
32 | import android.support.wearable.view.ProgressSpinner;
33 | import android.support.wearable.view.WatchViewStub;
34 | import android.support.wearable.view.WearableListView;
35 | import android.view.LayoutInflater;
36 | import android.view.View;
37 | import android.view.ViewGroup;
38 | import android.widget.TextView;
39 | import android.widget.Toast;
40 |
41 | import com.google.gson.Gson;
42 |
43 | import java.util.ArrayList;
44 |
45 | import io.thinger.wearinterface.WearDeviceInfo;
46 | import io.thinger.wearinterface.WearMessages;
47 |
48 | public class DeviceListActivity extends MessageActivity implements WearableListView.ClickListener{
49 | private WearableListView mListView;
50 | private MyAdapter mAdapter;
51 | private TextView mHeader;
52 | private ProgressSpinner spinner;
53 | public static String TAG = "WearListActivity";
54 |
55 | @Override
56 | protected void onCreate(Bundle savedInstanceState) {
57 | super.onCreate(savedInstanceState);
58 | setContentView(R.layout.activity_device_list);
59 |
60 | final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
61 |
62 | stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
63 | @Override
64 | public void onLayoutInflated(WatchViewStub stub) {
65 | mListView = (WearableListView) stub.findViewById(R.id.listView1);
66 | mAdapter = new MyAdapter(DeviceListActivity.this);
67 | mListView.setAdapter(mAdapter);
68 | mListView.setClickListener(DeviceListActivity.this);
69 |
70 | mHeader = (TextView) stub.findViewById(R.id.header);
71 |
72 | spinner = (ProgressSpinner) stub.findViewById(R.id.spinner);
73 |
74 | mListView.addOnScrollListener(new WearableListView.OnScrollListener() {
75 | @Override
76 | public void onScroll(int i) {
77 | mHeader.setY(mHeader.getY() - i);
78 | }
79 |
80 | @Override
81 | public void onAbsoluteScrollChange(int i) {
82 |
83 | }
84 |
85 | @Override
86 | public void onScrollStateChanged(int i) {
87 | }
88 |
89 | @Override
90 | public void onCentralPositionChanged(int i) {
91 | }
92 | });
93 | }
94 | });
95 | }
96 |
97 | @Override
98 | protected void onReady() {
99 | mAdapter.clear();
100 | spinner.setVisibility(View.VISIBLE);
101 | sendMessage(WearMessages.LIST_DEVICES);
102 | }
103 |
104 | @Override
105 | protected void onDeviceConnectionError() {
106 | super.onDeviceConnectionError();
107 | spinner.setVisibility(View.GONE);
108 | }
109 |
110 | @Override
111 | protected void onResponseReceived(String key, String value) {
112 | if(key.equals(WearMessages.LIST_DEVICES_OK)){
113 | spinner.setVisibility(View.GONE);
114 | Gson gson = new Gson();
115 | final WearDeviceInfo[] arr = gson.fromJson(value, WearDeviceInfo[].class);
116 | for(WearDeviceInfo device : arr) {
117 | mAdapter.add(device);
118 | }
119 | }else if(key.equals(WearMessages.LIST_DEVICES_ERROR)){
120 | spinner.setVisibility(View.GONE);
121 | Toast.makeText(this, "Cannot read devices!", Toast.LENGTH_LONG).show();
122 | }
123 | }
124 |
125 | @Override
126 | public void onClick(WearableListView.ViewHolder viewHolder) {
127 | WearDeviceInfo device = mAdapter.get(viewHolder.getAdapterPosition());
128 | Intent intent = new Intent(this, DeviceActivity.class);
129 | intent.putExtra("device", device);
130 | startActivity(intent);
131 | }
132 |
133 | @Override
134 | public void onTopEmptyRegionClick() {
135 | Toast.makeText(this, "You tapped on Top empty area", Toast.LENGTH_SHORT).show();
136 | }
137 |
138 | private class MyAdapter extends WearableListView.Adapter {
139 | private final LayoutInflater mInflater;
140 | private ArrayList data;
141 |
142 | public WearDeviceInfo get(int index){
143 | return data.get(index);
144 | }
145 |
146 | public void add(WearDeviceInfo device){
147 | data.add(device);
148 | notifyDataSetChanged();
149 | }
150 |
151 | private MyAdapter(Context context) {
152 | mInflater = LayoutInflater.from(context);
153 | data = new ArrayList<>();
154 | }
155 |
156 | public void clear(){
157 | data.clear();
158 | notifyDataSetChanged();
159 | }
160 |
161 | public boolean isEmpty(){
162 | return data.isEmpty();
163 | }
164 |
165 | @Override
166 | public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
167 | return new WearableListView.ViewHolder(
168 | mInflater.inflate(R.layout.row_device, null));
169 | }
170 |
171 | @Override
172 | public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
173 | TextView view = (TextView) holder.itemView.findViewById(R.id.textView);
174 | view.setText(get(position).getDevice());
175 | holder.itemView.setTag(position);
176 | }
177 |
178 | @Override
179 | public int getItemCount() {
180 | return data.size();
181 | }
182 | }
183 | }
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/services/WearListCallListenerService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.services;
27 |
28 | import android.util.Log;
29 |
30 | import com.google.android.gms.common.api.GoogleApiClient;
31 | import com.google.android.gms.wearable.MessageEvent;
32 | import com.google.android.gms.wearable.Wearable;
33 | import com.google.android.gms.wearable.WearableListenerService;
34 | import com.google.gson.Gson;
35 | import com.google.gson.JsonElement;
36 | import com.google.gson.JsonObject;
37 |
38 | import java.io.IOException;
39 | import java.util.List;
40 | import java.util.concurrent.TimeUnit;
41 |
42 | import io.thinger.api.ThingerAPI;
43 | import io.thinger.thinger.model.DeviceFromToken;
44 | import io.thinger.wearinterface.WearDeviceInfo;
45 | import io.thinger.wearinterface.WearDeviceResource;
46 | import io.thinger.wearinterface.WearMessages;
47 | import retrofit2.Call;
48 | import retrofit2.Response;
49 |
50 | public class WearListCallListenerService extends WearableListenerService {
51 | public static String TAG = "WearListCallListenerService";
52 |
53 | private void reply(String nodeId, String path, byte[] content) {
54 | Log.d(TAG, "Sending Message (" + nodeId + ")" + " - " + path + (content!=null ? ": " + new String(content) : ""));
55 |
56 | GoogleApiClient client = new GoogleApiClient.Builder(this)
57 | .addApi(Wearable.API)
58 | .build();
59 | client.blockingConnect(10000, TimeUnit.MILLISECONDS);
60 | Wearable.MessageApi.sendMessage(client, nodeId, path, content);
61 | client.disconnect();
62 | }
63 |
64 | @Override
65 | public void onMessageReceived(final MessageEvent messageEvent) {
66 | super.onMessageReceived(messageEvent);
67 | Log.d(TAG, "Received Message - " + messageEvent.getPath() + (messageEvent.getData()!=null ? ": " + new String(messageEvent.getData()) : ""));
68 |
69 | String event = messageEvent.getPath();
70 | if(event.equals(WearMessages.LIST_DEVICES)){
71 | List devices = DeviceFromToken.getWearDevices();
72 | Gson gson = new Gson();
73 | reply(messageEvent.getSourceNodeId(), WearMessages.LIST_DEVICES_OK, gson.toJson(devices).getBytes());
74 | }else if(event.equals(WearMessages.GET_DEVICE_API)){
75 | WearDeviceInfo device = WearDeviceInfo.fromJson(messageEvent.getData());
76 | Call call = ThingerAPI.getInstance().deviceAPI("Bearer " + device.getToken(), device.getUser(), device.getDevice());
77 | try {
78 | Response response = call.execute();
79 | if (response.isSuccessful()) {
80 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_DEVICE_API_OK, response.body().toString().getBytes());
81 | }else{
82 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_DEVICE_API_ERROR, null);
83 | }
84 | } catch (IOException e) {
85 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_DEVICE_API_ERROR, null);
86 | }
87 | }else if(event.equals(WearMessages.GET_RESOURCE_API)){
88 | WearDeviceResource resource = WearDeviceResource.fromJson(messageEvent.getData());
89 | WearDeviceInfo device = resource.getDeviceInfo();
90 | Call call = ThingerAPI.getInstance().resourceAPI("Bearer " + device.getToken(), device.getUser(), device.getDevice(), resource.getResource());
91 | try {
92 | Response response = call.execute();
93 | if (response.isSuccessful()) {
94 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_RESOURCE_API_OK, response.body().toString().getBytes());
95 | }else{
96 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_RESOURCE_API_ERROR, null);
97 | }
98 | } catch (IOException e) {
99 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_RESOURCE_API_ERROR, null);
100 | }
101 | }else if(event.equals(WearMessages.POST_DEVICE_RESOURCE)){
102 | WearDeviceResource resource = WearDeviceResource.fromJson(messageEvent.getData());
103 | WearDeviceInfo device = resource.getDeviceInfo();
104 | Call call = ThingerAPI.getInstance().postResource("Bearer " + device.getToken(), device.getUser(), device.getDevice(), resource.getResource(), resource.getJsonElement());
105 | try {
106 | Response response = call.execute();
107 | if (response.isSuccessful()) {
108 | reply(messageEvent.getSourceNodeId(), WearMessages.POST_DEVICE_RESOURCE_OK, response.body().toString().getBytes());
109 | }else{
110 | reply(messageEvent.getSourceNodeId(), WearMessages.POST_DEVICE_RESOURCE_ERROR, null);
111 | }
112 | } catch (IOException e) {
113 | reply(messageEvent.getSourceNodeId(), WearMessages.POST_DEVICE_RESOURCE_ERROR, null);
114 | }
115 | }else if(event.equals(WearMessages.GET_DEVICE_RESOURCE)){
116 | WearDeviceResource resource = WearDeviceResource.fromJson(messageEvent.getData());
117 | WearDeviceInfo device = resource.getDeviceInfo();
118 | Call call = ThingerAPI.getInstance().getResource("Bearer " + device.getToken(), device.getUser(), device.getDevice(), resource.getResource());
119 | try {
120 | Response response = call.execute();
121 | if (response.isSuccessful()) {
122 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_DEVICE_RESOURCE_OK, response.body().toString().getBytes());
123 | }else{
124 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_DEVICE_RESOURCE_ERROR, null);
125 | }
126 | } catch (IOException e) {
127 | reply(messageEvent.getSourceNodeId(), WearMessages.GET_DEVICE_RESOURCE_ERROR, null);
128 | }
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/wear/src/main/java/io/thinger/thinger/MessageActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.app.Activity;
29 | import android.content.Intent;
30 | import android.content.IntentSender;
31 | import android.os.Bundle;
32 | import android.support.wearable.activity.ConfirmationActivity;
33 | import android.util.Log;
34 |
35 | import com.google.android.gms.common.ConnectionResult;
36 | import com.google.android.gms.common.api.GoogleApiClient;
37 | import com.google.android.gms.common.api.ResultCallback;
38 | import com.google.android.gms.wearable.MessageApi;
39 | import com.google.android.gms.wearable.MessageEvent;
40 | import com.google.android.gms.wearable.Node;
41 | import com.google.android.gms.wearable.NodeApi;
42 | import com.google.android.gms.wearable.Wearable;
43 |
44 | public abstract class MessageActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, MessageApi.MessageListener {
45 | public static String TAG = "MessageActivity";
46 | protected Node mNode;
47 | protected GoogleApiClient mGoogleApiClient;
48 | private boolean mResolvingError=false;
49 |
50 | private static final int REQUEST_RESOLVE_ERROR = 1000;
51 |
52 | @Override
53 | protected void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 |
56 | //Connect the GoogleApiClient
57 | mGoogleApiClient = new GoogleApiClient.Builder(this)
58 | .addApi(Wearable.API)
59 | .addConnectionCallbacks(this)
60 | .addOnConnectionFailedListener(this)
61 | .build();
62 | }
63 |
64 | @Override
65 | protected void onStart() {
66 | super.onStart();
67 | if (!mResolvingError) {
68 | mGoogleApiClient.connect();
69 | }
70 | }
71 |
72 | @Override
73 | protected void onStop() {
74 | super.onStop();
75 | if (!mResolvingError) {
76 | Wearable.MessageApi.removeListener(mGoogleApiClient, this);
77 | mGoogleApiClient.disconnect();
78 | }
79 | }
80 |
81 | @Override
82 | public void onConnected(Bundle bundle) {
83 | mResolvingError = false;
84 |
85 | // add message listener
86 | Wearable.MessageApi.addListener(mGoogleApiClient, this);
87 |
88 | // get connected device and send the request to populate
89 | Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
90 | .setResultCallback(new ResultCallback() {
91 | @Override
92 | public void onResult(NodeApi.GetConnectedNodesResult nodes) {
93 | if(nodes.getNodes().isEmpty()){
94 | onDeviceConnectionError();
95 | }else{
96 | // save the connected nodes
97 | for (Node node : nodes.getNodes()) {
98 | mNode = node;
99 | }
100 | onReady();
101 | }
102 | }
103 | });
104 | }
105 |
106 | @Override
107 | public void onConnectionSuspended(int i) {
108 |
109 | }
110 |
111 | @Override
112 | public void onConnectionFailed(ConnectionResult result) {
113 | if (mResolvingError) {
114 | // Already attempting to resolve an error.
115 | return;
116 | } else if (result.hasResolution()) {
117 | try {
118 | mResolvingError = true;
119 | result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
120 | } catch (IntentSender.SendIntentException e) {
121 | // There was an error with the resolution intent. Try again.
122 | mGoogleApiClient.connect();
123 | }
124 | } else {
125 | Log.e(TAG, "Connection to Google API client has failed");
126 | mResolvingError = false;
127 | Wearable.MessageApi.removeListener(mGoogleApiClient, this);
128 | onDeviceConnectionError();
129 | }
130 | }
131 |
132 | @Override
133 | public void onMessageReceived(final MessageEvent messageEvent) {
134 | Log.d(TAG, "Received Message - " + messageEvent.getPath() + (messageEvent.getData() != null ? ": " + new String(messageEvent.getData()) : ""));
135 | runOnUiThread(new Runnable() {
136 | @Override
137 | public void run() {
138 | onResponseReceived(messageEvent.getPath(), messageEvent.getData() != null ? new String(messageEvent.getData()) : null);
139 | }
140 | });
141 | }
142 |
143 | protected abstract void onResponseReceived(String key, String value);
144 | protected abstract void onReady();
145 | protected void onDeviceConnectionError(){
146 | Intent intent = new Intent(this, ConfirmationActivity.class);
147 | intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, ConfirmationActivity.FAILURE_ANIMATION);
148 | intent.putExtra(ConfirmationActivity.EXTRA_MESSAGE, "Phone Not Available");
149 | startActivity(intent);
150 | }
151 |
152 | /**
153 | * Send message to mobile handheld
154 | */
155 | protected void sendMessage(String key) {
156 | sendMessage(key, null);
157 | }
158 |
159 | /**
160 | * Send message to mobile handheld
161 | */
162 | protected void sendMessage(final String key, final byte[] content) {
163 | if (mNode != null && mGoogleApiClient!= null && mGoogleApiClient.isConnected()) {
164 | Log.d(TAG, "Sending Message - " + key + (content!=null ? ": " + new String(content) : ""));
165 | Wearable.MessageApi.sendMessage(
166 | mGoogleApiClient, mNode.getId(), key, content).setResultCallback(
167 | new ResultCallback() {
168 | @Override
169 | public void onResult(MessageApi.SendMessageResult sendMessageResult) {
170 | if (sendMessageResult.getStatus().isSuccess()) {
171 | Log.d(TAG, "Sending Message - OK!");
172 | }else{
173 | Log.d(TAG, "Sending Message - Error (" + sendMessageResult.getStatus().getStatusCode() + ")");
174 | }
175 | }
176 | }
177 | );
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/NFCActivity.java:
--------------------------------------------------------------------------------
1 | package io.thinger.thinger;
2 |
3 | import android.app.Activity;
4 | import android.app.PendingIntent;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.nfc.NdefMessage;
8 | import android.nfc.NdefRecord;
9 | import android.nfc.NfcAdapter;
10 | import android.nfc.Tag;
11 | import android.nfc.tech.Ndef;
12 | import android.os.AsyncTask;
13 | import android.support.v7.app.AppCompatActivity;
14 | import android.os.Bundle;
15 | import android.util.Log;
16 | import android.widget.TextView;
17 | import android.widget.Toast;
18 |
19 | import java.io.UnsupportedEncodingException;
20 | import java.util.Arrays;
21 |
22 | public class NFCActivity extends AppCompatActivity {
23 | public static final String MIME_TEXT_PLAIN = "text/plain";
24 | public static final String TAG = "NfcDemo";
25 |
26 | private TextView mTextView;
27 | private NfcAdapter mNfcAdapter;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_main);
33 |
34 | mTextView = (TextView) findViewById(R.id.textView_explanation);
35 |
36 | mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
37 |
38 | if (mNfcAdapter == null) {
39 | // Stop here, we definitely need NFC
40 | Toast.makeText(this, R.string.nfc_not_supported, Toast.LENGTH_LONG).show();
41 | finish();
42 | return;
43 |
44 | }
45 |
46 | if (!mNfcAdapter.isEnabled()) {
47 | mTextView.setText("NFC is disabled.");
48 | } else {
49 | //mTextView.setText(R.string.explanation);
50 | }
51 |
52 | handleIntent(getIntent());
53 | }
54 |
55 | @Override
56 | protected void onResume() {
57 | super.onResume();
58 |
59 | /**
60 | * It's important, that the activity is in the foreground (resumed). Otherwise
61 | * an IllegalStateException is thrown.
62 | */
63 | setupForegroundDispatch(this, mNfcAdapter);
64 | }
65 |
66 | @Override
67 | protected void onPause() {
68 | /**
69 | * Call this before onPause, otherwise an IllegalArgumentException is thrown as well.
70 | */
71 | stopForegroundDispatch(this, mNfcAdapter);
72 |
73 | super.onPause();
74 | }
75 |
76 | @Override
77 | protected void onNewIntent(Intent intent) {
78 | /**
79 | * This method gets called, when a new Intent gets associated with the current activity instance.
80 | * Instead of creating a new activity, onNewIntent will be called. For more information have a look
81 | * at the documentation.
82 | *
83 | * In our case this method gets called, when the user attaches a Tag to the device.
84 | */
85 | handleIntent(intent);
86 | }
87 |
88 | private void handleIntent(Intent intent) {
89 | String action = intent.getAction();
90 | if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
91 |
92 | String type = intent.getType();
93 | if (MIME_TEXT_PLAIN.equals(type)) {
94 |
95 | Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
96 | new NdefReaderTask().execute(tag);
97 |
98 | } else {
99 | Log.d(TAG, "Wrong mime type: " + type);
100 | }
101 | } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
102 |
103 | // In case we would still use the Tech Discovered Intent
104 | Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
105 | String[] techList = tag.getTechList();
106 | String searchedTech = Ndef.class.getName();
107 |
108 | for (String tech : techList) {
109 | if (searchedTech.equals(tech)) {
110 | new NdefReaderTask().execute(tag);
111 | break;
112 | }
113 | }
114 | }
115 | }
116 |
117 | /**
118 | * Background task for reading the data. Do not block the UI thread while reading.
119 | *
120 | * @author Ralf Wondratschek
121 | *
122 | */
123 | private class NdefReaderTask extends AsyncTask {
124 |
125 | @Override
126 | protected String doInBackground(Tag... params) {
127 | Tag tag = params[0];
128 |
129 | Ndef ndef = Ndef.get(tag);
130 | if (ndef == null) {
131 | // NDEF is not supported by this Tag.
132 | return null;
133 | }
134 |
135 | NdefMessage ndefMessage = ndef.getCachedNdefMessage();
136 |
137 | NdefRecord[] records = ndefMessage.getRecords();
138 | for (NdefRecord ndefRecord : records) {
139 | if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
140 | try {
141 | return readText(ndefRecord);
142 | } catch (UnsupportedEncodingException e) {
143 | Log.e(TAG, "Unsupported Encoding", e);
144 | }
145 | }
146 | }
147 |
148 | return null;
149 | }
150 |
151 | private String readText(NdefRecord record) throws UnsupportedEncodingException {
152 | /*
153 | * See NFC forum specification for "Text Record Type Definition" at 3.2.1
154 | *
155 | * http://www.nfc-forum.org/specs/
156 | *
157 | * bit_7 defines encoding
158 | * bit_6 reserved for future use, must be 0
159 | * bit_5..0 length of IANA language code
160 | */
161 |
162 | byte[] payload = record.getPayload();
163 |
164 | // Get the Text Encoding
165 | String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
166 |
167 | // Get the Language Code
168 | int languageCodeLength = payload[0] & 0063;
169 |
170 | // String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
171 | // e.g. "en"
172 |
173 | // Get the Text
174 | return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
175 | }
176 |
177 | @Override
178 | protected void onPostExecute(String result) {
179 | if (result != null) {
180 | mTextView.setText("Read content: " + result);
181 | }
182 | }
183 | }
184 |
185 | /**
186 | * @param activity The corresponding {@link Activity} requesting the foreground dispatch.
187 | * @param adapter The {@link NfcAdapter} used for the foreground dispatch.
188 | */
189 | public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
190 | final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
191 | intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
192 |
193 | final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
194 |
195 | IntentFilter[] filters = new IntentFilter[1];
196 | String[][] techList = new String[][]{};
197 |
198 | // Notice that this is the same filter as in our manifest.
199 | filters[0] = new IntentFilter();
200 | filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
201 | filters[0].addCategory(Intent.CATEGORY_DEFAULT);
202 | try {
203 | filters[0].addDataType(MIME_TEXT_PLAIN);
204 | } catch (IntentFilter.MalformedMimeTypeException e) {
205 | throw new RuntimeException("Check your mime type.");
206 | }
207 | adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
208 | }
209 |
210 | public static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) {
211 | adapter.disableForegroundDispatch(activity);
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/DevicesActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.content.Intent;
29 | import android.content.pm.PackageManager;
30 | import android.os.Bundle;
31 | import android.support.design.widget.FloatingActionButton;
32 | import android.support.v4.app.ActivityCompat;
33 | import android.support.v4.content.ContextCompat;
34 | import android.support.v7.app.AppCompatActivity;
35 | import android.support.v7.view.ActionMode;
36 | import android.support.v7.widget.LinearLayoutManager;
37 | import android.support.v7.widget.RecyclerView;
38 | import android.view.Menu;
39 | import android.view.MenuInflater;
40 | import android.view.MenuItem;
41 | import android.view.View;
42 | import android.widget.Toast;
43 |
44 | import io.thinger.thinger.model.Device;
45 | import io.thinger.thinger.model.DeviceFromToken;
46 | import io.thinger.thinger.model.DeviceToken;
47 |
48 | public class DevicesActivity extends AppCompatActivity implements DeviceListAdapter.OnDeviceTokenClicked {
49 |
50 | private static final int MY_PERMISSIONS_REQUEST_CAMERA_ACCESS = 1;
51 | private RecyclerView mRecyclerView;
52 | private DeviceListAdapter mAdapter;
53 | private RecyclerView.LayoutManager mLayoutManager;
54 |
55 | @Override
56 | protected void onCreate(Bundle savedInstanceState) {
57 | super.onCreate(savedInstanceState);
58 | setContentView(R.layout.activity_devices);
59 |
60 | mRecyclerView = (RecyclerView) findViewById(R.id.devices_recycler_view);
61 | mLayoutManager = new LinearLayoutManager(this);
62 | mRecyclerView.setLayoutManager(mLayoutManager);
63 |
64 | mAdapter = new DeviceListAdapter(this);
65 | mRecyclerView.setAdapter(mAdapter);
66 |
67 | FloatingActionButton addButton = (FloatingActionButton) findViewById(R.id.add_device);
68 | addButton.setOnClickListener(new View.OnClickListener() {
69 | public void onClick(View v) {
70 | // Here, thisActivity is the current activity
71 | if (ContextCompat.checkSelfPermission(DevicesActivity.this,
72 | android.Manifest.permission.CAMERA)
73 | != PackageManager.PERMISSION_GRANTED) {
74 |
75 | ActivityCompat.requestPermissions(DevicesActivity.this,
76 | new String[]{android.Manifest.permission.CAMERA},
77 | MY_PERMISSIONS_REQUEST_CAMERA_ACCESS);
78 |
79 | }else{
80 | Intent cameraIntent = new Intent(getApplicationContext(), QRScannerActivity.class);
81 | startActivityForResult(cameraIntent, 0);
82 | }
83 | }
84 | });
85 |
86 | // read items from database and fill adapter
87 | mAdapter.addDevices(DeviceFromToken.getDeviceTokens());
88 | }
89 |
90 | @Override
91 | public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
92 | switch (requestCode) {
93 | case MY_PERMISSIONS_REQUEST_CAMERA_ACCESS: {
94 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
95 | Intent cameraIntent = new Intent(getApplicationContext(), QRScannerActivity.class);
96 | startActivityForResult(cameraIntent, 0);
97 | } else {
98 | Toast.makeText(this, R.string.camera_qr_access_error, Toast.LENGTH_SHORT).show();
99 | }
100 | return;
101 | }
102 | }
103 | }
104 |
105 | @Override
106 | public boolean onCreateOptionsMenu(Menu menu) {
107 | // Inflate the menu; this adds items to the action bar if it is present.
108 | getMenuInflater().inflate(R.menu.menu_devices, menu);
109 | return true;
110 | }
111 |
112 | @Override
113 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
114 | if(requestCode == 0){
115 | if(resultCode == RESULT_OK){
116 | DeviceToken device = DeviceToken.parse(data.getStringExtra("token"));
117 |
118 | if(device!=null){
119 | mAdapter.addDevice(DeviceFromToken.createDeviceFromToken(device));
120 | }else{
121 | // TODO SHOW ERROR
122 | }
123 | }
124 | }
125 | super.onActivityResult(requestCode, resultCode, data);
126 | }
127 |
128 | @Override
129 | public boolean onOptionsItemSelected(MenuItem item) {
130 | // Handle action bar item clicks here. The action bar will
131 | // automatically handle clicks on the Home/Up button, so long
132 | // as you specify a parent activity in AndroidManifest.xml.
133 | int id = item.getItemId();
134 |
135 | //noinspection SimplifiableIfStatement
136 | if (id == R.id.action_settings) {
137 | return true;
138 | }
139 |
140 | return super.onOptionsItemSelected(item);
141 | }
142 |
143 | @Override
144 | public void onDeviceTokenClick(Device token) {
145 | Intent devicesIntent = new Intent(getApplicationContext(), ThingerDevice.class);
146 | devicesIntent.putExtra("token", ((DeviceFromToken) token).getJWTToken());
147 | startActivity(devicesIntent);
148 | }
149 |
150 | private ActionMode mActionMode;
151 | private Device mActionModeDevice;
152 |
153 |
154 | @Override
155 | public void onDeviceTokenLongClick(Device token) {
156 | if (mActionMode != null) {
157 | return;
158 | }
159 |
160 | mActionModeDevice = token;
161 |
162 | // Start the CAB using the ActionMode.Callback defined above
163 | mActionMode = startSupportActionMode(mActionModeCallback);
164 | }
165 |
166 | private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
167 |
168 | // Called when the action mode is created; startActionMode() was called
169 | @Override
170 | public boolean onCreateActionMode(ActionMode mode, Menu menu) {
171 | // Inflate a menu resource providing context menu items
172 | MenuInflater inflater = mode.getMenuInflater();
173 | inflater.inflate(R.menu.devices_options, menu);
174 | return true;
175 | }
176 |
177 | // Called each time the action mode is shown. Always called after onCreateActionMode, but
178 | // may be called multiple times if the mode is invalidated.
179 | @Override
180 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
181 | return false; // Return false if nothing is done
182 | }
183 |
184 | // Called when the user selects a contextual menu item
185 | @Override
186 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
187 | switch (item.getItemId()) {
188 | case R.id.remove_device:
189 | mAdapter.remove(mActionModeDevice);
190 | mode.finish(); // Action picked, so close the CAB
191 | return true;
192 | default:
193 | return false;
194 | }
195 | }
196 |
197 | // Called when the user exits the action mode
198 | @Override
199 | public void onDestroyActionMode(ActionMode mode) {
200 | mActionMode = null;
201 | mActionModeDevice = null;
202 | }
203 | };
204 | }
--------------------------------------------------------------------------------
/wear/src/main/java/io/thinger/thinger/DeviceActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.content.Context;
29 | import android.content.Intent;
30 | import android.os.Bundle;
31 | import android.support.wearable.activity.ConfirmationActivity;
32 | import android.support.wearable.view.DelayedConfirmationView;
33 | import android.support.wearable.view.ProgressSpinner;
34 | import android.support.wearable.view.WatchViewStub;
35 | import android.support.wearable.view.WearableListView;
36 | import android.view.LayoutInflater;
37 | import android.view.View;
38 | import android.view.ViewGroup;
39 | import android.widget.TextView;
40 |
41 | import com.google.gson.JsonElement;
42 | import com.google.gson.JsonObject;
43 | import com.google.gson.JsonParser;
44 |
45 | import java.util.ArrayList;
46 | import java.util.List;
47 | import java.util.Map;
48 |
49 | import io.thinger.wearinterface.WearDeviceInfo;
50 | import io.thinger.wearinterface.WearDeviceResource;
51 | import io.thinger.wearinterface.WearMessages;
52 |
53 | public class DeviceActivity extends MessageActivity implements WearableListView.ClickListener, DelayedConfirmationView.DelayedConfirmationListener {
54 | private WearableListView mListView;
55 | public static String TAG = "DeviceActivity";
56 | private WearDeviceInfo deviceInfo;
57 | private ProgressSpinner spinner;
58 | private MyAdapter mAdapter;
59 | private TextView mHeader;
60 |
61 | @Override
62 | protected void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 |
65 | Bundle extras = getIntent().getExtras();
66 | if (extras != null) {
67 | deviceInfo = (WearDeviceInfo) extras.getSerializable("device");
68 | }
69 |
70 | mAdapter = new MyAdapter(this);
71 |
72 | setContentView(R.layout.activity_device);
73 | final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
74 | stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
75 | @Override
76 | public void onLayoutInflated(WatchViewStub stub) {
77 | mHeader = (TextView) stub.findViewById(R.id.header);
78 | mHeader.setText(deviceInfo.getDevice());
79 | mListView = (WearableListView) stub.findViewById(R.id.listViewAPI);
80 | mListView.setAdapter(mAdapter);
81 | mListView.setClickListener(DeviceActivity.this);
82 |
83 | mListView.addOnScrollListener(new WearableListView.OnScrollListener() {
84 | @Override
85 | public void onScroll(int i) {
86 | mHeader.setY(mHeader.getY() - i);
87 | }
88 |
89 | @Override
90 | public void onAbsoluteScrollChange(int i) {
91 |
92 | }
93 |
94 | @Override
95 | public void onScrollStateChanged(int i) {
96 | }
97 |
98 | @Override
99 | public void onCentralPositionChanged(int i) {
100 | }
101 | });
102 |
103 | spinner = (ProgressSpinner) stub.findViewById(R.id.spinner);
104 | }
105 | });
106 | }
107 |
108 | @Override
109 | protected void onReady() {
110 | mAdapter.clear();
111 | spinner.setVisibility(View.VISIBLE);
112 | // send a message to get the devices
113 | sendMessage(WearMessages.GET_DEVICE_API, deviceInfo.serializeToJson());
114 | }
115 |
116 | @Override
117 | protected void onDeviceConnectionError() {
118 |
119 | }
120 |
121 | void addResourcesFromAPIResponse(JsonObject object, String parent){
122 | if(object == null) return;
123 | if(parent == null) parent = "";
124 | for(Map.Entry entry : object.entrySet()){
125 | // create a new resource
126 | String res = parent + entry.getKey();
127 | final DeviceResourceDescription resource = new DeviceResourceDescription(res, entry.getValue());
128 |
129 | // only add those resources with some functionality
130 | if(resource.getResourceType()!=DeviceResourceDescription.ResourceType.NONE){
131 | mAdapter.addResource(resource);
132 | }
133 |
134 | // keep iterating if resource has sub-resources
135 | JsonObject value = entry.getValue().getAsJsonObject();
136 | if(value.has("/")){
137 | addResourcesFromAPIResponse(value.getAsJsonObject("/"), res + "/");
138 | }
139 | }
140 | }
141 |
142 | @Override
143 | protected void onResponseReceived(final String key, final String value) {
144 | if(key.equals(WearMessages.GET_DEVICE_API_ERROR)){
145 | Intent intent = new Intent(this, ConfirmationActivity.class);
146 | intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, ConfirmationActivity.FAILURE_ANIMATION);
147 | intent.putExtra(ConfirmationActivity.EXTRA_MESSAGE, "Device Not Available");
148 | startActivityForResult(intent, 0);
149 | }else if(key.equals(WearMessages.GET_DEVICE_API_OK)){
150 | spinner.setVisibility(View.GONE);
151 | JsonElement jsonElement = new JsonParser().parse(value);
152 | addResourcesFromAPIResponse(jsonElement.getAsJsonObject(), null);
153 | }
154 | }
155 |
156 | @Override
157 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
158 | finish();
159 | }
160 |
161 | @Override
162 | public void onClick(WearableListView.ViewHolder viewHolder) {
163 | DeviceResourceDescription device = mAdapter.getResource(viewHolder.getAdapterPosition());
164 | WearDeviceResource deviceResource = new WearDeviceResource(deviceInfo, device.getResourceName(), device.getResourceType().getNumber());
165 | Intent intent = new Intent(this, ResourceControlActivity.class);
166 | intent.putExtra("resource", deviceResource);
167 | startActivity(intent);
168 | }
169 |
170 | @Override
171 | public void onTopEmptyRegionClick() {
172 |
173 | }
174 |
175 | @Override
176 | public void onTimerFinished(View view) {
177 |
178 | }
179 |
180 | @Override
181 | public void onTimerSelected(View view) {
182 |
183 | }
184 |
185 | private class MyAdapter extends WearableListView.Adapter {
186 | private final LayoutInflater mInflater;
187 | private List resources;
188 |
189 | private MyAdapter(Context context) {
190 | mInflater = LayoutInflater.from(context);
191 | resources = new ArrayList<>();
192 | }
193 |
194 | public void clear(){
195 | resources.clear();
196 | notifyDataSetChanged();
197 | }
198 |
199 | void addResource(DeviceResourceDescription resource){
200 | resources.add(resource);
201 | notifyDataSetChanged();
202 | }
203 |
204 | DeviceResourceDescription getResource(int position){
205 | return resources.get(position);
206 | }
207 |
208 | @Override
209 | public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
210 | return new WearableListView.ViewHolder(mInflater.inflate(R.layout.row_device_resource, null));
211 | }
212 |
213 | @Override
214 | public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
215 | TextView view = (TextView) holder.itemView.findViewById(R.id.textView);
216 | view.setText(resources.get(position).getResourceName());
217 | holder.itemView.setTag(position);
218 | }
219 |
220 | @Override
221 | public int getItemCount() {
222 | return resources.size();
223 | }
224 | }
225 |
226 | /*
227 | @Override
228 | public void onWindowFocusChanged(boolean hasFocus) {
229 | if(hasFocus){
230 | final Animation slide = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slidein_top);
231 | // find the header or cache it elsewhere
232 | mHeader.startAnimation(slide);
233 | }
234 | }*/
235 | }
236 |
--------------------------------------------------------------------------------
/wear/src/main/java/io/thinger/thinger/ResourceControlActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.content.Context;
29 | import android.content.Intent;
30 | import android.os.Bundle;
31 | import android.support.wearable.activity.ConfirmationActivity;
32 | import android.support.wearable.view.CircularButton;
33 | import android.support.wearable.view.ProgressSpinner;
34 | import android.support.wearable.view.WatchViewStub;
35 | import android.view.LayoutInflater;
36 | import android.view.View;
37 | import android.widget.CompoundButton;
38 | import android.widget.LinearLayout;
39 | import android.widget.Switch;
40 | import android.widget.TextView;
41 |
42 | import com.google.gson.JsonElement;
43 | import com.google.gson.JsonObject;
44 |
45 | import io.thinger.wearinterface.WearDeviceResource;
46 | import io.thinger.wearinterface.WearMessages;
47 |
48 | public class ResourceControlActivity extends MessageActivity {
49 |
50 | private TextView mHeader;
51 | private WearDeviceResource mResource;
52 | private ProgressSpinner spinner;
53 | private LinearLayout content;
54 | private LayoutInflater mInflater;
55 |
56 | @Override
57 | protected void onCreate(Bundle savedInstanceState) {
58 | super.onCreate(savedInstanceState);
59 | setContentView(R.layout.activity_resource_control);
60 |
61 | mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
62 |
63 | Bundle extras = getIntent().getExtras();
64 | if (extras != null) {
65 | mResource = (WearDeviceResource) extras.getSerializable("resource");
66 | }
67 |
68 | final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
69 | stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
70 | @Override
71 | public void onLayoutInflated(WatchViewStub stub) {
72 | mHeader = (TextView) stub.findViewById(R.id.header);
73 | mHeader.setText(mResource.getResource());
74 | spinner = (ProgressSpinner) stub.findViewById(R.id.spinner);
75 | content = (LinearLayout) stub.findViewById(R.id.content);
76 | }
77 | });
78 | }
79 |
80 | @Override
81 | protected void onResponseReceived(String key, String value) {
82 | if(key.equals(WearMessages.GET_RESOURCE_API_OK)){
83 | spinner.setVisibility(View.GONE);
84 | mResource.setValue(value);
85 | JsonElement input = mResource.getInput();
86 | JsonElement output = mResource.getOutput();
87 | // only input
88 | if(input!=null && output==null){
89 | setInput(input);
90 | // only output
91 | }else if(output!=null && input==null){
92 | setOutput(output);
93 | }
94 | }else if(key.equals(WearMessages.GET_RESOURCE_API_ERROR)){
95 | spinner.setVisibility(View.GONE);
96 | }else if(key.equals(WearMessages.GET_DEVICE_RESOURCE_OK)){
97 | if(mResource.getType()== DeviceResourceDescription.ResourceType.RUN.getNumber()){
98 | Intent intent = new Intent(this, ConfirmationActivity.class);
99 | intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, ConfirmationActivity.SUCCESS_ANIMATION);
100 | startActivity(intent);
101 | }else{
102 | mResource.setValue(value);
103 | setOutput(mResource.getOutput());
104 | }
105 | }else if(key.equals(WearMessages.GET_DEVICE_RESOURCE_ERROR)){
106 | Intent intent = new Intent(this, ConfirmationActivity.class);
107 | intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, ConfirmationActivity.FAILURE_ANIMATION);
108 | intent.putExtra(ConfirmationActivity.EXTRA_MESSAGE, "Cannot Read the Resource");
109 | startActivity(intent);
110 | }else if(key.equals(WearMessages.POST_DEVICE_RESOURCE_OK)){
111 | Intent intent = new Intent(this, ConfirmationActivity.class);
112 | intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, ConfirmationActivity.SUCCESS_ANIMATION);
113 | startActivity(intent);
114 | }else if(key.equals(WearMessages.POST_DEVICE_RESOURCE_ERROR)){
115 | Intent intent = new Intent(this, ConfirmationActivity.class);
116 | intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, ConfirmationActivity.FAILURE_ANIMATION);
117 | intent.putExtra(ConfirmationActivity.EXTRA_MESSAGE, "Cannot Change the Resource");
118 | startActivity(intent);
119 | }
120 | }
121 |
122 | @Override
123 | protected void onReady() {
124 | if(mResource.getType()==DeviceResourceDescription.ResourceType.RUN.getNumber()){
125 | spinner.setVisibility(View.GONE);
126 | setRun();
127 | }else{
128 | sendMessage(WearMessages.GET_RESOURCE_API, mResource.serializeToJson());
129 | }
130 | }
131 |
132 | private void setInput(JsonElement input){
133 | if(input.isJsonPrimitive()){
134 | if(input.getAsJsonPrimitive().isBoolean()) {
135 | setInput(input.getAsJsonPrimitive().getAsBoolean());
136 | }
137 | }
138 | }
139 |
140 | private void setOutput(JsonElement output){
141 | if(output!=null){
142 | if(output.isJsonPrimitive()){
143 | if(output.getAsJsonPrimitive().isBoolean()){
144 | setOutput(output.getAsJsonPrimitive().getAsBoolean());
145 | }else if(output.getAsJsonPrimitive().isNumber()){
146 | setOutput(output.getAsJsonPrimitive().getAsNumber());
147 | }else if(output.getAsJsonPrimitive().isString()){
148 | setOutput(output.getAsJsonPrimitive().getAsString());
149 | }
150 | }
151 | }
152 | }
153 |
154 | private void setInput(boolean state){
155 | Switch check;
156 | if(content.getChildCount()>0){
157 | check = (Switch) content.getChildAt(0);
158 | check.setChecked(state);
159 | }else{
160 | check = new Switch(this);
161 | content.addView(check);
162 | check.setChecked(state); // do not set later as it will raise a change listener
163 | check.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
164 | @Override
165 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
166 | JsonObject jsonObject = new JsonObject();
167 | jsonObject.addProperty("in", isChecked);
168 | mResource.setValue(jsonObject.toString());
169 | sendMessage(WearMessages.POST_DEVICE_RESOURCE, mResource.serializeToJson());
170 | }
171 | });
172 | }
173 | }
174 |
175 | private void setOutput(String text){
176 | TextView textView;
177 | if(content.getChildCount()>0){
178 | textView = (TextView) content.getChildAt(0);
179 | }else{
180 | textView = new TextView(this);
181 | content.addView(textView);
182 | setOutputClickListener(textView);
183 | }
184 | textView.setText(text);
185 | }
186 |
187 | private void setRun(){
188 | CircularButton circularButton;
189 | if(content.getChildCount()>0){
190 | circularButton = (CircularButton) content.getChildAt(0);
191 | }else{
192 | circularButton = (CircularButton) mInflater.inflate(R.layout.resource_run_view, null);
193 | content.addView(circularButton);
194 | circularButton.setOnClickListener(new View.OnClickListener() {
195 | @Override
196 | public void onClick(View v) {
197 | sendMessage(WearMessages.GET_DEVICE_RESOURCE, mResource.serializeToJson());
198 | }
199 | });
200 | }
201 | }
202 |
203 | private void setOutput(Number number){
204 | TextView textView;
205 | if(content.getChildCount()>0){
206 | textView = (TextView) content.getChildAt(0);
207 | }else{
208 | textView = (TextView) mInflater.inflate(R.layout.resource_number_view, null);
209 | content.addView(textView);
210 | setOutputClickListener(textView);
211 | }
212 | textView.setText(number.toString());
213 | }
214 |
215 | private void setOutput(boolean value){
216 | Switch control;
217 | if(content.getChildCount()>0){
218 | control = (Switch) content.getChildAt(0);
219 | }else{
220 | control = new Switch(this);
221 | control.setEnabled(false);
222 | content.addView(control);
223 | setOutputClickListener(control);
224 | }
225 | control.setChecked(value);
226 | }
227 |
228 | private void setOutputClickListener(View view){
229 | view.setOnClickListener(new View.OnClickListener() {
230 | @Override
231 | public void onClick(View v) {
232 | sendMessage(WearMessages.GET_DEVICE_RESOURCE, mResource.serializeToJson());
233 | }
234 | });
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/ThingerDevice.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger;
27 |
28 | import android.graphics.Rect;
29 | import android.support.v4.widget.SwipeRefreshLayout;
30 | import android.support.v7.app.AppCompatActivity;
31 | import android.os.Bundle;
32 | import android.support.v7.widget.LinearLayoutManager;
33 | import android.support.v7.widget.RecyclerView;
34 | import android.util.Log;
35 | import android.util.TypedValue;
36 | import android.view.Menu;
37 | import android.view.MenuItem;
38 | import android.view.View;
39 |
40 | import com.google.gson.JsonElement;
41 | import com.google.gson.JsonObject;
42 |
43 | import java.io.IOException;
44 | import java.util.List;
45 | import java.util.Map;
46 |
47 | import io.thinger.api.ThingerAPI;
48 | import io.thinger.thinger.model.DeviceToken;
49 | import io.thinger.thinger.views.DeviceResource;
50 | import retrofit2.Call;
51 | import retrofit2.Callback;
52 | import retrofit2.Response;
53 |
54 | public class ThingerDevice extends AppCompatActivity implements DeviceResourceAdapter.OnDeviceResourceClicked {
55 | private DeviceToken token;
56 |
57 | private RecyclerView mRecyclerView;
58 | private DeviceResourceAdapter mAdapter;
59 | private SwipeRefreshLayout mSwipeRefreshLayout;
60 | private RecyclerView.LayoutManager mLayoutManager;
61 |
62 | public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
63 | private int space;
64 |
65 | public SpacesItemDecoration(int space) {
66 | this.space = space;
67 | }
68 |
69 | @Override
70 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
71 | outRect.left = space;
72 | outRect.right = space;
73 | outRect.bottom = space;
74 |
75 | // Add top margin only for the first item to avoid double space between items
76 | if(parent.getChildAdapterPosition(view) == 0)
77 | outRect.top = space;
78 | }
79 | }
80 |
81 | @Override
82 | protected void onCreate(Bundle savedInstanceState) {
83 | super.onCreate(savedInstanceState);
84 | setContentView(R.layout.activity_thinger_device);
85 |
86 | mRecyclerView = (RecyclerView) findViewById(R.id.resources_recycler_view);
87 | mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
88 |
89 | mLayoutManager = new LinearLayoutManager(this);
90 |
91 | int value = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (float) 16, getResources().getDisplayMetrics());
92 |
93 | mRecyclerView.addItemDecoration(new SpacesItemDecoration(value));
94 | mRecyclerView.setLayoutManager(mLayoutManager);
95 |
96 | mAdapter = new DeviceResourceAdapter(this);
97 | mRecyclerView.setAdapter(mAdapter);
98 |
99 | mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
100 | @Override
101 | public void onRefresh() {
102 | loadDeviceAPI();
103 | }
104 | });
105 |
106 | mSwipeRefreshLayout.setEnabled(false);
107 |
108 | token = DeviceToken.parse(getIntent().getExtras().getString("token"));
109 | if(token!=null){
110 | setTitle(token.getDevice());
111 | loadDeviceAPI();
112 | }
113 | }
114 |
115 | void loadDeviceAPI(){
116 | // some log
117 | Log.v("API", "Loading device API with token:\n" + token.toString());
118 | // clear all current resources
119 | mAdapter.clear();
120 | // initialize pending resource request to zero
121 | pendingResources = 0;
122 | // start showing the loading widget
123 | mSwipeRefreshLayout.post(new Runnable() {
124 | @Override
125 | public void run() {
126 | mSwipeRefreshLayout.setRefreshing(true);
127 | }
128 | });
129 | // check if the device token is limited to some resources
130 | List limitedResources = token.getResources();
131 | // we have access to everything, as there is no limited resources in the token
132 | if(limitedResources==null){
133 | Log.v("API", "Using Auth: " + token.getAuthorizationHeader());
134 | Call call = ThingerAPI.getInstance().deviceAPI(token.getAuthorizationHeader(), token.getUser(), token.getDevice());
135 | call.enqueue(new Callback() {
136 | @Override
137 | public void onResponse(Call call, Response response) {
138 | if(response.isSuccessful()){
139 | addResourcesFromAPIResponse(response.body(), "");
140 | }else{
141 | mSwipeRefreshLayout.setRefreshing(false);
142 | // TODO DISPLAY ERROR
143 | try {
144 | Log.e("API", "Error while fetching the query: " + response.errorBody().string());
145 | } catch (IOException e) {
146 | e.printStackTrace();
147 | }
148 | }
149 | }
150 |
151 | @Override
152 | public void onFailure(Call call, Throwable t) {
153 | mSwipeRefreshLayout.setRefreshing(false);
154 | Log.e("API", t.toString());
155 | }
156 | });
157 | // we have access to only some known resources so add them directly
158 | }else{
159 | // there is no any resource available
160 | if(limitedResources.isEmpty()){
161 | mSwipeRefreshLayout.setRefreshing(false);
162 | }else{
163 | for(String resource : limitedResources){
164 | addResourceFromTokenRestriction(resource);
165 | }
166 | }
167 | }
168 | }
169 |
170 | void addResourceFromTokenRestriction(String res){
171 | final DeviceResource resource = new DeviceResource(res);
172 | resource.setDeviceToken(token);
173 | addResource(resource);
174 | }
175 |
176 | private int pendingResources = 0;
177 |
178 | void addResource(final DeviceResource deviceResource){
179 | // avoid querying resources that does not define a function
180 | if(deviceResource.getResourceType()==DeviceResource.ResourceType.NONE) return;
181 |
182 | // fetch the resource api
183 | Call call = ThingerAPI.getInstance().resourceAPI(token.getAuthorizationHeader(), token.getUser(), token.getDevice(), deviceResource.getResourceName());
184 | pendingResources++;
185 | call.enqueue(new Callback() {
186 | @Override
187 | public void onResponse(Call call, Response response) {
188 | if (response.isSuccessful()) {
189 | deviceResource.setJsonObject(response.body());
190 | mAdapter.addResource(deviceResource);
191 | } else {
192 | // TODO DISPLAY ERROR
193 | Log.e("API", "Error while fetching the resource API: " + response.errorBody().toString());
194 | }
195 | pendingResources--;
196 | if(pendingResources<=0){
197 | mSwipeRefreshLayout.setRefreshing(false);
198 | }
199 | }
200 |
201 | @Override
202 | public void onFailure(Call call, Throwable t) {
203 | Log.e("API", "Error while fetching the resource API: " + t.toString());
204 | pendingResources--;
205 | if(pendingResources<=0){
206 | mSwipeRefreshLayout.setRefreshing(false);
207 | }
208 | }
209 | });
210 | }
211 |
212 | void addResourcesFromAPIResponse(JsonObject object, String parent){
213 | if(object == null) return;
214 | if(parent == null) parent = "";
215 | for(Map.Entry entry : object.entrySet()){
216 |
217 | // create a new resource
218 | String res = parent + entry.getKey();
219 | final DeviceResource resource = new DeviceResource(res, entry.getValue());
220 | resource.setDeviceToken(token);
221 | addResource(resource);
222 |
223 | // keep iterating if resource has sub-resources
224 | JsonObject value = entry.getValue().getAsJsonObject();
225 | if(value.has("/")){
226 | addResourcesFromAPIResponse(value.getAsJsonObject("/"), res + "/");
227 | }
228 | }
229 | }
230 |
231 | @Override
232 | public boolean onCreateOptionsMenu(Menu menu) {
233 | // Inflate the menu; this adds items to the action bar if it is present.
234 | getMenuInflater().inflate(R.menu.menu_thinger_device, menu);
235 | return true;
236 | }
237 |
238 | @Override
239 | public boolean onOptionsItemSelected(MenuItem item) {
240 | // Handle action bar item clicks here. The action bar will
241 | // automatically handle clicks on the Home/Up button, so long
242 | // as you specify a parent activity in AndroidManifest.xml.
243 | int id = item.getItemId();
244 |
245 | //noinspection SimplifiableIfStatement
246 | if (id == R.id.refresh_device_resources) {
247 | loadDeviceAPI();
248 | return true;
249 | }
250 |
251 | return super.onOptionsItemSelected(item);
252 | }
253 |
254 | @Override
255 | public void onDeviceTokenClick(DeviceResource token) {
256 |
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/mobile/src/main/java/io/thinger/thinger/views/DeviceResource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2016 THINK BIG LABS S.L.
5 | * Author: alvarolb@gmail.com (Alvaro Luis Bustamante)
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | */
25 |
26 | package io.thinger.thinger.views;
27 |
28 | import android.util.Log;
29 | import android.util.TypedValue;
30 | import android.view.LayoutInflater;
31 | import android.view.View;
32 | import android.view.animation.AnimationUtils;
33 | import android.view.animation.RotateAnimation;
34 | import android.widget.ImageButton;
35 | import android.widget.LinearLayout;
36 | import android.widget.TextView;
37 |
38 | import com.google.gson.JsonElement;
39 | import com.google.gson.JsonObject;
40 | import com.google.gson.JsonParser;
41 | import com.google.gson.JsonPrimitive;
42 |
43 | import io.thinger.api.ThingerAPI;
44 | import io.thinger.thinger.model.DeviceToken;
45 | import retrofit2.Call;
46 | import retrofit2.Callback;
47 | import retrofit2.Response;
48 |
49 | import io.thinger.thinger.R;
50 |
51 | public class DeviceResource {
52 | private String resourceName;
53 | private DeviceToken token;
54 |
55 | private JsonElement input;
56 | private Element inputElements;
57 |
58 | private JsonElement output;
59 | private Element outputElements;
60 |
61 | private ResourceType resourceType;
62 |
63 | public enum ResourceType {
64 | UNKNOWN(-1), NONE(0), RUN(1), PSON_IN(2), PSON_OUT(3), PSON_IN_OUT(4);
65 |
66 | private final int number;
67 |
68 | private ResourceType(int number) {
69 | this.number = number;
70 | }
71 |
72 | public int getNumber() {
73 | return number;
74 | }
75 |
76 | public static ResourceType get(int value){
77 | switch (value){
78 | case -1:
79 | return UNKNOWN;
80 | case 0:
81 | return NONE;
82 | case 1:
83 | return RUN;
84 | case 2:
85 | return PSON_IN;
86 | case 3:
87 | return PSON_OUT;
88 | case 4:
89 | return PSON_IN_OUT;
90 | default:
91 | return UNKNOWN;
92 | }
93 | }
94 | }
95 |
96 | public DeviceResource(String resourceName, JsonElement resourceDescription) {
97 | this.resourceName = resourceName;
98 |
99 | resourceType = ResourceType.NONE;
100 |
101 | if(resourceDescription.isJsonObject()){
102 | JsonObject object = resourceDescription.getAsJsonObject();
103 | if(object.has("fn")){
104 | JsonElement function = object.get("fn");
105 | if(function.isJsonPrimitive()){
106 | JsonPrimitive value = function.getAsJsonPrimitive();
107 | if(value.isNumber()){
108 | resourceType = ResourceType.get(value.getAsInt());
109 | }
110 | }
111 | }
112 | }
113 | }
114 |
115 | public DeviceResource(String resourceName) {
116 | this.resourceName = resourceName;
117 | resourceType = ResourceType.UNKNOWN;
118 | }
119 |
120 | public String getResourceName() {
121 | return resourceName;
122 | }
123 |
124 | public ResourceType getResourceType(){
125 | return resourceType;
126 | }
127 |
128 | public void setDeviceToken(DeviceToken token){
129 | this.token = token;
130 | }
131 |
132 | public void setJsonObject(JsonObject object){
133 |
134 | if(object.has("in")){
135 | input = object.get("in");
136 | }
137 |
138 | if(object.has("out")){
139 | output = object.get("out");
140 | }
141 |
142 | // try to infer resource type if it is unknown based on its input
143 | // useful when whe have obtained resources names from a device token instead of a
144 | // general api request that specifies all functions
145 | if(resourceType== ResourceType.UNKNOWN){
146 | if(input!=null && output==null){
147 | resourceType = ResourceType.PSON_IN;
148 | }else if(input==null && output!=null){
149 | resourceType = ResourceType.PSON_OUT;
150 | }else if(input!=null && output!=null){
151 | resourceType = ResourceType.PSON_IN_OUT;
152 | }else{
153 | resourceType = ResourceType.RUN;
154 | }
155 | }
156 |
157 | Log.d("API", "Resource '" + resourceName + "' function = {" + resourceType + "} input = {" + input + "} output = {" + output + "}" );
158 | }
159 |
160 | private void run(final ImageButton button){
161 |
162 | }
163 |
164 | public void fillView(final View view){
165 | // avoid displaying empty resource (like a parent resource that does not implement anything)
166 | if(resourceType== ResourceType.NONE) return;
167 |
168 | // - get element from your dataset at this position
169 | // - replace the contents of the view with that element
170 | TextView text = (TextView) view.findViewById(R.id.info_text);
171 | TextView apiText = (TextView) view.findViewById(R.id.api_text);
172 | //Button runButton = (Button) view.findViewById(R.id.run_button);
173 | LinearLayout layout = (LinearLayout) view.findViewById(R.id.card_content);
174 | LinearLayout titleContent = (LinearLayout) view.findViewById(R.id.card_header);
175 | LinearLayout contentLayout = (LinearLayout) view.findViewById(R.id.content_layout);
176 |
177 | text.setText(getResourceName());
178 |
179 | layout.removeAllViews();
180 | titleContent.removeAllViews();
181 |
182 | boolean hasInput = input!=null;
183 | if(hasInput && input.isJsonObject() && input.getAsJsonObject().entrySet().isEmpty()){
184 | hasInput = false;
185 | }
186 |
187 | boolean hasOutput = output!=null;
188 | if(hasOutput && output.isJsonObject() && output.getAsJsonObject().entrySet().isEmpty()){
189 | hasOutput = false;
190 | }
191 |
192 | if(hasInput){
193 | // if we have output also it is necessary to differentiate input/output
194 | if(hasOutput) {
195 | LayoutInflater inflater = LayoutInflater.from(layout.getContext());
196 | TextView categoryLayout = (TextView) inflater.inflate(R.layout.card_resource_io_category, null, false);
197 | categoryLayout.setText("Resource Input");
198 | layout.addView(categoryLayout);
199 | }
200 |
201 | // check if the input is a single boolean (and add it to title or content)
202 | if(input.isJsonPrimitive() && input.getAsJsonPrimitive().isBoolean()){
203 | contentLayout.setVisibility(View.GONE);
204 |
205 | float value = 8 * view.getResources().getDisplayMetrics().density;
206 | titleContent.setPadding(0,0, (int) value,0);
207 |
208 | inputElements = Element.createElement(titleContent, input, false);
209 | inputElements.setListener(new Element.ElementListener() {
210 | @Override
211 | public void onElementChanged() {
212 | execute(null);
213 | }
214 | });
215 | }else{
216 | contentLayout.setVisibility(View.VISIBLE);
217 | inputElements = Element.createElement(layout, input, false);
218 |
219 | final ImageButton runButton = new ImageButton(view.getContext());
220 | runButton.setImageResource(R.drawable.ic_action_play);
221 |
222 | final TypedValue outValue = new TypedValue();
223 | view.getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
224 | runButton.setBackgroundResource(outValue.resourceId);
225 |
226 | runButton.setOnClickListener(new View.OnClickListener() {
227 | @Override
228 | public void onClick(View v) {
229 | runButton.setEnabled(false);
230 | runButton.setImageDrawable(view.getResources().getDrawable(R.drawable.ic_action_refreshing));
231 | RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(view.getContext(), R.anim.rotate);
232 | runButton.setBackground(null);
233 | runButton.startAnimation(rotateAnimation);
234 | execute(new RunResourceListener(){
235 | @Override
236 | public void onResourceExecuted(boolean success) {
237 | runButton.clearAnimation();
238 | runButton.setImageResource(R.drawable.ic_action_play);
239 | runButton.setEnabled(true);
240 | runButton.setBackgroundResource(outValue.resourceId);
241 | }
242 | });
243 | }
244 | });
245 |
246 | titleContent.addView(runButton);
247 | }
248 | }
249 |
250 | if(hasOutput){
251 |
252 | contentLayout.setVisibility(View.VISIBLE);
253 |
254 | // if we have input also it is necessary to differentiate input/output
255 | if(hasInput) {
256 | LayoutInflater inflater = LayoutInflater.from(layout.getContext());
257 | TextView categoryLayout = (TextView) inflater.inflate(R.layout.card_resource_io_category, null, false);
258 | categoryLayout.setText("Resource Output");
259 | layout.addView(categoryLayout);
260 |
261 | // it is a only output resource
262 | }else{
263 | final ImageButton refreshButton = new ImageButton(view.getContext());
264 | refreshButton.setImageResource(R.drawable.ic_action_refresh);
265 | final TypedValue outValue = new TypedValue();
266 | view.getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
267 | refreshButton.setBackgroundResource(outValue.resourceId);
268 | titleContent.addView(refreshButton);
269 |
270 | refreshButton.setOnClickListener(new View.OnClickListener() {
271 | @Override
272 | public void onClick(View v) {
273 | refreshButton.setEnabled(false);
274 | refreshButton.setImageResource(R.drawable.ic_action_refreshing);
275 | RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(view.getContext(), R.anim.rotate);
276 | refreshButton.setBackground(null);
277 | refreshButton.startAnimation(rotateAnimation);
278 | execute(new RunResourceListener() {
279 | @Override
280 | public void onResourceExecuted(boolean success) {
281 | refreshButton.clearAnimation();
282 | refreshButton.setImageResource(R.drawable.ic_action_refresh);
283 | refreshButton.setEnabled(true);
284 | refreshButton.setBackgroundResource(outValue.resourceId);
285 | }
286 | });
287 | }
288 | });
289 | }
290 |
291 | outputElements = Element.createElement(layout, output, true);
292 | }
293 |
294 | // that is a running exmaple without input or output
295 | if(!hasInput && !hasOutput){
296 | contentLayout.setVisibility(View.GONE);
297 |
298 | final ImageButton runButton = new ImageButton(view.getContext());
299 | runButton.setImageResource(R.drawable.ic_action_play);
300 |
301 | final TypedValue outValue = new TypedValue();
302 | view.getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
303 | runButton.setBackgroundResource(outValue.resourceId);
304 |
305 | runButton.setOnClickListener(new View.OnClickListener() {
306 | @Override
307 | public void onClick(View v) {
308 | runButton.setEnabled(false);
309 | runButton.setImageDrawable(view.getResources().getDrawable(R.drawable.ic_action_refreshing));
310 | RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(view.getContext(), R.anim.rotate);
311 | runButton.setBackground(null);
312 | runButton.startAnimation(rotateAnimation);
313 | execute(new RunResourceListener(){
314 | @Override
315 | public void onResourceExecuted(boolean success) {
316 | runButton.clearAnimation();
317 | runButton.setImageResource(R.drawable.ic_action_play);
318 | runButton.setEnabled(true);
319 | runButton.setBackgroundResource(outValue.resourceId);
320 | }
321 | });
322 | }
323 | });
324 |
325 | titleContent.addView(runButton);
326 | }
327 | }
328 |
329 | private interface RunResourceListener{
330 | void onResourceExecuted(boolean success);
331 | }
332 |
333 | private void fillOutput(boolean success, JsonElement element){
334 | if(outputElements==null || element==null) return;
335 | if(success && element.isJsonObject()){
336 | JsonObject object = element.getAsJsonObject();
337 | if(object.has("out")){
338 | outputElements.refreshContent(object.get("out"));
339 | }
340 | }
341 | }
342 |
343 | private JsonElement getInput(){
344 | return new JsonParser().parse("{\"in\":" + inputElements.toString() + "}");
345 | }
346 |
347 | public void execute(final RunResourceListener listener){
348 | Call request;
349 |
350 | if(inputElements!=null){
351 | Log.v("API", "Sending request content: " + getInput());
352 | request = ThingerAPI.getInstance().postResource(token.getAuthorizationHeader(), token.getUser(), token.getDevice(), resourceName, getInput());
353 | }else{
354 | request = ThingerAPI.getInstance().getResource(token.getAuthorizationHeader(), token.getUser(), token.getDevice(), resourceName);
355 | }
356 |
357 | request.enqueue(new Callback() {
358 | @Override
359 | public void onResponse(Call call, Response response) {
360 | fillOutput(response.isSuccessful(), response.body());
361 | Log.v("API", "Response " + response.message() + ": " + response.body().toString());
362 | if(listener!=null) listener.onResourceExecuted(response.isSuccessful());
363 | }
364 |
365 | @Override
366 | public void onFailure(Call call, Throwable t) {
367 | Log.e("API", "Error: " + t.toString());
368 | if(listener!=null) listener.onResourceExecuted(false);
369 | }
370 | });
371 | }
372 |
373 |
374 | }
375 |
--------------------------------------------------------------------------------