SimpleClient Android Application change for BLE 82/214382/1
authorsamanway <samanway@samanway.sa.corp.samsungelectronics.net>
Tue, 3 Sep 2019 08:01:03 +0000 (13:31 +0530)
committerSudipto <sudipto.bal@samsung.com>
Fri, 20 Sep 2019 17:16:34 +0000 (22:46 +0530)
This patch is to provide BLE scanning functionality in Android BLE client side.

- First application will start BLE scan for nearby devices.
- Then app will provide list of BLE devices which are advertising.
- Then user will select a device and app will internally connect to it.

https://github.sec.samsung.net/RS7-IOTIVITY/IoTivity/pull/563/commits/6d7c44e1c073d74ff117a456007156214b310f1c
(cherry-picked from 6d7c44e1c073d74ff117a456007156214b310f1c)

Change-Id: I6efd7762ddb33c1e3dee2f2c225c8231d4381b93
Signed-off-by: samanway-dey <samanway.dey@samsung.com>
Signed-off-by: Sudipto <sudipto.bal@samsung.com>
20 files changed:
android/examples/simpleclientblescan/build.gradle [new file with mode: 0644]
android/examples/simpleclientblescan/proguard-rules.pro [new file with mode: 0644]
android/examples/simpleclientblescan/simpleclientblescan.iml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/AndroidManifest.xml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/assets/oic_svr_db_client.json [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/java/org/iotivity/base/examples/Light.java [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/java/org/iotivity/base/examples/SimpleClientBleScan.java [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/drawable/iotivityicon.png [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/drawable/iotivitylogo.png [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/layout/activity_simple_client.xml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/layout/device_name.xml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/mipmap-hdpi/iotivityicon.png [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/mipmap-mdpi/iotivityicon.png [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/mipmap-xhdpi/iotivityicon.png [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/mipmap-xxhdpi/iotivityicon.png [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/values-v21/styles.xml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/values-w820dp/dimens.xml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/values/dimens.xml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/values/strings.xml [new file with mode: 0644]
android/examples/simpleclientblescan/src/main/res/values/styles.xml [new file with mode: 0644]

diff --git a/android/examples/simpleclientblescan/build.gradle b/android/examples/simpleclientblescan/build.gradle
new file mode 100644 (file)
index 0000000..3a70696
--- /dev/null
@@ -0,0 +1,37 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 21
+    buildToolsVersion "20.0.0"
+
+    defaultConfig {
+        applicationId "org.iotivity.base.examples.simpleclientblescan"
+        minSdkVersion 21
+        targetSdkVersion 21
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    lintOptions {
+        abortOnError false
+    }
+}
+repositories {
+    flatDir {
+        dirs "../../android_api/base/build/outputs/aar/"
+    }
+}
+
+try {
+    dependencies {
+        compile ":iotivity-base-${TARGET_ARCH}-${RELEASE}@aar"
+    }
+} catch (all) {
+    print "${ERROR_MSG}"
+    assert all
+}
diff --git a/android/examples/simpleclientblescan/proguard-rules.pro b/android/examples/simpleclientblescan/proguard-rules.pro
new file mode 100644 (file)
index 0000000..d26150c
--- /dev/null
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/rahul/sdk/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/android/examples/simpleclientblescan/simpleclientblescan.iml b/android/examples/simpleclientblescan/simpleclientblescan.iml
new file mode 100644 (file)
index 0000000..65d6cf5
--- /dev/null
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":simpleclientblescan" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="examples" 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=":simpleclientblescan" />
+      </configuration>
+    </facet>
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="SELECTED_BUILD_VARIANT" value="debug" />
+        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
+        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
+        <option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
+        <afterSyncTasks>
+          <task>generateDebugAndroidTestSources</task>
+          <task>generateDebugSources</task>
+        </afterSyncTasks>
+        <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" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
+    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/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/res/resValues/debug" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/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/reports" />
+      <excludeFolder url="file://$MODULE_DIR$/build/test-results" />
+      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" exported="" name="iotivity-base-armeabi-release-" level="project" />
+    <orderEntry type="library" exported="" name="android-android-21" level="project" />
+  </component>
+</module>
diff --git a/android/examples/simpleclientblescan/src/main/AndroidManifest.xml b/android/examples/simpleclientblescan/src/main/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..021c1ec
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.iotivity.base.examples" >
+    <uses-feature android:name="android.hardware.nfc" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.NFC" />
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/iotivityicon"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:screenOrientation="portrait"
+            android:name=".SimpleClientBleScan"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/android/examples/simpleclientblescan/src/main/assets/oic_svr_db_client.json b/android/examples/simpleclientblescan/src/main/assets/oic_svr_db_client.json
new file mode 100644 (file)
index 0000000..c7f5c4e
--- /dev/null
@@ -0,0 +1,83 @@
+{
+    "acl": {
+        "aclist": {
+            "aces": [
+                {
+                    "subjectuuid": "*",
+                    "resources": [
+                        {
+                            "href": "/oic/res",
+                            "rel": "",
+                            "rt": ["oic.wk.res"],
+                            "if": ["oic.if.ll"]
+                        },
+                        {
+                            "href": "/oic/d",
+                            "rel": "",
+                            "rt": ["oic.wk.d"],
+                            "if": ["oic.if.baseline", "oic.if.r"]
+                        },
+                        {
+                            "href": "/oic/p",
+                            "rel": "",
+                            "rt": ["oic.wk.p"],
+                            "if": ["oic.if.baseline", "oic.if.r"]
+                        }
+                    ],
+                    "permission": 2
+                },
+                {
+                    "subjectuuid": "*",
+                    "resources": [
+                        {
+                            "href": "/oic/sec/doxm",
+                            "rel": "",
+                            "rt": ["oic.r.doxm"],
+                            "if": ["oic.if.baseline"]
+                        },
+                        {
+                            "href": "/oic/sec/pstat",
+                            "rel": "",
+                            "rt": ["oic.r.pstat"],
+                            "if": ["oic.if.baseline"]
+                        }
+                    ],
+                    "permission": 2
+                }
+            ]
+        },
+        "rowneruuid" : "32323232-3232-3232-3232-323232323232"
+    },
+    "pstat": {
+        "isop": true,
+        "deviceuuid": "32323232-3232-3232-3232-323232323232",
+        "rowneruuid": "32323232-3232-3232-3232-323232323232",
+        "cm": 0,
+        "tm": 0,
+        "om": 4,
+        "sm": 4
+        },
+    "doxm": {
+        "oxms": [0],
+        "oxmsel": 0,
+        "sct": 1,
+        "owned": true,
+        "deviceuuid": "32323232-3232-3232-3232-323232323232",
+        "devowneruuid": "32323232-3232-3232-3232-323232323232",
+        "rowneruuid": "32323232-3232-3232-3232-323232323232"
+    },
+    "cred": {
+        "creds": [
+            {
+                "credid": 1,
+                "subjectuuid": "31313131-3131-3131-3131-313131313131",
+                "credtype": 1,
+                "privatedata": {
+                    "data": "AAAAAAAAAAAAAAAA",
+                    "encoding": "oic.sec.encoding.raw"
+                }
+            }
+        ],
+        "rowneruuid": "32323232-3232-3232-3232-323232323232"
+    }
+}
\ No newline at end of file
diff --git a/android/examples/simpleclientblescan/src/main/java/org/iotivity/base/examples/Light.java b/android/examples/simpleclientblescan/src/main/java/org/iotivity/base/examples/Light.java
new file mode 100644 (file)
index 0000000..d40c196
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *******************************************************************
+ *
+ * Copyright 2015 Intel Corporation.
+ *
+ *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ *
+ * 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.
+ *
+ *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+package org.iotivity.base.examples;
+
+import org.iotivity.base.OcException;
+import org.iotivity.base.OcRepresentation;
+
+/**
+ * Light
+ * <p/>
+ * This class is used by SimpleClient to create an object representation of a remote light resource
+ * and update the values depending on the server response
+ */
+public class Light {
+    public static final String NAME_KEY =  "name";
+    public static final String STATE_KEY = "state";
+    public static final String POWER_KEY = "power";
+
+    private String mName;
+    private boolean mState;
+    private int mPower;
+
+    public Light() {
+        mName = "";
+        mState = false;
+        mPower = 0;
+    }
+
+    public void setOcRepresentation(OcRepresentation rep) throws OcException {
+        mName = rep.getValue(NAME_KEY);
+        mState = rep.getValue(Light.STATE_KEY);
+        mPower = rep.getValue(Light.POWER_KEY);
+    }
+
+    public OcRepresentation getOcRepresentation() throws OcException {
+        OcRepresentation rep = new OcRepresentation();
+        rep.setValue(NAME_KEY, mName);
+        rep.setValue(STATE_KEY, mState);
+        rep.setValue(POWER_KEY, mPower);
+        return rep;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public void setName(String name) {
+        this.mName = mName;
+    }
+
+    public boolean getState() {
+        return mState;
+    }
+
+    public void setState(boolean state) {
+        this.mState = state;
+    }
+
+    public int getPower() {
+        return mPower;
+    }
+
+    public void setPower(int power) {
+        this.mPower = power;
+    }
+
+    @Override
+    public String toString() {
+        return "\t" + NAME_KEY + ": " + mName +
+                "\n\t" + STATE_KEY + ": " + mState +
+                "\n\t" + POWER_KEY + ": " + mPower;
+    }
+}
diff --git a/android/examples/simpleclientblescan/src/main/java/org/iotivity/base/examples/SimpleClientBleScan.java b/android/examples/simpleclientblescan/src/main/java/org/iotivity/base/examples/SimpleClientBleScan.java
new file mode 100644 (file)
index 0000000..70162bd
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ *******************************************************************
+ *
+ * Copyright 2015 Intel Corporation.
+ *
+ *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ *
+ * 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.
+ *
+ *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+ */
+package org.iotivity.base.examples;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.os.ParcelUuid;
+import android.os.Handler;
+import android.text.TextUtils;
+
+import org.iotivity.base.ErrorCode;
+import org.iotivity.base.ModeType;
+import org.iotivity.base.ObserveType;
+import org.iotivity.base.OcConnectivityType;
+import org.iotivity.base.OcException;
+import org.iotivity.base.OcHeaderOption;
+import org.iotivity.base.OcPlatform;
+import org.iotivity.base.OcRepresentation;
+import org.iotivity.base.OcResource;
+import org.iotivity.base.OcResourceIdentifier;
+import org.iotivity.base.PlatformConfig;
+import org.iotivity.base.QualityOfService;
+import org.iotivity.base.ServiceType;
+
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import java.nio.charset.Charset;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+
+/**
+ * SimpleClientBleScan
+ * <p/>
+ * SimpleClientBleScan is a sample client app which should be started after the simpleServer is started.
+ * It finds resources advertised by the server and calls different operations on it (GET, PUT,
+ * POST, DELETE and OBSERVE).
+ */
+public class SimpleClientBleScan extends Activity implements
+        OcPlatform.OnResourceFoundListener,
+        OcResource.OnGetListener,
+        OcResource.OnPutListener,
+        OcResource.OnPostListener,
+        OcResource.OnObserveListener {
+
+    private Map<OcResourceIdentifier, OcResource> mFoundResources = new HashMap<>();
+    private OcResource mFoundLightResource = null;
+    //local representation of a server's light resource
+    private Light mLight = new Light();
+    //variables related observer
+    private int maxSequenceNumber = 0xFFFFFF;
+    private OcConnectivityType adapterFlag = OcConnectivityType.CT_ADAPTER_IP;
+    //flags related TCP transport test
+    private boolean isRequestFlag = false;
+    private boolean isTCPContained = false;
+
+    /**
+     * A local method to configure and initialize platform, and then search for the light resources.
+     */
+    private void startSimpleClientBleScan(OcConnectivityType type, String device_addr) {
+        Context context = this;
+        adapterFlag = type;
+
+        PlatformConfig platformConfig = new PlatformConfig(
+                this,
+                context,
+                ServiceType.IN_PROC,
+                ModeType.CLIENT,
+                "0.0.0.0", // By setting to "0.0.0.0", it binds to all available interfaces
+                0,         // Uses randomly available port
+                QualityOfService.LOW
+        );
+        msg("Configuring platform.");
+        OcPlatform.Configure(platformConfig);
+
+        try {
+            msg("Finding all resources of type \"core.light\".");
+           msg("Setting Device address: " + device_addr);
+            String requestUri = OcPlatform.WELL_KNOWN_QUERY + "?rt=core.light";
+            OcPlatform.findResource(device_addr,
+                    requestUri,
+                    EnumSet.of(OcConnectivityType.CT_ADAPTER_GATT_BTLE),
+                    this
+            );
+            sleep(1);
+
+            /*Find resource is done twice so that we discover the original resources a second time.
+            These resources will have the same uniqueidentifier (yet be different objects),
+            so that we can verify/show the duplicate-checking code in foundResource(above);
+             */
+            msg("Finding all resources of type \"core.light\" for the second time");
+            OcPlatform.findResource(device_addr,
+                    requestUri,
+                    EnumSet.of(OcConnectivityType.CT_ADAPTER_GATT_BTLE),
+                    this
+            );
+
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Failed to invoke find resource API");
+        }
+
+        printLine();
+    }
+
+    /**
+     * An event handler to be executed whenever a "findResource" request completes successfully
+     *
+     * @param ocResource found resource
+     */
+    @Override
+    public synchronized void onResourceFound(OcResource ocResource) {
+        if (null == ocResource) {
+            msg("Found resource is invalid");
+            return;
+        }
+
+        if (mFoundResources.containsKey(ocResource.getUniqueIdentifier())) {
+            msg("Found a previously seen resource again!");
+        } else {
+            msg("Found resource for the first time on server with ID: " + ocResource.getServerId());
+            mFoundResources.put(ocResource.getUniqueIdentifier(), ocResource);
+        }
+
+        if (null != mFoundLightResource) {
+            if (ocResource.getUri().equals("/a/light")) {
+                if (ocResource.getConnectivityTypeSet().contains(OcConnectivityType.CT_ADAPTER_TCP)) {
+                    msg("Found resource which has TCP transport");
+                    if (isTCPContained == false)
+                    {
+                        isTCPContained = true;
+                        return;
+                    }
+                }
+            }
+            msg("Found another resource, ignoring");
+            return;
+
+        }
+        // Get the resource URI
+        String resourceUri = ocResource.getUri();
+        // Get the resource host address
+        String hostAddress = ocResource.getHost();
+        msg("\tURI of the resource: " + resourceUri);
+        msg("\tHost address of the resource: " + hostAddress);
+        // Get the resource types
+        msg("\tList of resource types: ");
+        for (String resourceType : ocResource.getResourceTypes()) {
+            msg("\t\t" + resourceType);
+        }
+        msg("\tList of resource interfaces:");
+        for (String resourceInterface : ocResource.getResourceInterfaces()) {
+            msg("\t\t" + resourceInterface);
+        }
+        msg("\tList of resource connectivity types:");
+        for (OcConnectivityType connectivityType : ocResource.getConnectivityTypeSet()) {
+            msg("\t\t" + connectivityType);
+        }
+        printLine();
+
+        //In this example we are only interested in the light resources
+        if (resourceUri.equals("/a/light")) {
+            //Assign resource reference to a global variable to keep it from being
+            //destroyed by the GC when it is out of scope.
+            if (OcConnectivityType.CT_ADAPTER_TCP == adapterFlag)
+            {
+                if (ocResource.getConnectivityTypeSet().contains(OcConnectivityType.CT_ADAPTER_TCP))
+                {
+                    msg("set mFoundLightResource which has TCP transport");
+                    mFoundLightResource = ocResource;
+                    // Call a local method which will internally invoke "get" API
+                    getLightResourceRepresentation();
+                    return;
+                }
+            }
+            else
+            {
+                msg("set mFoundLightResource which has UDP transport");
+                mFoundLightResource = ocResource;
+                // Call a local method which will internally invoke "get" API on the foundLightResource
+                getLightResourceRepresentation();
+            }
+        }
+    }
+
+    @Override
+    public synchronized void onFindResourceFailed(Throwable throwable, String uri) {
+        msg("findResource request has failed");
+        Log.e(TAG, throwable.toString());
+    }
+
+    /**
+     * Local method to get representation of a found light resource
+     */
+    private void getLightResourceRepresentation() {
+        msg("Getting Light Representation...");
+
+        Map<String, String> queryParams = new HashMap<>();
+        try {
+            // Invoke resource's "get" API with a OcResource.OnGetListener event
+            // listener implementation
+            sleep(1);
+            mFoundLightResource.get(queryParams, this);
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Error occurred while invoking \"get\" API");
+        }
+    }
+
+    /**
+     * An event handler to be executed whenever a "get" request completes successfully
+     *
+     * @param list             list of the header options
+     * @param ocRepresentation representation of a resource
+     */
+    @Override
+    public synchronized void onGetCompleted(List<OcHeaderOption> list,
+                                            OcRepresentation ocRepresentation) {
+        msg("GET request was successful");
+        msg("Resource URI: " + ocRepresentation.getUri());
+
+        try {
+            //Read attribute values into local representation of a light
+            mLight.setOcRepresentation(ocRepresentation);
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Failed to read the attributes of a light resource");
+        }
+        msg("Light attributes: ");
+        msg(mLight.toString());
+        printLine();
+
+        //Call a local method which will internally invoke put API on the foundLightResource
+        putLightRepresentation();
+    }
+
+    /**
+     * An event handler to be executed whenever a "get" request fails
+     *
+     * @param throwable exception
+     */
+    @Override
+    public synchronized void onGetFailed(Throwable throwable) {
+        if (throwable instanceof OcException) {
+            OcException ocEx = (OcException) throwable;
+            Log.e(TAG, ocEx.toString());
+            ErrorCode errCode = ocEx.getErrorCode();
+            //do something based on errorCode
+            msg("Error code: " + errCode);
+        }
+        msg("Failed to get representation of a found light resource");
+    }
+
+    /**
+     * Local method to put a different state for this light resource
+     */
+    private void putLightRepresentation() {
+        //set new values
+        mLight.setState(true);
+        mLight.setPower(15);
+
+        msg("Putting light representation...");
+        OcRepresentation representation = null;
+        try {
+            representation = mLight.getOcRepresentation();
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Failed to get OcRepresentation from a light");
+        }
+
+        Map<String, String> queryParams = new HashMap<>();
+
+        try {
+            sleep(1);
+            // Invoke resource's "put" API with a new representation, query parameters and
+            // OcResource.OnPutListener event listener implementation
+            mFoundLightResource.put(representation, queryParams, this);
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Error occurred while invoking \"put\" API");
+        }
+    }
+
+    /**
+     * An event handler to be executed whenever a "put" request completes successfully
+     *
+     * @param list             list of the header options
+     * @param ocRepresentation representation of a resource
+     */
+    @Override
+    public synchronized void onPutCompleted(List<OcHeaderOption> list, OcRepresentation ocRepresentation) {
+        msg("PUT request was successful");
+        try {
+            mLight.setOcRepresentation(ocRepresentation);
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Failed to create Light representation");
+        }
+        msg("Light attributes: ");
+        msg(mLight.toString());
+        printLine();
+
+        //Call a local method which will internally invoke post API on the foundLightResource
+        postLightRepresentation();
+    }
+
+    /**
+     * An event handler to be executed whenever a "put" request fails
+     *
+     * @param throwable exception
+     */
+    @Override
+    public synchronized void onPutFailed(Throwable throwable) {
+        if (throwable instanceof OcException) {
+            OcException ocEx = (OcException) throwable;
+            Log.e(TAG, ocEx.toString());
+            ErrorCode errCode = ocEx.getErrorCode();
+            //do something based on errorCode
+            msg("Error code: " + errCode);
+        }
+        msg("Failed to \"put\" a new representation");
+    }
+
+    /**
+     * Local method to post a different state for this light resource
+     */
+    private void postLightRepresentation() {
+        //set new values
+        mLight.setState(false);
+        mLight.setPower(105);
+
+        msg("Posting light representation...");
+        OcRepresentation representation = null;
+        try {
+            representation = mLight.getOcRepresentation();
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Failed to get OcRepresentation from a light");
+        }
+
+        Map<String, String> queryParams = new HashMap<>();
+        try {
+            sleep(1);
+            // Invoke resource's "post" API with a new representation, query parameters and
+            // OcResource.OnPostListener event listener implementation
+            mFoundLightResource.post(representation, queryParams, this);
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Error occurred while invoking \"post\" API");
+        }
+    }
+
+    /**
+     * An event handler to be executed whenever a "post" request completes successfully
+     *
+     * @param list             list of the header options
+     * @param ocRepresentation representation of a resource
+     */
+    @Override
+    public synchronized void onPostCompleted(List<OcHeaderOption> list,
+                                             OcRepresentation ocRepresentation) {
+        msg("POST request was successful");
+        try {
+            if (ocRepresentation.hasAttribute(OcResource.CREATED_URI_KEY)) {
+                msg("\tUri of the created resource: " +
+                        ocRepresentation.getValue(OcResource.CREATED_URI_KEY));
+            } else {
+                mLight.setOcRepresentation(ocRepresentation);
+                msg(mLight.toString());
+            }
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+        }
+
+        //setting new values
+        mLight.setState(true);
+        mLight.setPower(55);
+        msg("Posting again light representation...");
+        OcRepresentation representation2 = null;
+        try {
+            representation2 = mLight.getOcRepresentation();
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Failed to get OcRepresentation from a light");
+        }
+
+        Map<String, String> queryParams = new HashMap<>();
+        try {
+            // Invoke resource's "post" API with a new representation, query parameters and
+            // OcResource.OnPostListener event listener implementation
+            mFoundLightResource.post(representation2, queryParams, onPostListener2);
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Error occurred while invoking \"post\" API");
+        }
+    }
+
+    /**
+     * An event handler to be executed whenever a "post" request fails
+     *
+     * @param throwable exception
+     */
+    @Override
+    public synchronized void onPostFailed(Throwable throwable) {
+        if (throwable instanceof OcException) {
+            OcException ocEx = (OcException) throwable;
+            Log.e(TAG, ocEx.toString());
+            ErrorCode errCode = ocEx.getErrorCode();
+            //do something based on errorCode
+            msg("Error code: " + errCode);
+        }
+        msg("Failed to \"post\" a new representation");
+    }
+
+    /**
+     * Declare and implement a second OcResource.OnPostListener
+     */
+    OcResource.OnPostListener onPostListener2 = new OcResource.OnPostListener() {
+        /**
+         * An event handler to be executed whenever a "post" request completes successfully
+         * @param list             list of the header options
+         * @param ocRepresentation representation of a resource
+         */
+        @Override
+        public synchronized void onPostCompleted(List<OcHeaderOption> list,
+                                                 OcRepresentation ocRepresentation) {
+            msg("Second POST request was successful");
+            try {
+                if (ocRepresentation.hasAttribute(OcResource.CREATED_URI_KEY)) {
+                    msg("\tUri of the created resource: " +
+                            ocRepresentation.getValue(OcResource.CREATED_URI_KEY));
+                } else {
+                    mLight.setOcRepresentation(ocRepresentation);
+                    msg(mLight.toString());
+                }
+            } catch (OcException e) {
+                Log.e(TAG, e.toString());
+            }
+
+            //Call a local method which will internally invoke observe API on the foundLightResource
+            observeFoundLightResource();
+        }
+
+        /**
+         * An event handler to be executed whenever a "post" request fails
+         *
+         * @param throwable exception
+         */
+        @Override
+        public synchronized void onPostFailed(Throwable throwable) {
+            if (throwable instanceof OcException) {
+                OcException ocEx = (OcException) throwable;
+                Log.e(TAG, ocEx.toString());
+                ErrorCode errCode = ocEx.getErrorCode();
+                //do something based on errorCode
+                msg("Error code: " + errCode);
+            }
+            msg("Failed to \"post\" a new representation");
+        }
+    };
+
+    /**
+     * Local method to start observing this light resource
+     */
+    private void observeFoundLightResource() {
+        try {
+            sleep(1);
+            // Invoke resource's "observe" API with a observe type, query parameters and
+            // OcResource.OnObserveListener event listener implementation
+            mFoundLightResource.observe(ObserveType.OBSERVE, new HashMap<String, String>(), this);
+        } catch (OcException e) {
+            Log.e(TAG, e.toString());
+            msg("Error occurred while invoking \"observe\" API");
+        }
+    }
+
+    // holds current number of observations
+    private static int mObserveCount = 0;
+
+    /**
+     * An event handler to be executed whenever a "post" request completes successfully
+     *
+     * @param list             list of the header options
+     * @param ocRepresentation representation of a resource
+     * @param sequenceNumber   sequence number
+     */
+    @Override
+    public synchronized void onObserveCompleted(List<OcHeaderOption> list,
+                                                OcRepresentation ocRepresentation,
+                                                int sequenceNumber) {
+
+        if (sequenceNumber != maxSequenceNumber + 1)
+        {
+            msg("OBSERVE Result:");
+            msg("\tSequenceNumber:" + sequenceNumber);
+            try {
+                mLight.setOcRepresentation(ocRepresentation);
+            } catch (OcException e) {
+                Log.e(TAG, e.toString());
+                msg("Failed to get the attribute values");
+            }
+            msg(mLight.toString());
+
+            if ((++mObserveCount) == 11) {
+                msg("Cancelling Observe...");
+                try {
+                    mFoundLightResource.cancelObserve(QualityOfService.HIGH);
+                } catch (OcException e) {
+                    Log.e(TAG, e.toString());
+                    msg("Error occurred while invoking \"cancelObserve\" API");
+                }
+
+                sleep(10);
+                resetGlobals();
+                if (true == isTCPContained && false == isRequestFlag)
+                {
+                    msg("Start TCP test...");
+                    //startSimpleClientBleScan(OcConnectivityType.CT_ADAPTER_TCP);
+                    isRequestFlag = true;
+                    return;
+                } else if (true == isRequestFlag)
+                {
+                    msg("End TCP test...");
+                    isRequestFlag = false;
+                }
+
+                msg("DONE");
+                //prepare for the next restart of the SimpleClientBleScan
+                //enableStartButton();
+            }
+        }
+    }
+
+    /**
+     * An event handler to be executed whenever a "observe" request fails
+     *
+     * @param throwable exception
+     */
+    @Override
+    public synchronized void onObserveFailed(Throwable throwable) {
+        if (throwable instanceof OcException) {
+            OcException ocEx = (OcException) throwable;
+            Log.e(TAG, ocEx.toString());
+            ErrorCode errCode = ocEx.getErrorCode();
+            //do something based on errorCode
+            msg("Error code: " + errCode);
+        }
+        msg("Observation of the found light resource has failed");
+    }
+
+    //******************************************************************************
+    // End of the OIC specific code
+    //******************************************************************************
+
+    private final static String TAG = SimpleClientBleScan.class.getSimpleName();
+    private TextView mConsoleTextView;
+    private ScrollView mScrollView;
+    private ArrayAdapter<String> mNewDevicesArrayAdapter;
+
+    private BluetoothLeScanner mBluetoothLeScanner;
+    private Handler mHandler = new Handler();
+
+    private ScanCallback mScanCallback = new ScanCallback() {
+        @Override
+        public  void onScanResult(int callbackType, ScanResult result) {
+           Log.d(TAG, "Scan callback called");
+            super.onScanResult(callbackType, result);
+            if( result == null
+                    || result.getDevice() == null)
+           {
+               return;
+           }
+
+            StringBuilder builder = new StringBuilder( result.getDevice().getAddress());
+
+           mNewDevicesArrayAdapter.add(result.getDevice().getName() + "\n" + result.getDevice().getAddress());
+            Log.d(TAG, builder.toString());
+        }
+
+        @Override
+        public  void onBatchScanResults(List<ScanResult> results) {
+           msg("Batch Scan callback called");
+            super.onBatchScanResults(results);
+        }
+
+        @Override
+        public  void onScanFailed(int errorCode) {
+           msg("Scan Failed !");
+            Log.e(TAG, "Discovery onScanFailed: " + errorCode );
+            super.onScanFailed(errorCode);
+       }
+    };
+
+    private  void discover() {
+        msg("Discovery started");
+       Log.i(TAG, "Discover called");
+        List<ScanFilter> filters = new ArrayList<ScanFilter>();
+        ScanFilter filter = new ScanFilter.Builder().setServiceUuid(new ParcelUuid(UUID.fromString("ADE3D529-C784-4F63-A987-EB69F70EE816"))).build();
+        filters.add(filter);
+
+        ScanSettings settings = new ScanSettings.Builder()
+                .setScanMode( ScanSettings.SCAN_MODE_BALANCED)
+                .build();
+
+        mBluetoothLeScanner.startScan(mScanCallback);
+
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public  void run() {
+                mBluetoothLeScanner.stopScan(mScanCallback);
+               msg("Scan Stopped");
+               Log.d(TAG, "Scan Stopped");
+            }
+        }, 10000);
+    }
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_simple_client);
+
+        mConsoleTextView = (TextView) findViewById(R.id.consoleTextView);
+        mConsoleTextView.setMovementMethod(new ScrollingMovementMethod());
+        mScrollView = (ScrollView) findViewById(R.id.scrollView);
+        mScrollView.fullScroll(View.FOCUS_DOWN);
+        final Button scan_button = (Button) findViewById(R.id.scan_button);
+        mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
+
+        final ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
+        newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
+
+        mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
+
+        if (null == savedInstanceState) {
+           scan_button.setOnClickListener(new View.OnClickListener() {
+               @Override
+                public void onClick(View v) {
+                    scan_button.setText("Scan Started");
+                    scan_button.setEnabled(false);
+                    new Thread(new Runnable() {
+                        public void run() {
+                            discover();
+                        }
+                    }).start();
+                }
+            });
+
+            newDevicesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+               @Override
+                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                    String value = newDevicesListView.getItemAtPosition(position).toString();
+                    String lines[] = value.split("\\r?\\n");
+                    startSimpleClientBleScan(OcConnectivityType.CT_ADAPTER_IP, lines[1]);
+                }
+            });
+        } else {
+            String consoleOutput = savedInstanceState.getString("consoleOutputString");
+            mConsoleTextView.setText(consoleOutput);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putString("consoleOutputString", mConsoleTextView.getText().toString());
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+
+        String consoleOutput = savedInstanceState.getString("consoleOutputString");
+        mConsoleTextView.setText(consoleOutput);
+    }
+
+    private void sleep(int seconds) {
+        try {
+            Thread.sleep(seconds * 1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+            Log.e(TAG, e.toString());
+        }
+    }
+
+    private void msg(final String text) {
+        runOnUiThread(new Runnable() {
+            public void run() {
+                mConsoleTextView.append("\n");
+                mConsoleTextView.append(text);
+                mScrollView.fullScroll(View.FOCUS_DOWN);
+            }
+        });
+        Log.i(TAG, text);
+    }
+
+    private void printLine() {
+        msg("------------------------------------------------------------------------");
+    }
+
+    private synchronized void resetGlobals() {
+        mFoundLightResource = null;
+        mFoundResources.clear();
+        mLight = new Light();
+        mObserveCount = 0;
+    }
+
+    @Override
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        Log.d(TAG, "onNewIntent with changes sending broadcast IN ");
+
+        Intent i = new Intent();
+        i.setAction(intent.getAction());
+        i.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES,
+                intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES));
+        sendBroadcast(i);
+        Log.d(TAG, "Initialize Context again resetting");
+    }
+
+}
diff --git a/android/examples/simpleclientblescan/src/main/res/drawable/iotivityicon.png b/android/examples/simpleclientblescan/src/main/res/drawable/iotivityicon.png
new file mode 100644 (file)
index 0000000..e1e4aa7
Binary files /dev/null and b/android/examples/simpleclientblescan/src/main/res/drawable/iotivityicon.png differ
diff --git a/android/examples/simpleclientblescan/src/main/res/drawable/iotivitylogo.png b/android/examples/simpleclientblescan/src/main/res/drawable/iotivitylogo.png
new file mode 100644 (file)
index 0000000..a7d3115
Binary files /dev/null and b/android/examples/simpleclientblescan/src/main/res/drawable/iotivitylogo.png differ
diff --git a/android/examples/simpleclientblescan/src/main/res/layout/activity_simple_client.xml b/android/examples/simpleclientblescan/src/main/res/layout/activity_simple_client.xml
new file mode 100644 (file)
index 0000000..ae38278
--- /dev/null
@@ -0,0 +1,64 @@
+<LinearLayout 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:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context=".SimpleClientBleScan">
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/layout1"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_gravity="center_vertical|center_horizontal">
+        <Button
+            android:id="@+id/scan_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="scan"
+            android:gravity="center"
+            android:layout_marginBottom="5sp"
+            android:layout_marginLeft="2sp"
+            android:layout_marginRight="5sp"
+            android:layout_marginTop="0sp"/>
+    </LinearLayout>
+    <LinearLayout
+       android:id="@+id/layout1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_gravity="center_vertical|center_horizontal">
+        <ListView
+            android:id="@+id/new_devices"
+           android:layout_below="@id/layout1"
+            android:layout_above="@+id/scrollView"
+            android:layout_width="match_parent"
+            android:layout_height="210dp"
+            android:layout_weight="2"
+            android:stackFromBottom="true"
+        />
+        <ScrollView
+            android:layout_below="@id/new_devices"
+            android:layout_above="@+id/imageView"
+            android:layout_width="fill_parent"
+            android:layout_height="210dp"
+            android:fillViewport="true"
+            android:id="@+id/scrollView">
+            <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/consoleTextView" />
+        </ScrollView>
+    </LinearLayout>
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="70dp"
+        android:id="@+id/imageView"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentStart="true"
+        android:src="@drawable/iotivitylogo" />
+</LinearLayout>
diff --git a/android/examples/simpleclientblescan/src/main/res/layout/device_name.xml b/android/examples/simpleclientblescan/src/main/res/layout/device_name.xml
new file mode 100644 (file)
index 0000000..7da622c
--- /dev/null
@@ -0,0 +1,6 @@
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:padding="5dp"
+          android:textSize="18sp"
+    />
diff --git a/android/examples/simpleclientblescan/src/main/res/mipmap-hdpi/iotivityicon.png b/android/examples/simpleclientblescan/src/main/res/mipmap-hdpi/iotivityicon.png
new file mode 100644 (file)
index 0000000..e1e4aa7
Binary files /dev/null and b/android/examples/simpleclientblescan/src/main/res/mipmap-hdpi/iotivityicon.png differ
diff --git a/android/examples/simpleclientblescan/src/main/res/mipmap-mdpi/iotivityicon.png b/android/examples/simpleclientblescan/src/main/res/mipmap-mdpi/iotivityicon.png
new file mode 100644 (file)
index 0000000..2e7bce6
Binary files /dev/null and b/android/examples/simpleclientblescan/src/main/res/mipmap-mdpi/iotivityicon.png differ
diff --git a/android/examples/simpleclientblescan/src/main/res/mipmap-xhdpi/iotivityicon.png b/android/examples/simpleclientblescan/src/main/res/mipmap-xhdpi/iotivityicon.png
new file mode 100644 (file)
index 0000000..afa486b
Binary files /dev/null and b/android/examples/simpleclientblescan/src/main/res/mipmap-xhdpi/iotivityicon.png differ
diff --git a/android/examples/simpleclientblescan/src/main/res/mipmap-xxhdpi/iotivityicon.png b/android/examples/simpleclientblescan/src/main/res/mipmap-xxhdpi/iotivityicon.png
new file mode 100644 (file)
index 0000000..3e6bc6a
Binary files /dev/null and b/android/examples/simpleclientblescan/src/main/res/mipmap-xxhdpi/iotivityicon.png differ
diff --git a/android/examples/simpleclientblescan/src/main/res/values-v21/styles.xml b/android/examples/simpleclientblescan/src/main/res/values-v21/styles.xml
new file mode 100644 (file)
index 0000000..dba3c41
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material.Light">
+    </style>
+</resources>
diff --git a/android/examples/simpleclientblescan/src/main/res/values-w820dp/dimens.xml b/android/examples/simpleclientblescan/src/main/res/values-w820dp/dimens.xml
new file mode 100644 (file)
index 0000000..63fc816
--- /dev/null
@@ -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/android/examples/simpleclientblescan/src/main/res/values/dimens.xml b/android/examples/simpleclientblescan/src/main/res/values/dimens.xml
new file mode 100644 (file)
index 0000000..47c8224
--- /dev/null
@@ -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/android/examples/simpleclientblescan/src/main/res/values/strings.xml b/android/examples/simpleclientblescan/src/main/res/values/strings.xml
new file mode 100644 (file)
index 0000000..ef32bb6
--- /dev/null
@@ -0,0 +1,6 @@
+<resources>
+    <string name="app_name">Simple Client BLE Scan</string>
+
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+</resources>
diff --git a/android/examples/simpleclientblescan/src/main/res/values/styles.xml b/android/examples/simpleclientblescan/src/main/res/values/styles.xml
new file mode 100644 (file)
index 0000000..ff6c9d2
--- /dev/null
@@ -0,0 +1,8 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+    </style>
+
+</resources>