summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.idea/.name1
-rw-r--r--.idea/codeStyleSettings.xml174
-rw-r--r--.idea/compiler.xml23
-rw-r--r--.idea/copyright/profiles_settings.xml3
-rw-r--r--.idea/dictionaries/trygvis.xml10
-rw-r--r--.idea/encodings.xml5
-rw-r--r--.idea/gradle.xml18
-rw-r--r--.idea/misc.xml10
-rw-r--r--.idea/modules.xml10
-rw-r--r--.idea/scopes/scope_settings.xml5
-rw-r--r--.idea/vcs.xml7
-rw-r--r--app/.gitignore1
-rw-r--r--app/app.iml86
-rw-r--r--app/build.gradle24
-rw-r--r--app/proguard-rules.pro17
-rw-r--r--app/src/androidTest/java/no/topi/fiken/display/ApplicationTest.java13
-rw-r--r--app/src/main/AndroidManifest.xml56
-rw-r--r--app/src/main/java/no/topi/fiken/display/Constants.java10
-rw-r--r--app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java255
-rw-r--r--app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java141
-rw-r--r--app/src/main/java/no/topi/fiken/display/DisplayService.java102
-rw-r--r--app/src/main/java/no/topi/fiken/display/ExceptionHandler.java22
-rw-r--r--app/src/main/java/no/topi/fiken/display/MainActivity.java335
-rw-r--r--app/src/main/res/demo/demo/activity_main2.xml36
-rw-r--r--app/src/main/res/demo/values/base-strings.xml31
-rw-r--r--app/src/main/res/demo/values/template-dimens.xml32
-rw-r--r--app/src/main/res/demo/values/template-styles.xml44
-rw-r--r--app/src/main/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 19388 bytes
-rw-r--r--app/src/main/res/layout/actionbar_indeterminate_progress.xml23
-rw-r--r--app/src/main/res/layout/activity_display_control.xml42
-rw-r--r--app/src/main/res/layout/activity_main.xml7
-rw-r--r--app/src/main/res/layout/fragment_main.xml36
-rw-r--r--app/src/main/res/layout/gatt_services_characteristics.xml71
-rw-r--r--app/src/main/res/layout/listitem_device.xml53
-rw-r--r--app/src/main/res/menu/gatt_services.xml29
-rw-r--r--app/src/main/res/menu/main.xml29
-rw-r--r--app/src/main/res/menu/menu_display_control.xml8
-rw-r--r--app/src/main/res/menu/menu_main.xml5
-rw-r--r--app/src/main/res/values-w820dp/dimens.xml6
-rw-r--r--app/src/main/res/values/dimens.xml5
-rw-r--r--app/src/main/res/values/strings.xml32
-rw-r--r--app/src/main/res/values/styles.xml8
-rw-r--r--build.gradle19
-rw-r--r--fiken-display-android.iml19
-rw-r--r--gradle.properties18
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 49896 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xgradlew164
-rw-r--r--gradlew.bat90
-rw-r--r--settings.gradle1
54 files changed, 2148 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..7d5a8b0
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Display \ No newline at end of file
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
new file mode 100644
index 0000000..29ab470
--- /dev/null
+++ b/.idea/codeStyleSettings.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectCodeStyleSettingsManager">
+ <option name="PER_PROJECT_SETTINGS">
+ <value>
+ <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
+ <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
+ <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
+ <value />
+ </option>
+ <option name="IMPORT_LAYOUT_TABLE">
+ <value>
+ <package name="android" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="com" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="junit" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="net" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="org" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="java" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="javax" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="" withSubpackages="true" static="true" />
+ <emptyLine />
+ </value>
+ </option>
+ <option name="RIGHT_MARGIN" value="100" />
+ <AndroidXmlCodeStyleSettings>
+ <option name="USE_CUSTOM_SETTINGS" value="true" />
+ </AndroidXmlCodeStyleSettings>
+ <XML>
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+ </XML>
+ <codeStyleSettings language="XML">
+ <option name="FORCE_REARRANGE_MODE" value="1" />
+ <indentOptions>
+ <option name="CONTINUATION_INDENT_SIZE" value="4" />
+ </indentOptions>
+ <arrangement>
+ <rules>
+ <rule>
+ <match>
+ <AND>
+ <NAME>xmlns:android</NAME>
+ <XML_NAMESPACE>Namespace:</XML_NAMESPACE>
+ </AND>
+ </match>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>xmlns:.*</NAME>
+ <XML_NAMESPACE>Namespace:</XML_NAMESPACE>
+ </AND>
+ </match>
+ <order>BY_NAME</order>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*:id</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*:name</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>name</NAME>
+ <XML_NAMESPACE>^$</XML_NAMESPACE>
+ </AND>
+ </match>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>style</NAME>
+ <XML_NAMESPACE>^$</XML_NAMESPACE>
+ </AND>
+ </match>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*</NAME>
+ <XML_NAMESPACE>^$</XML_NAMESPACE>
+ </AND>
+ </match>
+ <order>BY_NAME</order>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*:layout_width</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*:layout_height</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*:layout_.*</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ <order>BY_NAME</order>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*:width</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ <order>BY_NAME</order>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*:height</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ <order>BY_NAME</order>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*</NAME>
+ <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+ </AND>
+ </match>
+ <order>BY_NAME</order>
+ </rule>
+ <rule>
+ <match>
+ <AND>
+ <NAME>.*</NAME>
+ <XML_NAMESPACE>.*</XML_NAMESPACE>
+ </AND>
+ </match>
+ <order>BY_NAME</order>
+ </rule>
+ </rules>
+ </arrangement>
+ </codeStyleSettings>
+ </value>
+ </option>
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
+ </component>
+</project>
+
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..217af47
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ </component>
+</project>
+
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+<component name="CopyrightManager">
+ <settings default="" />
+</component> \ No newline at end of file
diff --git a/.idea/dictionaries/trygvis.xml b/.idea/dictionaries/trygvis.xml
new file mode 100644
index 0000000..9afac00
--- /dev/null
+++ b/.idea/dictionaries/trygvis.xml
@@ -0,0 +1,10 @@
+<component name="ProjectDictionaryState">
+ <dictionary name="trygvis">
+ <words>
+ <w>fiken</w>
+ <w>rssi</w>
+ <w>trygvis</w>
+ <w>xxxx</w>
+ </words>
+ </dictionary>
+</component> \ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..e206d70
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
+</project>
+
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..736c7b5
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="distributionType" value="DEFAULT_WRAPPED" />
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/app" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..2497215
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/build/classes" />
+ </component>
+</project>
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..342f96e
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
+ <module fileurl="file://$PROJECT_DIR$/fiken-display-android.iml" filepath="$PROJECT_DIR$/fiken-display-android.iml" />
+ </modules>
+ </component>
+</project>
+
diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
new file mode 100644
index 0000000..922003b
--- /dev/null
+++ b/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+<component name="DependencyValidationManager">
+ <state>
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+ </state>
+</component> \ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..275077f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+ </component>
+</project>
+
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/app.iml b/app/app.iml
new file mode 100644
index 0000000..6a4dade
--- /dev/null
+++ b/app/app.iml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="fiken-display-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="android-gradle" name="Android-Gradle">
+ <configuration>
+ <option name="GRADLE_PROJECT_PATH" value=":app" />
+ </configuration>
+ </facet>
+ <facet type="android" name="Android">
+ <configuration>
+ <option name="SELECTED_BUILD_VARIANT" value="debug" />
+ <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+ <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+ <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
+ <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
+ <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
+ <option name="ALLOW_USER_CONFIGURATION" value="false" />
+ <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+ <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+ <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
+ <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
+ <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
+ <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
+ <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
+ </content>
+ <orderEntry type="jdk" jdkName="Android API 18 Platform" jdkType="Android SDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..4073375
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,24 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 18
+ buildToolsVersion "19.1.0"
+
+ defaultConfig {
+ applicationId "no.topi.fiken.display"
+ minSdkVersion 18
+ targetSdkVersion 18
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ runProguard false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..875c86a
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/trygvis/opt/android-sdk-linux/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/app/src/androidTest/java/no/topi/fiken/display/ApplicationTest.java b/app/src/androidTest/java/no/topi/fiken/display/ApplicationTest.java
new file mode 100644
index 0000000..c306301
--- /dev/null
+++ b/app/src/androidTest/java/no/topi/fiken/display/ApplicationTest.java
@@ -0,0 +1,13 @@
+package no.topi.fiken.display;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..35956d8
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="no.topi.fiken.display" >
+
+ <!--
+ Declare this required feature if you want to make the app available to BLE-capable
+ devices only. If you want to make your app available to devices that don't support BLE,
+ you should omit this in the manifest. Instead, determine BLE capability by using
+ PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE)
+ -->
+ <uses-feature
+ android:name="android.hardware.bluetooth_le"
+ android:required="true" />
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+
+ <!--
+ <application android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@android:style/Theme.Holo.Light">
+ <activity android:name=".DeviceScanActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".DeviceControlActivity"/>
+ <service android:name=".BluetoothLeService" android:enabled="true"/>
+ </application>
+ -->
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="no.topi.fiken.display.MainActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="no.topi.fiken.display.DisplayControlActivity"
+ android:label="@string/title_activity_display_control" />
+
+ <service
+ android:name=".DefaultDisplayService"
+ android:enabled="true" />
+ </application>
+
+</manifest>
diff --git a/app/src/main/java/no/topi/fiken/display/Constants.java b/app/src/main/java/no/topi/fiken/display/Constants.java
new file mode 100644
index 0000000..f568e1c
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/Constants.java
@@ -0,0 +1,10 @@
+package no.topi.fiken.display;
+
+import java.util.UUID;
+
+public interface Constants {
+ String TRYGVIS_IO_BASE_UUID = "32D0xxxx-035D-59C5-70D3-BC8E4A1FD83F";
+ UUID TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0001"));
+ UUID TRYGVIS_IO_GAUGE_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0002"));
+ UUID TRYGVIS_IO_LED_UUID = UUID.fromString(TRYGVIS_IO_BASE_UUID.replace("xxxx", "0003"));
+}
diff --git a/app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java b/app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java
new file mode 100644
index 0000000..b1aa2e7
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/DefaultDisplayService.java
@@ -0,0 +1,255 @@
+package no.topi.fiken.display;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.Toast;
+
+public class DefaultDisplayService extends Service implements DisplayService {
+ private final Context context = DefaultDisplayService.this;
+ private final static String TAG = DefaultDisplayService.class.getSimpleName();
+
+ private final IBinder binder = new LocalBinder(this);
+
+ private BluetoothManager mBluetoothManager;
+ private BluetoothAdapter mBluetoothAdapter;
+ private BluetoothGattService displayService;
+ private BluetoothGatt gatt;
+
+ private Handler handler;
+ private int UPDATE_RSSI_DELAY = 1000;
+
+ private Runnable updateRssi = new Runnable() {
+ @Override
+ public void run() {
+ if(gatt != null) {
+ gatt.readRemoteRssi();
+ }
+
+ handler.postDelayed(this, UPDATE_RSSI_DELAY);
+ }
+ };
+
+ public static enum ServiceState {
+ BROKEN,
+ IDLE,
+ SCANNING,
+ CONNECTED,
+ }
+
+ private ServiceState serviceState = ServiceState.BROKEN;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ @Override
+ public void onCreate() {
+ handler = new Handler();
+
+ handler.postDelayed(updateRssi, UPDATE_RSSI_DELAY);
+ }
+
+ public boolean initialize() {
+ // For API level 18 and above, get a reference to BluetoothAdapter through
+ // BluetoothManager.
+ if (mBluetoothManager == null) {
+ mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ if (mBluetoothManager == null) {
+ Log.e(TAG, "Unable to initialize BluetoothManager.");
+ return false;
+ }
+ }
+
+ mBluetoothAdapter = mBluetoothManager.getAdapter();
+ if (mBluetoothAdapter == null) {
+ Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
+ return false;
+ }
+
+ Log.e(TAG, "Bluetooth initialized");
+
+ serviceState = ServiceState.IDLE;
+
+ return true;
+ }
+
+ @Override
+ public boolean connect(final String address) {
+ if (serviceState != ServiceState.IDLE) {
+ if (!(serviceState == ServiceState.CONNECTED && gatt.getDevice().getAddress().equals(address))) {
+ Log.e(TAG, "connect(): Not idle: " + serviceState);
+ return false;
+ }
+
+ Log.i(TAG, "connect(): already connected: " + serviceState);
+ return true;
+ }
+
+ BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+
+ gatt = device.connectGatt(this, false, new BluetoothGattCallback() {
+ @Override
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+// Toast.makeText(context, "Connected", Toast.LENGTH_SHORT).show();
+ if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothGatt.STATE_CONNECTED) {
+ boolean ok = gatt.discoverServices();
+
+ if (!ok) {
+ disconnect();
+ } else {
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), address);
+ intent.putExtra(IntentExtra.CONNECTED.name(), true);
+ sendBroadcast(intent);
+ }
+ } else {
+ Log.w(TAG, "Could not connect to device");
+// Toast.makeText(context, "Could not connect to device", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ Log.i(TAG, "onServicesDiscovered");
+
+ Log.i(TAG, "Constants.TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID = " + Constants.TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID);
+
+ for (BluetoothGattService bluetoothGattService : gatt.getServices()) {
+ Log.i(TAG, "bluetoothGattService.getUuid() = " + bluetoothGattService.getUuid());
+ }
+
+ displayService = gatt.getService(Constants.TRYGVIS_IO_FIKEN_STATUS_PANEL_UUID);
+
+ Log.i(TAG, "service=" + displayService);
+
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), address);
+ intent.putExtra(IntentExtra.DEVICE_IS_DISPLAY.name(), displayService != null);
+ sendBroadcast(intent);
+ }
+
+ @Override
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ Log.i(TAG, "onCharacteristicRead");
+ }
+
+ @Override
+ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ Log.i(TAG, "onCharacteristicWrite");
+ }
+
+ @Override
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ Log.i(TAG, "onCharacteristicChanged");
+ }
+
+ @Override
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ Log.i(TAG, "onDescriptorRead");
+ }
+
+ @Override
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+ Log.i(TAG, "onDescriptorWrite");
+ }
+
+ @Override
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ Log.i(TAG, "onReliableWriteCompleted");
+ }
+
+ @Override
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+ Log.i(TAG, "onReadRemoteRssi, status=" + status + ", rssi=" + rssi);
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), gatt.getDevice().getAddress());
+ intent.putExtra(IntentExtra.RSSI.name(), rssi);
+ sendBroadcast(intent);
+ }
+ }
+ });
+
+ if (gatt != null) {
+ serviceState = ServiceState.CONNECTED;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ if (serviceState != ServiceState.CONNECTED) {
+ Log.d(TAG, "disconnect(): Not connected: " + serviceState);
+ return;
+ }
+
+ serviceState = ServiceState.IDLE;
+
+ if (gatt != null) {
+ try {
+ gatt.disconnect();
+ } catch (Exception e) {
+ Log.w(TAG, "gatt.disconnect()", e);
+ }
+ try {
+ gatt.close();
+ } catch (Exception e) {
+ Log.w(TAG, "gatt.close()", e);
+ }
+ gatt = null;
+ }
+
+ displayService = null;
+ }
+
+ @Override
+ public void startScan() {
+ if (serviceState != ServiceState.IDLE) {
+ Toast.makeText(context, "startScan(): Not idle", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ serviceState = ServiceState.SCANNING;
+
+ mBluetoothAdapter.startLeScan(leScanCallback);
+ }
+
+ @Override
+ public void stopScan() {
+ Log.d(TAG, "stopScan(): stopping scanning");
+
+ if (serviceState != ServiceState.SCANNING) {
+ Log.d(TAG, "stopScan(): not scanning");
+ return;
+ }
+ mBluetoothAdapter.stopLeScan(leScanCallback);
+ serviceState = ServiceState.IDLE;
+ }
+
+ private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
+ @Override
+ public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
+ Log.i(TAG, "onLeScan()");
+ Intent intent = IntentAction.DEVICE_UPDATE.intent();
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), device.getAddress());
+ intent.putExtra(IntentExtra.DEVICE_NAME.name(), device.getName());
+ intent.putExtra(IntentExtra.RSSI.name(), rssi);
+ sendBroadcast(intent);
+ }
+ };
+}
diff --git a/app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java b/app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java
new file mode 100644
index 0000000..1147c0c
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/DisplayControlActivity.java
@@ -0,0 +1,141 @@
+package no.topi.fiken.display;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.Menu;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import static java.lang.String.valueOf;
+import static no.topi.fiken.display.DisplayService.DeviceInfo;
+import static no.topi.fiken.display.DisplayService.IntentAction;
+import static no.topi.fiken.display.DisplayService.IntentExtra;
+import static no.topi.fiken.display.DisplayService.LocalBinder;
+
+public class DisplayControlActivity extends Activity {
+ private final static String TAG = DisplayControlActivity.class.getSimpleName();
+
+ private DisplayService displayService;
+ private DeviceInfo deviceInfo;
+
+ private TextView deviceNameView;
+ private TextView deviceRssiView;
+ private LinearLayout gaugesLayout;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_display_control);
+
+ final Intent intent = getIntent();
+ String deviceAddress = intent.getStringExtra(IntentExtra.DEVICE_ADDRESS.name());
+ deviceInfo = new DeviceInfo(deviceAddress, 0);
+ deviceInfo.name = intent.getStringExtra(IntentExtra.DEVICE_NAME.name());
+
+ Intent displayServiceIntent = new Intent(this, DefaultDisplayService.class);
+ bindService(displayServiceIntent, serviceConnection, BIND_AUTO_CREATE);
+
+ deviceNameView = (TextView) findViewById(R.id.device_name);
+ deviceRssiView = (TextView) findViewById(R.id.device_rssi);
+ gaugesLayout = (LinearLayout) findViewById(R.id.gauges);
+
+ Button disconnectButton = (Button) findViewById(R.id.button_disconnect);
+ disconnectButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ disconnect();
+ }
+ });
+
+ updateValues();
+ }
+
+ private void updateValues() {
+ deviceNameView.setText(deviceInfo.name != null ? deviceInfo.name : getText(R.string.name_unknown));
+ deviceRssiView.setText(getText(R.string.rssi) + ": " + (deviceInfo.rssi != 0 ? valueOf(deviceInfo.rssi) : ""));
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ registerReceiver(displayServiceBroadcastReceiver, IntentAction.ALL_FILTER);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ unregisterReceiver(displayServiceBroadcastReceiver);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ displayService.disconnect();
+ unbindService(serviceConnection);
+ displayService = null;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_display_control, menu);
+ return true;
+ }
+
+ public void disconnect() {
+ if (displayService != null) {
+ displayService.disconnect();
+ }
+
+ finish();
+ }
+
+ private final ServiceConnection serviceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ displayService = ((LocalBinder) service).getService();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ displayService = null;
+ }
+ };
+
+ private final BroadcastReceiver displayServiceBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+
+ IntentAction action = IntentAction.valueOf(intent);
+
+ String deviceAddress = intent.getStringExtra(IntentExtra.DEVICE_ADDRESS.name());
+
+ if (action == IntentAction.DEVICE_UPDATE && deviceInfo.address.equals(deviceAddress)) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ deviceInfo.update(intent);
+
+ if (intent.hasExtra(IntentExtra.CONNECTED.name())) {
+ boolean connected = intent.getBooleanExtra(IntentExtra.CONNECTED.name(), false);
+
+ if (!connected) {
+ finish();
+ }
+ }
+ updateValues();
+ }
+ });
+ }
+ }
+ };
+}
diff --git a/app/src/main/java/no/topi/fiken/display/DisplayService.java b/app/src/main/java/no/topi/fiken/display/DisplayService.java
new file mode 100644
index 0000000..c907138
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/DisplayService.java
@@ -0,0 +1,102 @@
+package no.topi.fiken.display;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+
+public interface DisplayService {
+
+ enum IntentExtra {
+ DEVICE_NAME,
+ DEVICE_ADDRESS,
+ DEVICE_IS_DISPLAY,
+ RSSI,
+ SCANNING,
+ CONNECTED,
+ }
+
+ public static enum IntentAction {
+ DEVICE_UPDATE;
+
+ private final String key = getClass().getName() + "." + name();
+
+ public Intent intent() {
+ return new Intent(key);
+ }
+
+ public static final IntentFilter ALL_FILTER = new IntentFilter() {{
+ for (DefaultDisplayService.IntentAction intentAction : DefaultDisplayService.IntentAction.values()) {
+ addAction(intentAction.key);
+ }
+ }};
+
+ public static IntentAction valueOf(Intent intent) {
+ try {
+ return valueOf(intent.getAction().replaceAll(".*\\.", ""));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+ }
+
+ public class LocalBinder extends Binder {
+ private final DisplayService service;
+
+ public LocalBinder(DisplayService service) {
+ this.service = service;
+ }
+
+ DisplayService getService() {
+ return service;
+ }
+ }
+
+ boolean initialize();
+
+ void startScan();
+
+ void stopScan();
+
+ boolean connect(String address);
+
+ public void disconnect();
+
+ class DeviceInfo {
+ final String address;
+ int rssi = 0;
+ Boolean isDisplay = null;
+ String name;
+
+ DeviceInfo(String address, int rssi) {
+ this.address = address;
+ this.rssi = rssi;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DeviceInfo that = (DeviceInfo) o;
+
+ return address.equals(that.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return address.hashCode();
+ }
+
+ public void update(Intent intent) {
+ if (intent.hasExtra(IntentExtra.DEVICE_IS_DISPLAY.name())) {
+ isDisplay = intent.getBooleanExtra(IntentExtra.DEVICE_IS_DISPLAY.name(), false);
+ }
+ if (intent.hasExtra(IntentExtra.RSSI.name())) {
+ rssi = intent.getIntExtra(IntentExtra.RSSI.name(), 0);
+ }
+ if (intent.hasExtra(IntentExtra.DEVICE_NAME.name())) {
+ name = intent.getStringExtra(IntentExtra.DEVICE_NAME.name());
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/no/topi/fiken/display/ExceptionHandler.java b/app/src/main/java/no/topi/fiken/display/ExceptionHandler.java
new file mode 100644
index 0000000..3d4560b
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/ExceptionHandler.java
@@ -0,0 +1,22 @@
+package no.topi.fiken.display;
+
+import android.util.Log;
+
+public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
+ private final static String TAG = DefaultDisplayService.class.getSimpleName();
+
+ public static final ExceptionHandler EXCEPTION_HANDLER = new ExceptionHandler();
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ Log.e(TAG, "Uncaught", ex);
+
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ }
+ if (ex instanceof Error) {
+ throw (Error) ex;
+ }
+ throw new RuntimeException(ex);
+ }
+}
diff --git a/app/src/main/java/no/topi/fiken/display/MainActivity.java b/app/src/main/java/no/topi/fiken/display/MainActivity.java
new file mode 100644
index 0000000..d9dc073
--- /dev/null
+++ b/app/src/main/java/no/topi/fiken/display/MainActivity.java
@@ -0,0 +1,335 @@
+package no.topi.fiken.display;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.ListActivity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.lang.String.valueOf;
+import static no.topi.fiken.display.DisplayService.IntentAction;
+import static no.topi.fiken.display.DisplayService.IntentExtra;
+import static no.topi.fiken.display.ExceptionHandler.EXCEPTION_HANDLER;
+
+public class MainActivity extends ListActivity {
+ private final static String TAG = MainActivity.class.getSimpleName();
+
+ // Stops scanning after 10 seconds.
+ private static final long SCAN_PERIOD = 3 * 1000;
+
+ private static final int REQUEST_ENABLE_BT = 1;
+
+ private DisplayListAdapter displayList;
+ private Handler handler;
+ private BluetoothAdapter mBluetoothAdapter;
+ private boolean mScanning;
+ private DisplayService displayService;
+ private String deviceToShow;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG, "onCreate");
+ Thread.setDefaultUncaughtExceptionHandler(EXCEPTION_HANDLER);
+ super.onCreate(savedInstanceState);
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.title_devices);
+ }
+ handler = new Handler();
+
+ // Use this check to determine whether BLE is supported on the device. Then you can
+ // selectively disable BLE-related features.
+ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
+ Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
+ // BluetoothAdapter through BluetoothManager.
+ final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ mBluetoothAdapter = bluetoothManager.getAdapter();
+
+ // Checks if Bluetooth is supported on the device.
+ if (mBluetoothAdapter == null) {
+ Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ ServiceConnection serviceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ displayService = ((DisplayService.LocalBinder) service).getService();
+ if (!displayService.initialize()) {
+ finish();
+ }
+
+ startScan();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ stopScan();
+ displayService = null;
+ }
+ };
+
+ Intent displayServiceIntent = new Intent(this, DefaultDisplayService.class);
+ bindService(displayServiceIntent, serviceConnection, BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onResume() {
+ Log.i(TAG, "onResume");
+
+ super.onResume();
+
+ // Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
+ // fire an intent to display a dialog asking the user to grant permission to enable it.
+ if (!mBluetoothAdapter.isEnabled()) {
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+ }
+
+ registerReceiver(displayServiceBroadcastReceiver, IntentAction.ALL_FILTER);
+ }
+
+ @Override
+ protected void onPause() {
+ Log.i(TAG, "onPause");
+
+ super.onPause();
+ stopScan();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.i(TAG, "onActivityResult");
+
+ // User chose not to enable Bluetooth.
+ if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
+ finish();
+ return;
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ Log.i(TAG, "onCreateOptionsMenu");
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+
+ if (!mScanning) {
+ menu.findItem(R.id.menu_stop).setVisible(false);
+ menu.findItem(R.id.menu_scan).setVisible(true);
+ menu.findItem(R.id.menu_refresh).setActionView(null);
+ } else {
+ menu.findItem(R.id.menu_stop).setVisible(true);
+ menu.findItem(R.id.menu_scan).setVisible(false);
+ menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_indeterminate_progress);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ Log.i(TAG, "onOptionsItemSelected");
+
+ switch (item.getItemId()) {
+ case R.id.menu_scan:
+ startScan();
+ break;
+ case R.id.menu_stop:
+ stopScan();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void startScan() {
+ displayList = new DisplayListAdapter();
+ setListAdapter(displayList);
+
+ displayService.startScan();
+
+ // Stops scanning after a pre-defined scan period.
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ displayService.stopScan();
+ invalidateOptionsMenu();
+ }
+ }, SCAN_PERIOD);
+ }
+
+ private void stopScan() {
+ if (displayService != null) {
+ displayService.stopScan();
+ }
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ stopScan();
+
+ DisplayService.DeviceInfo state = displayList.getDevice(position);
+
+ if (!displayService.connect(state.address)) {
+ Toast.makeText(this, "Could not connect to " + state.address, Toast.LENGTH_SHORT).show();
+ } else {
+ deviceToShow = state.address;
+ }
+ }
+
+ private final BroadcastReceiver displayServiceBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ IntentAction action = IntentAction.valueOf(intent);
+
+ String deviceAddress = intent.getStringExtra(IntentExtra.DEVICE_ADDRESS.name());
+
+ if (action == IntentAction.DEVICE_UPDATE && deviceAddress != null) {
+ DisplayService.DeviceInfo device = displayList.getDevice(deviceAddress, true);
+
+ device.update(intent);
+
+ if (intent.hasExtra(IntentExtra.CONNECTED.name())) {
+ boolean connected = intent.getBooleanExtra(IntentExtra.CONNECTED.name(), false);
+
+ if (connected) {
+ if (deviceToShow != null && deviceToShow.equals(device.address)) {
+ Log.i(TAG, "connected to " + deviceToShow);
+ final Intent intent = new Intent(context, DisplayControlActivity.class);
+ intent.putExtra(IntentExtra.DEVICE_ADDRESS.name(), device.address);
+ intent.putExtra(IntentExtra.DEVICE_NAME.name(), device.name);
+ startActivity(intent);
+ }
+ }
+ }
+ displayList.notifyDataSetChanged();
+ }
+
+ if (intent.hasExtra(IntentExtra.SCANNING.name())) {
+ mScanning = intent.getBooleanExtra(IntentExtra.SCANNING.name(), false);
+ invalidateOptionsMenu();
+ }
+ }
+ });
+ }
+ };
+
+ static class ViewHolder {
+ final TextView deviceName;
+ final TextView deviceAddress;
+ final TextView rssi;
+ final TextView isDisplay;
+
+ ViewHolder(TextView deviceName, TextView deviceAddress, TextView rssi, TextView isDisplay) {
+ this.deviceName = deviceName;
+ this.deviceAddress = deviceAddress;
+ this.rssi = rssi;
+ this.isDisplay = isDisplay;
+ }
+ }
+
+ private class DisplayListAdapter extends BaseAdapter {
+ private List<DisplayService.DeviceInfo> devices = new ArrayList<DisplayService.DeviceInfo>();
+ private LayoutInflater inflater = MainActivity.this.getLayoutInflater();
+
+ public DisplayService.DeviceInfo getDevice(int position) {
+ return devices.get(position);
+ }
+
+ public DisplayService.DeviceInfo getDevice(String address, boolean create) {
+ for (DisplayService.DeviceInfo device : devices) {
+ if (device.address.equals(address)) {
+ return device;
+ }
+ }
+
+ DisplayService.DeviceInfo deviceInfo = null;
+ if (create) {
+ deviceInfo = new DisplayService.DeviceInfo(address, 0);
+ devices.add(deviceInfo);
+ }
+ return deviceInfo;
+ }
+
+ @Override
+ public int getCount() {
+ return devices.size();
+ }
+
+ @Override
+ public Object getItem(int i) {
+ return devices.get(i);
+ }
+
+ @Override
+ public long getItemId(int i) {
+ return i;
+ }
+
+ @Override
+ public View getView(int i, View view, ViewGroup viewGroup) {
+ ViewHolder viewHolder;
+
+ if (view == null) {
+ view = inflater.inflate(R.layout.listitem_device, null);
+ viewHolder = new ViewHolder(
+ (TextView) view.findViewById(R.id.device_name),
+ (TextView) view.findViewById(R.id.device_address),
+ (TextView) view.findViewById(R.id.device_rssi),
+ (TextView) view.findViewById(R.id.device_isDisplay));
+ view.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) view.getTag();
+ }
+
+ DisplayService.DeviceInfo state = devices.get(i);
+ if (state.name != null && state.name.length() > 0) {
+ viewHolder.deviceName.setText(state.name);
+ } else {
+ viewHolder.deviceName.setText(R.string.unknown_device);
+ }
+ viewHolder.deviceAddress.setText(state.address);
+
+ viewHolder.rssi.setText(getText(R.string.rssi) + ": " +
+ (state.rssi != 0 ? valueOf(state.rssi) : getText(R.string.rssi_unknown)));
+
+ viewHolder.isDisplay.setText("Is display: " +
+ (state.isDisplay != null ? state.isDisplay : "unknown"));
+ view.setClickable(state.isDisplay != null && state.isDisplay);
+
+ return view;
+ }
+ }
+}
diff --git a/app/src/main/res/demo/demo/activity_main2.xml b/app/src/main/res/demo/demo/activity_main2.xml
new file mode 100644
index 0000000..b48f349
--- /dev/null
+++ b/app/src/main/res/demo/demo/activity_main2.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout style="@style/Widget.SampleMessageTile"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView style="@style/Widget.SampleMessage"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/horizontal_page_margin"
+ android:layout_marginRight="@dimen/horizontal_page_margin"
+ android:layout_marginTop="@dimen/vertical_page_margin"
+ android:layout_marginBottom="@dimen/vertical_page_margin"
+ android:text="@string/intro_message" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/app/src/main/res/demo/values/base-strings.xml b/app/src/main/res/demo/values/base-strings.xml
new file mode 100644
index 0000000..6071269
--- /dev/null
+++ b/app/src/main/res/demo/values/base-strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+<!--
+ <string name="app_name">BluetoothLeGatt</string>
+-->
+ <string name="intro_message">
+ <![CDATA[
+
+
+ This sample demonstrates how to use the Bluetooth LE Generic Attribute Profile (GATT)
+ to transmit arbitrary data between devices.
+
+
+ ]]>
+ </string>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/demo/values/template-dimens.xml b/app/src/main/res/demo/values/template-dimens.xml
new file mode 100644
index 0000000..afacaf5
--- /dev/null
+++ b/app/src/main/res/demo/values/template-dimens.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+ <dimen name="margin_tiny">4dp</dimen>
+ <dimen name="margin_small">8dp</dimen>
+ <dimen name="margin_medium">16dp</dimen>
+ <dimen name="margin_large">32dp</dimen>
+ <dimen name="margin_huge">64dp</dimen>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/demo/values/template-styles.xml b/app/src/main/res/demo/values/template-styles.xml
new file mode 100644
index 0000000..f23e542
--- /dev/null
+++ b/app/src/main/res/demo/values/template-styles.xml
@@ -0,0 +1,44 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <!-- Activity themes -->
+
+ <style name="Theme.Base" parent="android:Theme.Light"/>
+
+ <style name="Theme.Sample" parent="Theme.Base"/>
+
+ <!--
+ <style name="AppTheme" parent="Theme.Sample" />
+ -->
+ <!-- Widget styling -->
+
+ <style name="Widget"/>
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceMedium</item>
+ <item name="android:lineSpacingMultiplier">1.1</item>
+ </style>
+
+ <style name="Widget.SampleMessageTile">
+ <item name="android:background">@drawable/tile</item>
+ <item name="android:shadowColor">#7F000000</item>
+ <item name="android:shadowDy">-3.5</item>
+ <item name="android:shadowRadius">2</item>
+ </style>
+
+</resources>
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/app/src/main/res/layout/actionbar_indeterminate_progress.xml b/app/src/main/res/layout/actionbar_indeterminate_progress.xml
new file mode 100644
index 0000000..c68bc3c
--- /dev/null
+++ b/app/src/main/res/layout/actionbar_indeterminate_progress.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright 2013 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="56dp"
+ android:minWidth="56dp">
+ <ProgressBar android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_gravity="center"/>
+</FrameLayout>
diff --git a/app/src/main/res/layout/activity_display_control.xml b/app/src/main/res/layout/activity_display_control.xml
new file mode 100644
index 0000000..4151184
--- /dev/null
+++ b/app/src/main/res/layout/activity_display_control.xml
@@ -0,0 +1,42 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context="no.topi.fiken.display.DisplayControlActivity">
+
+ <TextView
+ android:id="@+id/device_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="24sp"
+ android:layout_alignParentEnd="true"/>
+
+ <TextView
+ android:id="@+id/device_rssi"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"
+ android:layout_below="@+id/device_name"/>
+
+ <Button
+ android:id="@+id/button_disconnect"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/disconnect"
+ android:layout_below="@+id/device_rssi"
+ />
+
+ <LinearLayout
+ android:id="@+id/gauges"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_below="@+id/button_disconnect"
+ android:layout_alignParentStart="true">
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ead92a8
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,7 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity"
+ tools:ignore="MergeRootFrame"/>
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 0000000..8958013
--- /dev/null
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,36 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+ android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity$PlaceholderFragment">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="New Button"
+ android:id="@+id/button"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentStart="true" />
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_centerVertical="true"
+ android:layout_centerHorizontal="true"
+ android:visibility="gone" />
+
+ <ListView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/listView"
+ android:layout_toStartOf="@+id/progressBar"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentStart="true"
+ android:layout_above="@+id/button"
+ android:layout_alignParentEnd="true" />
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/gatt_services_characteristics.xml b/app/src/main/res/layout/gatt_services_characteristics.xml
new file mode 100644
index 0000000..0156afd
--- /dev/null
+++ b/app/src/main/res/layout/gatt_services_characteristics.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp">
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp">
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_device_address"
+ android:textSize="18sp"/>
+ <Space android:layout_width="5dp"
+ android:layout_height="wrap_content"/>
+ <TextView android:id="@+id/device_address"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"/>
+ </LinearLayout>
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp">
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_state"
+ android:textSize="18sp"/>
+ <Space android:layout_width="5dp"
+ android:layout_height="wrap_content"/>
+ <TextView android:id="@+id/connection_state"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/disconnected"
+ android:textSize="18sp"/>
+ </LinearLayout>
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp">
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_data"
+ android:textSize="18sp"/>
+ <Space android:layout_width="5dp"
+ android:layout_height="wrap_content"/>
+ <TextView android:id="@+id/data_value"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/no_data"
+ android:textSize="18sp"/>
+ </LinearLayout>
+ <ExpandableListView android:id="@+id/gatt_services_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/app/src/main/res/layout/listitem_device.xml b/app/src/main/res/layout/listitem_device.xml
new file mode 100644
index 0000000..54f529e
--- /dev/null
+++ b/app/src/main/res/layout/listitem_device.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/device_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="24sp"/>
+
+ <TextView
+ android:id="@+id/device_address"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"/>
+
+ <TextView
+ android:id="@+id/device_rssi"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"/>
+
+ <TextView
+ android:id="@+id/device_isDisplay"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"/>
+
+<!--
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/connect"
+ android:id="@+id/button2"/>
+-->
+
+</LinearLayout>
diff --git a/app/src/main/res/menu/gatt_services.xml b/app/src/main/res/menu/gatt_services.xml
new file mode 100644
index 0000000..25d64b6
--- /dev/null
+++ b/app/src/main/res/menu/gatt_services.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_refresh"
+ android:checkable="false"
+ android:orderInCategory="1"
+ android:showAsAction="ifRoom"/>
+ <item android:id="@+id/menu_connect"
+ android:title="@string/menu_connect"
+ android:orderInCategory="100"
+ android:showAsAction="ifRoom|withText"/>
+ <item android:id="@+id/menu_disconnect"
+ android:title="@string/menu_disconnect"
+ android:orderInCategory="101"
+ android:showAsAction="ifRoom|withText"/>
+</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml
new file mode 100644
index 0000000..08b604e
--- /dev/null
+++ b/app/src/main/res/menu/main.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_refresh"
+ android:checkable="false"
+ android:orderInCategory="1"
+ android:showAsAction="ifRoom"/>
+ <item android:id="@+id/menu_scan"
+ android:title="@string/menu_scan"
+ android:orderInCategory="100"
+ android:showAsAction="ifRoom|withText"/>
+ <item android:id="@+id/menu_stop"
+ android:title="@string/menu_stop"
+ android:orderInCategory="101"
+ android:showAsAction="ifRoom|withText"/>
+</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/menu_display_control.xml b/app/src/main/res/menu/menu_display_control.xml
new file mode 100644
index 0000000..51a408b
--- /dev/null
+++ b/app/src/main/res/menu/menu_display_control.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context="no.topi.fiken.display.DisplayControlActivity">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"/>
+</menu>
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..87a750e
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,5 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
+ <item android:id="@+id/action_settings" android:title="@string/action_settings"
+ android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0cea3b4
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">Display</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="action_settings">Settings</string>
+ <string name="ble_not_supported">BLE is not supported</string>
+ <string name="label_data">Data:</string>
+ <string name="label_device_address">Device address:</string>
+ <string name="label_state">State:</string>
+ <string name="no_data">No data</string>
+ <string name="connected">Connected</string>
+ <string name="disconnected">Disconnected</string>
+ <string name="title_devices">BLE Device Scan</string>
+ <string name="error_bluetooth_not_supported">Bluetooth not supported.</string>
+ <string name="unknown_device">Unknown device</string>
+ <string name="unknown_characteristic">Unknown characteristic</string>
+ <string name="unknown_service">Unknown service</string>
+ <string name="rssi">RSSI</string>
+ <string name="name_unknown">Unknown name</string>
+ <string name="rssi_unknown">unknown</string>
+ <string name="connect">Connect</string>
+
+ <!-- Menu items -->
+ <string name="menu_connect">Connect</string>
+ <string name="menu_disconnect">Disconnect</string>
+ <string name="menu_scan">Scan</string>
+ <string name="menu_stop">Stop</string>
+ <string name="title_activity_display_control">DisplayControlActivity</string>
+ <string name="disconnect">Disconnect</string>
+
+</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..ff6c9d2
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..2e3fc3e
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.13.2'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/fiken-display-android.iml b/fiken-display-android.iml
new file mode 100644
index 0000000..0bb6048
--- /dev/null
+++ b/fiken-display-android.iml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="java-gradle" name="Java-Gradle">
+ <configuration>
+ <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/.gradle" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true \ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ffaa281
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app'