Ver Fonte

최초 등록

bsshin0626 há 4 anos atrás
commit
79447c0fdc
100 ficheiros alterados com 23550 adições e 0 exclusões
  1. 21 0
      .gitignore
  2. 1 0
      .idea/.name
  3. 6 0
      .idea/vcs.xml
  4. 1 0
      app/.gitignore
  5. 99 0
      app/build.gradle
  6. 55 0
      app/google-services.json
  7. 21 0
      app/proguard-rules.pro
  8. 26 0
      app/src/androidTest/java/kr/co/icontrols/iotplatform/ExampleInstrumentedTest.java
  9. 172 0
      app/src/main/AndroidManifest.xml
  10. 118 0
      app/src/main/assets/bestinhome-agree.html
  11. 72 0
      app/src/main/assets/bestinhome-agree_files/css
  12. 55 0
      app/src/main/java/kr/co/icontrols/iotplatform/AWS/AmazonBLEinfo.java
  13. 33 0
      app/src/main/java/kr/co/icontrols/iotplatform/AWS/AmazonFreeRTOSAgent.java
  14. 90 0
      app/src/main/java/kr/co/icontrols/iotplatform/AWS/AmazonWifiInfo.java
  15. 190 0
      app/src/main/java/kr/co/icontrols/iotplatform/AWS/Util.java
  16. 57 0
      app/src/main/java/kr/co/icontrols/iotplatform/AddDeviceListActivity.java
  17. 363 0
      app/src/main/java/kr/co/icontrols/iotplatform/AddGroupActivity.java
  18. 231 0
      app/src/main/java/kr/co/icontrols/iotplatform/AddHomeActivity.java
  19. 266 0
      app/src/main/java/kr/co/icontrols/iotplatform/AdminMemberActivity.java
  20. 230 0
      app/src/main/java/kr/co/icontrols/iotplatform/AlarmSettingActivity.java
  21. 110 0
      app/src/main/java/kr/co/icontrols/iotplatform/AppInfoActivity.java
  22. 187 0
      app/src/main/java/kr/co/icontrols/iotplatform/ChangePasswordActivity.java
  23. 7 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/AddressInfo.java
  24. 78 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/DeviceGroupInfo.java
  25. 108 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/DeviceInfo.java
  26. 24 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/InviteInfo.java
  27. 18 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/KakaoLocationInfo.java
  28. 10 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/LocationInfo.java
  29. 13 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/MemberInfo.java
  30. 490 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/UserInfo.java
  31. 17 0
      app/src/main/java/kr/co/icontrols/iotplatform/DataClass/WeatherInfo.java
  32. 243 0
      app/src/main/java/kr/co/icontrols/iotplatform/Database/DBUtil.java
  33. 113 0
      app/src/main/java/kr/co/icontrols/iotplatform/Database/DatabaseHelper.java
  34. 377 0
      app/src/main/java/kr/co/icontrols/iotplatform/DeviceActivity.java
  35. 166 0
      app/src/main/java/kr/co/icontrols/iotplatform/DeviceChangeRankActivity.java
  36. 387 0
      app/src/main/java/kr/co/icontrols/iotplatform/DeviceSelectActivity.java
  37. 611 0
      app/src/main/java/kr/co/icontrols/iotplatform/DeviceSettingActivity.java
  38. 5 0
      app/src/main/java/kr/co/icontrols/iotplatform/DragListView/ChangeListener.java
  39. 30 0
      app/src/main/java/kr/co/icontrols/iotplatform/DragListView/ItemViewHolder.java
  40. 166 0
      app/src/main/java/kr/co/icontrols/iotplatform/DragListView/RankListAdapter.java
  41. 62 0
      app/src/main/java/kr/co/icontrols/iotplatform/DragListView/SwipeAndDragHelper.java
  42. 278 0
      app/src/main/java/kr/co/icontrols/iotplatform/FaqActivity.java
  43. 14 0
      app/src/main/java/kr/co/icontrols/iotplatform/ForgotPasswordActivity.java
  44. 186 0
      app/src/main/java/kr/co/icontrols/iotplatform/GroupChangeRankActivity.java
  45. 446 0
      app/src/main/java/kr/co/icontrols/iotplatform/GroupSelectActivity.java
  46. 60 0
      app/src/main/java/kr/co/icontrols/iotplatform/InviteMemberActivity.java
  47. 178 0
      app/src/main/java/kr/co/icontrols/iotplatform/InviteMemberInputActivity.java
  48. 84 0
      app/src/main/java/kr/co/icontrols/iotplatform/IotPlatformApplication.java
  49. 292 0
      app/src/main/java/kr/co/icontrols/iotplatform/JaesilModeActivity.java
  50. 825 0
      app/src/main/java/kr/co/icontrols/iotplatform/LightDetailActivity.java
  51. 1552 0
      app/src/main/java/kr/co/icontrols/iotplatform/LightRegisterDeviceActivity.java
  52. 586 0
      app/src/main/java/kr/co/icontrols/iotplatform/LightRegisterDeviceFinishActivity.java
  53. 62 0
      app/src/main/java/kr/co/icontrols/iotplatform/LightRegisterGuideActivity.java
  54. 713 0
      app/src/main/java/kr/co/icontrols/iotplatform/LoginActivity.java
  55. 1376 0
      app/src/main/java/kr/co/icontrols/iotplatform/MainActivity.java
  56. 180 0
      app/src/main/java/kr/co/icontrols/iotplatform/MainMenu.java
  57. 169 0
      app/src/main/java/kr/co/icontrols/iotplatform/Mqtt/MqttController.java
  58. 688 0
      app/src/main/java/kr/co/icontrols/iotplatform/MyGroupActivity.java
  59. 182 0
      app/src/main/java/kr/co/icontrols/iotplatform/NewPasswordActivity.java
  60. 47 0
      app/src/main/java/kr/co/icontrols/iotplatform/NotificationActivity.java
  61. 147 0
      app/src/main/java/kr/co/icontrols/iotplatform/PermissionActivity.java
  62. 146 0
      app/src/main/java/kr/co/icontrols/iotplatform/PhoneNumChangeActivity.java
  63. 574 0
      app/src/main/java/kr/co/icontrols/iotplatform/PopupActivity.java
  64. 54 0
      app/src/main/java/kr/co/icontrols/iotplatform/PrivacyPolicyActivity.java
  65. 330 0
      app/src/main/java/kr/co/icontrols/iotplatform/ProfileActivity.java
  66. 971 0
      app/src/main/java/kr/co/icontrols/iotplatform/ProfileUserActivity.java
  67. 19 0
      app/src/main/java/kr/co/icontrols/iotplatform/Protocol/SHA256.java
  68. 84 0
      app/src/main/java/kr/co/icontrols/iotplatform/Protocol/bleProtocol.java
  69. 99 0
      app/src/main/java/kr/co/icontrols/iotplatform/Protocol/bleProtocolState.java
  70. 337 0
      app/src/main/java/kr/co/icontrols/iotplatform/RegisterLocationActivity.java
  71. 155 0
      app/src/main/java/kr/co/icontrols/iotplatform/Service/MessagingService.java
  72. 70 0
      app/src/main/java/kr/co/icontrols/iotplatform/SettingActivity.java
  73. 52 0
      app/src/main/java/kr/co/icontrols/iotplatform/SignUpCertificationActivity.java
  74. 76 0
      app/src/main/java/kr/co/icontrols/iotplatform/SignUpFinishActivity.java
  75. 310 0
      app/src/main/java/kr/co/icontrols/iotplatform/SignUpInputActivity.java
  76. 181 0
      app/src/main/java/kr/co/icontrols/iotplatform/SmartGuideActivity.java
  77. 260 0
      app/src/main/java/kr/co/icontrols/iotplatform/SplashActivity.java
  78. 1394 0
      app/src/main/java/kr/co/icontrols/iotplatform/SwitchMapperRegisterActivity.java
  79. 56 0
      app/src/main/java/kr/co/icontrols/iotplatform/SwitchMapperRegisterGuideActivity.java
  80. 356 0
      app/src/main/java/kr/co/icontrols/iotplatform/TestHttpApiActivity.java
  81. 240 0
      app/src/main/java/kr/co/icontrols/iotplatform/TestIotMqttActivity.java
  82. 321 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/DeviceListAdapter.java
  83. 88 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/DeviceSelectAdapter.java
  84. 93 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/FirstTimeLoginAttributesDisplayAdapter.java
  85. 108 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/GroupSelectAdapter.java
  86. 100 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/KaKaoLoctionAdapter.java
  87. 129 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/MemberAdapter.java
  88. 318 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/MyGroupTabPagerAdapter.java
  89. 105 0
      app/src/main/java/kr/co/icontrols/iotplatform/adapter/UserAttributesAdapter.java
  90. 588 0
      app/src/main/java/kr/co/icontrols/iotplatform/cognito/AppHelper.java
  91. 128 0
      app/src/main/java/kr/co/icontrols/iotplatform/cognito/ItemToDisplay.java
  92. 994 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/CommonUtils.java
  93. 126 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/Constants.java
  94. 109 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/CustomDialog.java
  95. 91 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/CustomProgressDialog.java
  96. 40 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/DownloadImageTask.java
  97. 6 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/GetSessionCallback.java
  98. 907 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/HttpApi.java
  99. 120 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/SharedPrefUtil.java
  100. 21 0
      app/src/main/java/kr/co/icontrols/iotplatform/utils/hdcUUID.java

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+/.idea/codeStyles/Project.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/runConfigurations.xml
+/cropper/build
+/.idea/modules/app/google-services.json

+ 1 - 0
.idea/.name

@@ -0,0 +1 @@
+IotPlatform

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 99 - 0
app/build.gradle

@@ -0,0 +1,99 @@
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    signingConfigs {
+        debug {
+            storeFile file("../icontrols_keystore.jks")
+            storePassword "hdc!@icontrols34"
+            keyAlias "debug_key"
+            keyPassword "hdc!@12"
+        }
+        release {
+            storeFile file("../icontrols_keystore.jks")
+            storePassword "hdc!@icontrols34"
+            keyAlias "release_key"
+            keyPassword "hdc!@34"
+        }
+    }
+
+    compileSdkVersion 29
+
+    defaultConfig {
+        applicationId "kr.co.icontrols.iotplatform"
+        minSdkVersion 26
+        targetSdkVersion 29
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        debug {
+            minifyEnabled false
+            signingConfig signingConfigs.debug
+            manifestPlaceholders = [googleMapsKey: 'xxx', kakaoMapKey: '7b07afcee0753baf20590412749bd0a6']
+        }
+        release {
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.release
+            manifestPlaceholders = [googleMapsKey: 'xxx', kakaoMapKey: '7b07afcee0753baf20590412749bd0a6']
+        }
+    }
+    buildToolsVersion '29.0.2'
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+    implementation 'com.google.android.gms:play-services-auth:18.1.0'
+    implementation 'org.jsoup:jsoup:1.11.3'
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+    implementation 'com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.16.+'
+    implementation 'com.google.android.material:material:1.1.0'
+
+    def room_version = "2.2.5"
+    implementation "androidx.room:room-runtime:$room_version"
+    annotationProcessor "androidx.room:room-compiler:$room_version"
+
+    // BEGIN AWS S3
+    def aws_version = "2.16.+"
+    implementation "com.amazonaws:aws-android-sdk-s3:$aws_version"
+
+    // AWS FreeRTOS
+    //implementation project(':amazonfreertossdk')
+    implementation('software.amazon.freertos:amazonfreertossdk:1.1.0')
+    implementation('com.amazonaws:aws-android-sdk-mobile-client:2.13.5')
+    implementation('com.amazonaws:aws-android-sdk-auth-userpools:2.13.5')
+    implementation('com.amazonaws:aws-android-sdk-auth-ui:2.13.5')
+    implementation 'com.amazonaws:aws-android-sdk-iot:2.13.5'
+
+    // Nordic DFU (OTA)
+    implementation 'no.nordicsemi.android:dfu:1.10.4'
+    implementation 'no.nordicsemi.android:log:2.2.0'
+    implementation 'no.nordicsemi.android:ble-livedata:2.2.0'
+    implementation 'no.nordicsemi.android.support.v18:scanner:1.4.3'
+
+    // Add the dependencies for the Firebase Cloud Messaging and Analytics libraries
+    implementation 'com.google.firebase:firebase-messaging:20.2.4'
+    implementation 'com.google.firebase:firebase-analytics:17.5.0'
+
+    //OkHttp
+    implementation 'com.squareup.okhttp3:okhttp:3.9.1'
+
+    //JSON
+    implementation 'org.jsoup:jsoup:1.11.3'
+
+    api "androidx.appcompat:appcompat:$androidXLibraryVersion"
+    implementation project(":cropper")
+}

+ 55 - 0
app/google-services.json

@@ -0,0 +1,55 @@
+{
+  "project_info": {
+    "project_number": "153096101569",
+    "firebase_url": "https://hdci-iot-platform.firebaseio.com",
+    "project_id": "hdci-iot-platform",
+    "storage_bucket": "hdci-iot-platform.appspot.com"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:153096101569:android:2231919ad4b3c044817e40",
+        "android_client_info": {
+          "package_name": "kr.co.icontrols.iotplatform"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "153096101569-eablr6b6pourpns7kvf6mpftvgt7sdld.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "kr.co.icontrols.iotplatform",
+            "certificate_hash": "e1a53d7f4fdfd912facf483545216fc4cb43fe41"
+          }
+        },
+        {
+          "client_id": "153096101569-5gehmcbsrksqev10feuv2ug1akadeim0.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyBx_YSG1AqMK7aBIfCdg-ttSBuFd8Gv4ho"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "153096101569-5gehmcbsrksqev10feuv2ug1akadeim0.apps.googleusercontent.com",
+              "client_type": 3
+            },
+            {
+              "client_id": "153096101569-8npv85bvad4v850s8acbdfog0mcvdb32.apps.googleusercontent.com",
+              "client_type": 2,
+              "ios_info": {
+                "bundle_id": "com.hdc-icontrols.iotapp"
+              }
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 21 - 0
app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
app/src/androidTest/java/kr/co/icontrols/iotplatform/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("kr.co.icontrols.iotplatform", appContext.getPackageName());
+    }
+}

+ 172 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="kr.co.icontrols.iotplatform">
+
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-feature android:name="android.hardware.camera.autofocus" />
+
+    <application
+        android:name=".IotPlatformApplication"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        android:requestLegacyExternalStorage="true"
+        android:usesCleartextTraffic="true">
+
+        <!--
+        android:launchMode="singleTask"
+        android:clearTaskOnLaunch="true"
+        android:stateNotNeeded="true"
+        -->
+
+
+        <activity android:name=".TestIotMqttActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".TestHttpApiActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
+            android:theme="@style/Half_Transparent"/>
+        <activity android:name=".SignUpFinishActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".PhoneNumChangeActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".LightRegisterDeviceFinishActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".JaesilModeActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".AddHomeActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".SwitchMapperRegisterActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".SwitchMapperRegisterGuideActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".DeviceSettingActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".LightDetailActivity"
+        android:screenOrientation="portrait" />
+        <activity android:name=".DeviceSelectActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".DeviceChangeRankActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".RegisterLocationActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".GroupSelectActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".GroupChangeRankActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".AddGroupActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".LightRegisterDeviceActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".LightRegisterGuideActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".InviteMemberInputActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".InviteMemberActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".SmartGuideActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".AdminMemberActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".ProfileUserActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".MyGroupActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".DeviceActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".ChangePasswordActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".ForgotPasswordActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".NewPasswordActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".AppInfoActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".AlarmSettingActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".SettingActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".FaqActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".NotificationActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".ProfileActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".AddDeviceListActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".PrivacyPolicyActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".SignUpCertificationActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".PopupActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/Half_Transparent"
+            android:windowSoftInputMode="adjustResize"/>
+        <activity
+            android:name=".PermissionActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".LoginActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".MainActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".SignUpInputActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name=".SplashActivity"
+            android:screenOrientation="portrait" >
+            <!--
+            android:launchMode="singleTask"
+            android:excludeFromRecents="true"
+            -->
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <!--
+                <category android:name="android.intent.category.HOME" />
+                -->
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".Service.MessagingService">
+            <intent-filter>
+                <action android:name="com.google.firebase.MESSAGING_EVENT" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>

+ 118 - 0
app/src/main/assets/bestinhome-agree.html

@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<!-- saved from url=(0039)https://bestinhome-terms.hdc-smart.com/ -->
+<html lang="ko"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    
+    <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
+    <link href="./bestinhome-agree_files/css" rel="stylesheet">
+    <style>
+	* {font-size:15px; letter-spacing:-0.01em; -webkit-box-sizing: border-box; box-sizing: border-box; text-decoration:none; font-family:'Noto Sans', sans-serif;}
+	body {background-color:#fff; overflow-x:hidden; margin:0; padding:0;}
+	.agree-wrap {padding-bottom:32px;}
+	.agree-wrap > h1 {width:100%; background-color:#44423a; text-align:center; color:#fff; height:48px; line-height:46px; font-weight:700; margin-bottom:12px; font-size:18px; margin:0;}
+	.agree-wrap > p {line-height:22px;}
+	.agree-wrap > p > strong {}
+	.agree-wrap > ol {margin-bottom:48px; padding-left:24px;}
+	.agree-wrap > ol > li {list-style:decimal outside; margin-bottom:16px;}
+	.agree-wrap > ol > li > ul {padding-left:16px; margin-top:8px;}
+	.agree-wrap > ol > li > ul > li {margin:0; list-style:square outside; margin:0px; padding:0; color:#8c8c8c; margin-bottom:8px;}
+	.agree-wrap > .date {text-align:center;}
+
+    </style>
+</head>
+<body>
+<div class="agree-wrap">
+    <h1>개인정보 수집 및 이용 동의서</h1>
+    <p>
+        <strong>HDC 아이콘트롤스㈜</strong>(이하 "<strong>당사</strong>"라 한다), <strong>베스틴 홈 서비스</strong>(이하
+        "<strong>서비스</strong>"라 한다) 관련하여, 개인정보 제공자가 동의한 내용 외에 다른 목적으로 활용하지 않으며, 제공된 개인정보의 이용을 거부하고자
+        할 때에는 개인정보 관리 책임자를 통해 열람, 정정, 삭제를 요구할 수 있습니다.
+    </p>
+    <ol>
+        <li>
+            개인정보 수집﹒이용 목적
+            <ul>
+                <li>
+                    회원 관리: 회원제 서비스 이용에 따른 본인확인, 이용자 식별, 불량 회원의 부정 이용 및
+                    비인가 사용 방지, 가입 의사 확인, 가입 제한, 분쟁 처리를 위한 기록 보존, 불만 민원 처리,
+                    고지사항 전달 등
+                </li>
+                <li>
+                    서비스 제공: 당사가 제공하는 서비스 제공, 스마트 조명 사용 형태 및 스마트 조명 상태 모니터링,
+                    원격 제어, 진단 결과 확인을 통한 서비스 제공, 스마트 조명을 이용한 측정 정보 제공, 1:1 문의,
+                    온라인 상담, 기타 서비스 유지 보수, 회원 정보 및 스마트 조명 정보 활용을 통한 추가적인 서비스
+                    (빠른 상담 및 수리 서비스, 전화나 문자를 통한 고장 알림 서비스 등) 제공, 이용자의 기기
+                    사용 내역 및 관심사항 등을 고려한 1:1 맞춤형 서비스 및 광고 제공 등
+                </li>
+                <li>
+                    스마트 조명 개발: 로그 분석을 통한 기능 개선 및 맞춤 서비스 제공, 관심 분야 파악을 통한
+                    스마트 조명 개발, 고객 기호 확인을 통한 추가 서비스 개발, 서비스 및 이용행태 분석, 서비스
+                    개선, 부정 이용 방지, 정보통계, 신규서비스 연구, 신규 콘텐츠 개발, 고객 혜택적 서비스
+                    제공 등
+                </li>
+                <li>
+                    콘텐츠 제공 : 이용자 콘텐츠 관리 및 제공, 자동 상황 제어, 에너지 사용량 분석 등
+                </li>
+                <li>
+                    광고성 정보의 전송 및 이벤트 안내 등: 이벤트 및 광고성 정보 전달
+                </li>
+                <li>
+                    스마트 조명 구매자 혜택 서비스 제공 관련 제반 업무: 신청내역 확인, 스마트 조명 구매자 혜택
+                    서비스 참여자 정보 조회 및 통계/관리 등
+                </li>
+            </ul>
+        </li>
+        <li>
+            개인정보 수집항목
+            <ul>
+                <li>회원가입을 위해 필요한 정보 : 아이디(이메일), 비밀번호, 닉네임, 전화번호</li>
+                <li>각 서비스 및 기능 이용을 위해 필요한 정보 :
+                    <br>- 기기식별정보, 닉네임, 전화번호, Profile 사진,
+                    <br>- (홈 관리, 구성원 관리 목적) 홈, 방, 구성원 정보: 구성원 이용자번호, 구성원 Profile사진,
+                    구성원 이름, 구성원 이메일, Host 여부, 홈이 공유된 날짜, 공유된 홈 ID, 고객이 만든 홈
+                    및 방 정보(홈 이름, 홈 ID, 배경화면 URL, 주소, 지역 코드, 위∙경도)
+                    <br>- 위치 정보: 도시코드(Citycode), 서버 주소 등 각종 서버 Flag 정보, 위/경도, Radius (반경),
+                    이용자가 입력하는 지역정보,
+                    <br>- 연결 스마트 조명 기기 등록 정보(고유 식별값, 종류, 이름, 제조사, 모델명, 제조번호, 버전정보),
+                    IP주소, 기기 동작 및 사용 이력, 전원 (On/Off), 네트워크 연결상태 및 주변 네트워크 환경
+                    정보, 전력 사용량 정보, 서비스각종 기능 실행 정보 및 동작 정보, 스마트 조명 오류/오작동 관련
+                    정보, 기기 상태정보, 기기 설정정보, App 접속 정보(횟수, 시간)
+                    <br>- 1:1 문의 내용(파일 첨부 시 업로드된 이미지 포함), 챗봇(Chatbot) 서비스 이용(로그)정보
+                    (채팅 텍스트, 기기 제어 및 모니터링 정보, 이름, 전화번호, 주소, 접수 예약번호,
+                    위치정보(GPS), 고객이 입력하는 지역명)
+                    <br>- 서비스 이용 과정에서 생성되는 정보(로그데이터, 쿠키), 사진 정보, 스마트폰 갤러리 사진
+                </li>
+            </ul>
+        </li>
+        <li>
+            개인정보의 보유 및 이용기간
+            <ul>
+                <li>
+                    입당사는 정보주체로부터 개인정보를 수집하는 경우 서비스 이용기간 동안 개인정보를 이용·보관함을 원칙으로 하며 이용 목적이 달성 되면 지체 없이 파기 합니다.
+                    <br>
+                    다만, 관계법령의 규정이나 회사 내부 방침에 의하여 보존할 필요가 있는 경우 당사는 해당 법령 및 당사 개인정보처리방침에서 정한 바에 따라 개인정보를 보관할 수 있습니다.
+                    <br>
+                    귀하는 위 정보에 대한 수집∙이용 동의를 거부할 수 있는 권리가 있으나, 이에 동의하지 않을 경우 <strong>HDC 아이콘트롤스㈜, 베스틴 홈</strong> 서비스 이용이 제한될 수 있습니다.
+                </li>
+            </ul>
+        </li>
+    </ol>
+    <div class="date" id="today">2020년 12월 10일</div>
+</div>
+
+<script>
+	function getTodayString() {
+		var today = new Date();
+		var dd = String(today.getDate()).padStart(2, '0');
+		var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
+		var yyyy = today.getFullYear();
+
+		today = yyyy + '년 ' + mm + '월 ' + dd + '일';
+
+		document.getElementById("today").innerHTML = today
+	}
+
+	getTodayString()
+
+</script>
+
+</body></html>

+ 72 - 0
app/src/main/assets/bestinhome-agree_files/css

@@ -0,0 +1,72 @@
+/* cyrillic-ext */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr6DRAW_0.woff2) format('woff2');
+  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr4TRAW_0.woff2) format('woff2');
+  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* devanagari */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr5DRAW_0.woff2) format('woff2');
+  unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
+}
+/* greek-ext */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr6TRAW_0.woff2) format('woff2');
+  unicode-range: U+1F00-1FFF;
+}
+/* greek */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr5jRAW_0.woff2) format('woff2');
+  unicode-range: U+0370-03FF;
+}
+/* vietnamese */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr6jRAW_0.woff2) format('woff2');
+  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr6zRAW_0.woff2) format('woff2');
+  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+  font-family: 'Noto Sans';
+  font-style: normal;
+  font-weight: 400;
+  font-display: swap;
+  src: url(https://fonts.gstatic.com/s/notosans/v11/o-0IIpQlx3QUlC5A4PNr5TRA.woff2) format('woff2');
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}

+ 55 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AWS/AmazonBLEinfo.java

@@ -0,0 +1,55 @@
+package kr.co.icontrols.iotplatform.AWS;
+
+import android.bluetooth.BluetoothDevice;
+
+import java.util.Objects;
+
+public class AmazonBLEinfo {
+    private String name;
+    private String macAddr;
+    private BluetoothDevice mBluetoothDevice;
+
+    public AmazonBLEinfo(String name, String macAddr, BluetoothDevice bluetoothDevice) {
+        this.name = name;
+        this.macAddr = macAddr;
+        mBluetoothDevice = bluetoothDevice;
+    }
+
+    public BluetoothDevice getBluetoothDevice() {
+        return mBluetoothDevice;
+    }
+
+    public void setBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        mBluetoothDevice = bluetoothDevice;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getMacAddr() {
+        return macAddr;
+    }
+
+    public void setMacAddr(String macAddr) {
+        this.macAddr = macAddr;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AmazonBLEinfo amazonBLEinfo = (AmazonBLEinfo) o;
+        return Objects.equals(getMacAddr(), amazonBLEinfo.getMacAddr());
+    }
+
+    @Override
+    public int hashCode() {
+
+        return Objects.hash(getName(), getMacAddr(), getBluetoothDevice());
+    }
+}

+ 33 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AWS/AmazonFreeRTOSAgent.java

@@ -0,0 +1,33 @@
+package kr.co.icontrols.iotplatform.AWS;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+
+import software.amazon.freertos.amazonfreertossdk.AmazonFreeRTOSManager;
+
+/**
+ * A wrapper class to get the instance of AmazonFreeRTOSManager.
+ */
+public class AmazonFreeRTOSAgent {
+    private static AmazonFreeRTOSManager sAmazonFreeRTOSManager;
+    private static BluetoothAdapter sBluetoothAdapter;
+    private static final String TAG = "AmazonFreeRTOSAgent";
+
+    /**
+     * Return the instance of AmazonFreeRTOSManager. Initialize AmazonFreeRTOSManager if this is
+     * called for the first time.
+     * Bluetooth must be enabled before calling this method to get AmazonFreeRTOSManager.
+     * @param context
+     * @return The instance of AmazonFreeRTOSManager.
+     */
+    public static AmazonFreeRTOSManager getAmazonFreeRTOSManager(Context context) {
+        if (sAmazonFreeRTOSManager == null) {
+            BluetoothManager bluetoothManager
+                    = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
+            sBluetoothAdapter = bluetoothManager.getAdapter();
+            sAmazonFreeRTOSManager = new AmazonFreeRTOSManager(context, sBluetoothAdapter);
+        }
+        return sAmazonFreeRTOSManager;
+    }
+}

+ 90 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AWS/AmazonWifiInfo.java

@@ -0,0 +1,90 @@
+package kr.co.icontrols.iotplatform.AWS;
+
+import java.util.Objects;
+
+public class AmazonWifiInfo {
+    private static String[] NETWORK_TYPES = {"Open", "WEP", "WPA", "WPA2", "WPA2-Enterprise", "Other"};
+    private String ssid;
+    private byte[] bssid;
+    private int rssi;
+    private int networkType;
+    private int index;
+    private boolean connected;
+
+    public String getNetworkTypeName() {
+        return NETWORK_TYPES[networkType];
+    }
+
+    public AmazonWifiInfo(String ssid, byte[] bssid, int rssi, int networkType, int index, boolean connected) {
+        this.ssid = ssid;
+        this.bssid = bssid;
+        this.rssi = rssi;
+        this.networkType = networkType;
+        this.index = index;
+        this.connected = connected;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+        AmazonWifiInfo amazonWifiInfo = (AmazonWifiInfo) obj;
+        return Objects.equals(amazonWifiInfo.getSsid(), ssid);
+    }
+
+    public static String[] getNetworkTypes() {
+        return NETWORK_TYPES;
+    }
+
+    public static void setNetworkTypes(String[] networkTypes) {
+        NETWORK_TYPES = networkTypes;
+    }
+
+    public String getSsid() {
+        return ssid;
+    }
+
+    public void setSsid(String ssid) {
+        this.ssid = ssid;
+    }
+
+    public byte[] getBssid() {
+        return bssid;
+    }
+
+    public void setBssid(byte[] bssid) {
+        this.bssid = bssid;
+    }
+
+    public int getRssi() {
+        return rssi;
+    }
+
+    public void setRssi(int rssi) {
+        this.rssi = rssi;
+    }
+
+    public int getNetworkType() {
+        return networkType;
+    }
+
+    public void setNetworkType(int networkType) {
+        this.networkType = networkType;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public boolean isConnected() {
+        return connected;
+    }
+
+    public void setConnected(boolean connected) {
+        this.connected = connected;
+    }
+}

+ 190 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AWS/Util.java

@@ -0,0 +1,190 @@
+package kr.co.icontrols.iotplatform.AWS;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.internal.StaticCredentialsProvider;
+import com.amazonaws.mobile.client.AWSMobileClient;
+import com.amazonaws.mobile.client.Callback;
+import com.amazonaws.mobile.client.UserStateDetails;
+import com.amazonaws.mobile.config.AWSConfiguration;
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferObserver;
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility;
+import com.amazonaws.regions.Region;
+import com.amazonaws.services.s3.AmazonS3Client;
+
+import org.json.JSONException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Handles basic helper functions used throughout the app.
+ */
+public class Util {
+    private static final String TAG = Util.class.getSimpleName();
+
+    private AmazonS3Client sS3Client;
+    private AWSCredentialsProvider sMobileClient;
+    private TransferUtility sTransferUtility;
+
+    /**
+     * Gets an instance of AWSMobileClient which is
+     * constructed using the given Context.
+     *
+     * @param context Android context
+     * @return AWSMobileClient which is a credentials provider
+     */
+    private AWSCredentialsProvider getCredProvider(Context context) {
+        if (sMobileClient == null) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            AWSMobileClient.getInstance().initialize(context, new Callback<UserStateDetails>() {
+                @Override
+                public void onResult(UserStateDetails result) {
+                    latch.countDown();
+                }
+
+                @Override
+                public void onError(Exception e) {
+                    Log.e(TAG, "onError: ", e);
+                    latch.countDown();
+                }
+            });
+            try {
+                latch.await();
+                sMobileClient = AWSMobileClient.getInstance();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        return sMobileClient;
+    }
+
+    /**
+     * Gets an instance of a S3 client which is constructed using the given
+     * Context.
+     *
+     * @param context Android context
+     * @return A default S3 client.
+     */
+    public AmazonS3Client getS3Client(Context context) {
+        if (sS3Client == null) {
+            try {
+                String regionString = new AWSConfiguration(context)
+                        .optJsonObject("S3TransferUtility")
+                        .getString("Region");
+                Region region = Region.getRegion(regionString);
+
+                final String ACCESS_KEY = "AKIAUSTWEOGGE4XFHBWL";
+                final String SECRET_KEY = "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";
+
+                final AWSCredentialsProvider credentialsProvider = new StaticCredentialsProvider(
+                        new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
+
+                sS3Client = new AmazonS3Client(credentialsProvider, region);
+                //sS3Client = new AmazonS3Client(getCredProvider(context), region);
+
+
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+        return sS3Client;
+    }
+
+    /**
+     * Gets an instance of the TransferUtility which is constructed using the
+     * given Context
+     *
+     * @param context Android context
+     * @return a TransferUtility instance
+     */
+    public TransferUtility getTransferUtility(Context context) {
+        if (sTransferUtility == null) {
+            sTransferUtility = TransferUtility.builder()
+                    .context(context)
+                    .s3Client(getS3Client(context))
+                    .awsConfiguration(new AWSConfiguration(context))
+                    .build();
+        }
+
+        return sTransferUtility;
+    }
+
+    /**
+     * Converts number of bytes into proper scale.
+     *
+     * @param bytes number of bytes to be converted.
+     * @return A string that represents the bytes in a proper scale.
+     */
+    public String getBytesString(long bytes) {
+        String[] quantifiers = new String[]{
+                "KB", "MB", "GB", "TB"
+        };
+        double speedNum = bytes;
+        for (int i = 0; ; i++) {
+            if (i >= quantifiers.length) {
+                return "";
+            }
+            speedNum /= 1024;
+            if (speedNum < 512) {
+                return String.format(Locale.US, "%.2f", speedNum) + " " + quantifiers[i];
+            }
+        }
+    }
+
+    /**
+     * Copies the data from the passed in Uri, to a new file for use with the
+     * Transfer Service
+     *
+     * @param context Android context
+     * @param uri Content URI
+     * @return New file copied from given URI
+     * @throws IOException if data read/write fails
+     */
+    public File copyContentUriToFile(Context context, Uri uri) throws IOException {
+        InputStream is = context.getContentResolver().openInputStream(uri);
+        File copiedData = new File(context.getDir("SampleImagesDir", Context.MODE_PRIVATE),
+                UUID.randomUUID().toString());
+        copiedData.createNewFile();
+
+        FileOutputStream fos = new FileOutputStream(copiedData);
+        byte[] buf = new byte[2046];
+        int read = -1;
+        while ((read = is.read(buf)) != -1) {
+            fos.write(buf, 0, read);
+        }
+
+        fos.flush();
+        fos.close();
+
+        return copiedData;
+    }
+
+    /**
+     * Fills in the map with information in the observer so that it can be used
+     * with a SimpleAdapter to populate the UI
+     */
+    public void fillMap(Map<String, Object> map, TransferObserver observer, boolean isChecked) {
+        int progress = (int) ((double) observer.getBytesTransferred() * 100 / observer
+                .getBytesTotal());
+        map.put("id", observer.getId());
+        map.put("checked", isChecked);
+        map.put("fileName", observer.getAbsoluteFilePath());
+        map.put("progress", progress);
+        map.put("bytes",
+                getBytesString(observer.getBytesTransferred()) + "/"
+                        + getBytesString(observer.getBytesTotal()));
+        map.put("state", observer.getState());
+        map.put("percentage", progress + "%");
+    }
+}

+ 57 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AddDeviceListActivity.java

@@ -0,0 +1,57 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class AddDeviceListActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_add_device_list);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnLight:
+                startActivity(new Intent(this,LightRegisterGuideActivity.class));
+                break;
+            case R.id.btnCurtain:
+                break;
+            case R.id.btnHub:
+                break;
+            case R.id.btnIr:
+                break;
+        }
+    }
+}

+ 363 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AddGroupActivity.java

@@ -0,0 +1,363 @@
+package kr.co.icontrols.iotplatform;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_IMG_INDEX;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCircleBitmap;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageResource;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_BEFORE_ACTIVITY;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_01;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_00_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_01_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_02_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_03_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_04_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_05_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_06_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_07_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_08_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_09_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_00_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_01_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_02_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_03_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_04_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_05_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_06_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_07_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_08_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_09_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_LIST_COUNT_LINE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHANGE_GROUP_IMG;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_GROUP_NAME;
+
+public class AddGroupActivity extends AppCompatActivity implements View.OnClickListener,
+        TextWatcher {
+
+    private final String TAG = "AddGroupActivity";
+
+    private LinearLayout mLayoutGroups;
+    private EditText mTxtGroupName;
+    private ArrayList<DeviceGroupInfo> mGroupList;
+    private int mSelectedImgIndex = GROUP_01;
+    private String mBeforeActivity;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_add_group);
+
+        mLayoutGroups = findViewById(R.id.layoutGroups);
+        mTxtGroupName = findViewById(R.id.txtGroupName);
+        mTxtGroupName.addTextChangedListener(this);
+
+        mGroupList = (ArrayList<DeviceGroupInfo>) getIntent().getSerializableExtra(EXTRA_GROUP_LIST);
+        mBeforeActivity = getIntent().getStringExtra(EXTRA_BEFORE_ACTIVITY);
+        shwoGroupList();
+        changeGroupImage(mSelectedImgIndex);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if(mGroupList!=null) {
+            mGroupList.clear();
+        }
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            /*
+            case RESULT_INPUT_GROUP_NAME:
+                if (resultCode == Activity.RESULT_OK) {
+                    Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                    if(b != null) {
+                        ((TextView)findViewById(R.id.txtGroupName)).setText(b.getString(PopupActivity.POPUP_INPUT));
+                    }
+                }
+                break;
+            */
+            case RESULT_CHANGE_GROUP_IMG:
+                if (resultCode == RESULT_OK) {
+                    int selected_index = data.getIntExtra(POPUP_IMG_INDEX, 1);
+                    changeGroupImage(selected_index);
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+            case R.id.btnBackText:
+                onBackPressed();
+                break;
+            case R.id.imgGroup:{
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_SELECT_GROUP_IMG);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.group_image));
+                intent.putExtra(POPUP_IMG_INDEX, mSelectedImgIndex);
+                startActivityForResult(intent, RESULT_CHANGE_GROUP_IMG);
+            }
+                break;
+            /*
+            case R.id.txtGroupName: {
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+                Bundle b = new Bundle();
+                if (isEmptyGroupName()) {
+                    b.putString(PopupActivity.POPUP_INPUT, "");
+                } else {
+                    b.putString(PopupActivity.POPUP_INPUT, ((TextView) findViewById(R.id.txtGroupName)).getText().toString());
+                }
+                intent.putExtra(POPUP_BUNDLE, b);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.group_name));
+                startActivityForResult(intent, RESULT_INPUT_GROUP_NAME);
+                }
+                break;
+            */
+            case R.id.btnRight:
+                boolean isExistName = false;
+                for(DeviceGroupInfo d : mGroupList) {
+                    if(d.friendlyName.equals(mTxtGroupName.getText().toString())) {
+                        isExistName = true;
+                        break;
+                    }
+                }
+
+                if(isEmptyGroupName()) {
+                    showNotiMsg(AddGroupActivity.this,getString(R.string.snackbar_error_need_groupname));
+                } else if(isExistName) {
+                    showNotiMsg(AddGroupActivity.this,getString(R.string.snackbar_error_exist_groupname));
+                } else {
+                    String groupImgString[] = {GROUP_IMG_00_STRING,
+                            GROUP_IMG_01_STRING,GROUP_IMG_02_STRING,GROUP_IMG_03_STRING,
+                            GROUP_IMG_04_STRING,GROUP_IMG_05_STRING,GROUP_IMG_06_STRING,
+                            GROUP_IMG_07_STRING,GROUP_IMG_08_STRING,GROUP_IMG_09_STRING,
+                    };
+
+                    String groupBackgroundString[] = {GROUP_BACKGROUND_00_STRING,
+                            GROUP_BACKGROUND_01_STRING,GROUP_BACKGROUND_02_STRING,GROUP_BACKGROUND_03_STRING,
+                            GROUP_BACKGROUND_04_STRING,GROUP_BACKGROUND_05_STRING,GROUP_BACKGROUND_06_STRING,
+                            GROUP_BACKGROUND_07_STRING,GROUP_BACKGROUND_08_STRING,GROUP_BACKGROUND_09_STRING,
+                    };
+
+                    displayProgress(this,"","");
+                    try {
+                        HttpApi.registerDeviceGroup(mTxtGroupName.getText().toString(),
+                                groupImgString[mSelectedImgIndex],
+                                groupBackgroundString[mSelectedImgIndex],
+                                new Callback() {
+                                    @Override
+                                    public void onFailure(Call call, IOException e) {
+                                        e.printStackTrace();
+                                        ErrorLogOutput(TAG, "HttpApi.registerDeviceGroup(onFailure)\n" + e.toString());
+                                        showNotiMsg(AddGroupActivity.this,"그룹 추가를 실패하였습니다.");
+                                        DismissConnectDialog();
+                                    }
+
+                                    @Override
+                                    public void onResponse(Call call, Response response) throws IOException {
+                                        if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                            UserInfo.dgroups.clear();
+                                            WarnLogOutput(TAG, "HttpApi.registerDeviceGroup(onResponse) HTTP_NO_CONTENT");
+                                            if(mBeforeActivity != null && mBeforeActivity.equals("LightRegisterDeviceFinishActivity")) {
+                                                showNotiMsg(AddGroupActivity.this,"그룹를 추가하였습니다.",true);
+                                            } else {
+                                                showNotiMsg(AddGroupActivity.this,"그룹를 추가하였습니다.",MyGroupActivity.class);
+                                            }
+                                            DismissConnectDialog();
+                                        } else {
+                                            ErrorLogOutput(TAG, "HttpApi.registerDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                            showNotiMsg(AddGroupActivity.this,"그룹 추가를 실패하였습니다.");
+                                            DismissConnectDialog();
+                                            try {
+                                                JSONObject json_data = new JSONObject(response.body().string());
+                                                ErrorLogOutput(TAG, "HttpApi.registerDeviceGroup(onResponse) body : " + json_data.toString());
+                                            } catch (JSONException e) {
+                                                ErrorLogOutput(TAG, "HttpApi.registerDeviceGroup(onResponse) JSONException : " + e.toString());
+                                            }
+                                        }
+                                    }
+                                });
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.registerDeviceGroup(Exception)\n" + e.toString());
+                        showNotiMsg(AddGroupActivity.this,"그룹 추가를 실패하였습니다.");
+                        DismissConnectDialog();
+                    }
+                }
+                break;
+        }
+    }
+
+    private boolean isEmptyGroupName() {
+        return (mTxtGroupName.getText().toString().length() == 0 || mTxtGroupName.getText().toString().equals(getString(R.string.hint_input_text)));
+    }
+
+    private void changeGroupImage(final int index) {
+        int groupImgResource[] = {R.drawable.invisiableimage,
+                R.drawable.img_group_01,R.drawable.img_group_02,R.drawable.img_group_03,
+                R.drawable.img_group_04,R.drawable.img_group_05,R.drawable.img_group_06,
+                R.drawable.img_group_07,R.drawable.img_group_08,R.drawable.img_group_09
+        };
+        mSelectedImgIndex = index;
+        ((ImageView)findViewById(R.id.imgGroup)).setImageBitmap(getCircleBitmap(groupImgResource[mSelectedImgIndex]));
+    }
+
+    private void shwoGroupList() {
+        LinearLayout layoutHorizontal = null;
+        int lineCount = 0;
+        for(int i = 0; i < mGroupList.size(); i++){
+            if(i%GROUP_LIST_COUNT_LINE==0) {
+                lineCount = 0;
+                layoutHorizontal = new LinearLayout(this);
+                LinearLayout.LayoutParams lparam = new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                layoutHorizontal.setLayoutParams(lparam);
+                layoutHorizontal.setOrientation(LinearLayout.HORIZONTAL);
+                mLayoutGroups.addView(layoutHorizontal);
+
+                LinearLayout spacelayout = new LinearLayout(this);
+                LinearLayout.LayoutParams slparam = new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                slparam.weight = 1;
+                spacelayout.setLayoutParams(slparam);
+                spacelayout.setOrientation(LinearLayout.HORIZONTAL);
+                layoutHorizontal.addView(spacelayout);
+            }
+            
+            if(layoutHorizontal != null) {
+                lineCount++;
+                final View child = getLayoutInflater().inflate(R.layout.listview_horizontal_group_item, null);
+                final TextView txtGroupName = child.findViewById(R.id.txtGroup);
+                final ImageView imgGroup = child.findViewById(R.id.imgGroup);
+                txtGroupName.setText(mGroupList.get(i).friendlyName);
+                imgGroup.setImageBitmap(getCircleBitmap(getGroupImageResource(mGroupList.get(i).icon)));
+                layoutHorizontal.addView(child);
+
+                LinearLayout spacelayout = new LinearLayout(this);
+                LinearLayout.LayoutParams slparam = new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                slparam.weight = 1;
+                spacelayout.setLayoutParams(slparam);
+                spacelayout.setOrientation(LinearLayout.HORIZONTAL);
+                layoutHorizontal.addView(spacelayout);
+            }
+
+            if(i == mGroupList.size()-1 && lineCount < GROUP_LIST_COUNT_LINE) {
+                while(lineCount < GROUP_LIST_COUNT_LINE) {
+                    lineCount++;
+                    final View child = getLayoutInflater().inflate(R.layout.listview_horizontal_group_item, null);
+                    final TextView txtGroupName = child.findViewById(R.id.txtGroup);
+                    final ImageView imgGroup = child.findViewById(R.id.imgGroup);
+                    txtGroupName.setVisibility(View.INVISIBLE);
+                    imgGroup.setVisibility(View.INVISIBLE);
+                    layoutHorizontal.addView(child);
+
+                    LinearLayout spacelayout = new LinearLayout(this);
+                    LinearLayout.LayoutParams slparam = new LinearLayout.LayoutParams(
+                            LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                    slparam.weight = 1;
+                    spacelayout.setLayoutParams(slparam);
+                    spacelayout.setOrientation(LinearLayout.HORIZONTAL);
+                    layoutHorizontal.addView(spacelayout);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        if(mTxtGroupName.length() > 0) {
+            findViewById(R.id.btnBack).setVisibility(View.GONE);
+            findViewById(R.id.btnBackText).setVisibility(View.VISIBLE);
+            findViewById(R.id.btnRight).setVisibility(View.VISIBLE);
+        } else {
+            findViewById(R.id.btnBack).setVisibility(View.VISIBLE);
+            findViewById(R.id.btnBackText).setVisibility(View.GONE);
+            findViewById(R.id.btnRight).setVisibility(View.INVISIBLE);
+        }
+    }
+}

+ 231 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AddHomeActivity.java

@@ -0,0 +1,231 @@
+package kr.co.icontrols.iotplatform;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.Database.DatabaseHelper;
+import kr.co.icontrols.iotplatform.adapter.DeviceSelectAdapter;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+public class AddHomeActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "AddHomeActivity";
+    private ListView mListView;
+    private ArrayList<DeviceInfo> mDeviceList = new ArrayList<>();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_add_home);
+
+        mListView = findViewById(R.id.listView);
+        UserInfo.setUnSelectAllDevices();
+        getDeviceList();
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if(mDeviceList != null) {
+            mDeviceList.clear();
+        }
+        super.onDestroy();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+            case R.id.btnBackText:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+                if(!((DeviceSelectAdapter)mListView.getAdapter()).isSelected()) {
+                    showNotiMsg(AddHomeActivity.this,"추가할 스마트 조명을 선택해주세요.");
+                } else{
+                    boolean isCompleted = false;
+                    for(int i = 0; i < mDeviceList.size() ; i++) {
+                        if(mDeviceList.get(i).isSelected()) {
+                            if (DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT, mDeviceList.get(i).deviceId) != null) {
+                                //showNotiMsg(AddHomeActivity.this, "스마트 조명에 이미 추가된 조명입니다.");
+                            } else {
+                                if (DBUtil.putDeviceInfo(TABLE_SMART_LIGHT, mDeviceList.get(i)) == -1) {
+                                    //showNotiMsg(AddHomeActivity.this, "스마트 조명 등록에 실패하였습니다.");
+                                } else {
+                                    //showNotiMsg(AddHomeActivity.this, "스마트 조명 등록을 완료하였습니다.", true);
+                                    isCompleted = true;
+                                }
+                            }
+                        }
+                    }
+                    if(isCompleted) {
+                        showNotiMsg(AddHomeActivity.this, "스마트 조명 등록을 완료하였습니다.", true);
+                    } else {
+                        showNotiMsg(AddHomeActivity.this, "스마트 조명 등록에 실패하였습니다.");
+                    }
+                }
+                break;
+        }
+    }
+
+    private void getDeviceList() {
+
+        UserInfo.getDevice(this, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mDeviceList.clear();
+                        if(UserInfo.devices.size() > 0) {
+                            for (DeviceInfo d : UserInfo.devices) {
+                                mDeviceList.add(d);
+                            }
+                            ArrayList<DeviceInfo> tempDevices = DBUtil.getAllDeviceInfos(TABLE_SMART_LIGHT);
+                            for (int i = 0; i < mDeviceList.size(); i++) {
+                                for (DeviceInfo d : tempDevices) {
+                                    if (d.deviceId.equals(mDeviceList.get(i).deviceId)) {
+                                        mDeviceList.remove(i--);
+                                        break;
+                                    }
+                                }
+                            }
+                            tempDevices.clear();
+                        }
+
+                        if(mDeviceList.size() > 0) {
+                            mListView.setAdapter(new DeviceSelectAdapter(mDeviceList));
+                            mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                                @Override
+                                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                                    ((DeviceSelectAdapter)mListView.getAdapter()).setSelected(position);
+                                    ((DeviceSelectAdapter)mListView.getAdapter()).notifyDataSetChanged();
+
+                                    if(((DeviceSelectAdapter)mListView.getAdapter()).isSelected()) {
+                                        findViewById(R.id.btnBack).setVisibility(View.GONE);
+                                        findViewById(R.id.btnBackText).setVisibility(View.VISIBLE);
+                                        findViewById(R.id.btnRight).setVisibility(View.VISIBLE);
+                                    } else {
+                                        findViewById(R.id.btnBack).setVisibility(View.VISIBLE);
+                                        findViewById(R.id.btnBackText).setVisibility(View.GONE);
+                                        findViewById(R.id.btnRight).setVisibility(View.INVISIBLE);
+                                    }
+                                }
+                            });
+                        } else {
+                            showNotiMsg(AddHomeActivity.this, "추가 가능한 스마트 조명이 존재하지 않습니다.",true);
+                        }
+                    }
+                });
+            }
+        });
+        /*
+        try{
+            displayProgress(AddHomeActivity.this,"","");
+            HttpApi.getAllDevice(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getAllDevice(onFailure)\n" + e.toString());
+                    showNotiMsg(AddHomeActivity.this,"스마트 조명 목록 획득을 실패하였습니다.");
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getAllDevice(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                            for (int i = 0; i < jarrayAll.length(); i++) {
+                                JSONObject jObject = jarrayAll.getJSONObject(i);
+                                DeviceInfo item = new DeviceInfo(
+                                        jObject.isNull("deviceId") ? "" : jObject.getString("deviceId"),
+                                        jObject.isNull("deviceType") ? "" : jObject.getString("deviceType"),
+                                        jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                        jObject.isNull("sharedFriendlyName") ? "" : jObject.getString("sharedFriendlyName"),
+                                        jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                        jObject.isNull("dgroupName") ? "" : jObject.getString("dgroupName"),
+                                        jObject.isNull("ownerId") ? "" : jObject.getString("ownerId"),
+                                        DEFAULT_RANK
+                                );
+                                mDeviceList.add(item);
+                            }
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    ((DeviceSelectAdapter)mListView.getAdapter()).notifyDataSetChanged();
+                                    DismissConnectDialog();
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(JSONException)\n" + e.toString());
+                            showNotiMsg(AddHomeActivity.this,"스마트 조명 목록 획득을 실패하였습니다.");
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(AddHomeActivity.this,"스마트 조명 목록 획득을 실패하였습니다.");
+                        DismissConnectDialog();
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                }
+            });
+        } catch(Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(Exception)\n" + e.toString());
+            showNotiMsg(AddHomeActivity.this,"스마트 조명 목록 획득을 실패하였습니다.");
+            DismissConnectDialog();
+        }
+         */
+    }
+}

+ 266 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AdminMemberActivity.java

@@ -0,0 +1,266 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.MemberInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.adapter.MemberAdapter;
+import kr.co.icontrols.iotplatform.utils.CommonUtils;
+import kr.co.icontrols.iotplatform.utils.DownloadImageTask;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_IMG_INDEX;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHANGE_GROUP_IMG;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CONFIRM_DELETE_MEMBER;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_GROUP_NAME;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class AdminMemberActivity extends AppCompatActivity implements View.OnClickListener {
+    private final String TAG = "AdminMemberActivity";
+
+    private MemberAdapter mMemberAdapter = new MemberAdapter(this);
+    private ListView mMemberList;
+
+    public static String sSelectedMemberId = "";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_admin_member);
+
+        ((TextView)findViewById(R.id.txtName)).setText(UserInfo.nickname);
+
+        mMemberList = findViewById(R.id.listMember);
+        mMemberList.setAdapter(mMemberAdapter);
+        if(UserInfo.profile != null && !UserInfo.profile.isEmpty()) {
+            new DownloadImageTask(findViewById(R.id.imgProfile)).execute(UserInfo.profile);
+        } else {
+            ((ImageView)findViewById(R.id.imgProfile)).setImageResource(R.drawable.profile_thum_176);
+        }
+        getMemberList();
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if(mMemberAdapter != null) {
+            mMemberAdapter.removeAllItem();
+        }
+
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_CONFIRM_DELETE_MEMBER:
+                if (resultCode == Activity.RESULT_OK) {
+                    try {
+                        displayProgress(AdminMemberActivity.this,"","");
+                        HttpApi.deleteGuest(sSelectedMemberId, new Callback() {
+                            @Override
+                            public void onFailure(Call call, IOException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.deleteGuest(onFailure)\n" + e.toString());
+                                showNotiMsg(AdminMemberActivity.this,"맴버 삭제를 실패하였습니다.");
+                                DismissConnectDialog();
+                            }
+
+                            @Override
+                            public void onResponse(Call call, Response response) throws IOException {
+                                if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                    WarnLogOutput(TAG, "HttpApi.deleteGuest(onResponse) HTTP_NO_CONTENT");
+                                    runOnUiThread(new Runnable() {
+                                        @Override
+                                        public void run() {
+                                            mMemberAdapter.removeItem(sSelectedMemberId);
+                                            mMemberAdapter.notifyDataSetChanged();
+                                            showNotiMsg(AdminMemberActivity.this,"맴버를 삭제하였습니다.");
+
+                                            if(mMemberAdapter.getCount() == 0) {
+                                                findViewById(R.id.layoutMembers).setVisibility(View.INVISIBLE);
+                                            } else {
+                                                ((TextView)findViewById(R.id.txtMemberCount)).setText(String.format("멤버(%d명)",mMemberAdapter.getCount()));
+                                                findViewById(R.id.layoutMembers).setVisibility(View.VISIBLE);
+                                            }
+
+                                            DismissConnectDialog();
+                                        }
+                                    });
+                                } else {
+                                    ErrorLogOutput(TAG, "HttpApi.deleteGuest(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                    showNotiMsg(AdminMemberActivity.this,"맴버 삭제를 실패하였습니다.");
+                                    DismissConnectDialog();
+                                    try {
+                                        JSONObject json_data = new JSONObject(response.body().string());
+                                        ErrorLogOutput(TAG, "HttpApi.deleteGuest(onResponse) body : " + json_data.toString());
+                                    } catch (JSONException e) {
+                                        ErrorLogOutput(TAG, "HttpApi.deleteGuest(onResponse) JSONException : " + e.toString());
+                                    }
+                                }
+                            }
+                        });
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        showNotiMsg(AdminMemberActivity.this,"맴버 삭제를 실패하였습니다.");
+                        DismissConnectDialog();
+                        ErrorLogOutput(TAG, "HttpApi.deleteGuest(Exception)\n" + e.toString());
+                    }
+
+                } else {
+                    sSelectedMemberId = "";
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnInviteMember:
+                //startActivity(new Intent(this, InviteMemberActivity.class));
+                Intent intent = new Intent(this, InviteMemberInputActivity.class);
+                intent.putExtra("MODE","email");
+                startActivity(intent);
+                break;
+        }
+    }
+
+    private void getMemberList() {
+        try {
+            displayProgress(AdminMemberActivity.this,"","");
+            HttpApi.getGuestList(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getGuestList(onFailure)\n" + e.toString());
+                    showNotiMsg(AdminMemberActivity.this,"맴버 획득를 실패하였습니다.");
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getGuestList(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                            for (int i = 0; i < jarrayAll.length(); i++) {
+                                JSONObject jObject = jarrayAll.getJSONObject(i);
+                                MemberInfo item = new MemberInfo(jObject.isNull("customerId") ? "" : jObject.getString("customerId"),
+                                        jObject.isNull("nickname") ? "" : jObject.getString("nickname"),
+                                        jObject.isNull("profile") ? "" : jObject.getString("profile"));
+
+                                mMemberAdapter.addItem(item);
+                            }
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if(mMemberAdapter.getCount() == 0) {
+                                        //findViewById(R.id.btnInviteMember).setVisibility(View.VISIBLE);
+                                        findViewById(R.id.layoutMembers).setVisibility(View.INVISIBLE);
+                                        showNotiMsg(AdminMemberActivity.this,"맴버가 존재하지 않습니다.");
+                                    } else {
+                                        mMemberAdapter.notifyDataSetChanged();
+                                        ((TextView)findViewById(R.id.txtMemberCount)).setText(String.format("멤버(%d명)",mMemberAdapter.getCount()));
+                                        //findViewById(R.id.btnInviteMember).setVisibility(View.INVISIBLE);
+                                        findViewById(R.id.layoutMembers).setVisibility(View.VISIBLE);
+                                        showNotiMsg(AdminMemberActivity.this,"맴버 목록을 획득하였습니다.");
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getGuestList(JSONException)\n" + e.toString());
+                            showNotiMsg(AdminMemberActivity.this,"맴버 획득를 실패하였습니다.");
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getGuestList(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(AdminMemberActivity.this,"맴버 획득를 실패하였습니다.");
+                        DismissConnectDialog();
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getGuestList(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getGuestList(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getGuestList(Exception)\n" + e.toString());
+            showNotiMsg(AdminMemberActivity.this,"맴버 획득를 실패하였습니다.");
+            DismissConnectDialog();
+        }
+    }
+}

+ 230 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AlarmSettingActivity.java

@@ -0,0 +1,230 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_NOTI_ADD_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_NOTI_ADD_MEMBER;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_NOTI_DELETE_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_NOTI_REMOVE_MEMBER;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.NOTI_ADD_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.NOTI_ADD_MEMBER;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.NOTI_ALL;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.NOTI_DELETE_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.NOTI_REMOVE_MEMBER;
+
+public class AlarmSettingActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private String TAG = "AlarmSettingActivity";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_alarm_setting);
+
+        Switch switchAlram = (Switch)findViewById(R.id.switchAlram);
+        /**
+         * 알람 허용
+         */
+        switchAlram.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if(isChecked) {
+                    SharedPrefUtil.putBoolean(NOTI_ALL, true);
+                    findViewById(R.id.switchAlramAddDevice).setEnabled(true);
+                    findViewById(R.id.switchAlramDeleteDevice).setEnabled(true);
+                    findViewById(R.id.switchAlramAddMember).setEnabled(true);
+                    findViewById(R.id.switchAlramRemoveMember).setEnabled(true);
+                    findViewById(R.id.switchAlramAddDevice).setAlpha(1f);
+                    findViewById(R.id.switchAlramDeleteDevice).setAlpha(1f);
+                    findViewById(R.id.switchAlramAddMember).setAlpha(1f);
+                    findViewById(R.id.switchAlramRemoveMember).setAlpha(1f);
+                }
+                else {
+                    SharedPrefUtil.putBoolean(NOTI_ALL, false);
+                    findViewById(R.id.switchAlramAddDevice).setEnabled(false);
+                    findViewById(R.id.switchAlramDeleteDevice).setEnabled(false);
+                    findViewById(R.id.switchAlramAddMember).setEnabled(false);
+                    findViewById(R.id.switchAlramRemoveMember).setEnabled(false);
+                    findViewById(R.id.switchAlramAddDevice).setAlpha(0.5f);
+                    findViewById(R.id.switchAlramDeleteDevice).setAlpha(0.5f);
+                    findViewById(R.id.switchAlramAddMember).setAlpha(0.5f);
+                    findViewById(R.id.switchAlramRemoveMember).setAlpha(0.5f);
+                }
+                if(UserInfo.pushAccess != isChecked) {
+                    changeProfile(isChecked);
+                }
+            }
+        });
+        switchAlram.setChecked(UserInfo.pushAccess/*SharedPrefUtil.getBoolean(NOTI_ALL, SharedPrefUtil.DEFAULT_NOTI_ALL)*/);
+
+        if(switchAlram.isChecked()){
+            findViewById(R.id.switchAlramAddDevice).setAlpha(1f);
+            findViewById(R.id.switchAlramDeleteDevice).setAlpha(1f);
+            findViewById(R.id.switchAlramAddMember).setAlpha(1f);
+            findViewById(R.id.switchAlramRemoveMember).setAlpha(1f);
+        }else{
+            findViewById(R.id.switchAlramAddDevice).setAlpha(0.5f);
+            findViewById(R.id.switchAlramDeleteDevice).setAlpha(0.5f);
+            findViewById(R.id.switchAlramAddMember).setAlpha(0.5f);
+            findViewById(R.id.switchAlramRemoveMember).setAlpha(0.5f);
+        }
+
+
+
+        /**
+         * 스마트 조명추가 알람
+         */
+        Switch switchAlramAddDevice = (Switch)findViewById(R.id.switchAlramAddDevice);
+        switchAlramAddDevice.setEnabled(switchAlramAddDevice.isChecked());
+        switchAlramAddDevice.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if(isChecked)
+                    SharedPrefUtil.putBoolean(NOTI_ADD_DEVICE, true);
+                else
+                    SharedPrefUtil.putBoolean(NOTI_ADD_DEVICE, false);
+            }
+        });
+        switchAlramAddDevice.setChecked(SharedPrefUtil.getBoolean(NOTI_ADD_DEVICE, DEFAULT_NOTI_ADD_DEVICE));
+
+        /**
+         * 스마트 조명삭제 알람
+         */
+        Switch switchAlramDeleteDevice = (Switch)findViewById(R.id.switchAlramDeleteDevice);
+        switchAlramDeleteDevice.setEnabled(switchAlramAddDevice.isChecked());
+        switchAlramDeleteDevice.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if(isChecked)
+                    SharedPrefUtil.putBoolean(NOTI_DELETE_DEVICE, true);
+                else
+                    SharedPrefUtil.putBoolean(NOTI_DELETE_DEVICE, false);
+            }
+        });
+        switchAlramDeleteDevice.setChecked(SharedPrefUtil.getBoolean(NOTI_DELETE_DEVICE, DEFAULT_NOTI_DELETE_DEVICE));
+
+        /**
+         * 멤버추가 알람
+         */
+        Switch switchAlramAddMember = (Switch)findViewById(R.id.switchAlramAddMember);
+        switchAlramAddMember.setEnabled(switchAlramAddDevice.isChecked());
+        switchAlramAddMember.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if(isChecked)
+                    SharedPrefUtil.putBoolean(NOTI_ADD_MEMBER, true);
+                else
+                    SharedPrefUtil.putBoolean(NOTI_ADD_MEMBER, false);
+            }
+        });
+        switchAlramAddMember.setChecked(SharedPrefUtil.getBoolean(NOTI_ADD_MEMBER, DEFAULT_NOTI_ADD_MEMBER));
+
+        /**
+         * 멤버삭제 알람
+         */
+        Switch switchAlramRemoveMember = (Switch)findViewById(R.id.switchAlramRemoveMember);
+        switchAlramRemoveMember.setEnabled(switchAlramAddDevice.isChecked());
+        switchAlramRemoveMember.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if(isChecked)
+                    SharedPrefUtil.putBoolean(NOTI_REMOVE_MEMBER, true);
+                else
+                    SharedPrefUtil.putBoolean(NOTI_REMOVE_MEMBER, false);
+            }
+        });
+        switchAlramRemoveMember.setChecked(SharedPrefUtil.getBoolean(NOTI_REMOVE_MEMBER, DEFAULT_NOTI_REMOVE_MEMBER));
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+        }
+    }
+
+    private void changeProfile(final boolean pushAccess) {
+        try {
+            HttpApi.changeUserInfo("", "", "", pushAccess, UserInfo.autoUpdate, "", new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onFailure)\n" + e.toString());
+                    showNotiMsg(AlarmSettingActivity.this, "알람 설정을 실패하였습니다.");
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) HTTP_NO_CONTENT");
+                        if(pushAccess) {
+                            showNotiMsg(AlarmSettingActivity.this, "알람을 설정 하였습니다.");
+                        } else {
+                            showNotiMsg(AlarmSettingActivity.this, "알람을 해제 하였습니다.");
+                        }
+                        UserInfo.pushAccess = pushAccess;
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) JSONException : " + e.toString());
+                        }
+                        showNotiMsg(AlarmSettingActivity.this, "알람 설정을 실패하였습니다.");
+                    }
+                }
+            });
+        } catch (Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(Exception)\n" + e.toString());
+            showNotiMsg(AlarmSettingActivity.this, "알람 설정을 실패하였습니다.");
+        }
+    }
+}

+ 110 - 0
app/src/main/java/kr/co/icontrols/iotplatform/AppInfoActivity.java

@@ -0,0 +1,110 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.DATABASE_NAME;
+import static kr.co.icontrols.iotplatform.PopupActivity.DEFAULT_POPUP_TIMEOUT;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INIT_APP_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_LOGOUT_SUCCESS;
+
+public class AppInfoActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_app_info);
+
+        try {
+            String currentVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
+            ((TextView)findViewById(R.id.txtCurrentVersion)).setText(getString(R.string.current_version) + " " + currentVersion);
+        } catch (PackageManager.NameNotFoundException e){
+            e.printStackTrace();
+        }
+
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnInit:
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_init_app_confirm));
+                intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.popup_init_app_confirm_ok));
+                intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.no));
+                startActivityForResult(intent, RESULT_INIT_APP_CONFIRM);
+                break;
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_INIT_APP_CONFIRM:
+                if (resultCode == Activity.RESULT_OK) {
+                    SharedPrefUtil.reset();
+                    DBUtil.removeAll();
+                    deleteDatabase(DATABASE_NAME);
+                    IotPlatformApplication.getInstance().clearApplicationData();
+
+                    Intent intent = new Intent(this, PopupActivity.class);
+                    intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL);
+                    intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_success_init_app));
+                    intent.putExtra(PopupActivity.POPUP_TIMEOUT, DEFAULT_POPUP_TIMEOUT);
+                    startActivityForResult(intent, RESULT_LOGOUT_SUCCESS);
+                }
+                break;
+            case RESULT_LOGOUT_SUCCESS:
+                if (resultCode == Activity.RESULT_OK) {
+                    Intent intent = new Intent(this, SplashActivity.class);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    startActivity(intent);
+                    finish();
+                }
+                break;
+        }
+    }
+}

+ 187 - 0
app/src/main/java/kr/co/icontrols/iotplatform/ChangePasswordActivity.java

@@ -0,0 +1,187 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.GenericHandler;
+
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isPasswordValid;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setupUI;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+
+public class ChangePasswordActivity extends AppCompatActivity implements View.OnClickListener, TextWatcher{
+
+    private EditText txtPassword, txtNewPassword, txtComfirmPassword;
+
+    private AlertDialog userDialog;
+    private ProgressDialog waitDialog;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_change_password);
+
+        setupUI(this,findViewById(R.id.layoutMain));
+
+        txtPassword = findViewById(R.id.txtPassword);
+        txtNewPassword = findViewById(R.id.txtNewPassword);
+        txtComfirmPassword = findViewById(R.id.txtComfirmPassword);
+
+        txtPassword.addTextChangedListener(this);
+        txtNewPassword.addTextChangedListener(this);
+        txtComfirmPassword.addTextChangedListener(this);
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+            case R.id.btnBackText:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+            case R.id.btnOk:
+                if(txtPassword == null || txtPassword.getText().toString().isEmpty()) {
+                    Toast.makeText(this,"현재 비밀번호 없음",Toast.LENGTH_SHORT).show();
+                } else if(txtNewPassword == null || txtNewPassword.getText().toString().isEmpty()) {
+                    Toast.makeText(this,"새로운 비밀번호 없음",Toast.LENGTH_SHORT).show();
+                } else if(txtComfirmPassword == null || txtComfirmPassword.getText().toString().isEmpty()) {
+                    Toast.makeText(this,getString(R.string.input_password_comfirm),Toast.LENGTH_SHORT).show();
+                } else if(!(txtNewPassword.getText().toString().equals(txtComfirmPassword.getText().toString()))) {
+                    Toast.makeText(this,getString(R.string.not_same_password),Toast.LENGTH_SHORT).show();
+                } else if(!isPasswordValid(txtPassword.getText().toString())) {
+                    Toast.makeText(this,getString(R.string.invalid_password),Toast.LENGTH_SHORT).show();
+                } else {
+                    String cPass = txtPassword.getText().toString();
+                    String nPass = txtNewPassword.getText().toString();
+                    displayProgress(ChangePasswordActivity.this,"","");//showWaitDialog("Changing password...");
+                    AppHelper.getPool().getUser(AppHelper.getCurrUser()).changePasswordInBackground(cPass, nPass, callback);
+                }
+                break;
+        }
+    }
+
+    GenericHandler callback = new GenericHandler() {
+        @Override
+        public void onSuccess() {
+            DismissConnectDialog();//closeWaitDialog();
+            //showDialogMessage("Success!","Password has been changed",true);
+            showNotiMsg(ChangePasswordActivity.this,"Your password was changed");
+            clearInput();
+        }
+
+        @Override
+        public void onFailure(Exception exception) {
+            DismissConnectDialog();//closeWaitDialog();
+            showDialogMessage("Password change failed", AppHelper.formatException(exception), false);
+        }
+    };
+
+    private  void clearInput() {
+        txtPassword.setText("");
+        txtNewPassword.setText("");
+        txtComfirmPassword.setText("");
+    }
+
+    private void showDialogMessage(String title, String body, final boolean exitActivity) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(title).setMessage(body).setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                    if (exitActivity) {
+                        onBackPressed();
+                    }
+                } catch (Exception e) {
+                    onBackPressed();
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private void showWaitDialog(String message) {
+        closeWaitDialog();
+        waitDialog = new ProgressDialog(this);
+        waitDialog.setTitle(message);
+        waitDialog.show();
+    }
+
+    private void closeWaitDialog() {
+        try {
+            waitDialog.dismiss();
+        }
+        catch (Exception e) {
+            // Wait dialog is already closed or does not exist
+        }
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        if(txtComfirmPassword.length() > 0 && txtNewPassword.length() > 0 && txtPassword.length() > 0) {
+            findViewById(R.id.btnBack).setVisibility(View.GONE);
+            findViewById(R.id.btnBackText).setVisibility(View.VISIBLE);
+            findViewById(R.id.btnRight).setVisibility(View.VISIBLE);
+        } else {
+            findViewById(R.id.btnBack).setVisibility(View.VISIBLE);
+            findViewById(R.id.btnBackText).setVisibility(View.GONE);
+            findViewById(R.id.btnRight).setVisibility(View.INVISIBLE);
+        }
+    }
+}

+ 7 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/AddressInfo.java

@@ -0,0 +1,7 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+public class AddressInfo {
+    public String city;
+    public String district;
+    public String town;
+}

+ 78 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/DeviceGroupInfo.java

@@ -0,0 +1,78 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.Serializable;
+
+public class DeviceGroupInfo implements Serializable, Parcelable{
+    public String dgroupId;
+    public String friendlyName;
+    public String icon;
+    public int index;
+    public int deviceCount;
+    public int isSelected;
+
+    public DeviceGroupInfo(String dgroupId,
+                     String friendlyName,
+                     String icon,
+                     int index) {
+        this.dgroupId = dgroupId;
+        this.friendlyName = friendlyName;
+        this.icon = icon;
+        this.index = index;
+        this.deviceCount = 0;
+        this.isSelected = 0;
+    }
+
+    public DeviceGroupInfo(DeviceGroupInfo item) {
+        this.dgroupId = item.dgroupId;
+        this.friendlyName = item.friendlyName;
+        this.icon = item.icon;
+        this.index = item.index;
+        this.deviceCount = item.deviceCount;
+        this.isSelected = item.isSelected;
+    }
+
+    public DeviceGroupInfo(Parcel item) {
+        this.dgroupId = item.readString();
+        this.friendlyName = item.readString();
+        this.icon = item.readString();
+        this.index = item.readInt();
+        this.deviceCount = item.readInt();
+        this.isSelected = item.readInt();
+    }
+
+    public boolean isSelected() {
+        return isSelected != 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.dgroupId);
+        dest.writeString(this.friendlyName);
+        dest.writeString(this.icon);
+        dest.writeInt(this.index);
+        dest.writeInt(this.deviceCount);
+        dest.writeInt(this.isSelected);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+
+        @Override
+        public DeviceGroupInfo createFromParcel(Parcel in) {
+            return new DeviceGroupInfo(in);
+        }
+
+        @Override
+        public DeviceGroupInfo[] newArray(int size) {
+            return new DeviceGroupInfo[size];
+        }
+    };
+}

+ 108 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/DeviceInfo.java

@@ -0,0 +1,108 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.Serializable;
+
+public class DeviceInfo implements Serializable, Parcelable {
+    public String deviceId;
+    public String deviceType;
+    public String friendlyName;
+    public String sharedFriendlyName;
+    public String dgroupId;
+    public String dgroupName;
+    public String ownerId;
+    public int isSelected;
+    public int rank;
+    public int groupRank;
+
+    public DeviceInfo() {
+        isSelected = 0;
+    }
+
+    public DeviceInfo(
+            String deviceId,
+            String deviceType,
+            String friendlyName,
+            String sharedFriendlyName,
+            String dgroupId,
+            String dgroupName,
+            String ownerId,
+            int rank,
+            int groupRank) {
+        this.deviceId = deviceId;
+        this.deviceType = deviceType;
+        this.friendlyName = friendlyName;
+        this.sharedFriendlyName = sharedFriendlyName;
+        this.dgroupId = dgroupId;
+        this.dgroupName = dgroupName;
+        this.ownerId = ownerId;
+        this.rank = rank;
+        this.groupRank = groupRank;
+        this.isSelected = 0;
+    }
+
+    public DeviceInfo(DeviceInfo item) {
+        this.deviceId = item.deviceId;
+        this.deviceType = item.deviceType;
+        this.friendlyName = item.friendlyName;
+        this.sharedFriendlyName = item.sharedFriendlyName;
+        this.dgroupId = item.dgroupId;
+        this.dgroupName = item.dgroupName;
+        this.ownerId = item.ownerId;
+        this.rank = item.rank;
+        this.groupRank = item.groupRank;
+        this.isSelected = item.isSelected;
+    }
+
+    public DeviceInfo(Parcel item) {
+        this.deviceId = item.readString();
+        this.deviceType = item.readString();
+        this.friendlyName = item.readString();
+        this.sharedFriendlyName = item.readString();
+        this.dgroupId = item.readString();
+        this.dgroupName = item.readString();
+        this.ownerId = item.readString();
+        this.rank = item.readInt();
+        this.groupRank = item.readInt();
+        this.isSelected = item.readInt();
+    }
+
+    public boolean isSelected() {
+        return isSelected != 0;
+    }
+    
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.deviceId);
+        dest.writeString(this.deviceType);
+        dest.writeString(this.friendlyName);
+        dest.writeString(this.sharedFriendlyName);
+        dest.writeString(this.dgroupId);
+        dest.writeString(this.dgroupName);
+        dest.writeString(this.ownerId);
+        dest.writeInt(this.rank);
+        dest.writeInt(this.groupRank);
+        dest.writeInt(this.isSelected);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+
+        @Override
+        public DeviceInfo createFromParcel(Parcel in) {
+            return new DeviceInfo(in);
+        }
+
+        @Override
+        public DeviceInfo[] newArray(int size) {
+            return new DeviceInfo[size];
+        }
+    };
+}

+ 24 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/InviteInfo.java

@@ -0,0 +1,24 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+public class InviteInfo {
+    public String histId;
+    public String inviteType;
+    public String sender;
+    public String target;
+    public String inviteDate;
+    public String expireDate;
+
+    public InviteInfo (String histId,
+        String inviteType,
+        String sender,
+        String target,
+        String inviteDate,
+        String expireDate) {
+        this.histId = histId;
+        this.inviteType = inviteType;
+        this.sender = sender;
+        this.target = target;
+        this.inviteDate =inviteDate;
+        this.expireDate = expireDate;
+    }
+}

+ 18 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/KakaoLocationInfo.java

@@ -0,0 +1,18 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+public class KakaoLocationInfo {
+    public String address_name;
+    public String b_code;
+    public String h_code;
+    public String main_address_no;
+    public String mountain_yn;
+    public String region_1depth_name;
+    public String region_2depth_name;
+    public String region_3depth_h_name;
+    public String region_3depth_name;
+    public String sub_address_no;
+    public String x;
+    public String y;
+    public String address_type;
+    public String road_address;
+}

+ 10 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/LocationInfo.java

@@ -0,0 +1,10 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+import java.util.ArrayList;
+
+public class LocationInfo {
+    public String locationId;
+    public double latitude;
+    public double longitude;
+    public AddressInfo address = new AddressInfo();
+}

+ 13 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/MemberInfo.java

@@ -0,0 +1,13 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+public class MemberInfo {
+    public String customerId; // 게스트유저 고유아이디
+    public String nickname; // 게스트유저 닉네임
+    public String profile; // 게스트유저 프로필 이미지
+
+    public MemberInfo(String c, String n, String p) {
+        this.customerId = c;
+        this.nickname = n;
+        this.profile = p;
+    }
+}

+ 490 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/UserInfo.java

@@ -0,0 +1,490 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+import android.app.Activity;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.restartApp;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.DEFAULT_RANK;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_UNAUTHORIZED;
+
+public class UserInfo {
+
+    private static String TAG = "UserInfo";
+
+    public static String uid;
+    public static String email;
+    public static String phoneNumber;
+    public static String nickname;
+    public static String profile;
+    public static boolean confirmed;
+    public static boolean pushAccess;
+    public static boolean autoUpdate;
+    public static ArrayList<DeviceGroupInfo> dgroups = new ArrayList<>();
+    public static ArrayList<DeviceInfo> devices = new ArrayList<>();
+    public static ArrayList<LocationInfo> location = new ArrayList<>();
+    public static ArrayList<String> dgroupsString = new ArrayList<>();
+    public static int dgroupsCount;
+    public static ArrayList<String> devicesString = new ArrayList<>();
+    public static int deviceCount;
+    public static ArrayList<WeatherInfo> weatherInfos = new ArrayList<>();
+    public static boolean isSendPushToken = false;
+    public static boolean isCheckInvite = false;
+
+    private static long VALID_TIME = 500;//10 * 60 * 1000;
+    public static long updateTimeUserInfo = -1;
+    public static long updateTimeDevice = -1;
+    public static long updateTimeGroup = -1;
+    public static long updateTimeWeatherInfo = -1;
+
+    public interface CallbackForUpdate {
+        public void updateData();
+    }
+
+    public static void clear(){
+        uid= "";
+        email= "";
+        phoneNumber= "";
+        nickname= "";
+        profile= "";
+        confirmed= false;
+        pushAccess= false;
+        autoUpdate= false;
+        dgroups.clear();
+        devices.clear();
+        location.clear();
+        dgroupsString.clear();
+        dgroupsCount= 0;
+        devicesString.clear();
+        deviceCount= 0;
+    }
+
+    public static void setUnSelectAllDevices() {
+        for(int i = 0 ; i < devices.size() ; i++) {
+            devices.get(i).isSelected = 0;
+        }
+    }
+
+    public static void setUnSelectAllGroups() {
+        for(int i = 0 ; i < dgroups.size() ; i++) {
+            dgroups.get(i).isSelected = 0;
+        }
+    }
+
+    public static void getUserInfo(Activity activity, CallbackForUpdate callback) {
+        if(confirmed && ((System.currentTimeMillis() - updateTimeUserInfo) <= VALID_TIME)) {
+            if(callback != null) {
+                callback.updateData();
+            }
+        } else {
+            displayProgress(activity, null, null);
+            try {
+                HttpApi.getUserInfo(new okhttp3.Callback() {
+                    @Override
+                    public void onFailure(Call call, IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.getUserInfo(onFailure) : " + e.toString());
+                        DismissConnectDialog();
+                        showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+                    }
+
+                    @Override
+                    public void onResponse(Call call, Response response) throws IOException {
+                        if (response.code() == HTTP_OK) {
+                            WarnLogOutput(TAG, "HttpApi.getUserInfo(onResponse) : HTTP_OK");
+                            updateTimeUserInfo = System.currentTimeMillis();
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+
+                                uid = json_data.isNull("uid") ? "" : json_data.getString("uid");
+                                email = json_data.isNull("email") ? "" : json_data.getString("email");
+                                phoneNumber = json_data.isNull("phoneNumber") ? "" : json_data.getString("phoneNumber");
+                                nickname = json_data.isNull("nickname") ? "" : json_data.getString("nickname");
+                                profile = json_data.isNull("profile") ? "" : json_data.getString("profile");
+                                confirmed = json_data.isNull("confirmed") ? false : json_data.getBoolean("confirmed");
+                                pushAccess = json_data.isNull("pushAccess") ? false : json_data.getBoolean("pushAccess");
+                                autoUpdate = json_data.isNull("autoUpdate") ? false : json_data.getBoolean("autoUpdate");
+
+                                if (!json_data.isNull("locations")) {
+                                    location.clear();
+                                    JSONArray jarrayLocationInfo = new JSONArray(json_data.getString("locations"));
+                                    for (int i = 0; i < jarrayLocationInfo.length(); i++) {
+                                        JSONObject jObjectLocationInfo = jarrayLocationInfo.getJSONObject(i);
+                                        LocationInfo locationInfo = new LocationInfo();
+
+                                        locationInfo.locationId = jObjectLocationInfo.isNull("locationId") ? "" : jObjectLocationInfo.getString("locationId");
+                                        locationInfo.latitude = jObjectLocationInfo.isNull("latitude") ? 0 : jObjectLocationInfo.getDouble("latitude");
+                                        locationInfo.longitude = jObjectLocationInfo.isNull("longitude") ? 0 : jObjectLocationInfo.getDouble("longitude");
+
+                                        if (!jObjectLocationInfo.isNull("address")) {
+                                            JSONObject jObjectAddressInfo = new JSONObject(jObjectLocationInfo.getString("address"));
+
+                                            locationInfo.address.city = jObjectAddressInfo.isNull("city") ? "" : jObjectAddressInfo.getString("city");
+                                            locationInfo.address.district = jObjectAddressInfo.isNull("district") ? "" : jObjectAddressInfo.getString("district");
+                                            locationInfo.address.town = jObjectAddressInfo.isNull("town") ? "" : jObjectAddressInfo.getString("town");
+                                        }
+                                        location.add(locationInfo);
+                                    }
+                                }
+
+                                if (!json_data.isNull("dgroups")) {
+                                    dgroupsString.clear();
+                                    JSONObject jObjectDgroupInfo = new JSONObject(json_data.getString("dgroups"));
+                                    if (!jObjectDgroupInfo.isNull("results")) {
+                                        JSONArray arrJson = jObjectDgroupInfo.getJSONArray("results");
+                                        for (int i = 0; i < arrJson.length(); i++) {
+                                            dgroupsString.add(arrJson.getString(i));
+                                        }
+                                    }
+                                    dgroupsCount = jObjectDgroupInfo.isNull("count") ? 0 : jObjectDgroupInfo.getInt("count");
+                                }
+
+                                if (!json_data.isNull("devices")) {
+                                    devicesString.clear();
+                                    JSONObject jObjectDeviceInfo = new JSONObject(json_data.getString("devices"));
+                                    if (!jObjectDeviceInfo.isNull("results")) {
+                                        JSONArray arrJson = jObjectDeviceInfo.getJSONArray("results");
+                                        for (int i = 0; i < arrJson.length(); i++) {
+                                            devicesString.add(arrJson.getString(i));
+                                        }
+                                    }
+                                    deviceCount = jObjectDeviceInfo.isNull("count") ? 0 : jObjectDeviceInfo.getInt("count");
+                                }
+                                if(callback != null) {
+                                    callback.updateData();
+                                }
+                                DismissConnectDialog();
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.getUserInfo(JSONException)\n" + e.toString());
+                                DismissConnectDialog();
+                                showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+                            }
+                        } else if(response.code() == HTTP_UNAUTHORIZED) {
+                            showNotiMsg(activity, activity.getString(R.string.snackbar_expired_token));
+                            restartApp(activity);
+                        } else {
+                            ErrorLogOutput(TAG, "HttpApi.getUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                            showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+                            DismissConnectDialog();
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                ErrorLogOutput(TAG, "HttpApi.getUserInfo(onResponse) body : " + json_data.toString());
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.getUserInfo(onResponse) JSONException : " + e.toString());
+                            }
+                        }
+                    }
+                });
+            } catch (Exception e) {
+                e.printStackTrace();
+                ErrorLogOutput(TAG, "HttpApi.getUserInfo(Exception) : " + e.toString());
+                showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+                DismissConnectDialog();
+            }
+        }
+    }
+
+    public static void getWeatherInfo(Activity activity, String locationId, CallbackForUpdate callback) {
+        if(weatherInfos.size() > 0 && ((System.currentTimeMillis() - updateTimeWeatherInfo) <= VALID_TIME)) {
+            if(callback != null) {
+                callback.updateData();
+            }
+        } else {
+            displayProgress(activity, null, null);
+            try {
+                HttpApi.getWeatherInfo(locationId, new okhttp3.Callback() {
+                    @Override
+                    public void onFailure(Call call, IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onFailure) : " + e.toString());
+                        showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+
+                        if (callback != null) {
+                            callback.updateData();
+                        }
+                    }
+
+                    @Override
+                    public void onResponse(Call call, Response response) throws IOException {
+                        if (response.code() == HTTP_OK) {
+                            WarnLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) : HTTP_OK");
+                            updateTimeWeatherInfo = System.currentTimeMillis();
+                            weatherInfos.clear();
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+
+                                WeatherInfo weatherInfo = new WeatherInfo();
+
+                                weatherInfo.o3Value = json_data.isNull("o3Value") ? "-1" : json_data.getString("o3Value");
+                                weatherInfo.o3Grad = json_data.isNull("o3Grad") ? "-1" : json_data.getString("o3Grad");
+                                weatherInfo.pm10Value = json_data.isNull("pm10Value") ? "-1" : json_data.getString("pm10Value");
+                                weatherInfo.pm10Grade = json_data.isNull("pm10Grade") ? "-1" : json_data.getString("pm10Grade");
+                                weatherInfo.pm25Value = json_data.isNull("pm25Value") ? "-1" : json_data.getString("pm25Value");
+                                weatherInfo.pm25Grade = json_data.isNull("pm25Grade") ? "-1" : json_data.getString("pm25Grade");
+                                weatherInfo.adDatetime = json_data.isNull("adDatetime") ? "-1" : json_data.getString("adDatetime");
+
+                                weatherInfo.sky = json_data.isNull("sky") ? "-1" : json_data.getString("sky");
+                                weatherInfo.pty = json_data.isNull("pty") ? "-1" : json_data.getString("pty");
+                                weatherInfo.rn1 = json_data.isNull("rn1") ? "-1" : json_data.getString("rn1");
+                                weatherInfo.reh = json_data.isNull("reh") ? "-999" : json_data.getString("reh");
+                                weatherInfo.t1h = json_data.isNull("t1h") ? "-999" : json_data.getString("t1h");
+                                weatherInfo.wdDatetime = json_data.isNull("wdDatetime") ? "" : json_data.getString("wdDatetime");
+
+                                weatherInfos.add(weatherInfo);
+
+                                if (callback != null) {
+                                    callback.updateData();
+                                }
+
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(JSONException)\n" + e.toString());
+                                DismissConnectDialog();
+                                showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+
+                                if (callback != null) {
+                                    callback.updateData();
+                                }
+                            }
+                        } else if(response.code() == HTTP_UNAUTHORIZED) {
+                            showNotiMsg(activity, activity.getString(R.string.snackbar_expired_token));
+                            restartApp(activity);
+                        } else {
+                            ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                            showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+                            DismissConnectDialog();
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) body : " + json_data.toString());
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) JSONException : " + e.toString());
+                            }
+
+
+                            if (callback != null) {
+                                callback.updateData();
+                            }
+                        }
+                    }
+                });
+            } catch (Exception e) {
+                e.printStackTrace();
+                ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(Exception) : " + e.toString());
+                showNotiMsg(activity, activity.getString(R.string.snackbar_error_server));
+                DismissConnectDialog();
+
+                if (callback != null) {
+                    callback.updateData();
+                }
+            }
+        }
+    }
+
+    public static void getDevice(Activity activity, CallbackForUpdate callback) {
+        if(devices.size() > 0 && ((System.currentTimeMillis() - updateTimeDevice) <= VALID_TIME)) {
+            if(callback != null) {
+                callback.updateData();
+            }
+        } else {
+            devices.clear();
+            displayProgress(activity, null,null);
+            try{
+                HttpApi.getAllDevice(new okhttp3.Callback() {
+                    @Override
+                    public void onFailure(Call call, IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.getAllDevice(onFailure)\n" + e.toString());
+                        DismissConnectDialog();
+                        showNotiMsg(activity,"스마트 조명 목록 조회에 실패하였습니다");
+                        if(callback != null) {
+                            callback.updateData();
+                        }
+                    }
+
+                    @Override
+                    public void onResponse(Call call, Response response) throws IOException {
+                        if(response.code() == HTTP_OK) {
+                            WarnLogOutput(TAG, "HttpApi.getAllDevice(onResponse) : HTTP_OK");
+                            updateTimeDevice = System.currentTimeMillis();
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+
+                                UserInfo.deviceCount = json_data.isNull("count") ? 0 : json_data.getInt("count");
+
+                                if(UserInfo.deviceCount > 0) {
+                                    JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                                    for (int i = 0; i < jarrayAll.length(); i++) {
+                                        JSONObject jObject = jarrayAll.getJSONObject(i);
+                                        DeviceInfo item = new DeviceInfo(
+                                                jObject.isNull("deviceId") ? "" : jObject.getString("deviceId"),
+                                                jObject.isNull("deviceType") ? "" : jObject.getString("deviceType"),
+                                                jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                                jObject.isNull("sharedFriendlyName") ? "" : jObject.getString("sharedFriendlyName"),
+                                                jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                                jObject.isNull("dgroupName") ? "" : jObject.getString("dgroupName"),
+                                                jObject.isNull("ownerId") ? "" : jObject.getString("ownerId"),
+                                                DEFAULT_RANK,
+                                                DEFAULT_RANK
+                                        );
+
+                                        DeviceInfo localDbItem = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK, item.deviceId);
+                                        if (localDbItem == null) {
+                                            DBUtil.putDeviceInfo(TABLE_DEVICE_RANK, item);
+                                        } else {
+                                            item.rank = localDbItem.rank;
+                                            item.groupRank = localDbItem.groupRank;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_RANK, item);
+                                        }
+                                    }
+
+                                    ArrayList<DeviceInfo> deviceInfos = DBUtil.getAllDeviceInfos(TABLE_DEVICE_RANK);
+                                    for (DeviceInfo d : deviceInfos) {
+                                        devices.add(d);
+                                    }
+                                    deviceInfos.clear();
+                                }
+
+                                if(callback != null) {
+                                    callback.updateData();
+                                }
+                                DismissConnectDialog();
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.getAllDevice(JSONException)\n" + e.toString());
+                                showNotiMsg(activity,"스마트 조명 목록 조회에 실패하였습니다");
+                                DismissConnectDialog();
+                                if(callback != null) {
+                                    callback.updateData();
+                                }
+                            }
+                        } else if(response.code() == HTTP_UNAUTHORIZED) {
+                            showNotiMsg(activity, activity.getString(R.string.snackbar_expired_token));
+                            restartApp(activity);
+                        } else {
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                            showNotiMsg(activity,"스마트 조명 목록 조회에 실패하였습니다 : " + response.code());
+                            DismissConnectDialog();
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) body : " + json_data.toString());
+                            } catch (JSONException e) {
+                                ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) JSONException : " + e.toString());
+                            }
+                            if(callback != null) {
+                                callback.updateData();
+                            }
+                        }
+                    }
+                });
+            } catch(Exception e){
+                e.printStackTrace();
+                ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(Exception)\n" + e.toString());
+                showNotiMsg(activity,"스마트 조명 목록 조회에 실패하였습니다");
+                DismissConnectDialog();
+                if(callback != null) {
+                    callback.updateData();
+                }
+            }
+        }
+    }
+
+    public static void getGroup(Activity activity, CallbackForUpdate callback) {
+        dgroups.clear();
+        if(dgroups.size() > 0 && ((System.currentTimeMillis() - updateTimeGroup) <= VALID_TIME)) {
+            if(callback != null) {
+                callback.updateData();
+            }
+        } else {
+            displayProgress(activity,"","");
+            try {
+                HttpApi.getDeviceGroupList(new okhttp3.Callback() {
+                    @Override
+                    public void onFailure(Call call, IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onFailure)\n" + e.toString());
+                        DismissConnectDialog();
+                        if(callback != null) {
+                            callback.updateData();
+                        }
+                    }
+
+                    @Override
+                    public void onResponse(Call call, Response response) throws IOException {
+                        if(response.code() == HTTP_OK) {
+                            updateTimeGroup = System.currentTimeMillis();
+                            WarnLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) : HTTP_OK");
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                                for (int i = 0; i < jarrayAll.length(); i++) {
+                                    JSONObject jObject = jarrayAll.getJSONObject(i);
+                                    DeviceGroupInfo item = new DeviceGroupInfo(jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                            jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                            jObject.isNull("icon") ? "" : jObject.getString("icon"),
+                                            jObject.isNull("index") ? 99 : jObject.getInt("index"));
+
+                                    dgroups.add(item);
+                                }
+                                if(callback != null) {
+                                    callback.updateData();
+                                }
+                                DismissConnectDialog();
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(JSONException)\n" + e.toString());
+                                DismissConnectDialog();
+                                if(callback != null) {
+                                    callback.updateData();
+                                }
+                            }
+                        } else if(response.code() == HTTP_UNAUTHORIZED) {
+                            showNotiMsg(activity, activity.getString(R.string.snackbar_expired_token));
+                            restartApp(activity);
+                        } else {
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) body : " + json_data.toString());
+                            } catch (JSONException e) {
+                                ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) JSONException : " + e.toString());
+                            }
+                            DismissConnectDialog();
+                            if(callback != null) {
+                                callback.updateData();
+                            }
+                        }
+                    }
+                });
+            } catch (Exception e) {
+                e.printStackTrace();
+                ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(Exception)\n" + e.toString());
+                DismissConnectDialog();
+                if(callback != null) {
+                    callback.updateData();
+                }
+            }
+        }
+    }
+
+}

+ 17 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DataClass/WeatherInfo.java

@@ -0,0 +1,17 @@
+package kr.co.icontrols.iotplatform.DataClass;
+
+public class WeatherInfo {
+    public String o3Value; //오존 상태 값
+    public String o3Grad; //오존 상태 등급 ( “1” : 좋음, “2”: 보통, “3”: 나쁨, “4”: 매우나쁨 )
+    public String pm10Value; //미세먼지 상태 값
+    public String pm10Grade; //미세먼지 상태 등급 ( “1” : 좋음, “2”: 보통, “3”: 나쁨, “4”: 매우나쁨 )
+    public String pm25Value; //초미세먼지 상태 값
+    public String pm25Grade; //초미세먼지 상태 등급 ( “1” : 좋음, “2”: 보통, “3”: 나쁨, “4”: 매우나쁨 )
+    public String adDatetime; //공기질 데이터 조회시간 (형식: 2020-08-20 15:00 )
+    public String sky; //하늘 상태
+    public String pty; //강수 형태
+    public String rn1; //시간당 강수량
+    public String reh; //습도
+    public String t1h; //기온
+    public String wdDatetime; //날씨 데이터 조회시간 (형식: 2020-08-20 15:00 )
+}

+ 243 - 0
app/src/main/java/kr/co/icontrols/iotplatform/Database/DBUtil.java

@@ -0,0 +1,243 @@
+package kr.co.icontrols.iotplatform.Database;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.provider.BaseColumns;
+
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_GROUP_ID;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_GROUP_NAME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_GROUP_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_ID;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_NAME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_OWNER_ID;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_SHARED_NAME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.COLUMN_TYPE;
+
+/**
+ * DB 관리 Class
+ */
+public class DBUtil {
+    public static final String TAG = DBUtil.class.getSimpleName();
+
+    private static DBUtil sInstance = null;
+
+    private static Context mContext;
+
+    private static String[] mColumns = new String[] {
+            BaseColumns._ID,
+            COLUMN_ID,
+            COLUMN_NAME,
+            COLUMN_GROUP_ID,
+            COLUMN_GROUP_NAME,
+            COLUMN_SHARED_NAME,
+            COLUMN_OWNER_ID,
+            COLUMN_TYPE,
+            COLUMN_RANK,
+            COLUMN_GROUP_RANK,
+    };
+
+    private DBUtil(Context context) {
+        mContext = context;
+    }
+
+    public static void init(Context context) {
+        if (sInstance == null) {
+            sInstance = new DBUtil(context);
+        }
+    }
+
+    /**
+     * DB의 정보를 DeviceInfo class에 맞게 변경.
+     * @param c
+     * @return
+     */
+    private static DeviceInfo cursorToDeviceInfo(Cursor c) {
+        DeviceInfo deviceInfo = new DeviceInfo();
+        deviceInfo.deviceId = c.getString(1);
+        deviceInfo.friendlyName = c.getString(2);
+        deviceInfo.dgroupId = c.getString(3);
+        deviceInfo.dgroupName = c.getString(4);
+        deviceInfo.sharedFriendlyName = c.getString(5);
+        deviceInfo.ownerId = c.getString(6);
+        deviceInfo.deviceType = c.getString(7);
+        deviceInfo.rank = c.getInt(8);
+        deviceInfo.groupRank = c.getInt(9);
+        return deviceInfo;
+    }
+
+    /**
+     * DEVICE ID 값을 이용한 디바이스 정보 획득.
+     * @param tableName DB 테이블 이름
+     * @param deviceId 디바이스 ID
+     */
+    synchronized public static DeviceInfo getDeviceInfoForId(String tableName ,String deviceId) {
+        SQLiteDatabase db = DatabaseHelper.getInstance().getReadableDatabase();
+
+        DeviceInfo deviceInfo = null;
+        String [] selectionArg = new String [] { deviceId };
+
+        Cursor c = db.query(tableName, mColumns, COLUMN_ID+" == ?", selectionArg, null, null, null);
+
+        if (c != null) {
+            if (c.getCount() > 0) {
+                c.moveToFirst();
+                deviceInfo = cursorToDeviceInfo(c);
+            }
+            c.close();
+        }
+        db.close();
+        return deviceInfo;
+    }
+
+    /**
+     * GROUP ID 값을 이용한 디바이스 정보 획득.
+     * @param tableName DB 테이블 이름
+     * @param groupId 그룹 ID
+     */
+    synchronized public static  ArrayList<DeviceInfo> getDeviceInfoForGroupId(String tableName ,String groupId) {
+        SQLiteDatabase db = DatabaseHelper.getInstance().getReadableDatabase();
+        ArrayList<DeviceInfo> deviceInfos = new ArrayList<>();
+        String [] selectionArg = new String [] { groupId };
+        Cursor c = db.query(tableName, mColumns, COLUMN_GROUP_ID+" == ?", selectionArg, null, null, null);
+        if (c.moveToFirst()) {
+            do {
+                deviceInfos.add(cursorToDeviceInfo(c));
+            } while (c.moveToNext());
+        }
+        c.close();
+        return deviceInfos;
+    }
+
+
+
+    /**
+     * DB의 모든 디바이스 정보 획득.
+     * @param tableName DB 테이블 이름
+     * @return
+     */
+    synchronized public static ArrayList<DeviceInfo> getAllDeviceInfos(String tableName) {
+
+        SQLiteDatabase db = DatabaseHelper.getInstance().getReadableDatabase();
+        ArrayList<DeviceInfo> deviceInfos = new ArrayList<>();
+
+        Cursor c = db.query(tableName, mColumns, null, null, null, null, COLUMN_RANK + " ASC, " + BaseColumns._ID + " DESC");
+        if (c.moveToFirst()) {
+            do {
+                deviceInfos.add(cursorToDeviceInfo(c));
+            } while (c.moveToNext());
+        }
+        c.close();
+        return deviceInfos;
+    }
+
+    /**
+     * DB의 모든 디바이스 정보 획득.
+     * @param tableName DB 테이블 이름
+     * @return
+     */
+    synchronized public static void getAllDeviceInfos(String tableName, ArrayList<DeviceInfo> deviceInfos) {
+        SQLiteDatabase db = DatabaseHelper.getInstance().getReadableDatabase();
+        Cursor c = db.query(tableName, mColumns, null, null, null, null, COLUMN_RANK + " ASC, " + BaseColumns._ID + " DESC");
+        if (c.moveToFirst()) {
+            do {
+                deviceInfos.add(cursorToDeviceInfo(c));
+            } while (c.moveToNext());
+        }
+        c.close();
+    }
+
+    /**
+     * DB에 디바이스 정보 추가.
+     * @param tableName DB 테이블 이름
+     * @param deviceInfo 디바이스 정보 Class
+     * @return
+     */
+    synchronized public static long putDeviceInfo(String tableName, DeviceInfo deviceInfo) {
+        SQLiteDatabase db = DatabaseHelper.getInstance().getWritableDatabase();
+        long _id = -1;
+        ContentValues cv = new ContentValues();
+        cv.put(COLUMN_ID, deviceInfo.deviceId);
+        cv.put(COLUMN_TYPE, deviceInfo.deviceType);
+        cv.put(COLUMN_GROUP_ID, deviceInfo.dgroupId);
+        cv.put(COLUMN_GROUP_NAME, deviceInfo.dgroupName);
+        cv.put(COLUMN_NAME, deviceInfo.friendlyName);
+        cv.put(COLUMN_OWNER_ID, deviceInfo.ownerId);
+        cv.put(COLUMN_RANK, deviceInfo.rank);
+        cv.put(COLUMN_GROUP_RANK, deviceInfo.groupRank);
+        cv.put(COLUMN_SHARED_NAME, deviceInfo.sharedFriendlyName);
+        _id = db.insert(tableName, null, cv);
+        db.close();
+        return _id;
+    }
+
+    /**
+     * 해당 상품 정보를 DB 업데이트
+     * @param deviceInfo
+     */
+    synchronized public static void updateDeviceItem(String tableName, DeviceInfo deviceInfo) {
+        SQLiteDatabase db = DatabaseHelper.getInstance().getWritableDatabase();
+        String [] selectionArg = new String [] { deviceInfo.deviceId };
+
+        ContentValues cv = new ContentValues();
+        cv.put(COLUMN_ID, deviceInfo.deviceId);
+        cv.put(COLUMN_TYPE, deviceInfo.deviceType);
+        cv.put(COLUMN_GROUP_ID, deviceInfo.dgroupId);
+        cv.put(COLUMN_GROUP_NAME, deviceInfo.dgroupName);
+        cv.put(COLUMN_NAME, deviceInfo.friendlyName);
+        cv.put(COLUMN_OWNER_ID, deviceInfo.ownerId);
+        cv.put(COLUMN_RANK, deviceInfo.rank);
+        cv.put(COLUMN_GROUP_RANK, deviceInfo.groupRank);
+        cv.put(COLUMN_SHARED_NAME, deviceInfo.sharedFriendlyName);
+        db.update(tableName, cv, COLUMN_ID+" == ?", selectionArg);
+        db.close();
+    }
+
+    /**
+     * DB에서 특정 디바이스 정보 삭제.
+     * @param tableName DB 테이블 이름
+     * @param deviceInfo 디바이스 정보 Class
+     */
+    synchronized public static void removeDeviceInfo(String tableName, DeviceInfo deviceInfo) {
+        String [] selectionArg = new String [] { deviceInfo.deviceId };
+        SQLiteDatabase db = DatabaseHelper.getInstance().getWritableDatabase();
+        db.delete(tableName, COLUMN_ID+" == ?", selectionArg);
+        db.close();
+    }
+
+    /**
+     *  DB의 특정 Table의 모든 데이터 삭제.
+     */
+    synchronized public static void removeAllDeviceInfos(String tableName) {
+        SQLiteDatabase db = DatabaseHelper.getInstance().getWritableDatabase();
+        db.delete(tableName, null, null);
+        db.close();
+    }
+
+    synchronized public static void removeDeviceInfoAtAllTable(String deviceId) {
+        String [] selectionArg = new String [] { deviceId };
+        SQLiteDatabase db = DatabaseHelper.getInstance().getWritableDatabase();
+        db.delete(DatabaseHelper.TABLE_SMART_LIGHT, COLUMN_ID+" == ?", selectionArg);
+        db.delete(DatabaseHelper.TABLE_DEVICE_HOME, COLUMN_ID+" == ?", selectionArg);
+        db.delete(DatabaseHelper.TABLE_DEVICE_RANK, COLUMN_ID+" == ?", selectionArg);
+        db.close();
+    }
+
+
+    /**
+     *  DB의 모든 데이터 삭제.
+     */
+    synchronized public static void removeAll() {
+        SQLiteDatabase db = DatabaseHelper.getInstance().getWritableDatabase();
+        db.delete(DatabaseHelper.TABLE_SMART_LIGHT, null, null);
+        db.delete(DatabaseHelper.TABLE_DEVICE_HOME, null, null);
+        db.delete(DatabaseHelper.TABLE_DEVICE_RANK, null, null);
+        db.close();
+    }
+}

+ 113 - 0
app/src/main/java/kr/co/icontrols/iotplatform/Database/DatabaseHelper.java

@@ -0,0 +1,113 @@
+package kr.co.icontrols.iotplatform.Database;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.provider.BaseColumns;
+
+/**
+ * DB 파일 생성 및 Table 생성
+ */
+public class DatabaseHelper  extends SQLiteOpenHelper {
+    private static final String TAG = DatabaseHelper.class.getSimpleName();
+
+    private static DatabaseHelper sInstance = null;
+
+    public static final String DATABASE_NAME = "iot_platform.db";
+    static final int DATABASE_VERSION = 2;
+    private final Context mContext;
+
+    public static final String TABLE_SMART_LIGHT = "smart_light";
+    public static final String TABLE_DEVICE_HOME = "device_home";
+    public static final String TABLE_DEVICE_RANK = "device_rank";
+
+
+    public static final String COLUMN_ID = "id";
+    public static final String COLUMN_NAME = "name";
+    public static final String COLUMN_GROUP_ID = "group_id";
+    public static final String COLUMN_GROUP_NAME = "group_name";
+    public static final String COLUMN_SHARED_NAME = "shared_name";
+    public static final String COLUMN_OWNER_ID = "owner_id";
+    public static final String COLUMN_TYPE = "type";
+    public static final String COLUMN_RANK = "rank";
+    public static final String COLUMN_GROUP_RANK = "group_rank";
+
+    private DatabaseHelper(Context context) {
+        super(context, DATABASE_NAME, null, DATABASE_VERSION);
+
+        mContext = context;
+    }
+
+    public static void init(Context context) {
+        if (sInstance == null) {
+            sInstance = new DatabaseHelper(context);
+        }
+    }
+
+    public static DatabaseHelper getInstance() {
+        return sInstance;
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        createTables(db);
+    }
+
+    /**
+     * 배송정보에 대한 DB 테이블 생성 함수
+     * @param db
+     */
+    private void createTables(SQLiteDatabase db) {
+
+        db.execSQL("CREATE TABLE " + TABLE_SMART_LIGHT + " ("
+                + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+                + COLUMN_ID + " TEXT NOT NULL, "
+                + COLUMN_NAME + " TEXT NOT NULL, "
+                + COLUMN_GROUP_ID + " TEXT NOT NULL, "
+                + COLUMN_GROUP_NAME + " TEXT NOT NULL, "
+                + COLUMN_SHARED_NAME + " TEXT NOT NULL, "
+                + COLUMN_OWNER_ID + " TEXT NOT NULL, "
+                + COLUMN_TYPE + " TEXT NOT NULL, "
+                + COLUMN_RANK + " INTEGER NOT NULL, "
+                + COLUMN_GROUP_RANK + " INTEGER NOT NULL);");
+
+        db.execSQL("CREATE TABLE " + TABLE_DEVICE_HOME + " ("
+                + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+                + COLUMN_ID + " TEXT NOT NULL, "
+                + COLUMN_NAME + " TEXT NOT NULL, "
+                + COLUMN_GROUP_ID + " TEXT NOT NULL, "
+                + COLUMN_GROUP_NAME + " TEXT NOT NULL, "
+                + COLUMN_SHARED_NAME + " TEXT NOT NULL, "
+                + COLUMN_OWNER_ID + " TEXT NOT NULL, "
+                + COLUMN_TYPE + " TEXT NOT NULL, "
+                + COLUMN_RANK + " INTEGER NOT NULL, "
+                + COLUMN_GROUP_RANK + " INTEGER NOT NULL);");
+
+        db.execSQL("CREATE TABLE " + TABLE_DEVICE_RANK + " ("
+                + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+                + COLUMN_ID + " TEXT NOT NULL, "
+                + COLUMN_NAME + " TEXT NOT NULL, "
+                + COLUMN_GROUP_ID + " TEXT NOT NULL, "
+                + COLUMN_GROUP_NAME + " TEXT NOT NULL, "
+                + COLUMN_SHARED_NAME + " TEXT NOT NULL, "
+                + COLUMN_OWNER_ID + " TEXT NOT NULL, "
+                + COLUMN_TYPE + " TEXT NOT NULL, "
+                + COLUMN_RANK + " INTEGER NOT NULL, "
+                + COLUMN_GROUP_RANK + " INTEGER NOT NULL);");
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+        switch (oldVersion) {
+            case 0:
+            case 1:
+                // on upgrade drop older tables
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_SMART_LIGHT);
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_DEVICE_HOME);
+                db.execSQL("DROP TABLE IF EXISTS " + TABLE_DEVICE_RANK);
+                // create new tables
+                createTables(db);
+                return;
+        }
+    }
+}

+ 377 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DeviceActivity.java

@@ -0,0 +1,377 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.adapter.DeviceListAdapter;
+import kr.co.icontrols.iotplatform.utils.GetSessionCallback;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_IMG_INDEX;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.cancelToExit;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCognitoSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isExpiredSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isRestartAppForBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.resetBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.restartApp;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.DEFAULT_RANK;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_INFO;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHANGE_GROUP_IMG;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_GROUP_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_UPDATE_DATA;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class DeviceActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private String TAG = "DeviceActivity";
+
+    private ListView mListView;
+    private DeviceListAdapter mDeviceListAdapter;
+
+    private Handler mConnectedHandler;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_device);
+
+        mListView = findViewById(R.id.listView);
+
+        if(!UserInfo.confirmed) {
+            showNotiMsg(this, "인증 후 사용 가능합니다.");
+        } else if(isExpiredSession()) {
+            getCognitoSession(new GetSessionCallback() {
+                @Override
+                public void successed() {
+                    getDeviceList();
+                }
+
+                @Override
+                public void failed() {
+                    showNotiMsg(DeviceActivity.this, "세션을 갱신하지 못하였습니다.");
+                    System.exit(0);
+                }
+            });
+        } else {
+            getDeviceList();
+        }
+
+        mConnectedHandler = new Handler() {
+            @Override
+            public void handleMessage(@NonNull Message msg) {
+                super.handleMessage(msg);
+                WarnLogOutput(TAG, "mConnectedHandler : is mqtt connect ? " + MqttController.getInstance().isConneted());
+                if(MqttController.getInstance().isConneted()) {
+                    if(mDeviceListAdapter != null) {
+                        MqttController.getInstance().allUnsubscribeToTopics();
+                        mDeviceListAdapter.notifyDataSetChanged();
+                    }
+                } else {
+                    mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+                }
+            }
+        };
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if(isRestartAppForBackgroundWaitTime()) {
+            restartApp(this);
+            return;
+        }
+
+        if(mDeviceListAdapter != null) {
+            MqttController.getInstance().allUnsubscribeToTopics();
+            mDeviceListAdapter.notifyDataSetChanged();
+
+            if(!MqttController.getInstance().isConneted()) {
+                mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+            }
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        if(findViewById(R.id.layoutMore).getVisibility() == View.VISIBLE) {
+            findViewById(R.id.layoutMore).setVisibility(View.GONE);
+        } else {
+            //super.onBackPressed();
+            cancelToExit(this);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        mConnectedHandler.removeCallbacksAndMessages(null);
+        MqttController.getInstance().allUnsubscribeToTopics();
+        ForceDismissConnectDialog();
+        setBackgroundWaitTime();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        resetBackgroundWaitTime();
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_UPDATE_DATA:
+                if(resultCode == RESULT_OK) {
+                    MqttController.getInstance().allUnsubscribeToTopics();
+                    if(UserInfo.devices.size() == 0) {
+                        findViewById(R.id.layoutRegisterDevice).setVisibility(View.VISIBLE);
+                        findViewById(R.id.layoutDeviceList).setVisibility(View.GONE);
+                    } else {
+                        findViewById(R.id.layoutRegisterDevice).setVisibility(View.GONE);
+                        findViewById(R.id.layoutDeviceList).setVisibility(View.VISIBLE);
+                        mDeviceListAdapter.notifyDataSetChanged();
+                        UserInfo.deviceCount = UserInfo.devices.size();
+                        ((TextView)findViewById(R.id.txtDeviceCount)).setText(String.format(getString(R.string.string_format_device_count),UserInfo.deviceCount));
+                    }
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        if(!UserInfo.confirmed) {
+            showNotiMsg(this, "인증 후 사용 가능합니다.");
+            return;
+        }
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnRight2:
+                findViewById(R.id.layoutMore).setVisibility(View.VISIBLE);
+                break;
+            case R.id.btnRight:
+            case R.id.btnRegisterDevice:
+                //startActivity(new Intent(this, AddDeviceListActivity.class));
+                startActivity(new Intent(this,LightRegisterGuideActivity.class));
+                break;
+            case R.id.btnCloseMore:
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                break;
+            case R.id.layoutMoreRank:
+                if(UserInfo.devices.size() > 1) {
+                    Intent intent = new Intent(DeviceActivity.this, DeviceChangeRankActivity.class);
+                    intent.putExtra(EXTRA_DEVICE_LIST, UserInfo.devices);
+                    intent.putExtra("MODE","DEVICE_RANK");
+                    startActivity(intent);
+                } else {
+                    showNotiMsg(DeviceActivity.this,"스마트 조명 갯수가 2개 이상에만 변경 가능합니다.");
+                }
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                break;
+            case R.id.layoutMoreDelete:
+                if(UserInfo.devices.size() > 0) {
+                    UserInfo.setUnSelectAllDevices();
+                    Intent intent = new Intent(DeviceActivity.this, DeviceSelectActivity.class);
+                    intent.putExtra("MODE","AWS");
+                    intent.putExtra(EXTRA_DEVICE_LIST, UserInfo.devices);
+                    startActivityForResult(intent,RESULT_UPDATE_DATA);
+                } else {
+                    showNotiMsg(DeviceActivity.this,"삭제할 스마트 조명이 존재하지 않습니다.");
+                }
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                break;
+        }
+    }
+
+    private void getDeviceList() {
+        UserInfo.getDevice(this, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        if(UserInfo.devices.size() == 0) {
+                            findViewById(R.id.layoutRegisterDevice).setVisibility(View.VISIBLE);
+                            findViewById(R.id.layoutDeviceList).setVisibility(View.GONE);
+                        } else {
+
+                            mDeviceListAdapter = new DeviceListAdapter(DeviceActivity.this, UserInfo.devices);
+                            mListView.setAdapter(mDeviceListAdapter);
+                            mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                                @Override
+                                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                                    final DeviceInfo item = (DeviceInfo) parent.getItemAtPosition(position);
+                                    Intent intent = new Intent(DeviceActivity.this,LightDetailActivity.class);
+                                    intent.putExtra(EXTRA_DEVICE_INFO, (Parcelable) item);
+                                    startActivityForResult(intent,RESULT_UPDATE_DATA);
+                                }
+                            });
+
+                            findViewById(R.id.layoutRegisterDevice).setVisibility(View.GONE);
+                            findViewById(R.id.layoutDeviceList).setVisibility(View.VISIBLE);
+                            ((TextView)findViewById(R.id.txtDeviceCount)).setText(String.format(getString(R.string.string_format_device_count),UserInfo.deviceCount));
+                        }
+                    }
+                });
+            }
+        });
+
+        /*
+        UserInfo.devices.clear();
+        displayProgress(this, null,null);
+        try{
+            HttpApi.getAllDevice(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getAllDevice(onFailure)\n" + e.toString());
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getAllDevice(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+
+                            UserInfo.deviceCount = json_data.isNull("count") ? 0 : json_data.getInt("count");
+
+                            if(UserInfo.deviceCount > 0) {
+                                JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                                for (int i = 0; i < jarrayAll.length(); i++) {
+                                    JSONObject jObject = jarrayAll.getJSONObject(i);
+                                    DeviceInfo item = new DeviceInfo(
+                                            jObject.isNull("deviceId") ? "" : jObject.getString("deviceId"),
+                                            jObject.isNull("deviceType") ? "" : jObject.getString("deviceType"),
+                                            jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                            jObject.isNull("sharedFriendlyName") ? "" : jObject.getString("sharedFriendlyName"),
+                                            jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                            jObject.isNull("dgroupName") ? "" : jObject.getString("dgroupName"),
+                                            jObject.isNull("ownerId") ? "" : jObject.getString("ownerId"),
+                                            DEFAULT_RANK
+                                    );
+
+                                    DeviceInfo localDbItem = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK, item.deviceId);
+                                    if (localDbItem == null) {
+                                        DBUtil.putDeviceInfo(TABLE_DEVICE_RANK, item);
+                                    } else {
+                                        item.rank = localDbItem.rank;
+                                        DBUtil.updateDeviceItem(TABLE_DEVICE_RANK, item);
+                                    }
+                                }
+
+                                ArrayList<DeviceInfo> deviceInfos = DBUtil.getAllDeviceInfos(TABLE_DEVICE_RANK);
+                                for (DeviceInfo d : deviceInfos) {
+                                    UserInfo.devices.add(d);
+                                }
+                                deviceInfos.clear();
+                            }
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if(UserInfo.devices.size() == 0) {
+                                        findViewById(R.id.layoutRegisterDevice).setVisibility(View.VISIBLE);
+                                        findViewById(R.id.layoutDeviceList).setVisibility(View.GONE);
+                                    } else {
+                                        findViewById(R.id.layoutRegisterDevice).setVisibility(View.GONE);
+                                        findViewById(R.id.layoutDeviceList).setVisibility(View.VISIBLE);
+                                        MqttController.getInstance().allUnsubscribeToTopics();
+                                        mDeviceListAdapter.notifyDataSetChanged();
+                                        ((TextView)findViewById(R.id.txtDeviceCount)).setText(String.format(getString(R.string.string_format_device_count),UserInfo.deviceCount));
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        DismissConnectDialog();
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                }
+            });
+        } catch(Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(Exception)\n" + e.toString());
+            DismissConnectDialog();
+        }
+         */
+    }
+}

+ 166 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DeviceChangeRankActivity.java

@@ -0,0 +1,166 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.DragListView.ChangeListener;
+import kr.co.icontrols.iotplatform.DragListView.RankListAdapter;
+import kr.co.icontrols.iotplatform.DragListView.SwipeAndDragHelper;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_LIST;
+
+public class DeviceChangeRankActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "DeviceChangeRankActivity";
+
+    private String mMode;
+    private ArrayList<DeviceInfo> mDeviceList;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_device_change_rank);
+
+        mMode = getIntent().getStringExtra("MODE");
+        mDeviceList = (ArrayList<DeviceInfo>) getIntent().getSerializableExtra(EXTRA_DEVICE_LIST);
+
+        RecyclerView itemRecyclerView = findViewById(R.id.recyclerview_device_list);
+        itemRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+        RankListAdapter adapter = new RankListAdapter(new ChangeListener() {
+            @Override
+            public void onChangeData() {
+                findViewById(R.id.btnBack).setVisibility(View.GONE);
+                findViewById(R.id.btnBackText).setVisibility(View.VISIBLE);
+                findViewById(R.id.btnRight).setVisibility(View.VISIBLE);
+            }
+        });
+        adapter.setDeviceInfoList(mDeviceList);
+        SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(adapter);
+        ItemTouchHelper touchHelper = new ItemTouchHelper(swipeAndDragHelper);
+        adapter.setTouchHelper(touchHelper);
+        itemRecyclerView.setAdapter(adapter);
+        touchHelper.attachToRecyclerView(itemRecyclerView);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnAlarmSetting:
+                startActivity(new Intent(this, AlarmSettingActivity.class));
+                break;
+            case R.id.btnAppInfo:
+                startActivity(new Intent(this, AppInfoActivity.class));
+                break;
+            case R.id.btnBack:
+            case R.id.btnBackText:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+                //인덱스 변경
+                for(int i = 0 ; i < mDeviceList.size() ; i++) {
+                    if(mMode.equals("GROUP_RANK")) {
+                        mDeviceList.get(i).groupRank = i;
+                    } else {
+                        mDeviceList.get(i).rank = i;
+                    }
+
+                    if(mMode.equals("SMART_LIGHT")) {
+                        DBUtil.updateDeviceItem(TABLE_SMART_LIGHT, mDeviceList.get(i));
+                    } else if(mMode.equals("DEVICE_RANK") || mMode.equals("GROUP_RANK")) {
+                        DBUtil.updateDeviceItem(TABLE_DEVICE_RANK, mDeviceList.get(i));
+                    }
+                }
+
+                
+
+                if(mMode.equals("SMART_LIGHT")) {
+                    showNotiMsg(DeviceChangeRankActivity.this, "스마트 조명 순서를 변경하였습니다.", true);
+                } else if(mMode.equals("DEVICE_RANK")) {
+                    sortDeviceRank();
+                    showNotiMsg(DeviceChangeRankActivity.this, "스마트 조명 순서를 변경하였습니다.", true);
+                } else if(mMode.equals("GROUP_RANK")) {
+                    sortGroupRank();
+                    showNotiMsg(DeviceChangeRankActivity.this, "스마트 조명 순서를 변경하였습니다.", true);
+                }
+                break;
+        }
+    }
+
+    /**
+     * UserInfo.devices 순서 재정렬
+     */
+    private void sortDeviceRank() {
+        for(DeviceInfo d1 : mDeviceList) {
+            for(int i = 0 ; i < UserInfo.devices.size() ; i++) {
+                if(d1.deviceId.equals(UserInfo.devices.get(i).deviceId)) {
+                    UserInfo.devices.get(i).rank = d1.rank;
+                }
+            }
+        }
+
+        if(UserInfo.devices.size() > 1) {
+            for (int i = 0; i < UserInfo.devices.size() - 1; i++) {
+                for (int j = i + 1; j < UserInfo.devices.size(); j++) {
+                    if (UserInfo.devices.get(i).rank > UserInfo.devices.get(j).rank) {
+                        DeviceInfo item = UserInfo.devices.get(j);
+                        UserInfo.devices.remove(j);
+                        UserInfo.devices.add(i, item);
+                    }
+                }
+            }
+        }
+    }
+
+    private void sortGroupRank() {
+        for(DeviceInfo d1 : mDeviceList) {
+            for(int i = 0 ; i < UserInfo.devices.size() ; i++) {
+                if(d1.deviceId.equals(UserInfo.devices.get(i).deviceId)) {
+                    UserInfo.devices.get(i).groupRank = d1.groupRank;
+                }
+            }
+        }
+    }
+
+}

+ 387 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DeviceSelectActivity.java

@@ -0,0 +1,387 @@
+package kr.co.icontrols.iotplatform;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.adapter.DeviceSelectAdapter;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_HOME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_NAME;
+
+public class DeviceSelectActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "GroupSelectActivity";
+    private ListView mListView;
+    private ArrayList<DeviceInfo> mDeviceList;
+    private Handler mHandler;
+    private boolean isCompleted;
+    private String mMode;
+    private String mGroupId;
+    private String mGroupName;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_device_select);
+
+        isCompleted = false;
+
+        mMode = getIntent().getStringExtra("MODE");
+        if(mMode.equals("SMART_LIGHT")) {
+            ((TextView)findViewById(R.id.txtTitle)).setText(getString(R.string.delete_favor));
+            ((TextView)findViewById(R.id.btnRight)).setText(getString(R.string.delete));
+            findViewById(R.id.txtDescription).setVisibility(View.GONE);
+        } else if(mMode.equals("AWS")) {
+            ((TextView)findViewById(R.id.txtTitle)).setText(getString(R.string.delete_device));
+            ((TextView)findViewById(R.id.btnRight)).setText(getString(R.string.delete));
+            findViewById(R.id.txtDescription).setVisibility(View.GONE);
+        } else if(mMode.equals("MOVE_GROUP")) {
+            mGroupId = getIntent().getStringExtra(EXTRA_GROUP_ID);
+            mGroupName = getIntent().getStringExtra(EXTRA_GROUP_NAME);
+
+            ((TextView)findViewById(R.id.txtTitle)).setText(getString(R.string.add_device_group));
+            ((TextView)findViewById(R.id.btnRight)).setText(getString(R.string.add));
+            ((TextView)findViewById(R.id.txtDescription)).setText(String.format(getString(R.string.select_device_description),mGroupName));
+            findViewById(R.id.txtDescription).setVisibility(View.VISIBLE);
+        }
+
+        mDeviceList = (ArrayList<DeviceInfo>) getIntent().getSerializableExtra(EXTRA_DEVICE_LIST);
+
+        if(mMode.equals("MOVE_GROUP")) {
+            for(int i = 0 ; i < mDeviceList.size() ; i++) {
+                if(mGroupId.equals(mDeviceList.get(i).dgroupId)) {
+                    mDeviceList.remove(i--);
+                }
+            }
+        } else if(mMode.equals("AWS")) {
+            for(int i = 0 ; i < mDeviceList.size() ; i++) {
+                if(mDeviceList.get(i).ownerId != null && !mDeviceList.get(i).ownerId.isEmpty() && !mDeviceList.get(i).ownerId.equals(UserInfo.uid)) {
+                    mDeviceList.remove(i--);
+                }
+            }
+        }
+
+
+        if(mDeviceList.size() > 0) {
+            mListView = findViewById(R.id.listView);
+            mListView.setAdapter(new DeviceSelectAdapter(mDeviceList));
+            mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                @Override
+                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                    ((DeviceSelectAdapter) mListView.getAdapter()).setSelected(position);
+                    ((DeviceSelectAdapter) mListView.getAdapter()).notifyDataSetChanged();
+
+                    if (((DeviceSelectAdapter) mListView.getAdapter()).isSelected()) {
+                        findViewById(R.id.btnBack).setVisibility(View.GONE);
+                        findViewById(R.id.btnBackText).setVisibility(View.VISIBLE);
+                        findViewById(R.id.btnRight).setVisibility(View.VISIBLE);
+                    } else {
+                        findViewById(R.id.btnBack).setVisibility(View.VISIBLE);
+                        findViewById(R.id.btnBackText).setVisibility(View.GONE);
+                        findViewById(R.id.btnRight).setVisibility(View.INVISIBLE);
+                    }
+                }
+            });
+        } else {
+            if(mMode.equals("MOVE_GROUP")) {
+                showNotiMsg(DeviceSelectActivity.this, "추가할 스마트 조명이 존재하지 않습니다.", true);
+            } else {
+                showNotiMsg(DeviceSelectActivity.this, "삭제할 조명이 존재하지 않습니다.", true);
+            }
+        }
+
+        mHandler = new Handler() {
+            @Override
+            public void handleMessage(@NonNull Message msg) {
+                final int index = msg.what;
+                if (mDeviceList.size() > index && mDeviceList.get(index).isSelected()) {
+                    if (mMode.equals("AWS")) {
+                        try {
+                            final String deviceId = mDeviceList.get(index).deviceId;
+                            HttpApi.deleteDevice(mDeviceList.get(index).deviceId, new okhttp3.Callback() {
+                                @Override
+                                public void onFailure(Call call, IOException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.deleteDevice(onFailure)\n" + e.toString());
+                                    showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 삭제하지 못하였습니다.",true);
+                                    DismissConnectDialog();
+                                }
+
+                                @Override
+                                public void onResponse(Call call, Response response) throws IOException {
+                                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                        WarnLogOutput(TAG, "HttpApi.deleteDevice(onResponse) HTTP_NO_CONTENT");
+                                        DBUtil.removeDeviceInfoAtAllTable(deviceId);
+                                        for (int i = 0; i < UserInfo.devices.size(); i++) {
+                                            if (UserInfo.devices.get(i).deviceId.equals(deviceId)) {
+                                                UserInfo.devices.remove(i);
+                                                i--;
+                                            }
+                                        }
+                                        mHandler.sendEmptyMessage(index+1);
+                                        isCompleted = true;
+                                        //showNotiMsg(DeviceSelectActivity.this,"스마트 조명을 삭제하였습니다.",true);
+                                    } else {
+                                        ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                        showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 삭제하지 못하였습니다.",true);
+
+                                        try {
+                                            JSONObject json_data = new JSONObject(response.body().string());
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) body : " + json_data.toString());
+                                        } catch (JSONException e) {
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) JSONException : " + e.toString());
+                                        }
+                                        DismissConnectDialog();
+                                    }
+
+                                }
+                            });
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.deleteDevice(Exception)\n" + e.toString());
+                            showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 삭제하지 못하였습니다.",true);
+                            DismissConnectDialog();
+
+                        }
+                    } else if (mMode.equals("SMART_LIGHT")) {
+                        DBUtil.removeDeviceInfo(TABLE_SMART_LIGHT, mDeviceList.get(index));
+                        //showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 삭제하였습니다.", true);
+                        //DismissConnectDialog();
+                        mHandler.sendEmptyMessage(index+1);
+                        isCompleted = true;
+                    } else if(mMode.equals("MOVE_GROUP")) {
+                        try {
+                            HttpApi.connectDeviceInDeviceGroup(mGroupId, mDeviceList.get(index).deviceId, new okhttp3.Callback() {
+                                @Override
+                                public void onFailure(Call call, IOException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onFailure)\n" + e.toString());
+                                    showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 그룹 추가(이동) 중 오류가 발생하였습니다.");
+                                    DismissConnectDialog();
+                                }
+
+                                @Override
+                                public void onResponse(Call call, Response response) throws IOException {
+                                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                        WarnLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) HTTP_NO_CONTENT");
+
+                                        final DeviceInfo itemS = DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,mDeviceList.get(index).deviceId);
+                                        if(itemS != null) {
+                                            itemS.dgroupId = mGroupId;
+                                            itemS.dgroupName = mGroupName;
+                                            DBUtil.updateDeviceItem(TABLE_SMART_LIGHT,itemS);
+                                        }
+
+                                        final DeviceInfo itemH = DBUtil.getDeviceInfoForId(TABLE_DEVICE_HOME,mDeviceList.get(index).deviceId);
+                                        if(itemH != null) {
+                                            itemH.dgroupId = mGroupId;
+                                            itemH.dgroupName = mGroupName;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_HOME,itemH);
+                                        }
+
+                                        final DeviceInfo itemR = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK,mDeviceList.get(index).deviceId);
+                                        if(itemR != null) {
+                                            itemR.dgroupId = mGroupId;
+                                            itemR.dgroupName = mGroupName;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_RANK,itemR);
+                                        }
+
+                                        for(int i = 0; i < UserInfo.devices.size() ; i++) {
+                                            if(UserInfo.devices.get(i).deviceId.equals(mDeviceList.get(index).deviceId)) {
+                                                UserInfo.devices.get(i).dgroupName = mGroupName;
+                                                UserInfo.devices.get(i).dgroupId = mGroupId;
+                                            }
+                                        }
+
+                                        //showNotiMsg(DeviceSelectActivity.this, "그룹을 변경하였습니다.", true);
+                                        mHandler.sendEmptyMessage(index+1);
+                                        isCompleted = true;
+                                    } else {
+                                        ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                        showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 그룹 추가(이동) 중 오류가 발생하였습니다.");
+                                        try {
+                                            JSONObject json_data = new JSONObject(response.body().string());
+                                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) body : " + json_data.toString());
+                                        } catch (JSONException e) {
+                                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) JSONException : " + e.toString());
+                                        }
+                                        DismissConnectDialog();
+                                    }
+
+                                }
+                            });
+                        }  catch (Exception e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(Exception)\n" + e.toString());
+                            showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 그룹 추가(이동) 중 오류가 발생하였습니다.");
+                            DismissConnectDialog();
+                        }
+                    }
+                } else if(mDeviceList.size() > index) {
+                    mHandler.sendEmptyMessage(index+1);
+                } else {
+                    if(isCompleted) {
+                        if(mMode.equals("MOVE_GROUP")){
+                            showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 그룹에 추가(이동) 하였습니다.", true);
+                        } else {
+                            showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 삭제하였습니다.", true);
+                        }
+                    } else {
+                        if(mMode.equals("MOVE_GROUP")){
+                            showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 그룹 추가(이동) 중 오류가 발생하였습니다.");
+                        } else {
+                            showNotiMsg(DeviceSelectActivity.this, "스마트 조명을 삭제하지 못하였습니다.");
+                        }
+
+                    }
+                    DismissConnectDialog();
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+            case R.id.btnBackText:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+                if(((DeviceSelectAdapter)mListView.getAdapter()).isSelected()) {
+                    displayProgress(this, null, null);
+                    mHandler.sendEmptyMessage(0);
+                } else {
+                    if(mMode.equals("MOVE_GROUP")) {
+                        showNotiMsg(DeviceSelectActivity.this,getString(R.string.snackbar_error_select_device_for_group));
+                    } else {
+                        showNotiMsg(DeviceSelectActivity.this,getString(R.string.snackbar_error_select_device_for_deleting));
+                    }
+                }
+                /*
+                final int selectedIndex = ((DeviceSelectAdapter)mListView.getAdapter()).getSelectedIndex();
+                if(selectedIndex == -1) {
+                    showNotiMsg(DeviceSelectActivity.this,getString(R.string.snackbar_error_select_group_for_deleting));
+                } else {
+                    if (mMode.equals("AWS")) {
+                        displayProgress(this,null,null);
+                        try {
+                            final String deviceId = mDeviceList.get(selectedIndex).deviceId;
+                            HttpApi.deleteDevice(mDeviceList.get(selectedIndex).deviceId, new Callback() {
+                                @Override
+                                public void onFailure(Call call, IOException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.deleteDevice(onFailure)\n" + e.toString());
+                                    showNotiMsg(DeviceSelectActivity.this,"스마트 조명을 삭제하지 못하였습니다.");
+                                    DismissConnectDialog();
+                                }
+
+                                @Override
+                                public void onResponse(Call call, Response response) throws IOException {
+                                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                        WarnLogOutput(TAG, "HttpApi.deleteDevice(onResponse) HTTP_NO_CONTENT");
+                                        DBUtil.removeDeviceInfoAtAllTable(deviceId);
+                                        for(int i = 0 ; i < UserInfo.devices.size() ; i++) {
+                                            if(UserInfo.devices.get(i).deviceId.equals(deviceId)) {
+                                                UserInfo.devices.remove(i);
+                                                i--;
+                                            }
+                                        }
+                                        showNotiMsg(DeviceSelectActivity.this,"스마트 조명을 삭제하였습니다.",true);
+                                    } else {
+                                        ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                        showNotiMsg(DeviceSelectActivity.this,"스마트 조명을 삭제하지 못하였습니다.");
+
+                                        try {
+                                            JSONObject json_data = new JSONObject(response.body().string());
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) body : " + json_data.toString());
+                                        } catch (JSONException e) {
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) JSONException : " + e.toString());
+                                        }
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.deleteDevice(Exception)\n" + e.toString());
+                            showNotiMsg(DeviceSelectActivity.this,"스마트 조명을 삭제하지 못하였습니다.");
+                            DismissConnectDialog();
+
+                        }
+                    } else if(mMode.equals("SMART_LIGHT")) {
+                        DBUtil.removeDeviceInfo(TABLE_SMART_LIGHT,mDeviceList.get(selectedIndex));
+                        showNotiMsg(DeviceSelectActivity.this,"스마트 조명을 삭제하였습니다.",true);
+                        DismissConnectDialog();
+                    }
+                }
+                */
+                break;
+        }
+    }
+}

+ 611 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DeviceSettingActivity.java

@@ -0,0 +1,611 @@
+package kr.co.icontrols.iotplatform;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttNewMessageCallback;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_INFO;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.MQTT_PUBLISH_DELAY_TIME;
+import static kr.co.icontrols.iotplatform.utils.Constants.NOTING;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_ADD_HOME_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INIT_DEVICE_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_UPDATE_DATA;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class DeviceSettingActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private String TAG = "DeviceSettingActivity";
+    private DeviceInfo deviceInfo;
+    private Button btnInit;
+    private TextView txtDeviceName, txtGroupName, txtAddHomeStatus;
+    private boolean isChangeData;
+    private String currentVersionForFirmware, newVersionForFirmware, detailDeviceInfo;
+    private LinearLayout layoutFirmware;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_device_setting);
+
+        isChangeData = false;
+        newVersionForFirmware = "";
+        currentVersionForFirmware = "";
+        detailDeviceInfo = "";
+
+        deviceInfo = getIntent().getParcelableExtra(EXTRA_DEVICE_INFO);
+
+        txtDeviceName = findViewById(R.id.txtDeviceName);
+        txtGroupName = findViewById(R.id.txtGroupName);
+        txtAddHomeStatus = findViewById(R.id.txtAddHomeStatus);
+
+        txtDeviceName.setText(deviceInfo.friendlyName.isEmpty()?deviceInfo.sharedFriendlyName.isEmpty()?getString(R.string.no_name):deviceInfo.sharedFriendlyName:deviceInfo.friendlyName);
+        txtGroupName.setText(deviceInfo.dgroupName);
+
+        layoutFirmware = findViewById(R.id.layoutFirmware);
+
+        //마스터 스마트 조명의 경우 friendlyName이 없음, 없는 경우 스마트 조명 초기화 불가능하게 수정.
+        btnInit = findViewById(R.id.btnInit);
+        if(deviceInfo.friendlyName.isEmpty()) {
+            btnInit.setEnabled(false);
+        } else {
+            btnInit.setEnabled(true);
+        }
+
+        if(DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,deviceInfo.deviceId) == null) {
+            txtAddHomeStatus.setText(getString(R.string.no_added));
+        } else {
+            txtAddHomeStatus.setText(getString(R.string.added));
+        }
+
+        getNewVersionForFirmware();
+    }
+
+    @Override
+    public void onBackPressed() {
+        if(isChangeData) {
+            setResult(RESULT_OK);
+            finish();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        getJaesilData();
+    }
+
+    @Override
+    protected void onPause() {
+        MqttController.getInstance().allUnsubscribeToTopics();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_ADD_HOME_CONFIRM:
+                if(resultCode == Activity.RESULT_OK) {
+                    isChangeData = true;
+                    if(DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,deviceInfo.deviceId) == null) {
+                        if (DBUtil.putDeviceInfo(TABLE_SMART_LIGHT, deviceInfo) != -1) {
+                            showNotiMsg(DeviceSettingActivity.this, "홈에 추가되었습니다.");
+                            txtAddHomeStatus.setText(getString(R.string.added));
+                        } else {
+                            showNotiMsg(DeviceSettingActivity.this, "홈에 추가를 실패하였습니다.");
+                        }
+                    } else {
+                        DBUtil.removeDeviceInfo(TABLE_SMART_LIGHT, deviceInfo);
+                        showNotiMsg(DeviceSettingActivity.this, "홈에서 제거 되었습니다.");
+                        txtAddHomeStatus.setText(getString(R.string.no_added));
+                    }
+                } else {
+
+                }
+                break;
+            case RESULT_UPDATE_DATA:
+                if(resultCode == RESULT_OK) {
+                    for(int i = 0; i < UserInfo.devices.size() ; i++) {
+                        if(UserInfo.devices.get(i).deviceId.equals(deviceInfo.deviceId)) {
+                            if(!UserInfo.devices.get(i).dgroupId.equals(deviceInfo.dgroupId)) {
+                                deviceInfo.dgroupName = UserInfo.devices.get(i).dgroupName;
+                                deviceInfo.dgroupId = UserInfo.devices.get(i).dgroupId;
+                                isChangeData = true;
+                            }
+                        }
+                    }
+                    txtGroupName.setText(deviceInfo.dgroupName);
+                }
+                break;
+            case RESULT_INIT_DEVICE_CONFIRM:
+                if(resultCode == RESULT_OK) {
+                    displayProgress(this,null,null);
+                    try {
+                        final String deviceId = deviceInfo.deviceId;
+                        HttpApi.deleteDevice(deviceInfo.deviceId, new Callback() {
+                            @Override
+                            public void onFailure(Call call, IOException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.deleteDevice(onFailure)\n" + e.toString());
+                                showNotiMsg(DeviceSettingActivity.this,"스마트 조명을 초기화를 실패하였습니다.");
+                                DismissConnectDialog();
+                            }
+
+                            @Override
+                            public void onResponse(Call call, Response response) throws IOException {
+                                if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                    WarnLogOutput(TAG, "HttpApi.deleteDevice(onResponse) HTTP_NO_CONTENT");
+                                    DBUtil.removeDeviceInfoAtAllTable(deviceId);
+                                    for(int i = 0 ; i < UserInfo.devices.size() ; i++) {
+                                        if(UserInfo.devices.get(i).deviceId.equals(deviceId)) {
+                                            UserInfo.devices.remove(i);
+                                            i--;
+                                        }
+                                    }
+                                    showNotiMsg(DeviceSettingActivity.this,"스마트 조명을 초기화를 하였습니다.",DeviceActivity.class);
+                                } else {
+                                    ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                    showNotiMsg(DeviceSettingActivity.this,"스마트 조명을 초기화를 실패하였습니다.");
+
+                                    try {
+                                        JSONObject json_data = new JSONObject(response.body().string());
+                                        ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) body : " + json_data.toString());
+                                    } catch (JSONException e) {
+                                        ErrorLogOutput(TAG, "HttpApi.deleteDevice(onResponse) JSONException : " + e.toString());
+                                    }
+                                }
+                                DismissConnectDialog();
+                            }
+                        });
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.deleteDevice(Exception)\n" + e.toString());
+                        showNotiMsg(DeviceSettingActivity.this,"스마트 조명을 초기화를 실패하였습니다.");
+                        DismissConnectDialog();
+
+                    }
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.layoutGroup:
+                getMyDeviceGroup();
+                break;
+            case R.id.layoutAddHome:{
+                Intent intent = new Intent(DeviceSettingActivity.this,PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE,PopupActivity.MODE_YESNO);
+                if(DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,deviceInfo.deviceId) == null) {
+                    intent.putExtra(PopupActivity.POPUP_TITLE_TEXT,String.format(getString(R.string.popup_confirm_add_home),deviceInfo.friendlyName.isEmpty()?deviceInfo.sharedFriendlyName.isEmpty()?getString(R.string.no_name):deviceInfo.sharedFriendlyName:deviceInfo.friendlyName));
+                } else {
+                    intent.putExtra(PopupActivity.POPUP_TITLE_TEXT,String.format(getString(R.string.popup_confirm_remove_home),deviceInfo.friendlyName.isEmpty()?deviceInfo.sharedFriendlyName.isEmpty()?getString(R.string.no_name):deviceInfo.sharedFriendlyName:deviceInfo.friendlyName));
+                }
+                startActivityForResult(intent,RESULT_ADD_HOME_CONFIRM);
+                }
+                break;
+            case R.id.layoutJaesil:{
+                Intent intent = new Intent(this, JaesilModeActivity.class);
+                intent.putExtra(EXTRA_DEVICE_INFO, (Parcelable) deviceInfo);
+                startActivityForResult(intent, RESULT_UPDATE_DATA);
+                }
+                break;
+            case R.id.layoutDeviceInfo:
+                //getDetailDeviceInfo();
+                if(detailDeviceInfo.isEmpty()){
+                    showNotiMsg(this, "디바이스 정보를 획득하지 못하였습니다.");
+                } else {
+                    Intent intent = new Intent(DeviceSettingActivity.this, PopupActivity.class);
+                    intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL);
+                    intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, "디바이스 정보");
+                    intent.putExtra(PopupActivity.POPUP_BODY_TEXT, detailDeviceInfo);
+                    startActivity(intent);
+                }
+                break;
+            case R.id.layoutFirmware:
+                updateFirmware();
+                break;
+            case R.id.btnInit: {
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO_WITH_ICON);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, String.format(getString(R.string.popup_init_device_confirm),deviceInfo.friendlyName));
+                intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.init));
+                intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.cancel));
+                startActivityForResult(intent, RESULT_INIT_DEVICE_CONFIRM);
+                }
+                break;
+        }
+    }
+
+
+    public void getMyDeviceGroup(){
+        UserInfo.getGroup(this, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        if(UserInfo.dgroups.size() == 0) {
+                            showNotiMsg(DeviceSettingActivity.this, "변경할 그룹이 존재하지 않습니다.");
+                        } else {
+                            UserInfo.setUnSelectAllGroups();
+                            Intent intent = new Intent(DeviceSettingActivity.this, GroupSelectActivity.class);
+                            intent.putExtra("MODE","SELECT");
+                            intent.putExtra(EXTRA_GROUP_LIST, UserInfo.dgroups);
+                            intent.putExtra(EXTRA_DEVICE_ID, deviceInfo.deviceId);
+                            intent.putExtra(EXTRA_DEVICE_NAME, deviceInfo.friendlyName.isEmpty()?deviceInfo.sharedFriendlyName.isEmpty()?getString(R.string.no_name):deviceInfo.sharedFriendlyName:deviceInfo.friendlyName);
+                            intent.putExtra(EXTRA_GROUP_ID, deviceInfo.dgroupId);
+                            startActivityForResult(intent,RESULT_UPDATE_DATA);
+                        };
+                    }
+                });
+            }
+        });
+
+        /*
+        if(mGroupList != null) {
+            mGroupList.clear();
+        } else {
+            mGroupList = new ArrayList<>();
+        }
+        displayProgress(this,"","");
+        try {
+            HttpApi.getDeviceGroupList(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onFailure)\n" + e.toString());
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+
+                            for (int i = 0; i < jarrayAll.length(); i++) {
+                                JSONObject jObject = jarrayAll.getJSONObject(i);
+                                DeviceGroupInfo item = new DeviceGroupInfo(jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                        jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                        jObject.isNull("icon") ? "" : jObject.getString("icon"),
+                                        jObject.isNull("index") ? 99 : jObject.getInt("index"));
+
+                                mGroupList.add(item);
+                            }
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if(mGroupList.size() == 0) {
+                                        showNotiMsg(DeviceSettingActivity.this, "변경할 그룹이 존재하지 않습니다.");
+                                    } else {
+                                        Intent intent = new Intent(DeviceSettingActivity.this, GroupSelectActivity.class);
+                                        intent.putExtra("MODE","SELECT");
+                                        intent.putExtra(EXTRA_GROUP_LIST, mGroupList);
+                                        intent.putExtra(EXTRA_DEVICE_ID, deviceInfo.deviceId);
+                                        intent.putExtra(EXTRA_DEVICE_NAME, deviceInfo.friendlyName);
+                                        intent.putExtra(EXTRA_GROUP_ID, deviceInfo.dgroupId);
+                                        startActivityForResult(intent,RESULT_UPDATE_DATA);
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) JSONException : " + e.toString());
+                        }
+                        DismissConnectDialog();
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(Exception)\n" + e.toString());
+            DismissConnectDialog();
+        }
+         */
+    }
+
+    public void getDetailDeviceInfo(){
+        try {
+            HttpApi.getDeviceInfo(deviceInfo.deviceId, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onFailure)\n" + e.toString());
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+
+                            detailDeviceInfo = "deviceId : " + (json_data.isNull("deviceId") ? "" : json_data.getString("deviceId"))
+                                    + "\ndeviceType : " + (json_data.isNull("deviceType") ? "" : json_data.getString("deviceType"))
+                                    + "\nfirmwareVersion : " + (json_data.isNull("firmwareVersion") ? "" : json_data.getString("firmwareVersion"))
+                                    + "\nfriendlyName : " + (json_data.isNull("friendlyName") ? "" : json_data.getString("friendlyName"))
+                                    + "\nsharedFriendlyName : " + (json_data.isNull("sharedFriendlyName") ? "" : json_data.getString("sharedFriendlyName"))
+                                    + "\nownerId : " + (json_data.isNull("ownerId") ? "" : json_data.getString("ownerId"))
+                                    + "\ndgroupId : " + (json_data.isNull("dgroupId") ? "" : json_data.getString("dgroupId"))
+                                    + "\ndgroupName : " + (json_data.isNull("dgroupName") ? "" : json_data.getString("dgroupName"));
+
+                            currentVersionForFirmware = json_data.isNull("firmwareVersion") ? "" : json_data.getString("firmwareVersion");
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    showFirmwareMenu();
+                                    DismissConnectDialog();
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) JSONException : " + e.toString());
+                        }
+                        DismissConnectDialog();
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(Exception)\n" + e.toString());
+            DismissConnectDialog();
+        }
+    }
+
+    private void getJaesilData() {
+
+        final String topicGetP = String.format(getString(R.string.topic_get_jaesil_data),deviceInfo.deviceId);
+        final String topicGetS1 = String.format(getString(R.string.topic_response_get_jaesil_data),deviceInfo.deviceId);
+
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                MqttController.getInstance().publish(topicGetP,"{}");
+            }
+        },MQTT_PUBLISH_DELAY_TIME);
+
+        MqttController.getInstance().subscribeToTopic(topicGetS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+
+                int temp_power = NOTING;
+
+                try {
+                    JSONObject jsonObject = new JSONObject(new String(data));
+
+                    if(!jsonObject.isNull("state")) {  //Update topic
+                        JSONObject jsonState = jsonObject.getJSONObject("state");
+                        if (!jsonState.isNull("reported")) {
+                            JSONObject jsonReported = jsonState.getJSONObject("reported");
+                            //Power
+                            if (!jsonReported.isNull("sse")) {
+                                //power off
+                                temp_power = jsonReported.getInt("sse");
+
+                            }
+                        }
+                    }
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+
+                final int powerFix = temp_power;
+                if(powerFix != NOTING) {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            ((TextView)findViewById(R.id.txtJaesilStatus)).setText(getString(powerFix == 1?R.string.on:R.string.off));
+                        }
+                    });
+                }
+                MqttController.getInstance().unsubscribeToTopic(topicGetS1);
+            }
+        });
+    }
+
+    private void getNewVersionForFirmware() {
+        displayProgress(this,"","");
+        try {
+            HttpApi.getFirmwareInfoAtDevice(deviceInfo.deviceId, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getFirmwareInfoAtDevice(onFailure)\n" + e.toString());
+                    showNotiMsg(DeviceSettingActivity.this,"스마트 조명 최신 펌웨어 정보 획득을 실패하였습니다.");
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if (response.code() == HttpApi.HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getFirmwareInfoAtDevice(onResponse) HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            newVersionForFirmware = json_data.isNull("version") ? "" : json_data.getString("version");
+                            DebugLogOutput(TAG, "new Firmware Version : " + newVersionForFirmware);
+
+                            getDetailDeviceInfo();
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                        }
+
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getFirmwareInfoAtDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(DeviceSettingActivity.this,"스마트 조명 최신 펌웨어 정보 획득을 실패하였습니다.");
+
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getFirmwareInfoAtDevice(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getFirmwareInfoAtDevice(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                    DismissConnectDialog();
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getFirmwareInfoAtDevice(Exception)\n" + e.toString());
+            showNotiMsg(DeviceSettingActivity.this,"스마트 조명 최신 펌웨어 정보 획득을 실패하였습니다.");
+            DismissConnectDialog();
+        }
+    }
+
+    private void updateFirmware() {
+        displayProgress(this,"","");
+        try {
+            HttpApi.updateFirmwareInfoAtDevice(deviceInfo.deviceId, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.updateFirmwareInfoAtDevice(onFailure)\n" + e.toString());
+                    showNotiMsg(DeviceSettingActivity.this,"스마트 조명 최신 펌웨어 업데이트를 실패하였습니다.");
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.updateFirmwareInfoAtDevice(onResponse) HTTP_NO_CONTENT");
+                        showNotiMsg(DeviceSettingActivity.this,"스마트 조명 최신 펌웨어를 업데이트 하였습니다.");
+                        runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                layoutFirmware.setVisibility(View.GONE);
+                                DismissConnectDialog();
+                            }
+                        });
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.updateFirmwareInfoAtDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(DeviceSettingActivity.this,"스마트 조명 최신 펌웨어 업데이트를 실패하였습니다.");
+
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.updateFirmwareInfoAtDevice(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.updateFirmwareInfoAtDevice(onResponse) JSONException : " + e.toString());
+                        }
+                        DismissConnectDialog();
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.updateFirmwareInfoAtDevice(Exception)\n" + e.toString());
+            showNotiMsg(DeviceSettingActivity.this,"스마트 조명 최신 펌웨어 업데이트를 실패하였습니다.");
+            DismissConnectDialog();
+        }
+    }
+
+    private void showFirmwareMenu() {
+        if(currentVersionForFirmware.isEmpty() || newVersionForFirmware.isEmpty() || currentVersionForFirmware.equals(newVersionForFirmware)) {
+            layoutFirmware.setVisibility(View.GONE);
+        } else {
+            layoutFirmware.setVisibility(View.VISIBLE);
+        }
+    }
+}

+ 5 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DragListView/ChangeListener.java

@@ -0,0 +1,5 @@
+package kr.co.icontrols.iotplatform.DragListView;
+
+public interface ChangeListener {
+    void onChangeData();
+}

+ 30 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DragListView/ItemViewHolder.java

@@ -0,0 +1,30 @@
+package kr.co.icontrols.iotplatform.DragListView;
+
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import kr.co.icontrols.iotplatform.R;
+
+/**
+ * 드래그 & 드랍 리스트 컴퍼넌트 클래스
+ */
+public class ItemViewHolder extends RecyclerView.ViewHolder {
+
+    TextView txtGroupName;
+    TextView txtDeviceCount;
+    ImageView imgIcon;
+    ImageView imgDrag;
+
+    public ItemViewHolder(View itemView) {
+        super(itemView);
+
+        txtGroupName = itemView.findViewById(R.id.txtGroupName);
+        txtDeviceCount = itemView.findViewById(R.id.txtDeviceCount);
+        imgIcon = itemView.findViewById(R.id.imgIcon);
+        imgDrag = itemView.findViewById(R.id.imgDrag);
+    }
+}
+

+ 166 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DragListView/RankListAdapter.java

@@ -0,0 +1,166 @@
+package kr.co.icontrols.iotplatform.DragListView;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.R;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCircleBitmap;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageResource;
+import static kr.co.icontrols.iotplatform.utils.Constants.DEFUALT_GROUP_INDEX;
+
+/**
+ * 그룹 순서 리스트 Adapter
+ */
+public class RankListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements
+        SwipeAndDragHelper.ActionCompletionContract {
+
+    public static final int MODE_GROUP = 0;
+    public static final int MODE_DEVICE = 1;
+
+    private static int mMode = 0;
+
+    private static final int GROUP_TYPE = 1;
+    private List<DeviceGroupInfo> itemsGroupList;
+    private List<DeviceInfo> itemsDeviceList;
+    private ItemTouchHelper touchHelper;
+    private ChangeListener listener;
+
+    private Activity activity;
+
+    public RankListAdapter(ChangeListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view;
+        activity = (Activity)parent.getContext();
+        view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.listview_group_rank_list_item, parent, false);
+        return new ItemViewHolder(view);
+    }
+
+    @Override
+
+    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
+        int itemViewType = getItemViewType(position);
+        switch (mMode) {
+            case MODE_GROUP:
+                if (itemViewType == GROUP_TYPE) {
+                    ((ItemViewHolder) holder).txtGroupName.setText(itemsGroupList.get(position).friendlyName);
+                    ((ItemViewHolder) holder).txtDeviceCount.setText(itemsGroupList.get(position).deviceCount + "개 스마트 조명");
+                    ((ItemViewHolder) holder).imgIcon.setImageBitmap(getCircleBitmap(getGroupImageResource(itemsGroupList.get(position).icon)));
+                    if (itemsGroupList.get(position).index == DEFUALT_GROUP_INDEX) {
+                        ((ItemViewHolder) holder).imgDrag.setVisibility(View.INVISIBLE);
+                    } else {
+                        ((ItemViewHolder) holder).imgDrag.setOnTouchListener(new View.OnTouchListener() {
+                            @Override
+                            public boolean onTouch(View v, MotionEvent event) {
+                                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                                    touchHelper.startDrag(holder);
+                                }
+                                return false;
+                            }
+                        });
+                    }
+                }
+                break;
+
+            case MODE_DEVICE:
+                ((ItemViewHolder) holder).txtGroupName.setText(itemsDeviceList.get(position).friendlyName.isEmpty()?itemsDeviceList.get(position).sharedFriendlyName.isEmpty()?activity.getString(R.string.no_name):itemsDeviceList.get(position).sharedFriendlyName:itemsDeviceList.get(position).friendlyName);
+                ((ItemViewHolder) holder).txtDeviceCount.setText(itemsDeviceList.get(position).dgroupName);
+                ((ItemViewHolder) holder).imgIcon.setImageResource(R.drawable.icon_add_device_light);
+                ((ItemViewHolder) holder).imgIcon.setBackgroundResource(R.drawable.background_circle_add_device_icon);
+                ((ItemViewHolder) holder).imgDrag.setOnTouchListener(new View.OnTouchListener() {
+                    @Override
+                    public boolean onTouch(View v, MotionEvent event) {
+                        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                            touchHelper.startDrag(holder);
+                        }
+                        return false;
+                    }
+                });
+                break;
+
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        if(mMode == MODE_GROUP) {
+            return itemsGroupList == null ? 0 : itemsGroupList.size();
+        } else if(mMode == MODE_DEVICE) {
+            return itemsDeviceList == null ? 0 : itemsDeviceList.size();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return GROUP_TYPE;
+    }
+
+    public void setIGroupInfoList(List<DeviceGroupInfo> itemsList) {
+        mMode = MODE_GROUP;
+        this.itemsGroupList = itemsList;
+    }
+
+    public void setDeviceInfoList(List<DeviceInfo> itemsList) {
+        mMode = MODE_DEVICE;
+        this.itemsDeviceList = itemsList;
+    }
+
+    @Override
+    public void onViewMoved(int oldPosition, int newPosition) {
+
+        if(mMode == MODE_GROUP) {
+            DeviceGroupInfo targetUser = itemsGroupList.get(oldPosition);
+            DeviceGroupInfo user = new DeviceGroupInfo(targetUser);
+            itemsGroupList.remove(oldPosition);
+            itemsGroupList.add(newPosition, user);
+
+        } else if(mMode == MODE_DEVICE) {
+            DeviceInfo targetUser = itemsDeviceList.get(oldPosition);
+            DeviceInfo user = new DeviceInfo(targetUser);
+            itemsDeviceList.remove(oldPosition);
+            itemsDeviceList.add(newPosition, user);
+        }
+        notifyItemMoved(oldPosition, newPosition);
+
+        if(listener != null) {
+            listener.onChangeData();
+        }
+    }
+
+    @Override
+    public void onViewSwiped(int position) {
+        if(mMode == MODE_GROUP) {
+            itemsGroupList.remove(position);
+        } else if(mMode == MODE_DEVICE) {
+            itemsDeviceList.remove(position);
+        }
+        notifyItemRemoved(position);
+
+        if(listener != null) {
+            listener.onChangeData();
+        }
+    }
+
+    public void setTouchHelper(ItemTouchHelper touchHelper) {
+        this.touchHelper = touchHelper;
+    }
+}
+

+ 62 - 0
app/src/main/java/kr/co/icontrols/iotplatform/DragListView/SwipeAndDragHelper.java

@@ -0,0 +1,62 @@
+package kr.co.icontrols.iotplatform.DragListView;
+
+import android.graphics.Canvas;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.ItemTouchHelper;
+
+/**
+ * 드래그&드랍을 위한 Class
+ */
+public class SwipeAndDragHelper  extends ItemTouchHelper.Callback {
+
+    private ActionCompletionContract contract;
+
+    public SwipeAndDragHelper(ActionCompletionContract contract) {
+        this.contract = contract;
+    }
+
+    @Override
+    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
+        int swipeFlags = 0/*ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT*/;
+        return makeMovementFlags(dragFlags, swipeFlags);
+    }
+
+    @Override
+    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+        contract.onViewMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
+        return true;
+    }
+
+    @Override
+    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+        contract.onViewSwiped(viewHolder.getAdapterPosition());
+    }
+
+    @Override
+    public boolean isLongPressDragEnabled() {
+        return false;
+    }
+
+    @Override
+    public void onChildDraw(Canvas c,
+                            RecyclerView recyclerView,
+                            RecyclerView.ViewHolder viewHolder,
+                            float dX,
+                            float dY,
+                            int actionState,
+                            boolean isCurrentlyActive) {
+        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
+            float alpha = 1 - (Math.abs(dX) / recyclerView.getWidth());
+            viewHolder.itemView.setAlpha(alpha);
+        }
+        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
+    }
+
+    public interface ActionCompletionContract {
+        void onViewMoved(int oldPosition, int newPosition);
+
+        void onViewSwiped(int position);
+    }
+
+}

+ 278 - 0
app/src/main/java/kr/co/icontrols/iotplatform/FaqActivity.java

@@ -0,0 +1,278 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class FaqActivity extends AppCompatActivity implements View.OnClickListener {
+
+    ExpandableListAdapter listAdapter;
+    ExpandableListView expListView;
+    List<String> listDataHeader;
+    HashMap<String, List<String>> listDataChild;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_faq);
+
+        // get the listview
+        expListView = findViewById(R.id.listView);
+        expListView.setGroupIndicator(null);
+
+        // preparing list data
+        prepareListData();
+
+        listAdapter = new ExpandableListAdapter(this, listDataHeader, listDataChild);
+
+        // setting list adapter
+        expListView.setAdapter(listAdapter);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+        }
+    }
+
+    public class ExpandableListAdapter extends BaseExpandableListAdapter {
+
+        private Context _context;
+        private List<String> _listDataHeader; // header titles
+        // child data in format of header title, child title
+        private HashMap<String, List<String>> _listDataChild;
+
+        public ExpandableListAdapter(Context context, List<String> listDataHeader,
+                                     HashMap<String, List<String>> listChildData) {
+            this._context = context;
+            this._listDataHeader = listDataHeader;
+            this._listDataChild = listChildData;
+        }
+
+        @Override
+        public Object getChild(int groupPosition, int childPosititon) {
+            return this._listDataChild.get(this._listDataHeader.get(groupPosition))
+                    .get(childPosititon);
+        }
+
+        @Override
+        public long getChildId(int groupPosition, int childPosition) {
+            return childPosition;
+        }
+
+        @Override
+        public View getChildView(int groupPosition, final int childPosition,
+                                 boolean isLastChild, View convertView, ViewGroup parent) {
+
+            final String childText = (String) getChild(groupPosition, childPosition);
+
+            if (convertView == null) {
+                LayoutInflater infalInflater = (LayoutInflater) this._context
+                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                convertView = infalInflater.inflate(R.layout.expandlistview_list_item, null);
+            }
+
+            TextView txtListChild = (TextView) convertView
+                    .findViewById(R.id.lblListItem);
+
+            txtListChild.setText(childText);
+            return convertView;
+        }
+
+        @Override
+        public int getChildrenCount(int groupPosition) {
+            return this._listDataChild.get(this._listDataHeader.get(groupPosition))
+                    .size();
+        }
+
+        @Override
+        public Object getGroup(int groupPosition) {
+            return this._listDataHeader.get(groupPosition);
+        }
+
+        @Override
+        public int getGroupCount() {
+            return this._listDataHeader.size();
+        }
+
+        @Override
+        public long getGroupId(int groupPosition) {
+            return groupPosition;
+        }
+
+        @Override
+        public View getGroupView(int groupPosition, boolean isExpanded,
+                                 View convertView, ViewGroup parent) {
+            String headerTitle = (String) getGroup(groupPosition);
+            if (convertView == null) {
+                LayoutInflater infalInflater = (LayoutInflater) this._context
+                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                convertView = infalInflater.inflate(R.layout.expandlistview_list_group, null);
+            }
+
+            TextView lblListHeader = (TextView) convertView.findViewById(R.id.lblListHeader);
+            lblListHeader.setText(headerTitle);
+            ImageView imgArrow = (ImageView) convertView.findViewById(R.id.imgArrow);
+            if(isExpanded){
+                //lblListHeader.setTextColor(Color.rgb(0xfb,0x8a,0x3c));
+                imgArrow.setImageResource(R.drawable.list_sel_arrow_open);
+            } else {
+                //lblListHeader.setTextColor(Color.rgb(0x00,0x00,0x00));
+                imgArrow.setImageResource(R.drawable.list_sel_arrow);
+            }
+
+            return convertView;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return false;
+        }
+
+        @Override
+        public boolean isChildSelectable(int groupPosition, int childPosition) {
+            return true;
+        }
+    }
+
+    /**
+     * 자주하는 질문 리스트 구성 함수
+     */
+    private void prepareListData() {
+        listDataHeader = new ArrayList<String>();
+        listDataChild = new HashMap<String, List<String>>();
+
+        // Adding child data
+        listDataHeader.add(getString(R.string.faq_title_1));
+        listDataHeader.add(getString(R.string.faq_title_2));
+        listDataHeader.add(getString(R.string.faq_title_3));
+        listDataHeader.add(getString(R.string.faq_title_4));
+        listDataHeader.add(getString(R.string.faq_title_5));
+        listDataHeader.add(getString(R.string.faq_title_6));
+        listDataHeader.add(getString(R.string.faq_title_7));
+        listDataHeader.add(getString(R.string.faq_title_8));
+        listDataHeader.add(getString(R.string.faq_title_9));
+        listDataHeader.add(getString(R.string.faq_title_10));
+        listDataHeader.add(getString(R.string.faq_title_11));
+        listDataHeader.add(getString(R.string.faq_title_12));
+        listDataHeader.add(getString(R.string.faq_title_13));
+        listDataHeader.add(getString(R.string.faq_title_14));
+        listDataHeader.add(getString(R.string.faq_title_15));
+        listDataHeader.add(getString(R.string.faq_title_16));
+        listDataHeader.add(getString(R.string.faq_title_17));
+        listDataHeader.add(getString(R.string.faq_title_18));
+
+        // Adding child data
+        List<String> faq1 = new ArrayList<String>();
+        faq1.add(getString(R.string.faq_description_1));
+
+        List<String> faq2 = new ArrayList<String>();
+        faq2.add(getString(R.string.faq_description_2));
+
+        List<String> faq3 = new ArrayList<String>();
+        faq3.add(getString(R.string.faq_description_3));
+
+        List<String> faq4 = new ArrayList<String>();
+        faq4.add(getString(R.string.faq_description_4));
+
+        List<String> faq5 = new ArrayList<String>();
+        faq5.add(getString(R.string.faq_description_5));
+
+        List<String> faq6 = new ArrayList<String>();
+        faq6.add(getString(R.string.faq_description_6));
+
+        List<String> faq7 = new ArrayList<String>();
+        faq7.add(getString(R.string.faq_description_7));
+
+        List<String> faq8 = new ArrayList<String>();
+        faq8.add(getString(R.string.faq_description_8));
+
+        List<String> faq9 = new ArrayList<String>();
+        faq9.add(getString(R.string.faq_description_9));
+
+        List<String> faq10 = new ArrayList<String>();
+        faq10.add(getString(R.string.faq_description_10));
+
+        List<String> faq11 = new ArrayList<String>();
+        faq11.add(getString(R.string.faq_description_11));
+
+        List<String> faq12 = new ArrayList<String>();
+        faq12.add(getString(R.string.faq_description_12));
+
+        List<String> faq13 = new ArrayList<String>();
+        faq13.add(getString(R.string.faq_description_13));
+
+        List<String> faq14 = new ArrayList<String>();
+        faq14.add(getString(R.string.faq_description_14));
+
+        List<String> faq15 = new ArrayList<String>();
+        faq15.add(getString(R.string.faq_description_15));
+
+        List<String> faq16 = new ArrayList<String>();
+        faq16.add(getString(R.string.faq_description_16));
+
+        List<String> faq17 = new ArrayList<String>();
+        faq17.add(getString(R.string.faq_description_17));
+
+        List<String> faq18 = new ArrayList<String>();
+        faq18.add(getString(R.string.faq_description_18));
+
+        listDataChild.put(listDataHeader.get(0), faq1); // Header, Child data
+        listDataChild.put(listDataHeader.get(1), faq2);
+        listDataChild.put(listDataHeader.get(2), faq3);
+        listDataChild.put(listDataHeader.get(3), faq4);
+        listDataChild.put(listDataHeader.get(4), faq5);
+        listDataChild.put(listDataHeader.get(5), faq6);
+        listDataChild.put(listDataHeader.get(6), faq7);
+        listDataChild.put(listDataHeader.get(7), faq8);
+        listDataChild.put(listDataHeader.get(8), faq9);
+        listDataChild.put(listDataHeader.get(9), faq10);
+        listDataChild.put(listDataHeader.get(10), faq11);
+        listDataChild.put(listDataHeader.get(11), faq12);
+        listDataChild.put(listDataHeader.get(12), faq13);
+        listDataChild.put(listDataHeader.get(13), faq14);
+        listDataChild.put(listDataHeader.get(14), faq15);
+        listDataChild.put(listDataHeader.get(15), faq16);
+        listDataChild.put(listDataHeader.get(16), faq17);
+        listDataChild.put(listDataHeader.get(17), faq18);
+    }
+}

+ 14 - 0
app/src/main/java/kr/co/icontrols/iotplatform/ForgotPasswordActivity.java

@@ -0,0 +1,14 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+
+public class ForgotPasswordActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_forgot_password);
+    }
+}

+ 186 - 0
app/src/main/java/kr/co/icontrols/iotplatform/GroupChangeRankActivity.java

@@ -0,0 +1,186 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.DragListView.ChangeListener;
+import kr.co.icontrols.iotplatform.DragListView.RankListAdapter;
+import kr.co.icontrols.iotplatform.DragListView.SwipeAndDragHelper;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_LIST;
+
+public class GroupChangeRankActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "GroupChangeRankActivity";
+
+    private ArrayList<DeviceGroupInfo> mGroupList;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_group_change_rank);
+
+        mGroupList = (ArrayList<DeviceGroupInfo>) getIntent().getSerializableExtra(EXTRA_GROUP_LIST);
+
+        RecyclerView itemRecyclerView = findViewById(R.id.recyclerview_group_list);
+        itemRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+        RankListAdapter adapter = new RankListAdapter(new ChangeListener() {
+            @Override
+            public void onChangeData() {
+                findViewById(R.id.btnBack).setVisibility(View.GONE);
+                findViewById(R.id.btnBackText).setVisibility(View.VISIBLE);
+                findViewById(R.id.btnRight).setVisibility(View.VISIBLE);
+            }
+        });
+        adapter.setIGroupInfoList(mGroupList);
+        SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(adapter);
+        ItemTouchHelper touchHelper = new ItemTouchHelper(swipeAndDragHelper);
+        adapter.setTouchHelper(touchHelper);
+        itemRecyclerView.setAdapter(adapter);
+        touchHelper.attachToRecyclerView(itemRecyclerView);
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnAlarmSetting:
+                startActivity(new Intent(this, AlarmSettingActivity.class));
+                break;
+            case R.id.btnAppInfo:
+                startActivity(new Intent(this, AppInfoActivity.class));
+                break;
+            case R.id.btnBack:
+            case R.id.btnBackText:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+                //인덱스 변경
+                for(int i = 0 ; i < mGroupList.size() ; i++) {
+                    mGroupList.get(i).index = i;
+                }
+
+                displayProgress(this,"","");
+                try {
+                    HttpApi.changeDeviceGroupListRank(mGroupList, new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.changeDeviceGroupListRank(onFailure)\n" + e.toString());
+                            showNotiMsg(GroupChangeRankActivity.this, "그룹 순서를 실패하였습니다.");
+                            DismissConnectDialog();
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                WarnLogOutput(TAG, "HttpApi.changeDeviceGroupListRank(onResponse) HTTP_NO_CONTENT");
+                                sortGroupRank();
+                                showNotiMsg(GroupChangeRankActivity.this, "그룹 순서를 저장하였습니다.",MyGroupActivity.class);
+                                DismissConnectDialog();
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.changeDeviceGroupListRank(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                DismissConnectDialog();
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.changeDeviceGroupListRank(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.changeDeviceGroupListRank(onResponse) JSONException : " + e.toString());
+                                }
+                                showNotiMsg(GroupChangeRankActivity.this, "그룹 순서를 실패하였습니다.");
+                            }
+                        }
+                    });
+                } catch (Exception e){
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.changeDeviceGroupListRank(Exception)\n" + e.toString());
+                    showNotiMsg(GroupChangeRankActivity.this, "그룹 순서를 실패하였습니다.");
+                    DismissConnectDialog();
+                }
+                break;
+        }
+    }
+
+    /**
+     * UserInfo.dgroup 순서 재정렬
+     */
+    private void sortGroupRank() {
+        for(DeviceGroupInfo g1 : mGroupList) {
+            for(int i = 0 ; i < UserInfo.dgroups.size() ; i++) {
+                if(g1.dgroupId.equals(UserInfo.dgroups.get(i).dgroupId)) {
+                    UserInfo.dgroups.get(i).index = g1.index;
+                }
+            }
+        }
+
+        if(UserInfo.dgroups.size() > 1) {
+            for (int i = 0; i < UserInfo.dgroups.size() - 1; i++) {
+                for (int j = i + 1; j < UserInfo.dgroups.size(); j++) {
+                    if (UserInfo.dgroups.get(i).index > UserInfo.dgroups.get(j).index) {
+                        DeviceGroupInfo item = UserInfo.dgroups.get(j);
+                        UserInfo.dgroups.remove(j);
+                        UserInfo.dgroups.add(i, item);
+                    }
+                }
+            }
+        }
+    }
+}

+ 446 - 0
app/src/main/java/kr/co/icontrols/iotplatform/GroupSelectActivity.java

@@ -0,0 +1,446 @@
+package kr.co.icontrols.iotplatform;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.adapter.GroupSelectAdapter;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_HOME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_LIST;
+
+public class GroupSelectActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "GroupSelectActivity";
+    private ListView mListView;
+    private ArrayList<DeviceGroupInfo> mGroupList;
+    private Handler mHandler;
+    private boolean isCompleted;
+    private String mMode;
+    private String mDeviceId;
+    private String mDeviceName;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_group_select);
+
+        isCompleted = false;
+        mMode = getIntent().getStringExtra("MODE");
+
+        mListView = findViewById(R.id.listView);
+
+        if(mMode.equals("DELETE")) {
+            mGroupList = (ArrayList<DeviceGroupInfo>) getIntent().getSerializableExtra(EXTRA_GROUP_LIST);
+            mListView.setAdapter(new GroupSelectAdapter(mGroupList));
+            ((TextView)findViewById(R.id.txtTitle)).setText(getString(R.string.delete_group));
+            ((TextView)findViewById(R.id.btnRight)).setText(getString(R.string.delete));
+            ((TextView)findViewById(R.id.txtDescription)).setText(getString(R.string.delete_group_description));
+        } else if(mMode.equals("SELECT")) {
+            mGroupList = (ArrayList<DeviceGroupInfo>) getIntent().getSerializableExtra(EXTRA_GROUP_LIST);
+            mListView.setAdapter(new GroupSelectAdapter(mGroupList,getIntent().getStringExtra(EXTRA_GROUP_ID)));
+            mDeviceId = getIntent().getStringExtra(EXTRA_DEVICE_ID);
+            mDeviceName = getIntent().getStringExtra(EXTRA_DEVICE_NAME);
+            ((TextView)findViewById(R.id.txtTitle)).setText(getString(R.string.change_group));
+            ((TextView)findViewById(R.id.btnRight)).setText(getString(R.string.save));
+            ((TextView)findViewById(R.id.txtDescription)).setText(String.format(getString(R.string.select_group_description),mDeviceName));
+        }
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+
+                if(mMode.equals("SELECT")) {
+                    ((GroupSelectAdapter)mListView.getAdapter()).setSelectedForSingle(position);
+                } else {
+                    if(position == 0) {
+                        showNotiMsg(GroupSelectActivity.this, "기본그룹은 삭제가 불가능합니다.");
+                    } else {
+                        ((GroupSelectAdapter) mListView.getAdapter()).setSelected(position);
+                    }
+                }
+
+                ((GroupSelectAdapter)mListView.getAdapter()).notifyDataSetChanged();
+
+                if(((GroupSelectAdapter)mListView.getAdapter()).isSelected()) {
+                    findViewById(R.id.btnBack).setVisibility(View.GONE);
+                    findViewById(R.id.btnBackText).setVisibility(View.VISIBLE);
+                    findViewById(R.id.btnRight).setVisibility(View.VISIBLE);
+                } else {
+                    findViewById(R.id.btnBack).setVisibility(View.VISIBLE);
+                    findViewById(R.id.btnBackText).setVisibility(View.GONE);
+                    findViewById(R.id.btnRight).setVisibility(View.INVISIBLE);
+                }
+            }
+        });
+
+        mHandler = new Handler() {
+            @Override
+            public void handleMessage(@NonNull Message msg) {
+                final int index = msg.what;
+                if (mGroupList.size() > index && mGroupList.get(index).isSelected()) {
+                    if(mMode.equals("DELETE")) {
+                        try {
+                            HttpApi.deleteDeviceGroup(mGroupList.get(index).dgroupId, new okhttp3.Callback() {
+                                @Override
+                                public void onFailure(Call call, IOException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onFailure)\n" + e.toString());
+                                    showNotiMsg(GroupSelectActivity.this, "그룹을 삭제중 오류가 발생하였습니다.");
+                                    DismissConnectDialog();
+                                }
+
+                                @Override
+                                public void onResponse(Call call, Response response) throws IOException {
+                                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                        WarnLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) HTTP_NO_CONTENT");
+
+                                        if(mGroupList.size() > 0) {
+                                            final ArrayList<DeviceInfo> itemS = DBUtil.getDeviceInfoForGroupId(TABLE_SMART_LIGHT, mGroupList.get(index).dgroupId);
+                                            if (itemS != null) {
+                                                for(DeviceInfo d : itemS) {
+                                                    d.dgroupId = mGroupList.get(0).dgroupId;
+                                                    d.dgroupName = mGroupList.get(0).friendlyName;
+                                                    DBUtil.updateDeviceItem(TABLE_SMART_LIGHT, d);
+                                                }
+                                                itemS.clear();
+                                            }
+
+                                            final ArrayList<DeviceInfo> itemH = DBUtil.getDeviceInfoForGroupId(TABLE_DEVICE_HOME, mGroupList.get(index).dgroupId);
+                                            if (itemH != null) {
+                                                for(DeviceInfo d : itemH) {
+                                                    d.dgroupId = mGroupList.get(0).dgroupId;
+                                                    d.dgroupName = mGroupList.get(0).friendlyName;
+                                                    DBUtil.updateDeviceItem(TABLE_DEVICE_HOME, d);
+                                                }
+                                                itemH.clear();
+                                            }
+
+                                            final ArrayList<DeviceInfo> itemR = DBUtil.getDeviceInfoForGroupId(TABLE_DEVICE_RANK, mGroupList.get(index).dgroupId);
+                                            if (itemR != null) {
+                                                for(DeviceInfo d : itemR) {
+                                                    d.dgroupId = mGroupList.get(0).dgroupId;
+                                                    d.dgroupName = mGroupList.get(0).friendlyName;
+                                                    DBUtil.updateDeviceItem(TABLE_DEVICE_RANK, d);
+                                                }
+                                                itemR.clear();
+                                            }
+
+                                            for (int i = 0; i < UserInfo.devices.size(); i++) {
+                                                if (UserInfo.devices.get(i).dgroupId.equals(mGroupList.get(index).dgroupId)) {
+                                                    UserInfo.devices.get(i).dgroupName = mGroupList.get(0).friendlyName;
+                                                    UserInfo.devices.get(i).dgroupId = mGroupList.get(0).dgroupId;
+                                                }
+                                            }
+                                        }
+
+                                        mHandler.sendEmptyMessage(index+1);
+                                        isCompleted = true;
+                                        //showNotiMsg(GroupSelectActivity.this, "그룹을 삭제하였습니다.", MyGroupActivity.class);
+                                    } else {
+                                        ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                        showNotiMsg(GroupSelectActivity.this, "그룹을 삭제중 오류가 발생하였습니다.");
+                                        try {
+                                            JSONObject json_data = new JSONObject(response.body().string());
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) body : " + json_data.toString());
+                                        } catch (JSONException e) {
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) JSONException : " + e.toString());
+                                        }
+                                        DismissConnectDialog();
+                                    }
+                                }
+                            });
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(Exception)\n" + e.toString());
+                            showNotiMsg(GroupSelectActivity.this, "그룹을 삭제중 오류가 발생하였습니다.");
+                            DismissConnectDialog();
+                        }
+                    } else if(mMode.equals("SELECT")) {
+                        try {
+                            HttpApi.connectDeviceInDeviceGroup(mGroupList.get(index).dgroupId, mDeviceId, new okhttp3.Callback() {
+                                @Override
+                                public void onFailure(Call call, IOException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onFailure)\n" + e.toString());
+                                    showNotiMsg(GroupSelectActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                                    DismissConnectDialog();
+                                }
+
+                                @Override
+                                public void onResponse(Call call, Response response) throws IOException {
+                                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                        WarnLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) HTTP_NO_CONTENT");
+
+                                        final DeviceInfo itemS = DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,mDeviceId);
+                                        if(itemS != null) {
+                                            itemS.dgroupId = mGroupList.get(index).dgroupId;
+                                            itemS.dgroupName = mGroupList.get(index).friendlyName;
+                                            DBUtil.updateDeviceItem(TABLE_SMART_LIGHT,itemS);
+                                        }
+
+                                        final DeviceInfo itemH = DBUtil.getDeviceInfoForId(TABLE_DEVICE_HOME,mDeviceId);
+                                        if(itemH != null) {
+                                            itemH.dgroupId = mGroupList.get(index).dgroupId;
+                                            itemH.dgroupName = mGroupList.get(index).friendlyName;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_HOME,itemH);
+                                        }
+
+                                        final DeviceInfo itemR = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK,mDeviceId);
+                                        if(itemR != null) {
+                                            itemR.dgroupId = mGroupList.get(index).dgroupId;
+                                            itemR.dgroupName = mGroupList.get(index).friendlyName;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_RANK,itemR);
+                                        }
+
+                                        for(int i = 0; i < UserInfo.devices.size() ; i++) {
+                                            if(UserInfo.devices.get(i).deviceId.equals(mDeviceId)) {
+                                                UserInfo.devices.get(i).dgroupName = mGroupList.get(index).friendlyName;
+                                                UserInfo.devices.get(i).dgroupId = mGroupList.get(index).dgroupId;
+                                            }
+                                        }
+                                        mHandler.sendEmptyMessage(index+1);
+                                        isCompleted = true;
+                                        //showNotiMsg(GroupSelectActivity.this, "그룹을 변경하였습니다.", true);
+                                    } else {
+                                        ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                        showNotiMsg(GroupSelectActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                                        try {
+                                            JSONObject json_data = new JSONObject(response.body().string());
+                                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) body : " + json_data.toString());
+                                        } catch (JSONException e) {
+                                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) JSONException : " + e.toString());
+                                        }
+                                        DismissConnectDialog();
+                                    }
+                                }
+                            });
+                        }  catch (Exception e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(Exception)\n" + e.toString());
+                            showNotiMsg(GroupSelectActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                            DismissConnectDialog();
+                        }
+                    }
+                } else if(mGroupList.size() > index) {
+                    mHandler.sendEmptyMessage(index+1);
+                } else {
+                    if(isCompleted) {
+                        if(mMode.equals("SELECT")){
+                            showNotiMsg(GroupSelectActivity.this, "그룹을 변경하였습니다.", true);
+                        } else {
+                            UserInfo.dgroups.clear();
+                            showNotiMsg(GroupSelectActivity.this, "그룹을 삭제하였습니다.", MyGroupActivity.class);
+                        }
+                    } else {
+                        if(mMode.equals("SELECT")){
+                            showNotiMsg(GroupSelectActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                        } else {
+                            showNotiMsg(GroupSelectActivity.this, "그룹을 삭제중 오류가 발생하였습니다.");
+                        }
+                    }
+                    DismissConnectDialog();
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+            case R.id.btnBackText:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+                if(((GroupSelectAdapter)mListView.getAdapter()).isSelected()) {
+                    displayProgress(this, null, null);
+                    mHandler.sendEmptyMessage(0);
+                } else {
+                    if(mMode.equals("DELETE")) {
+                        showNotiMsg(GroupSelectActivity.this, "삭제할 그룹을 선택해주세요.");
+                    } else {
+                        showNotiMsg(GroupSelectActivity.this,"변경할 그룹을 선택해주세요.");
+                    }
+                }
+                /*
+                final int selectedIndex = ((GroupSelectAdapter)mListView.getAdapter()).getSelectedIndex();
+
+                if(mMode.equals("DELETE")) {
+                    if (selectedIndex == -1) {
+                        showNotiMsg(GroupSelectActivity.this, "삭제할 그룹을 선택해주세요.");
+                    } else {
+                        displayProgress(this, "", "");
+                        try {
+                            HttpApi.deleteDeviceGroup(mGroupList.get(selectedIndex).dgroupId, new Callback() {
+                                @Override
+                                public void onFailure(Call call, IOException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onFailure)\n" + e.toString());
+                                    showNotiMsg(GroupSelectActivity.this, "그룹을 삭제중 오류가 발생하였습니다.");
+                                    DismissConnectDialog();
+                                }
+
+                                @Override
+                                public void onResponse(Call call, Response response) throws IOException {
+                                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                        WarnLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) HTTP_NO_CONTENT");
+                                        showNotiMsg(GroupSelectActivity.this, "그룹을 삭제하였습니다.", MyGroupActivity.class);
+                                    } else {
+                                        ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                        showNotiMsg(GroupSelectActivity.this, "그룹을 삭제중 오류가 발생하였습니다.");
+                                        try {
+                                            JSONObject json_data = new JSONObject(response.body().string());
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) body : " + json_data.toString());
+                                        } catch (JSONException e) {
+                                            ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(onResponse) JSONException : " + e.toString());
+                                        }
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.deleteDeviceGroup(Exception)\n" + e.toString());
+                            showNotiMsg(GroupSelectActivity.this, "그룹을 삭제중 오류가 발생하였습니다.");
+                            DismissConnectDialog();
+                        }
+                    }
+                } else if(mMode.equals("SELECT")) {
+                    if (selectedIndex == -1) {
+                        showNotiMsg(GroupSelectActivity.this, "변경할 그룹을 선택해주세요.");
+                    } else {
+                        displayProgress(this, "", "");
+                        try {
+                            HttpApi.connectDeviceInDeviceGroup(mGroupList.get(selectedIndex).dgroupId, mDeviceId, new Callback() {
+                                @Override
+                                public void onFailure(Call call, IOException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onFailure)\n" + e.toString());
+                                    showNotiMsg(GroupSelectActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                                    DismissConnectDialog();
+                                }
+
+                                @Override
+                                public void onResponse(Call call, Response response) throws IOException {
+                                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                        WarnLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) HTTP_NO_CONTENT");
+
+                                        final DeviceInfo itemS = DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,mDeviceId);
+                                        if(itemS != null) {
+                                            itemS.dgroupId = mGroupList.get(selectedIndex).dgroupId;
+                                            itemS.dgroupName = mGroupList.get(selectedIndex).friendlyName;
+                                            DBUtil.updateDeviceItem(TABLE_SMART_LIGHT,itemS);
+                                        }
+
+                                        final DeviceInfo itemH = DBUtil.getDeviceInfoForId(TABLE_DEVICE_HOME,mDeviceId);
+                                        if(itemH != null) {
+                                            itemH.dgroupId = mGroupList.get(selectedIndex).dgroupId;
+                                            itemH.dgroupName = mGroupList.get(selectedIndex).friendlyName;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_HOME,itemH);
+                                        }
+
+                                        final DeviceInfo itemR = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK,mDeviceId);
+                                        if(itemR != null) {
+                                            itemR.dgroupId = mGroupList.get(selectedIndex).dgroupId;
+                                            itemR.dgroupName = mGroupList.get(selectedIndex).friendlyName;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_RANK,itemR);
+                                        }
+
+                                        for(int i = 0; i < UserInfo.devices.size() ; i++) {
+                                            if(UserInfo.devices.get(i).deviceId.equals(mDeviceId)) {
+                                                UserInfo.devices.get(i).dgroupName = mGroupList.get(selectedIndex).friendlyName;
+                                                UserInfo.devices.get(i).dgroupId = mGroupList.get(selectedIndex).dgroupId;
+                                            }
+                                        }
+
+                                        showNotiMsg(GroupSelectActivity.this, "그룹을 변경하였습니다.", true);
+                                    } else {
+                                        ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                        showNotiMsg(GroupSelectActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                                        try {
+                                            JSONObject json_data = new JSONObject(response.body().string());
+                                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) body : " + json_data.toString());
+                                        } catch (JSONException e) {
+                                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) JSONException : " + e.toString());
+                                        }
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+                        }  catch (Exception e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(Exception)\n" + e.toString());
+                            showNotiMsg(GroupSelectActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                            DismissConnectDialog();
+                        }
+                    }
+                }
+                 */
+                break;
+        }
+    }
+}

+ 60 - 0
app/src/main/java/kr/co/icontrols/iotplatform/InviteMemberActivity.java

@@ -0,0 +1,60 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class InviteMemberActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_invite_member);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnEmail:{
+                Intent intent = new Intent(this, InviteMemberInputActivity.class);
+                intent.putExtra("MODE","email");
+                startActivity(intent);
+                }
+                break;
+            case R.id.btnHp: {
+                Intent intent = new Intent(this, InviteMemberInputActivity.class);
+                intent.putExtra("MODE","phone");
+                startActivity(intent);
+                }
+                break;
+        }
+    }
+}

+ 178 - 0
app/src/main/java/kr/co/icontrols/iotplatform/InviteMemberInputActivity.java

@@ -0,0 +1,178 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.PopupActivity.DEFAULT_POPUP_TIMEOUT;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CONFIRM_SEND_INVITE_MSG;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INIT_APP_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_LOGOUT_SUCCESS;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_NO_CONTENT;
+
+public class InviteMemberInputActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "InviteMemberInputActivity";
+
+    private String mMode;
+    private String mValue = "";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_invite_member_input);
+
+        Intent intent = getIntent();
+        mMode = intent.getStringExtra("MODE");
+        if(mMode.equals("email")) {
+            ((TextView) findViewById(R.id.txtSubscription1)).setText(getString(R.string.activity_invite_member_input_text_email));
+            ((EditText)findViewById(R.id.txtInput)).setHint("abcde@abcde.com");
+        } else if(mMode.equals("phone")) {
+            ((TextView) findViewById(R.id.txtSubscription1)).setText(getString(R.string.activity_invite_member_input_text_phone));
+            ((EditText)findViewById(R.id.txtInput)).setHint("01012341234");
+        } else {
+            finish();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+                mValue = ((EditText)findViewById(R.id.txtInput)).getText().toString();
+                //TODO - 유효성 검사 필요함.
+                if(mValue.length()==0) {
+                    Intent intent = new Intent(this, PopupActivity.class);
+                    intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL);
+                    if(mMode.equals("email")) {
+                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_input_email));
+                    } else {
+                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_input_phone_number));
+                    }
+                    startActivity(intent);
+                } else {
+                    Intent intent = new Intent(this, PopupActivity.class);
+                    intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                    if(mMode.equals("email")) {
+                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_confirm_send_invite_email));
+                    } else {
+                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_confirm_send_invite_sms));
+                    }
+                    startActivityForResult(intent, RESULT_CONFIRM_SEND_INVITE_MSG);
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_CONFIRM_SEND_INVITE_MSG:
+                if (resultCode == Activity.RESULT_OK) {
+                    try {
+                        displayProgress(this,"","");
+                        if(mMode.equals("email")) {
+                            HttpApi.inviteGuestForMaster(true, mValue, mInviteGuestCallback);
+                        } else {
+                            HttpApi.inviteGuestForMaster(false, mValue, mInviteGuestCallback);
+                        }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.inviteGuestForMaster(Exception)\n" + e.toString());
+                        showNotiMsg(InviteMemberInputActivity.this, "맴버 초대를 실패하였습니다.");
+                        DismissConnectDialog();
+                    }
+                }
+                break;
+        }
+    }
+
+    Callback mInviteGuestCallback = new Callback() {
+        @Override
+        public void onFailure(Call call, IOException e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.inviteGuestForMaster(onFailure)\n" + e.toString());
+            DismissConnectDialog();
+        }
+
+        @Override
+        public void onResponse(Call call, Response response) throws IOException {
+            if(response.code() == HTTP_NO_CONTENT) {
+                WarnLogOutput(TAG, "HttpApi.inviteGuestForMaster(onResponse) Status code : " + response.code());
+                String msg = "";
+                if(mMode.equals("email")) {
+                    msg = "초대 이메일이 발송되었습니다";
+                } else {
+                    msg = "초대 문자가 발송되었습니다";
+                }
+                showNotiMsg(InviteMemberInputActivity.this,msg,AdminMemberActivity.class,Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                DismissConnectDialog();
+            } else {
+                ErrorLogOutput(TAG, "HttpApi.inviteGuestForMaster(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                showNotiMsg(InviteMemberInputActivity.this, "맴버 초대를 실패하였습니다.");
+                DismissConnectDialog();
+                try {
+                    JSONObject json_data = new JSONObject(response.body().string());
+                    ErrorLogOutput(TAG, "HttpApi.inviteGuestForMaster(onResponse) body : " + json_data.toString());
+                } catch (JSONException e) {
+                    ErrorLogOutput(TAG, "HttpApi.inviteGuestForMaster(onResponse) JSONException : " + e.toString());
+                }
+            }
+        }
+    };
+}

+ 84 - 0
app/src/main/java/kr/co/icontrols/iotplatform/IotPlatformApplication.java

@@ -0,0 +1,84 @@
+package kr.co.icontrols.iotplatform;
+
+import android.app.Application;
+import android.content.Context;
+
+import java.io.File;
+
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.Database.DatabaseHelper;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+public class IotPlatformApplication  extends Application {
+
+    public static final String TAG = IotPlatformApplication.class.getSimpleName();
+    public static Context mContext;
+    private static IotPlatformApplication instance;
+
+
+    public static IotPlatformApplication getInstance() {
+        return instance;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        instance = this;
+
+        if (mContext == null) {
+            mContext = getApplicationContext();
+
+            //앱 저장소 생성
+            SharedPrefUtil.init(mContext);
+
+            //DB 생성
+            DatabaseHelper.init(mContext);
+            DBUtil.init(mContext);
+
+            //Cognito 초기화
+            AppHelper.init(mContext);
+
+            //Mqtt 초기화
+            MqttController.init(mContext);
+        }
+    }
+
+    /**
+     * 앱 캐시 삭제
+     */
+    public void clearApplicationData() {
+        File cacheDirectory = getCacheDir();
+        File applicationDirectory = new File(cacheDirectory.getParent());
+        if (applicationDirectory.exists()) {
+            String[] fileNames = applicationDirectory.list();
+            for (String fileName : fileNames) {
+                if (!fileName.equals("lib")) {
+                    deleteFile(new File(applicationDirectory, fileName));
+                }
+            }
+        }
+    }
+
+    /**
+     * 파일 삭제 루틴.
+     * @param file
+     * @return
+     */
+    public static boolean deleteFile(File file) {
+        boolean deletedAll = true;
+        if (file != null) {
+            if (file.isDirectory()) {
+                String[] children = file.list();
+                for (int i = 0; i < children.length; i++) {
+                    deletedAll = deleteFile(new File(file, children[i])) && deletedAll;
+                }
+            } else {
+                deletedAll = file.delete();
+            }
+        }
+        return deletedAll;
+    }
+}

+ 292 - 0
app/src/main/java/kr/co/icontrols/iotplatform/JaesilModeActivity.java

@@ -0,0 +1,292 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttNewMessageCallback;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_TIME_INDEX;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_INFO;
+import static kr.co.icontrols.iotplatform.utils.Constants.MQTT_PUBLISH_DELAY_TIME;
+import static kr.co.icontrols.iotplatform.utils.Constants.NOTING;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_OFF;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_ON;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHANGE_JAESIL_TIME;
+
+public class JaesilModeActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private String TAG = "JaesilModeActivity";
+//    private DeviceInfo deviceInfo;
+//    private boolean isChangeData;
+//    private Switch switchJaesil;
+//    private int power = NOTING;
+//    private int mSelectTime;  //0:2시간, 1:1시간30분, 2:1시간, 3:30분, 4:10분
+//    private String topicGetP,topicGetS1,topicSetP,topicSetS1;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_jaesil_mode);
+
+//        isChangeData = false;
+//        mSelectTime = 60;
+//        deviceInfo = getIntent().getParcelableExtra(EXTRA_DEVICE_INFO);
+//
+//        topicGetP = String.format(getString(R.string.topic_get_jaesil_data),deviceInfo.deviceId);
+//        topicGetS1 = String.format(getString(R.string.topic_response_get_jaesil_data),deviceInfo.deviceId);
+//        topicSetP = String.format(getString(R.string.topic_set_jaesil_data),deviceInfo.deviceId);
+//        topicSetS1 = String.format(getString(R.string.topic_response_set_jaesil_data),deviceInfo.deviceId);
+
+        //getJaesilData();
+    }
+
+    @Override
+    protected void onPause() {
+        //MqttController.getInstance().allUnsubscribeToTopics();
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        //getJaesilData();
+    }
+
+    @Override
+    public void onBackPressed() {
+        super.onBackPressed();
+        /*
+        if(isChangeData) {
+            setResult(RESULT_OK);
+            finish();
+        } else {
+            super.onBackPressed();
+        }
+         */
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            /*
+            case R.id.btnOnOff:
+                if(power == NOTING) {
+                    showNotiMsg(JaesilModeActivity.this,"재실센서가 동작되지 않습니다.");
+                } else if(power == POWER_OFF) {
+                    changeSetTime(true, POWER_ON, mSelectTime, 0);
+                } else if(power == POWER_ON){
+                    changeSetTime(true, POWER_OFF, mSelectTime, 0);
+                }
+                break;
+            case R.id.layoutSetTime:
+                {
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_SET_JAESIL_TIME);
+                intent.putExtra(POPUP_TIME_INDEX, 60);
+                startActivityForResult(intent, RESULT_CHANGE_JAESIL_TIME);
+                }
+                break;
+             */
+            case R.id.btnRight:
+                break;
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+        }
+    }
+    /*
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_CHANGE_JAESIL_TIME:
+                if (resultCode == RESULT_OK) {
+                    final int selectedTime = data.getIntExtra(POPUP_TIME_INDEX, 60);
+                    new Handler().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            changeSetTime(true, power, selectedTime, 0);
+                        }
+                    },MQTT_PUBLISH_DELAY_TIME+20);
+
+                }
+                break;
+        }
+    }
+
+    private void getJaesilData() {
+
+        findViewById(R.id.btnOnOff).setEnabled(false);
+        findViewById(R.id.layoutSetTime).setEnabled(false);
+
+        MqttController.getInstance().subscribeToTopic(topicGetS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                parsingData(new String(data));
+                MqttController.getInstance().unsubscribeToTopic(topicGetS1);
+            }
+        });
+
+        MqttController.getInstance().subscribeToTopic(topicSetS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                parsingData(new String(data));
+            }
+        });
+
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                MqttController.getInstance().publish(topicGetP,"{}");
+            }
+        },MQTT_PUBLISH_DELAY_TIME);
+    }
+
+    private void parsingData(final String data) {
+        int temp_power = NOTING;
+        int temp_time = NOTING;
+        int temp_step = NOTING;
+
+        try {
+            JSONObject jsonObject = new JSONObject(data);
+
+            if(!jsonObject.isNull("state")) {  //Update topic
+                JSONObject jsonState = jsonObject.getJSONObject("state");
+                if (!jsonState.isNull("reported")) {
+                    JSONObject jsonReported = jsonState.getJSONObject("reported");
+                    //Power
+                    if (!jsonReported.isNull("sse")) {
+                        //power off
+                        temp_power = jsonReported.getInt("sse");
+
+                    }
+                    //Time
+                    if (!jsonReported.isNull("sot")) {
+                        temp_time = jsonReported.getInt("sot");
+                    }
+
+                    //Jaesil Statu
+                    if (!jsonReported.isNull("sn")) {
+                        temp_step = jsonReported.getInt("sn");
+                    }
+                }
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        final int powerFix = temp_power;
+        final int timeFix = temp_time;
+        final int stepFix = temp_step;
+
+        if(powerFix != NOTING && timeFix != NOTING && stepFix != NOTING) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    findViewById(R.id.btnOnOff).setEnabled(true);
+                    changeSetTime(false, powerFix, timeFix, stepFix);
+                }
+            });
+        }
+    }
+
+    private void changeSetTime(boolean send, int onOff, int time, int step) {
+        mSelectTime = time;
+        power = onOff;
+        findViewById(R.id.btnOnOff).setSelected(onOff==POWER_ON?true:false);
+        ((Button)findViewById(R.id.btnOnOff)).setText(onOff==POWER_ON?getString(R.string.turn_off_jaesil):getString(R.string.turn_on_jaesil));
+
+        findViewById(R.id.layoutSetTime).setEnabled(onOff==POWER_ON?true:false);
+        switch (mSelectTime) {
+            case 120:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("2시간");
+                break;
+            case 90:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("1시간 30분");
+                break;
+            case 60:
+            default:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("1시간");
+                break;
+            case 50:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("50분");
+                break;
+            case 40:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("40분");
+                break;
+            case 30:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("30분");
+                break;
+            case 20:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("20분");
+                break;
+            case 10:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("10분");
+                break;
+            case 5:
+                ((TextView)findViewById(R.id.txtSetTime)).setText("5분");
+                break;
+        }
+
+        if(send) {
+            isChangeData = true;
+            setJaesil(onOff, time, step);
+        }
+    }
+
+    private void setJaesil(int onOff, int time, int step) {
+        String payload = "{\n" +
+                "\"state\": {\n" +
+                "\"reported\" : {\n" +
+                "\"sse\" : " + onOff + ",\n" +
+                "\"sot\" : " + time + ",\n" +
+                "\"sn\" : " + step + "\n" +
+                "}\n" +
+                "}\n" +
+                "}";
+        MqttController.getInstance().publish(topicSetP,payload);
+    }
+     */
+}

+ 825 - 0
app/src/main/java/kr/co/icontrols/iotplatform/LightDetailActivity.java

@@ -0,0 +1,825 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttNewMessageCallback;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.UUID;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.adapter.MyGroupTabPagerAdapter;
+import kr.co.icontrols.iotplatform.utils.CommonUtils;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_HOME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_TIME_INDEX;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.IsRunningProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isRestartAppForBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.resetBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.restartApp;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_INFO;
+import static kr.co.icontrols.iotplatform.utils.Constants.MQTT_PUBLISH_DELAY_TIME;
+import static kr.co.icontrols.iotplatform.utils.Constants.NOTING;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_OFF;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_ON;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHANGE_JAESIL_TIME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_DEVICE_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_HP;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_UPDATE_DATA;
+
+public class LightDetailActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private String TAG = "LightDetailActivity";
+
+    private boolean isConnectedDevice;
+    private boolean isChangeData;
+
+    private DeviceInfo deviceInfo;
+    private TextView txtLightName,txtGroupName;
+    private Button btnOnOff;
+    private ImageButton btnEdit;
+    private SeekBar seekBarDimLevel, seekBarColorLevel;
+    private LinearLayout layoutModeLife, layoutModeStudy, layoutModeRead;
+
+    private int power = NOTING;
+    private int dimming = NOTING;
+    private int colorTemp = NOTING;
+
+    private String topicGetP, topicGetS1, topicUpdateP, topicUpdateS1, topicUpdateS2;
+
+    private int powerJaesil = NOTING;
+    private int mSelectJaesilTime;  //Minutes
+    private String topicJaesilGetP,topicJaesilGetS1,topicJaesilSetP,topicJaesilSetS1;
+
+    private Handler mConnectedHandler;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_light_detail);
+
+        isConnectedDevice = false;
+        isChangeData = false;
+
+        deviceInfo = getIntent().getParcelableExtra(EXTRA_DEVICE_INFO);
+
+        topicGetP = String.format(getString(R.string.topic_get_status),deviceInfo.deviceType,deviceInfo.deviceId);
+        topicGetS1 = String.format(getString(R.string.topic_response_status),deviceInfo.deviceType,deviceInfo.deviceId);
+        topicUpdateP = String.format(getString(R.string.topic_update),deviceInfo.deviceId);
+        topicUpdateS1 = String.format(getString(R.string.topic_update_accepted),deviceInfo.deviceId);
+        topicUpdateS2 = String.format(getString(R.string.topic_update_delta),deviceInfo.deviceId);
+
+        mSelectJaesilTime = 60;
+        topicJaesilGetP = String.format(getString(R.string.topic_get_jaesil_data),deviceInfo.deviceId);
+        topicJaesilGetS1 = String.format(getString(R.string.topic_response_get_jaesil_data),deviceInfo.deviceId);
+        topicJaesilSetP = String.format(getString(R.string.topic_set_jaesil_data),deviceInfo.deviceId);
+        topicJaesilSetS1 = String.format(getString(R.string.topic_response_set_jaesil_data),deviceInfo.deviceId);
+
+        initUI();
+
+        mConnectedHandler = new Handler() {
+            @Override
+            public void handleMessage(@NonNull Message msg) {
+                super.handleMessage(msg);
+                WarnLogOutput(TAG, "mConnectedHandler : is mqtt connect ? " + MqttController.getInstance().isConneted());
+                if(MqttController.getInstance().isConneted()) {
+                    setControllMqtt();
+                    getJaesilData();
+                } else {
+                    mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if(isRestartAppForBackgroundWaitTime()) {
+            restartApp(this);
+            return;
+        }
+
+        if(!MqttController.getInstance().isConneted()) {
+            mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+        } else {
+            setControllMqtt();
+            getJaesilData();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        if(IsRunningProgress()) {
+            ForceDismissConnectDialog();
+        }
+        MqttController.getInstance().allUnsubscribeToTopics();
+        setBackgroundWaitTime();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        resetBackgroundWaitTime();
+        super.onDestroy();
+    }
+
+    @Override
+    public void onBackPressed() {
+        if(isChangeData) {
+            setResult(RESULT_OK);
+            finish();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_INPUT_DEVICE_NAME:
+                if (resultCode == RESULT_OK) {
+                    Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                    if(b != null) {
+                        try {
+                            String newValue = b.getString(PopupActivity.POPUP_INPUT);
+                            if(!newValue.equals(txtLightName.getText().toString())) {
+                                changeDeviceName(newValue);
+                            }
+                        } catch (Exception e) {
+                            // Log failure
+                            showNotiMsg(LightDetailActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                        }
+                    } else {
+                        showNotiMsg(LightDetailActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                    }
+                }
+                break;
+            case RESULT_UPDATE_DATA:
+                if(resultCode == RESULT_OK) {
+                    for(int i = 0 ; i < UserInfo.devices.size() ; i++) {
+                        if(UserInfo.devices.get(i).deviceId.equals(deviceInfo.deviceId)) {
+                            if(!UserInfo.devices.get(i).dgroupId.equals(deviceInfo.dgroupId)) {
+                                deviceInfo.dgroupName = UserInfo.devices.get(i).dgroupName;
+                                deviceInfo.dgroupId = UserInfo.devices.get(i).dgroupId;
+                                isChangeData = true;
+                            }
+                        }
+                    }
+                    txtGroupName.setText(deviceInfo.dgroupName);
+                }
+                break;
+            case RESULT_CHANGE_JAESIL_TIME:
+                if (resultCode == RESULT_OK) {
+                    final int selectedTime = data.getIntExtra(POPUP_TIME_INDEX, 60);
+                    new Handler().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            displayProgress(LightDetailActivity.this, "","",2000);
+                            changeSetTime(true, power, selectedTime, 0);
+                        }
+                    },MQTT_PUBLISH_DELAY_TIME+20);
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnOnOff:
+                if(!isConnectedDevice) {
+                    showNotiMsg(LightDetailActivity.this,"스마트 조명이 연결되지 않았습니다.");
+                } else if(power == POWER_ON) { //켜진상태
+                    displayProgress(this, "","",2000);
+                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"power\" : 0}}}");
+
+                    btnOnOff.setText(getString(R.string.turn_off_light));
+                    btnOnOff.setEnabled(true);
+                    btnOnOff.setSelected(true);
+                    seekBarDimLevel.setEnabled(true);
+                    seekBarColorLevel.setEnabled(true);
+                } else /*if(power == POWER_OFF)*/ { //꺼진상태
+                    displayProgress(this, "","",2000);
+                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"power\" : 1}}}");
+
+                    btnOnOff.setText(getString(R.string.turn_on_light));
+                    btnOnOff.setEnabled(true);
+                    btnOnOff.setSelected(false);
+                    seekBarDimLevel.setEnabled(false);
+                    seekBarColorLevel.setEnabled(false);
+                }
+                break;
+            case R.id.btnSetting:{
+                Intent intent = new Intent(this, DeviceSettingActivity.class);
+                intent.putExtra(EXTRA_DEVICE_INFO, (Parcelable) deviceInfo);
+                startActivityForResult(intent, RESULT_UPDATE_DATA);
+                }
+                break;
+            case R.id.btnEdit:{
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+                Bundle b = new Bundle();
+                if (txtLightName.getText().toString().isEmpty()) {
+                    b.putString(PopupActivity.POPUP_INPUT, "");
+                } else {
+                    b.putString(PopupActivity.POPUP_INPUT, txtLightName.getText().toString());
+                }
+                intent.putExtra(POPUP_BUNDLE, b);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.change_device_name));
+                startActivityForResult(intent, RESULT_INPUT_DEVICE_NAME);
+                }
+                break;
+
+            case R.id.btnJaesilOnOff:
+                if(powerJaesil == NOTING) {
+                    showNotiMsg(LightDetailActivity.this,"재실센서가 동작되지 않습니다.");
+                } else if(powerJaesil == POWER_OFF) {
+                    displayProgress(LightDetailActivity.this, "","",2000);
+                    changeSetTime(true, POWER_ON, mSelectJaesilTime, 0);
+                } else if(powerJaesil == POWER_ON){
+                    displayProgress(LightDetailActivity.this, "","",2000);
+                    changeSetTime(true, POWER_OFF, mSelectJaesilTime, 0);
+                }
+                break;
+            case R.id.btnJaesilTime: {
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_SET_JAESIL_TIME);
+                intent.putExtra(POPUP_TIME_INDEX, 60);
+                startActivityForResult(intent, RESULT_CHANGE_JAESIL_TIME);
+                }
+                break;
+            case R.id.layoutModeLife:
+                displayProgress(this, "","",2000);
+                MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"power\" : 1, \"dimm\" : 10, \"temp\" : 8}}}");
+                break;
+            case R.id.layoutModeRead:
+                displayProgress(this, "","",2000);
+                MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"power\" : 1, \"dimm\" : 20, \"temp\" : 14}}}");
+                break;
+            case R.id.layoutModeStudy:
+                displayProgress(this, "","",2000);
+                MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"power\" : 1, \"dimm\" : 20, \"temp\" : 20}}}");
+                break;
+            case R.id.btnJaesilInfo:
+                startActivity(new Intent(this, JaesilModeActivity.class));
+                break;
+        }
+    }
+
+    private void initUI() {
+        txtLightName = findViewById(R.id.txtLightName);
+        txtGroupName = findViewById(R.id.txtGroupName);
+        btnOnOff = findViewById(R.id.btnOnOff);
+        btnEdit = findViewById(R.id.btnEdit);
+        seekBarDimLevel = findViewById(R.id.seekBarDimLevel);
+        seekBarColorLevel = findViewById(R.id.seekBarColorLevel);
+        layoutModeLife = findViewById(R.id.layoutModeLife);
+        layoutModeRead = findViewById(R.id.layoutModeRead);
+        layoutModeStudy = findViewById(R.id.layoutModeStudy);
+
+        txtLightName.setText(deviceInfo.friendlyName.isEmpty()?deviceInfo.sharedFriendlyName.isEmpty()?getString(R.string.no_name):deviceInfo.sharedFriendlyName:deviceInfo.friendlyName);
+        txtGroupName.setText(deviceInfo.dgroupName);
+        if(isConnectedDevice) {
+            btnOnOff.setText(getString(R.string.turn_on_light));
+            seekBarDimLevel.setProgress(0);
+            seekBarColorLevel.setProgress(0);
+            seekBarDimLevel.setEnabled(false);
+            seekBarColorLevel.setEnabled(false);
+            layoutModeLife.setEnabled(true);
+            layoutModeRead.setEnabled(true);
+            layoutModeStudy.setEnabled(true);
+            findViewById(R.id.btnJaesilOnOff).setEnabled(true);
+            findViewById(R.id.btnJaesilTime).setEnabled(true);
+        } else {
+            btnOnOff.setText(getString(R.string.not_connected));
+            btnOnOff.setEnabled(false);
+            seekBarDimLevel.setProgress(0);
+            seekBarColorLevel.setProgress(0);
+            seekBarDimLevel.setEnabled(false);
+            seekBarColorLevel.setEnabled(false);
+            layoutModeLife.setEnabled(false);
+            layoutModeRead.setEnabled(false);
+            layoutModeStudy.setEnabled(false);
+            findViewById(R.id.btnJaesilOnOff).setEnabled(false);
+            findViewById(R.id.btnJaesilTime).setEnabled(false);
+        }
+
+        //마스터 조명의 경우 friendlyName이 없음, 없는 경우 조명 이름 수정 불가능하게 수정.
+        if(deviceInfo.friendlyName.isEmpty()) {
+            btnEdit.setVisibility(View.INVISIBLE);
+        } else {
+            btnEdit.setVisibility(View.VISIBLE);
+        }
+
+
+        seekBarDimLevel.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                /*
+                if(progress != dimming) {
+                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"dimm\" : "+(progress+1)+"}}}");
+                }
+                 */
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                if(seekBar.getProgress() != dimming) {
+                    displayProgress(LightDetailActivity.this, "","",2000);
+                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"dimm\" : "+(seekBar.getProgress()+1)+"}}}");
+                }
+            }
+        });
+
+        seekBarColorLevel.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                /*
+                if(progress != colorTemp) {
+                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"temp\" : "+(progress+1)+"}}}");
+                }
+                 */
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+                if(seekBar.getProgress() != colorTemp) {
+                    displayProgress(LightDetailActivity.this, "","",2000);
+                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"temp\" : "+(seekBar.getProgress()+1)+"}}}");
+                }
+            }
+        });
+    }
+
+    private void setControllMqtt() {
+        MqttController.getInstance().subscribeToTopic(topicGetS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                changeUI(new String(data));
+                MqttController.getInstance().unsubscribeToTopic(topicGetS1);
+            }
+        });
+
+        MqttController.getInstance().subscribeToTopic(topicUpdateS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                changeUI(new String(data));
+                if(IsRunningProgress()){
+                    DismissConnectDialog();
+                }
+            }
+        });
+
+        MqttController.getInstance().subscribeToTopic(topicUpdateS2, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                //TODO 오류 발생 로직 구현.
+            }
+        });
+
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                MqttController.getInstance().publish(topicGetP,"{\"msgId\" : \""+UUID.randomUUID().toString()+"\"}");
+                //MqttController.getInstance().publish(topicGetP,"{\"msgId\" : \""+deviceInfo.deviceId+"\"}");
+            }
+        },MQTT_PUBLISH_DELAY_TIME);
+    }
+
+    private void changeUI(final String data) {
+        int temp_power = NOTING;
+        int temp_dimming = NOTING;
+        int temp_colorTemp = NOTING;
+
+        try {
+            JSONObject jsonObject = new JSONObject(data);
+
+            if(!jsonObject.isNull("data")) {//Get status topic
+                isConnectedDevice = true;
+                JSONObject jsonState = jsonObject.getJSONObject("data");
+                if (!jsonState.isNull("state")) {
+                    JSONObject jsonReported = jsonState.getJSONObject("state");
+                    //Power
+                    if (!jsonReported.isNull("power")) {
+                        //power off
+                        temp_power = jsonReported.getInt("power");
+
+                    }
+                    //dimming
+                    if (!jsonReported.isNull("dimm")) {
+                        temp_dimming = jsonReported.getInt("dimm");
+                    }
+
+                    //colorTemp
+                    if (!jsonReported.isNull("temp")) {
+                        temp_colorTemp = jsonReported.getInt("temp");
+                    }
+                }
+            } else if(!jsonObject.isNull("state")) {  //Update topic
+                JSONObject jsonState = jsonObject.getJSONObject("state");
+                if (!jsonState.isNull("reported")) {
+                    JSONObject jsonReported = jsonState.getJSONObject("reported");
+                    //Power
+                    if (!jsonReported.isNull("power")) {
+                        //power off
+                        temp_power = jsonReported.getInt("power");
+
+                    }
+                    //dimming
+                    if (!jsonReported.isNull("dimm")) {
+                        temp_dimming = jsonReported.getInt("dimm");
+                    }
+
+                    //colorTemp
+                    if (!jsonReported.isNull("temp")) {
+                        temp_colorTemp = jsonReported.getInt("temp");
+                    }
+                }
+
+                if (!jsonState.isNull("desired")) {
+                    JSONObject jsonDesired = jsonState.getJSONObject("desired");
+                    //Power
+                    if (!jsonDesired.isNull("power")) {
+                        //power off
+                        temp_power = jsonDesired.getInt("power");
+
+                    }
+                    //dimming
+                    if (!jsonDesired.isNull("dimm")) {
+                        temp_dimming = jsonDesired.getInt("dimm");
+                    }
+
+                    //colorTemp
+                    if (!jsonDesired.isNull("temp")) {
+                        temp_colorTemp = jsonDesired.getInt("temp");
+                    }
+                }
+
+                if(!jsonState.isNull("power")) {
+                    temp_power = jsonState.getInt("power");
+                }
+
+                if(!jsonState.isNull("dimm")) {
+                    temp_dimming = jsonState.getInt("dimm");
+                }
+
+                if(!jsonState.isNull("temp")) {
+                    temp_colorTemp = jsonState.getInt("temp");
+                }
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        final int powerFix = temp_power;
+        final int dimmingFix = temp_dimming;
+        final int colorTempFix = temp_colorTemp;
+
+        if(temp_power != NOTING) {
+            power = temp_power;
+        }
+
+        if(temp_dimming != NOTING) {
+            dimming = temp_dimming;
+        }
+
+
+        if(temp_colorTemp == NOTING) {
+            colorTemp = temp_colorTemp;
+        }
+
+        DebugLogOutput(TAG, "LIGHT_STATUS - power : "+powerFix + " / dimm : " + dimmingFix + " / tempFix : " + colorTempFix);
+
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+
+                if(!isConnectedDevice) {
+                    btnOnOff.setText(getString(R.string.not_connected));
+                    btnOnOff.setEnabled(false);
+                    seekBarDimLevel.setEnabled(false);
+                    seekBarColorLevel.setEnabled(false);
+                    layoutModeLife.setEnabled(false);
+                    layoutModeRead.setEnabled(false);
+                    layoutModeStudy.setEnabled(false);
+                } else if(powerFix == POWER_OFF) {
+                    btnOnOff.setText(getString(R.string.turn_on_light));
+                    btnOnOff.setEnabled(true);
+                    btnOnOff.setSelected(false);
+                    seekBarDimLevel.setEnabled(false);
+                    seekBarColorLevel.setEnabled(false);
+                    layoutModeLife.setEnabled(true);
+                    layoutModeRead.setEnabled(true);
+                    layoutModeStudy.setEnabled(true);
+                } else if(powerFix == POWER_ON) {
+                    btnOnOff.setText(getString(R.string.turn_off_light));
+                    btnOnOff.setEnabled(true);
+                    btnOnOff.setSelected(true);
+                    seekBarDimLevel.setEnabled(true);
+                    seekBarColorLevel.setEnabled(true);
+                    layoutModeLife.setEnabled(true);
+                    layoutModeRead.setEnabled(true);
+                    layoutModeStudy.setEnabled(true);
+                }
+
+                if(dimmingFix != NOTING) {
+                    seekBarDimLevel.setProgress(dimmingFix-1);
+                }
+
+                if(colorTempFix != NOTING) {
+                    seekBarColorLevel.setProgress(colorTempFix-1);
+                }
+            }
+        });
+    }
+
+    private void getJaesilData() {
+        MqttController.getInstance().subscribeToTopic(topicJaesilGetS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                parsingData(new String(data));
+                MqttController.getInstance().unsubscribeToTopic(topicJaesilGetS1);
+            }
+        });
+
+        MqttController.getInstance().subscribeToTopic(topicJaesilSetS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                parsingData(new String(data));
+                if(IsRunningProgress()){
+                    DismissConnectDialog();
+                }
+            }
+        });
+
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                MqttController.getInstance().publish(topicJaesilGetP,"{}");
+            }
+        },MQTT_PUBLISH_DELAY_TIME);
+    }
+
+    private void parsingData(final String data) {
+        int temp_power = NOTING;
+        int temp_time = NOTING;
+        int temp_step = NOTING;
+
+        try {
+            JSONObject jsonObject = new JSONObject(data);
+
+            if(!jsonObject.isNull("state")) {  //Update topic
+                JSONObject jsonState = jsonObject.getJSONObject("state");
+                if (!jsonState.isNull("reported")) {
+                    JSONObject jsonReported = jsonState.getJSONObject("reported");
+                    //Power
+                    if (!jsonReported.isNull("sse")) {
+                        //power off
+                        temp_power = jsonReported.getInt("sse");
+
+                    }
+                    //Time
+                    if (!jsonReported.isNull("sot")) {
+                        temp_time = jsonReported.getInt("sot");
+                    }
+
+                    //Jaesil Statu
+                    if (!jsonReported.isNull("sn")) {
+                        temp_step = jsonReported.getInt("sn");
+                    }
+                }
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        final int powerFix = temp_power;
+        final int timeFix = temp_time;
+        final int stepFix = temp_step;
+
+        if(powerFix != NOTING && timeFix != NOTING && stepFix != NOTING) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    findViewById(R.id.btnJaesilOnOff).setEnabled(true);
+                    changeSetTime(false, powerFix, timeFix, stepFix);
+                }
+            });
+        }
+    }
+
+    private void changeSetTime(boolean send, int onOff, int time, int step) {
+        mSelectJaesilTime = time;
+        powerJaesil = onOff;
+        findViewById(R.id.btnJaesilOnOff).setSelected(onOff==POWER_ON?true:false);
+        ((TextView)findViewById(R.id.txtJaesilOnOff)).setText(onOff==POWER_ON?getString(R.string.on2):getString(R.string.off2));
+        ((TextView)findViewById(R.id.txtStatusJaesil)).setText(onOff==POWER_ON?getText(R.string.jaesil_on):getText(R.string.jaesil_off));
+
+        findViewById(R.id.btnJaesilTime).setEnabled(onOff==POWER_ON?true:false);
+        switch (mSelectJaesilTime) {
+            case 120:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("2시간");
+                break;
+            case 90:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("1시간 30분");
+                break;
+            case 60:
+            default:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("1시간");
+                break;
+            case 50:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("50분");
+                break;
+            case 40:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("40분");
+                break;
+            case 30:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("30분");
+                break;
+            case 20:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("20분");
+                break;
+            case 10:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("10분");
+                break;
+            case 5:
+                ((TextView)findViewById(R.id.txtJaesilTime)).setText("5분");
+                break;
+        }
+
+        if(send) {
+            isChangeData = true;
+            setJaesil(onOff, time, step);
+        }
+    }
+
+    private void setJaesil(int onOff, int time, int step) {
+        String payload = "{\n" +
+                "\"state\": {\n" +
+                "\"reported\" : {\n" +
+                "\"sse\" : " + onOff + ",\n" +
+                "\"sot\" : " + time + ",\n" +
+                "\"sn\" : " + step + "\n" +
+                "}\n" +
+                "}\n" +
+                "}";
+        MqttController.getInstance().publish(topicJaesilSetP,payload);
+    }
+
+    private void changeDeviceName(final String name) {
+        displayProgress(this,"","");
+        try {
+            HttpApi.modifyDeviceInfo(deviceInfo.deviceId,
+                    name,
+                    new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onFailure)\n" + e.toString());
+                            showNotiMsg(LightDetailActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                            DismissConnectDialog();
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                WarnLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) HTTP_NO_CONTENT");
+
+                                final DeviceInfo itemS = DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,deviceInfo.deviceId);
+                                if(itemS != null) {
+                                    itemS.friendlyName = name;
+                                    DBUtil.updateDeviceItem(TABLE_SMART_LIGHT,itemS);
+                                }
+
+                                final DeviceInfo itemH = DBUtil.getDeviceInfoForId(TABLE_DEVICE_HOME,deviceInfo.deviceId);
+                                if(itemH != null) {
+                                    itemH.friendlyName = name;
+                                    DBUtil.updateDeviceItem(TABLE_DEVICE_HOME,itemH);
+                                }
+
+                                final DeviceInfo itemR = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK,deviceInfo.deviceId);
+                                if(itemR != null) {
+                                    itemR.friendlyName = name;
+                                    DBUtil.updateDeviceItem(TABLE_DEVICE_RANK,itemR);
+                                }
+
+                                isChangeData = true;
+                                deviceInfo.friendlyName = name;
+                                for(int i = 0 ; i < UserInfo.devices.size() ; i++) {
+                                    if(UserInfo.devices.get(i).deviceId.equals(deviceInfo.deviceId)) {
+                                        if(!UserInfo.devices.get(i).friendlyName.equals(deviceInfo.friendlyName)) {
+                                            UserInfo.devices.get(i).friendlyName = deviceInfo.friendlyName;
+                                        }
+                                    }
+                                }
+
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        showNotiMsg(LightDetailActivity.this,getString(R.string.snackbar_successed_change_device_name));
+                                        txtLightName.setText(deviceInfo.friendlyName);
+                                        DismissConnectDialog();
+                                    }
+                                });
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                showNotiMsg(LightDetailActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                                DismissConnectDialog();
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) JSONException : " + e.toString());
+                                }
+                            }
+                        }
+                    });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(Exception)\n" + e.toString());
+            showNotiMsg(LightDetailActivity.this,getString(R.string.snackbar_fail_change_device_name));
+            DismissConnectDialog();
+        }
+    }
+}

+ 1552 - 0
app/src/main/java/kr/co/icontrols/iotplatform/LightRegisterDeviceActivity.java

@@ -0,0 +1,1552 @@
+package kr.co.icontrols.iotplatform;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+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.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.amazonaws.mobile.client.AWSMobileClient;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import kr.co.icontrols.iotplatform.AWS.AmazonBLEinfo;
+import kr.co.icontrols.iotplatform.AWS.AmazonFreeRTOSAgent;
+import kr.co.icontrols.iotplatform.AWS.AmazonWifiInfo;
+import kr.co.icontrols.iotplatform.Protocol.bleProtocol;
+import kr.co.icontrols.iotplatform.Protocol.bleProtocolState;
+import kr.co.icontrols.iotplatform.utils.CommonUtils;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import kr.co.icontrols.iotplatform.utils.hdcUUID;
+import okhttp3.Call;
+import okhttp3.Response;
+import software.amazon.freertos.amazonfreertossdk.AmazonFreeRTOSConstants;
+import software.amazon.freertos.amazonfreertossdk.AmazonFreeRTOSDevice;
+import software.amazon.freertos.amazonfreertossdk.AmazonFreeRTOSManager;
+import software.amazon.freertos.amazonfreertossdk.BleConnectionStatusCallback;
+import software.amazon.freertos.amazonfreertossdk.NetworkConfigCallback;
+import software.amazon.freertos.amazonfreertossdk.networkconfig.ListNetworkReq;
+import software.amazon.freertos.amazonfreertossdk.networkconfig.ListNetworkResp;
+import software.amazon.freertos.amazonfreertossdk.networkconfig.SaveNetworkReq;
+import software.amazon.freertos.amazonfreertossdk.networkconfig.SaveNetworkResp;
+
+import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.IsRunningProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.bssidToString;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.byteArrayToHex;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getProgressDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isBlueToothEnable;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.restartApp;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_ENABLE_BT_MAIN;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_AP_PASS;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_LIGHT_BLINK_SUCCESS;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_BUT_NO_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_MAPPING_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_SCAN_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RSSI_LEVEL_1;
+import static kr.co.icontrols.iotplatform.utils.Constants.RSSI_LEVEL_2;
+import static kr.co.icontrols.iotplatform.utils.Constants.RSSI_LEVEL_3;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_UNAUTHORIZED;
+
+public class LightRegisterDeviceActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "LightRegisterDevice";
+
+    private final long USER_REGISTER_REQUEST_TIMEOUT_TIME = 60000;
+    private final long DEFAULT_WAIT_WIFI_STATUS_TIME = 20000;
+    private final long DEFAULT_REG_DEVICE_LOADING_TIME = 10000;
+
+    // device scan ui
+    private LinearLayout layoutBleList;
+    private TextView txtBleList;
+    private ListView bleListView;
+    private BleArrayAdapter bleArrayAdapter;
+    private List<AmazonBLEinfo> bleList = new ArrayList<>();
+
+    // wifi scan ui
+    private LinearLayout layoutWifiList, layoutListWifi;
+    private TextView txtWifiList,txtWifiDeviceName,txtCompletedDeviceName;
+    //private ListView wifiListView;
+    //private WifiArrayAdapter wifiArrayAdapter;
+    private List<AmazonWifiInfo> wifiInfoList = new ArrayList<AmazonWifiInfo>();
+
+    // completed ui
+    private LinearLayout layoutCompleted;
+    private TextView txtCompleted;
+
+    // FreeRTOS
+    private String wifipw;
+    private String wifibssid;
+    private AmazonFreeRTOSManager amazonFreeRTOSManager;
+    private AmazonFreeRTOSDevice amazonFreeRTOSDevice = null;
+    private HashMap<String, AmazonWifiInfo> mBssid2WifiInfoMap = new HashMap<>();
+
+    // Bluetooth
+    private static final int REQUEST_ENABLE_BT = 1;
+    private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
+
+    // ble
+    //private BluetoothGatt bluetoothGatt = null;
+    private BluetoothAdapter bluetoothAdapter = null;
+    private BluetoothGattCharacteristic bluetoothGattCharacteristic = null;
+    private Map<String, BluetoothGatt> connectedDeviceMap;
+
+    //status
+    private final int UI_STATUS_LOADING = 0;
+    private final int UI_STATUS_SHOW_BLE_LIST = 1;
+    private final int UI_STATUS_SHOW_WIFI_LIST = 3;
+    private final int UI_STATUS_SHOW_FAIL = 4;
+    private final int UI_STATUS_SHOW_COMPLETED = 5;
+    private final int UI_STATUS_SHOW_ETC = 6;
+
+    private final int PROCESS_STATUS_NOTHING = 0;
+    private final int PROCESS_STATUS_BLINK = 1;
+    private final int PROCESS_STATUS_USER_REGISTER = 2;
+    private final int PROCESS_STATUS_INIT_LIGHT = 3;
+    private final int PROCESS_STATUS_SEND_ACK_STATUS_SUCCESS = 4;
+    private final int PROCESS_STATUS_SEND_ACK_STATUS_FAIL = 5;
+
+    // flag
+    private int status_process = PROCESS_STATUS_NOTHING;
+
+    //retry to scan device
+    private boolean isRetry = false;
+    private boolean isRetryMapping = false;
+
+    //Wait userReg
+    private boolean isUserRegisteStatus = false;
+
+    // handler
+    private Handler handler;
+    private Handler mHandler;
+    private Handler requestHandler;
+    private int SCAN_PERIOD = 5000;
+    private int FIRST_SCAN_PERIOD = 500;
+
+    private int selectedLightPosition = -1;
+    private String selectedBleMac = null;
+    private String macNumber = "";
+    private String modelIdNumber = "";
+    private String deviceIdNumber = "";
+    private String userRegisterDetailState = "";
+    private String awsProvisionDetailState = "";
+    private String userMappingDetailState = "";
+
+    private AmazonWifiInfo amazonWifiInfo = null;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_light_register_device);
+
+        // init Activity
+        {
+            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+            requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_FINE_LOCATION);
+
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+            final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+            if (bluetoothManager != null) {
+                bluetoothAdapter = bluetoothManager.getAdapter();
+            }
+
+            setUi();
+        }
+
+
+        // Light Device Selected & blink led
+        bleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final AmazonBLEinfo item = (AmazonBLEinfo) parent.getItemAtPosition(position);
+                selectedBleMac = item.getMacAddr();
+                selectedLightPosition = position;
+                startBlinkLight();
+                //selectedLight(selectedLightPosition);
+            }
+        });
+
+        // send, selected wifi, smartphone -> esp32 light device
+        /*
+        wifiListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                //String tempStr = awsWifiArrayAdapter.getItem(position).toString();
+                //wifibssid = tempStr.substring(tempStr.length() - 17);
+                wifibssid = bssidToString(((AmazonWifiInfo)wifiArrayAdapter.getItem(position)).getBssid());
+                amazonWifiInfo = (AmazonWifiInfo)wifiArrayAdapter.getItem(position);
+                inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
+            }
+        });
+        */
+
+        requestHandler = new Handler(Looper.myLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+
+                switch (msg.what) {
+
+                    // aws ble connect
+                    case bleProtocolState.PROVISIONING_BLE_CONNECTED:
+                        ListNetwork();
+                        changeUI(UI_STATUS_LOADING,"WIFI를 검색중입니다.");
+                        DebugLogOutput(TAG, "AmazonFreeRTOS BLE connection success -> request wifi list -> execute ListNetWork() Method & Progressbar Start");
+                        break;
+
+                    case bleProtocolState.PROVISIONING_BLE_DISCONNECTED:
+                        changeUI(UI_STATUS_SHOW_FAIL,"Bluetooth 연결이 해제되었습니다.");
+                        DebugLogOutput(TAG, "AmazonFreeRTOS BLE Disconnect -> Call gatt.Disconnect Method or Ble Status Problem");
+                        break;
+
+                    // wi-fi list renew
+                    case bleProtocolState.PROVISIONING_LIST_NETWORK_SUCCESS:
+                        DebugLogOutput(TAG, "Start ListNetWork() Method");
+                        changeUI(UI_STATUS_SHOW_WIFI_LIST,"WIFI 목록 검색 완료\nWIFI를 선택해주세요.");
+
+
+                        //bottom sheet part
+                        final BottomSheetBehavior bottom_Sheet = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
+                        bottom_Sheet.setHideable(false);
+                        bottom_Sheet.setState(BottomSheetBehavior.STATE_EXPANDED);
+                        bottom_Sheet.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
+                            @Override
+                            public void onStateChanged(View bottomSheet, int newState) {
+                                if (newState == BottomSheetBehavior.STATE_EXPANDED) {
+
+                                }
+                                else if (newState == BottomSheetBehavior.STATE_HIDDEN) {
+                                    bottom_Sheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
+                                }
+                                else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
+
+                                }
+                            }
+
+                            @Override
+                            public void onSlide(View bottomSheet, float slideOffset) {
+                            }
+                        });
+
+
+                        break;
+
+                    // wi-fi list renew fail
+                    case bleProtocolState.PROVISIONING_LIST_NETWORK_FAIL:
+                        DebugLogOutput(TAG, "Fail ListNetWork() Method");
+                        changeUI(UI_STATUS_SHOW_FAIL,"WIFI 목록 검색 실패");
+                        bleAwsDisconnect();
+                        break;
+
+                    // wi-fi provisioning end
+                    case bleProtocolState.PROVISIONING_SAVE_NETWORK_SUCCESS:
+                        isUserRegisteStatus = true;
+                        DebugLogOutput(TAG, "Start SaveNetWork() Method");
+                        changeUI(UI_STATUS_LOADING,"Wi-Fi Provisioning을 저장 중입니다.",USER_REGISTER_REQUEST_TIMEOUT_TIME);
+                        RequestHandlerSendMessage(bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT);
+                        break;
+
+                    case bleProtocolState.PROVISIONING_SAVE_NETWORK_FAIL:
+                        DebugLogOutput(TAG, "Fail SaveNetWork() Method");
+                        changeUI(UI_STATUS_SHOW_FAIL,"조명 모듈에게 Wi-Fi 정보 저정에 대한 응답을 받지 못하였습니다.");
+                        break;
+
+                    case bleProtocolState.BLUETOOTH_NOT_CONNECTED:
+                        DebugLogOutput(TAG, "BLUETOOTH_NOT_CONNECTED");
+                        //changeUI(UI_STATUS_SHOW_FAIL,"조명모듈과 Bluetooth 연결에 실패하였습니다.");
+                        bleGattDisconnect();
+                        retryScanDevice();
+                        break;
+
+                    case bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT:
+                        isUserRegisteStatus = false;
+                        DebugLogOutput(TAG, "User Register Timeout");
+                        changeUI(UI_STATUS_SHOW_FAIL,"User Register 요청 Timeout "+(int)(USER_REGISTER_REQUEST_TIMEOUT_TIME/1000)+"'sec");
+                        break;
+
+                    case bleProtocolState.USER_REGISTER_COMPLETE_SUCCESS:
+                        isUserRegisteStatus = false;
+                        requestHandler.removeMessages(bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT);
+                        bleGattWriteRequestCommand(bleProtocol.userRegisterCompleteSuccessProtocol);
+                        DebugLogOutput(TAG, "User Register Success : " + byteArrayToHex(bleProtocol.userRegisterCompleteSuccessProtocol));
+                        break;
+
+                    case bleProtocolState.USER_REGISTER_COMPLETE_FAIL:
+                        isUserRegisteStatus = false;
+                        requestHandler.removeMessages(bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT);
+                        bleGattWriteRequestCommand(bleProtocol.userRegisterCompleteFailProtocol);
+                        break;
+
+                    case bleProtocolState.USER_REGISTER_END:
+                        changeUI(UI_STATUS_LOADING,"User Register 확인 응답 성공, AWS IOT 매핑을 시도합니다.");
+                        RequestHandlerSendMessage(bleProtocolState.REQ_REGISTER_DEVICE_ID);
+                        new Handler().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                bleAwsDisconnect();
+                                bleGattDisconnect();
+                            }
+                        }, 100);
+                        break;
+
+                    case bleProtocolState.USER_REGISTER_FAIL:
+                        changeUI(UI_STATUS_SHOW_FAIL,"사용자 등록을 하지 못하였습니다.");
+                        new Handler().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+
+                                bleAwsDisconnect();
+                                bleGattDisconnect();
+                            }
+                        }, 100);
+                        break;
+
+                    case bleProtocolState.HDC_ICON_LIGHT_REGISTER_SUCCESS:
+                        bleAwsDisconnect();
+                        bleGattDisconnect();
+                        break;
+
+                    case bleProtocolState.RSP_BLINK_PROTOCOL_SUCCESS: {
+                        requestHandler.removeMessages(bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT);
+                        Intent intent = new Intent(LightRegisterDeviceActivity.this, PopupActivity.class);
+                        intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_confirm_register_light_title));
+                        intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_confirm_register_light_description));
+                        intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.yes));
+                        intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.re_select));
+                        startActivityForResult(intent, RESULT_LIGHT_BLINK_SUCCESS);
+                    }
+                    break;
+                    case bleProtocolState.RSP_BLINK_PROTOCOL_FAIL:
+                        requestHandler.removeMessages(bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT);
+                        //TODO 조명 깜빡임 실패시 로직 구현해야함.
+                        break;
+                    case bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT:
+                        //TODO 조명 깜빡임 Timeout 로직 구현해야함.
+                        break;
+                    case bleProtocolState.RSP_GET_WIFI_STATUS_SUCCESS:
+                        changeUI(UI_STATUS_LOADING,"사용자 등록 완료를 기다리는 중입니다.",USER_REGISTER_REQUEST_TIMEOUT_TIME);
+                        sendAckStatus(true);
+                        bleAwsDisconnect();
+                        requestHandler.removeMessages(bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT);
+                        RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT);
+                        break;
+                    case bleProtocolState.RSP_GET_WIFI_STATUS_FAIL:
+                        //TODO - 와이파이 AP 등록 실패시(오류)
+                        isUserRegisteStatus = false;
+                        sendAckStatus(false);
+                        requestHandler.removeMessages(bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT);
+                        inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
+                        changeUI(UI_STATUS_SHOW_FAIL,"AP 암호 등록중 오류가 발생하였습니다.");
+                        break;
+                    case bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT:
+                        //TODO - 와이파이 AP 등록 실패시(타임아웃)
+                        isUserRegisteStatus = false;
+                        //sendAckStatus(false);
+                        inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
+                        changeUI(UI_STATUS_SHOW_FAIL,"AP 암호가 일치하지 않습니다.");
+                        break;
+                    case bleProtocolState.REQ_REGISTER_DEVICE_ID:
+                        startMappingLightDevice();
+                        break;
+                    case bleProtocolState.BLUETOOTH_CONNECTED:
+                        //changeUI(UI_STATUS_LOADING,"스마트 조명과 블루투스를 연결하였습니다.");
+                        break;
+                    case bleProtocolState.ON_BOARDING_BLE_DISCONNECTED:
+                        //TODO - 연결 해제시 동작 구현해야함.
+                        break;
+                    default:
+                        throw new IllegalStateException("Unexpected value: " + msg.what);
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        checkEnableBluetooth();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        scanLeDevice(false);
+        CommonUtils.ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        if (amazonFreeRTOSDevice != null) {
+            DebugLogOutput(TAG, " onDestroy] disconnectFromDevice");
+            try {
+                amazonFreeRTOSManager.disconnectFromDevice(amazonFreeRTOSDevice);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+            amazonFreeRTOSManager = null;
+            amazonFreeRTOSDevice = null;
+        }
+
+        for(String key : connectedDeviceMap.keySet()){
+            DebugLogOutput(TAG, " onDestroy] connectedDeviceMap bluetoothGatt disconnect / MAC :"+key);
+            connectedDeviceMap.get(key).disconnect();
+            connectedDeviceMap.get(key).close();
+        }
+
+        if(bleList != null) {
+            bleList.clear();
+        }
+
+        if(wifiInfoList != null) {
+            wifiInfoList.clear();
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_ENABLE_BT_MAIN:
+                checkEnableBluetooth2();
+                break;
+            case RESULT_RETRY_SCAN_DEVICE:
+                if (resultCode == Activity.RESULT_OK) {
+                    isRetry= true;
+                    startScanningLightDevice();
+                } else {
+                    finish();
+                }
+                break;
+            case RESULT_RETRY_MAPPING_DEVICE:
+                if (resultCode == Activity.RESULT_OK) {
+                    isRetryMapping= true;
+                    startMappingLightDevice();
+                } else {
+                    finish();
+                }
+                break;
+            case RESULT_RETRY_BUT_NO_DEVICE:
+                finish();
+                break;
+            case RESULT_LIGHT_BLINK_SUCCESS:
+                if (resultCode == Activity.RESULT_OK) {
+                    //initLight(selectedLightPosition);
+                    selectedLight(selectedLightPosition);
+                } else {
+                    selectedLightPosition = -1;
+                    changeUI(UI_STATUS_SHOW_BLE_LIST);
+                }
+                break;
+            case RESULT_INPUT_AP_PASS:
+                if (resultCode == RESULT_OK) {
+                    Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                    if(b != null) {
+                        try {
+                            String wifipw = b.getString(PopupActivity.POPUP_INPUT);
+                            changeUI(UI_STATUS_LOADING,"Wi-Fi PassWord 저장 중입니다.",60000);
+                            saveNetwork(wifibssid, wifipw);
+                        } catch (Exception e) {
+                            // Log failure
+                            showNotiMsg(LightRegisterDeviceActivity.this,"암호 입력중 오류가 발생하였습니다.");
+                        }
+                    } else {
+                        showNotiMsg(LightRegisterDeviceActivity.this,"암호 입력중 오류가 발생하였습니다.");
+                    }
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        if(layoutCompleted.getVisibility()==View.VISIBLE) {
+            findViewById(R.id.btnCompleted).callOnClick();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnCompleted:
+                actvityExit();
+                break;
+            case R.id.replaceListViewWifi:
+                selectedLight(selectedLightPosition);
+                break;
+        }
+    }
+
+    private void actvityExit() {
+        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
+    private void checkEnableBluetooth() {
+        if(!isBlueToothEnable(this)){
+            Intent intent = new Intent(this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_ENABLE_BT);
+            startActivityForResult(intent, RESULT_ENABLE_BT_MAIN);
+        } else {
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    startScanningLightDevice();
+                }
+            }, FIRST_SCAN_PERIOD);
+        }
+    }
+
+    private void checkEnableBluetooth2() {
+        if(!isBlueToothEnable(this)){
+            showNotiMsg(this,"블루투스 활성화를 실패하였습니다.",true);
+        } else {
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    startScanningLightDevice();
+                }
+            }, FIRST_SCAN_PERIOD);
+        }
+    }
+
+    private void setUi() {
+
+        connectedDeviceMap = new HashMap<String, BluetoothGatt>();
+
+        // Scan Device
+        bleListView = findViewById(R.id.listViewBle);
+        bleArrayAdapter = new BleArrayAdapter(bleList);
+        bleListView.setAdapter(bleArrayAdapter);
+        bleListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+
+        // Scan Wifi
+        /*
+        wifiListView = findViewById(R.id.listViewWifi);
+        wifiArrayAdapter = new WifiArrayAdapter(wifiInfoList);
+        wifiListView.setAdapter(wifiArrayAdapter);
+        wifiListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+        */
+
+        // AWS FreeRTOS
+        amazonFreeRTOSManager = AmazonFreeRTOSAgent.getAmazonFreeRTOSManager(getApplicationContext());
+
+        // ui
+        layoutBleList = findViewById(R.id.layoutBleList);
+        layoutWifiList = findViewById(R.id.layoutWifiList);
+        layoutListWifi = findViewById(R.id.layoutListWifi);
+        layoutCompleted = findViewById(R.id.layoutCompleted);
+        txtBleList = findViewById(R.id.txtBleList);
+        txtWifiList = findViewById(R.id.txtWifiList);
+        txtWifiDeviceName = findViewById(R.id.txtWifiDeviceName);
+        txtCompleted = findViewById(R.id.txtCompleted);
+        txtCompletedDeviceName = findViewById(R.id.txtCompletedDeviceName);
+    }
+
+    private void changeUI(final int status) {
+        changeUI(status, null, DEFAULT_REG_DEVICE_LOADING_TIME);
+    }
+
+    private void changeUI(final int status,final String msg) {
+        changeUI(status, msg, DEFAULT_REG_DEVICE_LOADING_TIME);
+    }
+    private void changeUI(final int status,final String msg, final long hideTime) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                switch (status) {
+                    case UI_STATUS_LOADING:
+                        if(IsRunningProgress()){
+                            getProgressDialog().showTitle(msg);
+                        } else {
+                            if(hideTime > 0) {
+                                displayProgress(LightRegisterDeviceActivity.this, msg, null, hideTime);
+                            } else {
+                                displayProgress(LightRegisterDeviceActivity.this, msg, null);
+                            }
+                        }
+                        break;
+                    case UI_STATUS_SHOW_BLE_LIST:
+                        layoutBleList.setVisibility(View.VISIBLE);
+                        layoutWifiList.setVisibility(View.GONE);
+                        layoutCompleted.setVisibility(View.GONE);
+                        if(msg!=null) {
+                            txtBleList.setText(msg);
+                        }
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                    case UI_STATUS_SHOW_WIFI_LIST:
+                        layoutBleList.setVisibility(View.GONE);
+                        layoutWifiList.setVisibility(View.VISIBLE);
+                        layoutCompleted.setVisibility(View.GONE);
+                        if(msg!=null) {
+                            txtWifiList.setText(msg);
+                        }
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                    case UI_STATUS_SHOW_COMPLETED:
+                        findViewById(R.id.btnBack).setVisibility(View.INVISIBLE);
+                        layoutBleList.setVisibility(View.GONE);
+                        layoutWifiList.setVisibility(View.GONE);
+                        layoutCompleted.setVisibility(View.VISIBLE);
+                        if(msg!=null) {
+                            txtCompleted.setText(msg);
+                        }
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                    case UI_STATUS_SHOW_FAIL:
+                        showNotiMsg(LightRegisterDeviceActivity.this,msg);
+                        /*
+                        new Handler().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                actvityExit();
+                            }
+                        },3000);
+                         */
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                }
+            }
+        });
+    }
+
+    private void bleGattDisconnect() {
+        DebugLogOutput(TAG, " connectedDeviceMap bluetoothGatt disconnect / MAC :"+selectedBleMac);
+        if(connectedDeviceMap.containsKey(selectedBleMac)) {
+            if(connectedDeviceMap.get(selectedBleMac) != null) {
+                connectedDeviceMap.get(selectedBleMac).disconnect();
+                connectedDeviceMap.get(selectedBleMac).close();
+            }
+            connectedDeviceMap.remove(selectedBleMac);
+        }
+    }
+
+    private void bleGattDisconnect(String mac) {
+        DebugLogOutput(TAG, " connectedDeviceMap bluetoothGatt disconnect / MAC :"+mac);
+        if(connectedDeviceMap.containsKey(mac)) {
+            if(connectedDeviceMap.get(mac) != null) {
+                connectedDeviceMap.get(mac).disconnect();
+                connectedDeviceMap.get(mac).close();
+            }
+            connectedDeviceMap.remove(mac);
+        }
+    }
+
+    private void bleAwsDisconnect() {
+        if (amazonFreeRTOSDevice != null) {
+            DebugLogOutput(TAG, "AWS disconnectFromDevice");
+            try {
+                amazonFreeRTOSManager.disconnectFromDevice(amazonFreeRTOSDevice);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+            amazonFreeRTOSDevice = null;
+        }
+    }
+
+    /**
+     * Ble 검색
+     */
+    private void startScanningLightDevice() {
+        changeUI(UI_STATUS_LOADING,"등록할 스마트 조명을 찾고 있습니다.");
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+        scanLeDevice(true);
+    }
+
+    private void startBlinkLight() {
+        changeUI(UI_STATUS_LOADING,"스마트 조명과 블루투스를 연결하였습니다.");
+        status_process = PROCESS_STATUS_BLINK;
+        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
+            }
+
+        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
+            bleGattWriteRequestCommand(bleProtocol.blinkLightProtocol);
+            DebugLogOutput(TAG, "blinkLightProtocol : " + byteArrayToHex(bleProtocol.blinkLightProtocol));
+        }
+    }
+
+    private void startUserRegister() {
+        status_process = PROCESS_STATUS_USER_REGISTER;
+        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
+            bleGattWriteRequestCommand(bleProtocol.userRegisterRequestProtocol);
+            DebugLogOutput(TAG, "userRegisterRequestProtocol : " + byteArrayToHex(bleProtocol.userRegisterRequestProtocol));
+        }
+    }
+
+    private void initLight(final int pos) {
+        changeUI(UI_STATUS_LOADING,getString(R.string.activity_light_register_find_device_text_6));
+        status_process = PROCESS_STATUS_INIT_LIGHT;
+
+        String dname = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getName();
+        selectedBleMac = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getMacAddr();
+
+        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
+            bleGattWriteRequestCommand(bleProtocol.FactoryResetLightProtocol);
+            DebugLogOutput(TAG, "FactoryResetLightProtocol : " + byteArrayToHex(bleProtocol.FactoryResetLightProtocol));
+        }
+    }
+
+    private void sendAckStatus(boolean success) {
+        if(success) {
+            status_process = PROCESS_STATUS_SEND_ACK_STATUS_SUCCESS;
+        } else {
+            status_process = PROCESS_STATUS_SEND_ACK_STATUS_FAIL;
+        }
+        if (!connectedDeviceMap.containsKey(selectedBleMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedBleMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback,TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedBleMac, bluetoothDevice.connectGatt(LightRegisterDeviceActivity.this, false, gattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedBleMac)) {
+            if(success) {
+                bleGattWriteRequestCommand(bleProtocol.lightStatusSuccessResponseProtocol);
+                DebugLogOutput(TAG, "lightStatusSuccessResponseProtocol : " + byteArrayToHex(bleProtocol.lightStatusSuccessResponseProtocol));
+            } else {
+                bleGattWriteRequestCommand(bleProtocol.lightStatusFailResponseProtocol);
+                DebugLogOutput(TAG, "lightStatusFailResponseProtocol : " + byteArrayToHex(bleProtocol.lightStatusFailResponseProtocol));
+            }
+        }
+    }
+
+    private void selectedLight(final int pos) {
+        changeUI(UI_STATUS_LOADING,getString(R.string.activity_light_register_find_device_text_3));
+        layoutListWifi.removeAllViews();
+        String dname = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getName();
+        selectedBleMac = ((AmazonBLEinfo)bleArrayAdapter.getItem(pos)).getMacAddr();
+        txtWifiDeviceName.setText(dname);
+        txtCompletedDeviceName.setText(dname);
+
+        DebugLogOutput(TAG, "selectedLight: User Choice Device Mac, After using onBoarding : " + selectedBleMac);
+        DebugLogOutput(TAG, "selectedLight: Compare Device Scan List Data & user Choice Data, Because Ui index, data index difference");
+        int savePos = 0;
+        for (int i = 0; i < bleList.size(); i++) {
+            if (bleList.get(i).getName().equals(dname)) {
+                savePos = i;
+                DebugLogOutput(TAG, "onClick: find Same Deice");
+                DebugLogOutput(TAG, "selectedLight: List Device Name: " + bleList.get(i).getName());
+                DebugLogOutput(TAG, "selectedLight: Select Device Name: " + dname);
+            }
+        }
+
+        DebugLogOutput(TAG, "AWS BLE Disconnect");
+        bleAwsDisconnect();
+
+        AWSCredentialsProvider awsCredentialsProvider = AWSMobileClient.getInstance();
+        DebugLogOutput(TAG, "getInstance awsCredentialsProvider : " + awsCredentialsProvider);
+
+        try {
+            DebugLogOutput(TAG, "AWS BLE New Connect");
+            amazonFreeRTOSDevice = amazonFreeRTOSManager.connectToDevice(
+                    bleList.get(savePos).getBluetoothDevice(),
+                    connectionStatusCallback,
+                    awsCredentialsProvider,
+                    false);
+            DebugLogOutput(TAG, "selectedLight: amazonFreeRTOSDevice : " + amazonFreeRTOSDevice);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void ListNetwork() {
+        wifiInfoList.clear();
+        ListNetworkReq listNetworkReq = new ListNetworkReq();
+        listNetworkReq.maxNetworks = 20;
+        listNetworkReq.timeout = 5;
+        if (amazonFreeRTOSDevice != null) {
+            amazonFreeRTOSDevice.listNetworks(listNetworkReq, mNetworkConfigCallback);
+        } else {
+            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_LIST_NETWORK_FAIL);
+        }
+    }
+
+    private void saveNetwork(String bssid, String pw) {
+        DebugLogOutput(TAG, "Wi-Provisioning Info : BSSID : " + bssid + " / Password :" + pw);
+        SaveNetworkReq saveNetworkReq = new SaveNetworkReq();
+        AmazonWifiInfo wifiInfo = mBssid2WifiInfoMap.get(bssid);
+        saveNetworkReq.ssid = wifiInfo.getSsid();
+        saveNetworkReq.bssid = wifiInfo.getBssid();
+        saveNetworkReq.psk = pw;
+        saveNetworkReq.security = wifiInfo.getNetworkType();
+        saveNetworkReq.index = wifiInfo.getIndex();
+        if (amazonFreeRTOSDevice != null) {
+            amazonFreeRTOSDevice.saveNetwork(saveNetworkReq, mNetworkConfigCallback);
+        } else {
+            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_SAVE_NETWORK_FAIL);
+        }
+    }
+
+    private void inputPw(boolean needPw) {
+        /*
+        // user input text
+        final EditText editText = new EditText(this);
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle("Wi-Fi PassWord 입력");
+        builder.setView(editText);
+        builder.setMessage("[주의] PassWord를 입력하지 않으면 비밀번호가 없는 상태로 연결을 진행합니다.");
+        builder.setPositiveButton("선택",
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        wifipw = editText.getText().toString();
+                        changeUI(UI_STATUS_LOADING,"Wi-Fi PassWord 저장 완료");
+                        saveNetwork(wifibssid, wifipw);
+                    }
+                });
+
+        builder.setNegativeButton("exit", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+            }
+        });
+        builder.show();
+        */
+        if(needPw) {
+            Intent intent = new Intent(this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+            Bundle b = new Bundle();
+            b.putString(PopupActivity.POPUP_INPUT, "");
+            intent.putExtra(POPUP_BUNDLE, b);
+            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.input_ap_password));
+            startActivityForResult(intent, RESULT_INPUT_AP_PASS);
+        } else {
+            changeUI(UI_STATUS_LOADING,"Wi-Fi PassWord 저장 완료");
+            saveNetwork(wifibssid, "");
+        }
+    }
+
+
+    @SuppressLint("MissingPermission")
+    private void scanLeDevice(final boolean enable) {
+        handler = new Handler();
+        if (enable) {
+            bleList.clear();
+            // Stops scanning after a pre-defined scan period.
+            handler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    bluetoothAdapter.stopLeScan(leScanCallback);
+                    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+
+                    DebugLogOutput(TAG, "run: Stop Scan");
+                    bluetoothAdapter.stopLeScan(leScanCallback);
+
+                    if(bleArrayAdapter == null) {
+                        changeUI(UI_STATUS_SHOW_BLE_LIST,"검색된 장치가 없습니다.");
+                        retryScanDevice();
+                    } else if(bleArrayAdapter.getCount() == 0) {
+                        changeUI(UI_STATUS_SHOW_BLE_LIST,"검색된 장치가 없습니다.");
+                        retryScanDevice();
+                    } else {
+                        changeUI(UI_STATUS_SHOW_BLE_LIST,getString(R.string.activity_light_register_find_device_text_2));
+                        bleArrayAdapter.notifyDataSetChanged();
+                    }
+                }
+            }, SCAN_PERIOD);
+            bluetoothAdapter.startLeScan(leScanCallback);
+        } else {
+            bluetoothAdapter.stopLeScan(leScanCallback);
+        }
+    }
+
+    @SuppressLint("MissingPermission")
+    private final BluetoothAdapter.LeScanCallback leScanCallback =
+            new BluetoothAdapter.LeScanCallback() {
+                @Override
+                public void onLeScan(final BluetoothDevice device, int rssi,
+                                     final byte[] scanRecord) {
+                    DebugLogOutput(TAG, "leScanCallback: Find AmazonBLEinfo Device");
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (device.getName() != null && device.getName().toUpperCase().contains("ICON_LM")) {
+                                AmazonBLEinfo thisDevice = new AmazonBLEinfo(device.getName(),
+                                        device.getAddress(), device);
+                                if (!bleList.contains(thisDevice)) {
+                                    bleList.add(thisDevice);
+                                }
+                            }
+                        }
+                    });
+                }
+            };
+
+    private final NetworkConfigCallback mNetworkConfigCallback = new NetworkConfigCallback() {
+        @Override
+        public void onListNetworkResponse(final ListNetworkResp response) {
+            mHandler = new Handler(Looper.getMainLooper());
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    AmazonWifiInfo wifiInfo =
+                            new AmazonWifiInfo(response.getSsid(), response.getBssid(),
+                                    response.getRssi(), response.getSecurity(), response.getIndex(),
+                                    response.getConnected());
+
+                    if (!wifiInfoList.contains(wifiInfo)) {
+                        wifiInfoList.add(wifiInfo);
+                        mBssid2WifiInfoMap.put(bssidToString(wifiInfo.getBssid()), wifiInfo);
+
+                        //wifiArrayAdapter.notifyDataSetChanged();
+
+                        // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
+                        final View child = getLayoutInflater().inflate(R.layout.listview_add_wifi_list, null);
+                        LinearLayout layoutWifi = child.findViewById(R.id.layoutWifi);
+                        TextView txtSsid = child.findViewById(R.id.txtSsid);
+                        ImageView imgPw = child.findViewById(R.id.imgPw);
+                        ImageView imgRssi = child.findViewById(R.id.imgRssi);
+
+                        // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
+                        layoutWifi.setBackgroundResource(R.drawable.background_actionbar_button);
+                        txtSsid.setText(wifiInfo.getSsid());
+                        if(wifiInfo.getNetworkTypeName().equals("Open")) {
+                            imgPw.setVisibility(View.INVISIBLE);
+                        } else {
+                            imgPw.setVisibility(View.VISIBLE);
+                        }
+                        if(wifiInfo.getRssi() > RSSI_LEVEL_3) {
+                            imgRssi.setImageLevel(3);
+                        } else if(wifiInfo.getRssi() > RSSI_LEVEL_2) {
+                            imgRssi.setImageLevel(2);
+                        } else if(wifiInfo.getRssi() > RSSI_LEVEL_1) {
+                            imgRssi.setImageLevel(1);
+                        } else {
+                            imgRssi.setImageLevel(0);
+                        }
+
+                        child.setOnClickListener(new View.OnClickListener() {
+                            @Override
+                            public void onClick(View v) {
+                                wifibssid = bssidToString(wifiInfo.getBssid());
+                                amazonWifiInfo = wifiInfo;
+                                inputPw(!amazonWifiInfo.getNetworkTypeName().equals("Open"));
+                            }
+                        });
+                        layoutListWifi.addView(child);
+                    }
+                }
+            }, 300);
+            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_LIST_NETWORK_SUCCESS);
+        }
+
+        @Override
+        public void onSaveNetworkResponse(final SaveNetworkResp response) {
+            DebugLogOutput(TAG, "onSaveNetworkResponse(SaveNetworkResp) : " + response);
+            RequestHandlerSendMessage(bleProtocolState.PROVISIONING_SAVE_NETWORK_SUCCESS);
+        }
+    };
+
+    final BleConnectionStatusCallback connectionStatusCallback = new BleConnectionStatusCallback() {
+        @Override
+        public void onBleConnectionStatusChanged(AmazonFreeRTOSConstants.BleConnectionState connectionStatus) {
+            DebugLogOutput(TAG, "AWS connection status changed to : " + connectionStatus);
+            if (connectionStatus == AmazonFreeRTOSConstants.BleConnectionState.BLE_CONNECTED) {
+                RequestHandlerSendMessage(bleProtocolState.PROVISIONING_BLE_CONNECTED);
+
+            } else if (connectionStatus == AmazonFreeRTOSConstants.BleConnectionState.BLE_DISCONNECTED) {
+                DebugLogOutput(TAG, "AWS connection Fail State : " + connectionStatus);
+                RequestHandlerSendMessage(bleProtocolState.PROVISIONING_BLE_DISCONNECTED);
+            }
+        }
+    };
+
+    private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+            super.onConnectionStateChange(gatt, status, newState);
+
+            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "BluetoothGatt.STATE_CONNECTED State from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_CONNECTED);
+                gatt.discoverServices();
+
+            } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "bluetoothGatt.STATE_DISCONNECTED State from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+                RequestHandlerSendMessage(bleProtocolState.ON_BOARDING_BLE_DISCONNECTED);
+
+            } else if (status != BluetoothGatt.GATT_SUCCESS) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "bluetoothGatt.GATT GATT_FAIL from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+                bleGattDisconnect(gatt.getDevice().getAddress());
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_NOT_CONNECTED);
+            }
+        }
+
+        @Override
+        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+            super.onMtuChanged(gatt, mtu, status);
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onCharacteristicChanged State from %s (%s) - mtu %d",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    mtu
+            ));
+            gatt.discoverServices();
+        }
+
+        @Override
+        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+            super.onServicesDiscovered(gatt, status);
+
+            DebugLogOutput(TAG, "onServicesDiscovered LIGHT Device");
+            BluetoothGattService deviceGattService = gatt.getService(hdcUUID.LIGHT_GATT_SERVICE_UUID);
+            if (deviceGattService == null) {
+                DebugLogOutput(TAG, "LIGHT/gattInit # No deviceGattService !!");
+                return;
+            }
+
+            BluetoothGattCharacteristic bluetoothGattCharacteristic = deviceGattService.getCharacteristic(hdcUUID.LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID);
+            if (bluetoothGattCharacteristic == null) {
+                DebugLogOutput(TAG, "LIGHT/gattInit # No mDeviceGattCharacteristic !!");
+                return;
+            }
+
+            DebugLogOutput(TAG, "LIGHT/Set on Notification");
+            gatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);
+
+            if (hdcUUID.LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID.equals(bluetoothGattCharacteristic.getUuid())) {
+                DebugLogOutput(TAG, "LIGHT/Write Descriptor");
+                BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(hdcUUID.LIGHT_GATT_DESCRIPTORS_UUID);
+                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+                gatt.writeDescriptor(descriptor);
+            }
+        }
+
+        @Override
+        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+            super.onCharacteristicChanged(gatt, characteristic);
+            byte[] data = characteristic.getValue();
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onCharacteristicChanged State from %s (%s) - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    byteArrayToHex(data)
+            ));
+
+            if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_USER_REGISTERATION_COMPLETE_REQ_TO_APP) {
+                if (data[3] == bleProtocol.DEVICE_ID_TYPE) {
+                    if (data[4] == (byte) 0x01) {
+                        resetDeviceData();
+                        // device id 14 byte
+                        DebugLogOutput(TAG, "1st Device ID Type Data Length : " + data.length);
+                        for (int i = 6; i < data.length; i++) {
+                            deviceIdNumber += (char) data[i];
+                        }
+                        DebugLogOutput(TAG, String.format("1st Device id : %s", deviceIdNumber));
+                    } else if (data[4] == (byte) 0x02) {
+                        DebugLogOutput(TAG, "2nd Device ID Type Data Length : " + data.length);
+                        for (int i = 5; i < data.length; i++) {
+                            deviceIdNumber += (char) data[i];
+                        }
+                    }
+                    DebugLogOutput(TAG, String.format("1st + 2nd Device id : %s", deviceIdNumber));
+                } else if (data[3] == bleProtocol.MODEL_ID_TYPE) {
+                    DebugLogOutput(TAG, "Model ID Type Data Length : " + data.length);
+                    for (int i = 5; i < data.length; i++) {
+                        modelIdNumber += (char) data[i];
+                    }
+                    DebugLogOutput(TAG, String.format("Model id : %s", modelIdNumber));
+
+                } else if (data[3] == bleProtocol.MAC_TYPE) {
+                    // Mac
+                    DebugLogOutput(TAG, "Mac Type Data Length : " + data.length);
+                    for (int i = 0; i < (int) data[4]; i++) {
+                        macNumber += String.format(": %02x ", data[i + 5] & 0xff);
+                    }
+
+                    DebugLogOutput(TAG, String.format("Mac : %s", macNumber));
+
+                    if(data[11] == (byte) 0x01){
+                        if(isUserRegisteStatus) {
+                            DebugLogOutput(TAG, "get USER_REGISTER_COMPLETE_SUCCESS");
+                            RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_COMPLETE_SUCCESS);
+                        } else {
+                            DebugLogOutput(TAG, "get USER_REGISTER_COMPLETE_FAIL(cmd is USER_REGISTER_COMPLETE_SUCCESS, but Timeout. so it's error.)");
+                            RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_COMPLETE_FAIL);
+                        }
+                    } else {
+                        DebugLogOutput(TAG, "get USER_REGISTER_COMPLETE_FAIL");
+                        RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_COMPLETE_FAIL);
+                    }
+                }
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_BLINK_LIGHT_RSP_TO_APP && data[2] == (byte)0x01) {
+                if (data[3] == (byte) 0x01) {
+                    DebugLogOutput(TAG, "get RSP_BLINK_PROTOCOL_SUCCESS");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_SUCCESS);
+                } else if (data[3] == (byte) 0x02) {
+                    DebugLogOutput(TAG, "get RSP_BLINK_PROTOCOL_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_FAIL);
+                }
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_ATOL_APP_STATUS_REQ_TO_LIGHT /*&& data[2] == (byte)0x02*/) {
+                if ((data[3] & (byte) 0x01) == 0x01) {
+                    DebugLogOutput(TAG, "get RSP_GET_WIFI_STATUS_SUCCESS");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_GET_WIFI_STATUS_SUCCESS);
+                } else {
+                    DebugLogOutput(TAG, "get RSP_GET_WIFI_STATUS_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_GET_WIFI_STATUS_FAIL);
+                }
+            }
+        }
+
+        @Override
+        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+            super.onCharacteristicWrite(gatt, characteristic, status);
+            byte[] data = characteristic.getValue();
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onCharacteristicWrite State from %s (%s) - status %d - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status,
+                    byteArrayToHex(data)
+            ));
+
+            if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_ATOL_USER_REGISTERATION_COMPLETE_RSP_TO_LIGHT && data[2] == (byte) (0x01)) {
+                if (data[3] == (byte) (0x01)) {
+                    RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_END);
+                } else if (data[3] == (byte) (0x02)) {
+                    RequestHandlerSendMessage(bleProtocolState.USER_REGISTER_FAIL);
+                }
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_ATOL_BLINK_LIGHT_REQ_TO_LIGHT && data[2] == (byte) (0x01)) {
+                RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT);
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+            super.onDescriptorWrite(gatt, descriptor, status);
+            byte[] data = descriptor.getValue();
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onDescriptorWrite State from %s (%s) - status %d - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status,
+                    byteArrayToHex(data)
+            ));
+
+            if (data[0] == (byte) 0x01 && data[1] == (byte) 0x00) {
+                if (status_process == PROCESS_STATUS_USER_REGISTER) {
+                    DebugLogOutput(TAG, "사용자등록 요청\nonDescriptorWrite: status_process = " + status_process);
+                    bleGattWriteRequestCommand(bleProtocol.userRegisterRequestProtocol);
+                } else if (status_process == PROCESS_STATUS_BLINK) {
+                    DebugLogOutput(TAG, "깜빡임 요청\nonDescriptorWrite: status_process = " + status_process);
+                    bleGattWriteRequestCommand(bleProtocol.blinkLightProtocol);
+                } else if(status_process == PROCESS_STATUS_INIT_LIGHT) {
+                    DebugLogOutput(TAG, "초기화 요청\nonDescriptorWrite: status_process = " + status_process);
+                    bleGattWriteRequestCommand(bleProtocol.FactoryResetLightProtocol);
+                } else if(status_process == PROCESS_STATUS_SEND_ACK_STATUS_SUCCESS) {
+                    DebugLogOutput(TAG, "0x28 CMD STATUS 응답(0xA8) / SUCCESS\nonDescriptorWrite: status_process = " + status_process);
+                    bleGattWriteRequestCommand(bleProtocol.lightStatusSuccessResponseProtocol);
+                } else if(status_process == PROCESS_STATUS_SEND_ACK_STATUS_FAIL){
+                    DebugLogOutput(TAG, "0x28 CMD STATUS 응답(0xA8) / FAIL\nonDescriptorWrite: status_process = " + status_process);
+                    bleGattWriteRequestCommand(bleProtocol.lightStatusFailResponseProtocol);
+                }
+            }
+        }
+    };
+
+    private void RequestHandlerSendMessage(int deviceState) {
+        Message msg = new Message();
+        msg.what = deviceState;
+        if (deviceState == bleProtocolState.ON_BOARDING_TIMEOUT
+                || deviceState == bleProtocolState.USER_REGISTER_REQUEST_TIMEOUT) {
+            requestHandler.sendMessageDelayed(msg, USER_REGISTER_REQUEST_TIMEOUT_TIME);
+        } else if (deviceState == bleProtocolState.HDC_ICON_LIGHT_REGISTER_SUCCESS) {
+            requestHandler.sendMessageDelayed(msg, 3000);
+        } else if(deviceState == bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT) {
+            requestHandler.sendMessageDelayed(msg, DEFAULT_REG_DEVICE_LOADING_TIME);
+        } else if(deviceState == bleProtocolState.RSP_GET_WIFI_STATUS_TIMEOUT) {
+            requestHandler.sendMessageDelayed(msg, DEFAULT_WAIT_WIFI_STATUS_TIME);
+        } else {
+            requestHandler.sendMessage(msg);
+        }
+    }
+
+    private void bleGattWriteRequestCommand(byte[] protocol) {
+        bluetoothGattCharacteristic = connectedDeviceMap.get(selectedBleMac).getService(hdcUUID.LIGHT_GATT_SERVICE_UUID)
+                .getCharacteristic(hdcUUID.LIGHT_GATT_WRITE_CHARACTERISTIC_UUID);
+        bluetoothGattCharacteristic.setValue(protocol);
+        connectedDeviceMap.get(selectedBleMac).writeCharacteristic(bluetoothGattCharacteristic);
+    }
+
+    private void resetDeviceData() {
+        deviceIdNumber = "";
+        modelIdNumber = "";
+        macNumber = "";
+    }
+
+    private void retryScanDevice() {
+        if(isRetry) {
+            Intent intent = new Intent(LightRegisterDeviceActivity.this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL_WITH_ICON);
+            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_light_device_title));
+            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_not_found_light_device_description_2));
+            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.ok));
+            startActivityForResult(intent, RESULT_RETRY_BUT_NO_DEVICE);
+        } else {
+            Intent intent = new Intent(LightRegisterDeviceActivity.this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO_WITH_ICON);
+            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_light_device_title));
+            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_not_found_light_device_description_1));
+            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.retry));
+            intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.cancel));
+            startActivityForResult(intent, RESULT_RETRY_SCAN_DEVICE);
+        }
+    }
+
+    private void retryMappingDevice() {
+        if(isRetryMapping) {
+            Intent intent = new Intent(LightRegisterDeviceActivity.this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL_WITH_ICON);
+            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_error_wifi_for_mapping_title));
+            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_error_wifi_for_mapping_description_2));
+            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.ok));
+            startActivityForResult(intent, RESULT_RETRY_BUT_NO_DEVICE);
+        } else {
+            Intent intent = new Intent(LightRegisterDeviceActivity.this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO_WITH_ICON);
+            intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_error_wifi_for_mapping_title));
+            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_error_wifi_for_mapping_description_1));
+            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.retry));
+            intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.cancel));
+            startActivityForResult(intent, RESULT_RETRY_MAPPING_DEVICE);
+        }
+    }
+
+    /**
+     * 매핑 API를 호출하여 조명모듈 매핑 요청 함수.
+     */
+    private void startMappingLightDevice() {
+        if(deviceIdNumber.isEmpty()) {
+            changeUI(UI_STATUS_SHOW_FAIL,"매핑할 Device ID가 존재하지 않습니다.");
+            retryScanDevice();
+        } else {
+            if(!IsRunningProgress()) {
+                displayProgress(LightRegisterDeviceActivity.this,"","");
+            }
+            try {
+                HttpApi.mappingDevice(deviceIdNumber, new okhttp3.Callback() {
+                    @Override
+                    public void onFailure(Call call, IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.mappingDevice(onFailure)\n" + e.toString());
+                        changeUI(UI_STATUS_SHOW_FAIL,"AWS IOT 매핑에 실패하였습니다.");
+                        retryMappingDevice();
+                    }
+
+                    @Override
+                    public void onResponse(Call call, Response response) throws IOException {
+                        if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                            WarnLogOutput(TAG, "HttpApi.mappingDevice(onResponse) HTTP_NO_CONTENT");
+
+                            //다른 Activity에서 처리하게 수정 막음
+                            //RequestHandlerSendMessage(bleProtocolState.HDC_ICON_LIGHT_REGISTER_SUCCESS);
+                            //changeUI(UI_STATUS_SHOW_COMPLETED,"등록이 완료되었습니다.");
+
+                            //매핑 완료시 BLE 연결해제 로직을 이동 - > bleProtocolState.USER_REGISTER_END, bleProtocolState.USER_REGISTER_FAIL에 항상 호출.
+                            //bleAwsDisconnect();
+                            //bleGattDisconnect();
+
+                            if(IsRunningProgress()) {
+                                DismissConnectDialog();
+                            }
+
+                            Intent intent = new Intent(LightRegisterDeviceActivity.this, LightRegisterDeviceFinishActivity.class);
+                            intent.putExtra(EXTRA_DEVICE_ID,deviceIdNumber);
+                            startActivity(intent);
+                            finish();
+
+                        } else if(response.code() == HTTP_UNAUTHORIZED) {
+                            showNotiMsg(LightRegisterDeviceActivity.this, getString(R.string.snackbar_expired_token));
+                            restartApp(LightRegisterDeviceActivity.this);
+                        } else {
+                            ErrorLogOutput(TAG, "HttpApi.mappingDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                ErrorLogOutput(TAG, "HttpApi.mappingDevice(onResponse) body : " + json_data.toString());
+                            } catch (JSONException e) {
+                                ErrorLogOutput(TAG, "HttpApi.mappingDevice(onResponse) JSONException : " + e.toString());
+                            }
+                            changeUI(UI_STATUS_SHOW_FAIL,"AWS IOT 매핑에 실패하였습니다.");
+                            retryMappingDevice();
+                        }
+                    }
+                });
+            } catch (Exception e) {
+                e.printStackTrace();
+                ErrorLogOutput(TAG, "HttpApi.mappingDevice(Exception)\n" + e.toString());
+                changeUI(UI_STATUS_SHOW_FAIL,"AWS IOT 매핑에 실패하였습니다.");
+                retryMappingDevice();
+            }
+        }
+    }
+
+    /**
+     * Ble 리스트 아답터
+     */
+    public class BleArrayAdapter extends BaseAdapter {
+        // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+        private List<AmazonBLEinfo> listViewItemList;
+
+        // ListViewAdapter의 생성자
+        public BleArrayAdapter(List<AmazonBLEinfo> listItem) {
+            listViewItemList = listItem;
+        }
+
+        // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+        @Override
+        public int getCount() {
+            return listViewItemList==null?0:listViewItemList.size() ;
+        }
+
+        // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+        @Override
+        public View getView(final int position, View convertView, ViewGroup parent) {
+            final int pos = position;
+            final Context context = parent.getContext();
+
+            // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+            if (convertView == null) {
+                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                convertView = inflater.inflate(R.layout.listview_add_device_list, parent, false);
+            }
+
+            // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
+            TextView txtName = convertView.findViewById(R.id.txtName);
+            ImageView imgIcon = convertView.findViewById(R.id.imgIcon);
+
+            // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
+            imgIcon.setImageResource(R.drawable.icon_add_device_light);
+            txtName.setText(listViewItemList.get(position).getName());
+
+            return convertView;
+        }
+
+        // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+        @Override
+        public long getItemId(int position) {
+            return position ;
+        }
+
+        // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
+        @Override
+        public Object getItem(int position) {
+            return listViewItemList.get(position) ;
+        }
+
+        public void removeAll() {
+            listViewItemList.clear();
+        }
+    }
+
+    /**
+     * Wifi 리스트 아답터
+     */
+    public class WifiArrayAdapter extends BaseAdapter {
+        // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+        private List<AmazonWifiInfo> listViewItemList;
+
+        // ListViewAdapter의 생성자
+        public WifiArrayAdapter(List<AmazonWifiInfo> listItem) {
+            listViewItemList = listItem;
+        }
+
+        // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+        @Override
+        public int getCount() {
+            return listViewItemList==null?0:listViewItemList.size() ;
+        }
+
+        // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+        @Override
+        public View getView(final int position, View convertView, ViewGroup parent) {
+            final int pos = position;
+            final Context context = parent.getContext();
+
+            // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+            if (convertView == null) {
+                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                convertView = inflater.inflate(R.layout.listview_add_wifi_list, parent, false);
+            }
+
+            // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
+            TextView txtSsid = convertView.findViewById(R.id.txtSsid);
+            ImageView imgPw = convertView.findViewById(R.id.imgPw);
+            ImageView imgRssi = convertView.findViewById(R.id.imgRssi);
+
+            // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
+            txtSsid.setText(listViewItemList.get(position).getSsid());
+
+            if(listViewItemList.get(position).getNetworkTypeName().equals("Open")) {
+                imgPw.setVisibility(View.INVISIBLE);
+            } else {
+                imgPw.setVisibility(View.VISIBLE);
+            }
+
+            if(listViewItemList.get(position).getRssi() > RSSI_LEVEL_3) {
+                imgRssi.setImageLevel(3);
+            } else if(listViewItemList.get(position).getRssi() > RSSI_LEVEL_2) {
+                imgRssi.setImageLevel(2);
+            } else if(listViewItemList.get(position).getRssi() > RSSI_LEVEL_1) {
+                imgRssi.setImageLevel(1);
+            } else {
+                imgRssi.setImageLevel(0);
+            }
+
+            return convertView;
+        }
+
+        // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+        @Override
+        public long getItemId(int position) {
+            return position ;
+        }
+
+        // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
+        @Override
+        public Object getItem(int position) {
+            return listViewItemList.get(position) ;
+        }
+
+        public void removeAll() {
+            listViewItemList.clear();
+        }
+    }
+}

+ 586 - 0
app/src/main/java/kr/co/icontrols/iotplatform/LightRegisterDeviceFinishActivity.java

@@ -0,0 +1,586 @@
+package kr.co.icontrols.iotplatform;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_HOME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCircleBitmap;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageResource;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_BEFORE_ACTIVITY;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_LIST_COUNT_LINE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_ADD_GROUP;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_DEVICE_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_UPDATE_DATA;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class LightRegisterDeviceFinishActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "LightRegisterDeviceFinish";
+
+    private TextView txtCompletedDeviceName;
+    private LinearLayout mLayoutGroups;
+    private ArrayList<ImageView> mImgViewList = new ArrayList<>();
+
+
+    private String mDeviceId, deviceId, deviceType, firmwareVersion, friendlyName, sharedFriendlyName, ownerId, dgroupId, dgroupName;
+
+    private boolean isChangeName, isChangeGroup;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_light_register_device_finish);
+
+        mDeviceId = getIntent().getStringExtra(EXTRA_DEVICE_ID);
+        isChangeName = false;
+        isChangeGroup = true;
+
+        txtCompletedDeviceName = findViewById(R.id.txtCompletedDeviceName);
+        mLayoutGroups = findViewById(R.id.layoutGroups);
+
+        //UserInfo, Device 재갱신을 위한 Device list 초기화
+        UserInfo.updateTimeUserInfo = 0;
+        UserInfo.updateTimeDevice = 0;
+        UserInfo.devices.clear();
+
+        getDetailDeviceInfo(mDeviceId);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    public void onBackPressed() {
+        //super.onBackPressed();
+        actvityExit();
+    }
+
+    @Override
+    protected void onDestroy() {
+
+        if(mImgViewList != null) {
+            mImgViewList.clear();
+        }
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnCompleted:
+                actvityExit();
+                break;
+            case R.id.btnEditDeviceName:{
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+                Bundle b = new Bundle();
+                if (txtCompletedDeviceName.getText().toString().isEmpty()) {
+                    b.putString(PopupActivity.POPUP_INPUT, "");
+                } else {
+                    b.putString(PopupActivity.POPUP_INPUT, txtCompletedDeviceName.getText().toString());
+                }
+                intent.putExtra(POPUP_BUNDLE, b);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.change_device_name));
+                startActivityForResult(intent, RESULT_INPUT_DEVICE_NAME);
+                }
+                break;
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_INPUT_DEVICE_NAME:
+                if (resultCode == RESULT_OK) {
+                    Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                    if(b != null) {
+                        try {
+                            String newValue = b.getString(PopupActivity.POPUP_INPUT);
+                            if(!newValue.equals(txtCompletedDeviceName.getText().toString())) {
+                                changeDeviceName(mDeviceId, newValue);
+                            }
+                        } catch (Exception e) {
+                            // Log failure
+                            showNotiMsg(LightRegisterDeviceFinishActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                        }
+                    } else {
+                        showNotiMsg(LightRegisterDeviceFinishActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                    }
+                }
+                break;
+
+            case RESULT_ADD_GROUP:
+                if (resultCode == RESULT_OK) {
+                    displayProgress(this,"","");
+                    getMyDeviceGroup();
+                }
+                break;
+        }
+    }
+
+    private void actvityExit() {
+        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
+    public void getDetailDeviceInfo(String device_Id){
+        displayProgress(this,"","");
+        try {
+            HttpApi.getDeviceInfo(device_Id, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onFailure)\n" + e.toString());
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+
+                            deviceId = json_data.isNull("deviceId") ? "" : json_data.getString("deviceId");
+                            deviceType = json_data.isNull("deviceType") ? "" : json_data.getString("deviceType");
+                            firmwareVersion = json_data.isNull("firmwareVersion") ? "" : json_data.getString("firmwareVersion");
+                            friendlyName = json_data.isNull("friendlyName") ? "" : json_data.getString("friendlyName");
+                            sharedFriendlyName = json_data.isNull("sharedFriendlyName") ? "" : json_data.getString("sharedFriendlyName");
+                            ownerId = json_data.isNull("ownerId") ? "" : json_data.getString("ownerId");
+                            dgroupId = json_data.isNull("dgroupId") ? "" : json_data.getString("dgroupId");
+                            dgroupName = json_data.isNull("dgroupName") ? "" : json_data.getString("dgroupName");
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    txtCompletedDeviceName.setText(friendlyName);
+                                    getMyDeviceGroup();
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(onResponse) JSONException : " + e.toString());
+                        }
+                        DismissConnectDialog();
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceInfo(Exception)\n" + e.toString());
+            DismissConnectDialog();
+        }
+    }
+
+    private void changeDeviceName(final String deviceId, final String name) {
+        displayProgress(this,"","");
+        try {
+            HttpApi.modifyDeviceInfo(deviceId,
+                    name,
+                    new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onFailure)\n" + e.toString());
+                            showNotiMsg(LightRegisterDeviceFinishActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                            DismissConnectDialog();
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                WarnLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) HTTP_NO_CONTENT");
+
+                                final DeviceInfo itemS = DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,deviceId);
+                                if(itemS != null) {
+                                    itemS.friendlyName = name;
+                                    DBUtil.updateDeviceItem(TABLE_SMART_LIGHT,itemS);
+                                }
+
+                                final DeviceInfo itemH = DBUtil.getDeviceInfoForId(TABLE_DEVICE_HOME,deviceId);
+                                if(itemH != null) {
+                                    itemH.friendlyName = name;
+                                    DBUtil.updateDeviceItem(TABLE_DEVICE_HOME,itemH);
+                                }
+
+                                final DeviceInfo itemR = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK,deviceId);
+                                if(itemR != null) {
+                                    itemR.friendlyName = name;
+                                    DBUtil.updateDeviceItem(TABLE_DEVICE_RANK,itemR);
+                                }
+
+                                isChangeName = true;
+                                friendlyName = name;
+                                for(int i = 0 ; i < UserInfo.devices.size() ; i++) {
+                                    if(UserInfo.devices.get(i).deviceId.equals(deviceId)) {
+                                        if(!UserInfo.devices.get(i).friendlyName.equals(friendlyName)) {
+                                            UserInfo.devices.get(i).friendlyName = friendlyName;
+                                        }
+                                    }
+                                }
+
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        showNotiMsg(LightRegisterDeviceFinishActivity.this,getString(R.string.snackbar_successed_change_device_name));
+                                        txtCompletedDeviceName.setText(friendlyName);
+                                        DismissConnectDialog();
+                                    }
+                                });
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                showNotiMsg(LightRegisterDeviceFinishActivity.this,getString(R.string.snackbar_fail_change_device_name));
+                                DismissConnectDialog();
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(onResponse) JSONException : " + e.toString());
+                                }
+                            }
+                        }
+                    });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.modifyDeviceInfo(Exception)\n" + e.toString());
+            showNotiMsg(LightRegisterDeviceFinishActivity.this,getString(R.string.snackbar_fail_change_device_name));
+            DismissConnectDialog();
+        }
+    }
+
+    public void getMyDeviceGroup(){
+        UserInfo.getGroup(this, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        if(UserInfo.dgroups.size() == 0) {
+                            ErrorLogOutput(TAG, "mGroupList size : " + UserInfo.dgroups.size());
+                        } else {
+                            shwoGroupList();
+                        }
+                        DismissConnectDialog();
+                    }
+                });
+            }
+        });
+
+        /*
+        UserInfo.dgroups.clear();
+        try {
+            HttpApi.getDeviceGroupList(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onFailure)\n" + e.toString());
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                            for (int i = 0; i < jarrayAll.length(); i++) {
+                                JSONObject jObject = jarrayAll.getJSONObject(i);
+                                DeviceGroupInfo item = new DeviceGroupInfo(jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                        jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                        jObject.isNull("icon") ? "" : jObject.getString("icon"),
+                                        jObject.isNull("index") ? 99 : jObject.getInt("index"));
+
+                                UserInfo.dgroups.add(item);
+                            }
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if(UserInfo.dgroups.size() == 0) {
+                                        ErrorLogOutput(TAG, "mGroupList size : " + UserInfo.dgroups.size());
+                                    } else {
+                                        shwoGroupList();
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) JSONException : " + e.toString());
+                        }
+                        DismissConnectDialog();
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(Exception)\n" + e.toString());
+            DismissConnectDialog();
+        }
+        */
+    }
+
+
+    private void changeGroup(final String dgroupId, final String groupFriendlyName, final String deviceId, final int index) {
+        displayProgress(this,"","");
+        try {
+            HttpApi.connectDeviceInDeviceGroup(dgroupId, deviceId, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onFailure)\n" + e.toString());
+                    showNotiMsg(LightRegisterDeviceFinishActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) HTTP_NO_CONTENT");
+
+                        final DeviceInfo itemS = DBUtil.getDeviceInfoForId(TABLE_SMART_LIGHT,deviceId);
+                        if(itemS != null) {
+                            itemS.dgroupId = dgroupId;
+                            itemS.dgroupName = groupFriendlyName;
+                            DBUtil.updateDeviceItem(TABLE_SMART_LIGHT,itemS);
+                        }
+
+                        final DeviceInfo itemH = DBUtil.getDeviceInfoForId(TABLE_DEVICE_HOME,deviceId);
+                        if(itemH != null) {
+                            itemH.dgroupId = dgroupId;
+                            itemH.dgroupName = groupFriendlyName;
+                            DBUtil.updateDeviceItem(TABLE_DEVICE_HOME,itemH);
+                        }
+
+                        final DeviceInfo itemR = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK,deviceId);
+                        if(itemR != null) {
+                            itemR.dgroupId = dgroupId;
+                            itemR.dgroupName = groupFriendlyName;
+                            DBUtil.updateDeviceItem(TABLE_DEVICE_RANK,itemR);
+                        }
+
+                        for(int i = 0; i < UserInfo.devices.size() ; i++) {
+                            if(UserInfo.devices.get(i).deviceId.equals(deviceId)) {
+                                UserInfo.devices.get(i).dgroupName = friendlyName;
+                                UserInfo.devices.get(i).dgroupId = groupFriendlyName;
+                            }
+                        }
+
+                        runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                for(int i = 0; i < mImgViewList.size() ; i++) {
+                                    mImgViewList.get(i).setImageBitmap(getCircleBitmap(getGroupImageResource(UserInfo.dgroups.get(i).icon),i==index));
+                                }
+                                showNotiMsg(LightRegisterDeviceFinishActivity.this, "그룹을 변경하였습니다.");
+                                DismissConnectDialog();
+                            }
+                        });
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(LightRegisterDeviceFinishActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                    DismissConnectDialog();
+                }
+            });
+        }  catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.connectDeviceInDeviceGroup(Exception)\n" + e.toString());
+            showNotiMsg(LightRegisterDeviceFinishActivity.this, "그룹 이동 중 오류가 발생하였습니다.");
+            DismissConnectDialog();
+        }
+    }
+    private void shwoGroupList() {
+        mImgViewList.clear();
+        mLayoutGroups.removeAllViews();
+        LinearLayout layoutHorizontal = null;
+        int lineCount = 0;
+        for(int i = 0; i <= UserInfo.dgroups.size(); i++){
+            final int pos = i;
+            if(i%GROUP_LIST_COUNT_LINE==0) {
+                lineCount = 0;
+                layoutHorizontal = new LinearLayout(this);
+                LinearLayout.LayoutParams lparam = new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                layoutHorizontal.setLayoutParams(lparam);
+                layoutHorizontal.setOrientation(LinearLayout.HORIZONTAL);
+                mLayoutGroups.addView(layoutHorizontal);
+
+                LinearLayout spacelayout = new LinearLayout(this);
+                LinearLayout.LayoutParams slparam = new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                slparam.weight = 1;
+                spacelayout.setLayoutParams(slparam);
+                spacelayout.setOrientation(LinearLayout.HORIZONTAL);
+                layoutHorizontal.addView(spacelayout);
+            }
+
+            if(layoutHorizontal != null) {
+                lineCount++;
+                final View child = getLayoutInflater().inflate(R.layout.listview_horizontal_group_item, null);
+                final TextView txtGroupName = child.findViewById(R.id.txtGroup);
+                final ImageView imgGroup = child.findViewById(R.id.imgGroup);
+                if(pos < UserInfo.dgroups.size()) {
+                    mImgViewList.add(imgGroup);
+                    txtGroupName.setText(UserInfo.dgroups.get(pos).friendlyName);
+                    imgGroup.setImageBitmap(getCircleBitmap(getGroupImageResource(UserInfo.dgroups.get(pos).icon), pos == 0));
+                } else {
+                    txtGroupName.setText("추가하기");
+                    imgGroup.setScaleType(ImageView.ScaleType.CENTER);
+                    imgGroup.setImageResource(R.drawable.ico_add);
+                    imgGroup.setBackgroundResource(R.drawable.background_circle_add_device_icon);
+                }
+                layoutHorizontal.addView(child);
+
+                LinearLayout spacelayout = new LinearLayout(this);
+                LinearLayout.LayoutParams slparam = new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                slparam.weight = 1;
+                spacelayout.setLayoutParams(slparam);
+                spacelayout.setOrientation(LinearLayout.HORIZONTAL);
+                layoutHorizontal.addView(spacelayout);
+
+                //Click Event
+                if(pos < UserInfo.dgroups.size()) {
+                    child.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            changeGroup(UserInfo.dgroups.get(pos).dgroupId, UserInfo.dgroups.get(pos).friendlyName, mDeviceId, pos);
+                        }
+                    });
+                } else {
+                    child.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            Intent intent = new Intent(LightRegisterDeviceFinishActivity.this, AddGroupActivity.class);
+                            intent.putExtra(EXTRA_GROUP_LIST, UserInfo.dgroups);
+                            intent.putExtra(EXTRA_BEFORE_ACTIVITY,"LightRegisterDeviceFinishActivity");
+                            startActivityForResult(intent,RESULT_ADD_GROUP);
+                        }
+                    });
+                }
+            }
+
+            if(i == UserInfo.dgroups.size() && lineCount < GROUP_LIST_COUNT_LINE) {
+                while(lineCount < GROUP_LIST_COUNT_LINE) {
+                    lineCount++;
+                    final View child = getLayoutInflater().inflate(R.layout.listview_horizontal_group_item, null);
+                    final TextView txtGroupName = child.findViewById(R.id.txtGroup);
+                    final ImageView imgGroup = child.findViewById(R.id.imgGroup);
+                    txtGroupName.setVisibility(View.INVISIBLE);
+                    imgGroup.setVisibility(View.INVISIBLE);
+                    layoutHorizontal.addView(child);
+
+                    LinearLayout spacelayout = new LinearLayout(this);
+                    LinearLayout.LayoutParams slparam = new LinearLayout.LayoutParams(
+                            LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+                    slparam.weight = 1;
+                    spacelayout.setLayoutParams(slparam);
+                    spacelayout.setOrientation(LinearLayout.HORIZONTAL);
+                    layoutHorizontal.addView(spacelayout);
+                }
+            }
+        }
+    }
+}

+ 62 - 0
app/src/main/java/kr/co/icontrols/iotplatform/LightRegisterGuideActivity.java

@@ -0,0 +1,62 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.ViewFlipper;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+public class LightRegisterGuideActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_light_register_guide);
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnRegister:
+                startActivity(new Intent(this,LightRegisterDeviceActivity.class));;
+                break;
+        }
+    }
+}

+ 713 - 0
app/src/main/java/kr/co/icontrols/iotplatform/LoginActivity.java

@@ -0,0 +1,713 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationDetails;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChallengeContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChooseMfaContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ForgotPasswordContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.MultiFactorAuthenticationContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.NewPasswordContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.ForgotPasswordHandler;
+import com.google.android.gms.auth.api.signin.GoogleSignIn;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+import com.google.android.gms.auth.api.signin.GoogleSignInClient;
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
+import com.google.android.gms.common.Scopes;
+import com.google.android.gms.common.api.ApiException;
+import com.google.android.gms.common.api.Scope;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.utils.CommonUtils;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isEmailValid;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setupUI;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHOOSE_MFA;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CONFIRM_SIGN_UP;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_FORGOT_PASSWORD_CODE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_GOOGE_GET_AUTH_CODE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_LAUNCH_USER;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_MFA;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_NEW_PASSWORD;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_SIGN_UP;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.AUTO_LOGIN;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_AUTO_LOGIN;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_LOGIN_TIME;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_USER_ID;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_USER_PASS;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.LOGIN_TIME;
+
+public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "LoginActivity";
+
+    private AlertDialog userDialog;
+    private ProgressDialog waitDialog;
+
+    // Screen fields
+    private EditText inUsername;
+    private EditText inPassword;
+
+    //Continuations
+    private MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation;
+    private ForgotPasswordContinuation forgotPasswordContinuation;
+    private NewPasswordContinuation newPasswordContinuation;
+    private ChooseMfaContinuation mfaOptionsContinuation;
+
+    // User Details
+    private String username;
+    private String password;
+
+    //GOOGLE AUTH
+    private GoogleSignInClient mGoogleSignInClient;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_login);
+
+        setupUI(this, findViewById(R.id.layoutMain));
+
+        initApp();
+
+        ((CheckBox)findViewById(R.id.chkAutoLogin)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+                SharedPrefUtil.putBoolean(SharedPrefUtil.AUTO_LOGIN,b);
+            }
+        });
+
+        if(SharedPrefUtil.getBoolean(AUTO_LOGIN,DEFAULT_AUTO_LOGIN)) {
+            ((CheckBox)findViewById(R.id.chkAutoLogin)).setChecked(true);
+            findCurrent();
+        } else {
+            //기존 세션을 가지고 있으면 강제 제거
+            AppHelper.getPool().getUser(AppHelper.getPool().getCurrentUser().getUserId()).signOut();
+        }
+
+        // For sample only: make sure there is a valid server client ID.
+        validateServerClientID();
+
+        // [START configure_signin]
+        // Configure sign-in to request offline access to the user's ID, basic
+        // profile, and Google Drive. The first time you request a code you will
+        // be able to exchange it for an access token and refresh token, which
+        // you should store. In subsequent calls, the code will only result in
+        // an access token. By asking for profile access (through
+        // DEFAULT_SIGN_IN) you will also get an ID Token as a result of the
+        // code exchange.
+        String serverClientId = getString(R.string.google_auth_server_client_id);
+        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+                .requestScopes(new Scope(Scopes.EMAIL), new Scope(Scopes.PROFILE), new Scope(Scopes.OPEN_ID))
+                .requestServerAuthCode(serverClientId)
+                .requestEmail()
+                .build();
+        // [END configure_signin]
+
+        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnLogin:
+                //기존 세션을 가지고 있으면 강제 제거
+                AppHelper.getPool().getUser(AppHelper.getPool().getCurrentUser().getUserId()).signOut();
+                logIn(v);
+                break;
+            case R.id.txtFindId:
+                break;
+            case R.id.txtFindPassword:
+                forgotPassword(v);
+                break;
+            case R.id.txtSignUp:
+                signUp(v);
+                break;
+            case R.id.btnLoginWithGoogle:
+                getAuthCode();
+                break;
+        }
+    }
+
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        switch (requestCode) {
+            case RESULT_GOOGE_GET_AUTH_CODE: {
+                // [START get_auth_code]
+                Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
+                try {
+                    GoogleSignInAccount account = task.getResult(ApiException.class);
+                    String authCode = account.getServerAuthCode();
+
+                    // Show signed-un UI
+                    updateUI(account);
+                } catch (ApiException e) {
+                    ErrorLogOutput(TAG, "Sign-in failed : " + e);
+                    updateUI(null);
+                }
+                // [END get_auth_code]
+            }
+            break;
+            case RESULT_SIGN_UP:
+                // Register user
+                if(resultCode == RESULT_OK) {
+                    String name = data.getStringExtra("name");
+                    if (!name.isEmpty()) {
+                        inUsername.setText(name);
+                        inPassword.setText("");
+                        inPassword.requestFocus();
+                    }
+                    String userPasswd = data.getStringExtra("password");
+                    if (!userPasswd.isEmpty()) {
+                        inPassword.setText(userPasswd);
+                    }
+                    if (!name.isEmpty() && !userPasswd.isEmpty()) {
+                        // We have the user details, so sign in!
+                        username = name;
+                        password = userPasswd;
+                        AppHelper.getPool().getUser(username).getSessionInBackground(authenticationHandler);
+                    }
+                }
+                break;
+            case RESULT_CONFIRM_SIGN_UP:
+                // Confirm register user
+                if(resultCode == RESULT_OK) {
+                    String name = data.getStringExtra("name");
+                    if (!name.isEmpty()) {
+                        inUsername.setText(name);
+                        inPassword.setText("");
+                        inPassword.requestFocus();
+                    }
+                }
+                break;
+            case RESULT_FORGOT_PASSWORD_CODE:
+                // Forgot password
+                if(resultCode == RESULT_OK) {
+                    String newPass = data.getStringExtra("newPass");
+                    String code = data.getStringExtra("code");
+                    if (newPass != null && code != null) {
+                        if (!newPass.isEmpty() && !code.isEmpty()) {
+                            displayProgress(LoginActivity.this,"","");//showWaitDialog("Setting new password...");
+                            forgotPasswordContinuation.setPassword(newPass);
+                            forgotPasswordContinuation.setVerificationCode(code);
+                            forgotPasswordContinuation.continueTask();
+                        }
+                    }
+                }
+                break;
+            case RESULT_LAUNCH_USER:
+                // User
+                if(resultCode == RESULT_OK) {
+                    clearInput();
+                    String name = data.getStringExtra("TODO");
+                    if(name != null) {
+                        if (!name.isEmpty()) {
+                            name.equals("exit");
+                            onBackPressed();
+                        }
+                    }
+                }
+                break;
+            case RESULT_MFA:
+                //MFA
+                DismissConnectDialog(); //closeWaitDialog();
+                if(resultCode == RESULT_OK) {
+                    String code = data.getStringExtra("mfacode");
+                    if(code != null) {
+                        if (code.length() > 0) {
+                            displayProgress(LoginActivity.this,"","");//showWaitDialog("Signing in...");
+                            multiFactorAuthenticationContinuation.setMfaCode(code);
+                            multiFactorAuthenticationContinuation.continueTask();
+                        } else {
+                            inPassword.setText("");
+                            inPassword.requestFocus();
+                        }
+                    }
+                }
+                break;
+            case RESULT_NEW_PASSWORD:
+                //New password
+                DismissConnectDialog(); //closeWaitDialog();
+                Boolean continueSignIn = false;
+                if (resultCode == RESULT_OK) {
+                    continueSignIn = data.getBooleanExtra("continueSignIn", false);
+                }
+                if (continueSignIn) {
+                    continueWithFirstTimeSignIn();
+                }
+                break;
+            case RESULT_CHOOSE_MFA:
+                // Choose MFA
+                DismissConnectDialog(); //closeWaitDialog();
+                if(resultCode == RESULT_OK) {
+                    String option = data.getStringExtra("mfaOption");
+                    if (option != null) {
+                        if (option.length() > 0) {
+                            DebugLogOutput(TAG, " -- Selected Option: " + option);
+                            conitnueWithSelectedMfa(option);
+                        } else {
+                            inPassword.setText("");
+                            inPassword.requestFocus();
+                            Toast.makeText(LoginActivity.this,"Login cancelled",Toast.LENGTH_SHORT).show();
+                            /*
+                            TextView label = (TextView) findViewById(R.id.textViewUserPasswordMessage);
+                            label.setText("Login cancelled");
+                             */
+                        }
+                    }
+                }
+        }
+    }
+
+    // App methods
+    // Register user - start process
+    public void signUp(View view) {
+        signUpNewUser();
+    }
+
+    // Login if a user is already present
+    public void logIn(View view) {
+        signInUser();
+    }
+
+    // Forgot password processing
+    public void forgotPassword(View view) {
+        forgotpasswordUser();
+    }
+
+    private void signUpNewUser() {
+        Intent registerActivity = new Intent(this, SignUpInputActivity.class);
+        startActivityForResult(registerActivity, RESULT_SIGN_UP);
+    }
+
+    private void signInUser() {
+        username = inUsername.getText().toString().replace(" ","");
+
+        if(username == null || username.length() < 1) {
+            showNotiMsg(LoginActivity.this,"아이디를 입력해주세요.");
+            return;
+        } else if (!isEmailValid(username)) {
+            showNotiMsg(LoginActivity.this,"이메일 형식을 입력해주세요.");
+            return;
+        }
+
+        AppHelper.setUser(username);
+
+        password = inPassword.getText().toString();
+        if(password == null || password.length() < 1) {
+            showNotiMsg(LoginActivity.this,"비밀번호를 입력해주세요.");
+            return;
+        }
+
+        displayProgress(LoginActivity.this,"","");//showWaitDialog("Signing in...");
+        AppHelper.getPool().getUser(username).getSessionInBackground(authenticationHandler);
+    }
+
+    private void forgotpasswordUser() {
+        username = inUsername.getText().toString();
+        if(username == null) {
+            Toast.makeText(LoginActivity.this,inUsername.getHint()+" cannot be empty",Toast.LENGTH_SHORT).show();
+            /*
+            TextView label = (TextView) findViewById(R.id.textViewUserIdMessage);
+            label.setText(inUsername.getHint()+" cannot be empty");
+            inUsername.setBackground(getDrawable(R.drawable.text_border_error));
+            */
+            return;
+        }
+
+        if(username.length() < 1) {
+            Toast.makeText(LoginActivity.this,inUsername.getHint()+" cannot be empty",Toast.LENGTH_SHORT).show();
+            /*
+            TextView label = (TextView) findViewById(R.id.textViewUserIdMessage);
+            label.setText(inUsername.getHint()+" cannot be empty");
+            inUsername.setBackground(getDrawable(R.drawable.text_border_error));
+            */
+            return;
+        }
+
+        displayProgress(LoginActivity.this,"","");//showWaitDialog("");
+        AppHelper.getPool().getUser(username).forgotPasswordInBackground(forgotPasswordHandler);
+    }
+
+    private void getForgotPasswordCode(ForgotPasswordContinuation forgotPasswordContinuation) {
+        ErrorLogOutput(TAG, "call getForgotPasswordCode (ForgotPasswordActivity)");
+        this.forgotPasswordContinuation = forgotPasswordContinuation;
+        Intent intent = new Intent(this, ForgotPasswordActivity.class);
+        intent.putExtra("destination",forgotPasswordContinuation.getParameters().getDestination());
+        intent.putExtra("deliveryMed", forgotPasswordContinuation.getParameters().getDeliveryMedium());
+        startActivityForResult(intent, RESULT_FORGOT_PASSWORD_CODE);
+    }
+
+    private void mfaAuth(MultiFactorAuthenticationContinuation continuation) {
+        ErrorLogOutput(TAG, "call mfaAuth (Do noting)");
+        /*
+        multiFactorAuthenticationContinuation = continuation;
+        Intent mfaActivity = new Intent(this, MFAActivity.class);
+        mfaActivity.putExtra("mode", multiFactorAuthenticationContinuation.getParameters().getDeliveryMedium());
+        startActivityForResult(mfaActivity, RESULT_MFA);
+         */
+    }
+
+    private void firstTimeSignIn() {
+        ErrorLogOutput(TAG, "call firstTimeSignIn (NewPasswordActivity)");
+        Intent newPasswordActivity = new Intent(this, NewPasswordActivity.class);
+        startActivityForResult(newPasswordActivity, RESULT_NEW_PASSWORD);
+    }
+
+    private void selectMfaToSignIn(List<String> options, Map<String, String> parameters) {
+        ErrorLogOutput(TAG, "call selectMfaToSignIn (Do noting)");
+        /*
+        Intent chooseMfaActivity = new Intent(this, ChooseMFA.class);
+        AppHelper.setMfaOptionsForDisplay(options, parameters);
+        startActivityForResult(chooseMfaActivity, RESULT_CHOOSE_MFA);
+         */
+    }
+
+    private void conitnueWithSelectedMfa(String option) {
+        // mfaOptionsContinuation.setChallengeResponse("ANSWER", option);
+        mfaOptionsContinuation.setMfaOption(option);
+        mfaOptionsContinuation.continueTask();
+    }
+
+    private void continueWithFirstTimeSignIn() {
+        newPasswordContinuation.setPassword(AppHelper.getPasswordForFirstTimeLogin());
+        Map <String, String> newAttributes = AppHelper.getUserAttributesForFirstTimeLogin();
+        if (newAttributes != null) {
+            for(Map.Entry<String, String> attr: newAttributes.entrySet()) {
+                DebugLogOutput(TAG, String.format(" -- Adding attribute: %s, %s", attr.getKey(), attr.getValue()));
+                newPasswordContinuation.setUserAttribute(attr.getKey(), attr.getValue());
+            }
+        }
+        try {
+            newPasswordContinuation.continueTask();
+        } catch (Exception e) {
+            DismissConnectDialog(); //closeWaitDialog();
+            showNotiMsg(LoginActivity.this,"로그인에 실패하였습니다.");
+            //Toast.makeText(LoginActivity.this,"Sign-in failed",Toast.LENGTH_SHORT).show();
+            /*
+            TextView label = (TextView) findViewById(R.id.textViewUserIdMessage);
+            label.setText("Sign-in failed");
+            inPassword.setBackground(getDrawable(R.drawable.text_border_error));
+
+            label = (TextView) findViewById(R.id.textViewUserIdMessage);
+            label.setText("Sign-in failed");
+            inUsername.setBackground(getDrawable(R.drawable.text_border_error));
+            */
+            //showDialogMessage("Sign-in failed", AppHelper.formatException(e));
+        }
+    }
+
+    private void confirmUser() {
+        ErrorLogOutput(TAG, "call confirmUser (SignUpCertificationActivity)");
+        Intent confirmActivity = new Intent(this, SignUpCertificationActivity.class);
+        confirmActivity.putExtra("source","main");
+        startActivityForResult(confirmActivity, RESULT_CONFIRM_SIGN_UP);
+
+    }
+
+    private void launchUser() {
+        ErrorLogOutput(TAG, "call launchUser (MainActivity)");
+        Intent userActivity = new Intent(this, MainActivity.class);
+        userActivity.putExtra("name", username);
+        startActivityForResult(userActivity, RESULT_LAUNCH_USER);
+        finish();
+    }
+
+    private void findCurrent() {
+        CognitoUser user = AppHelper.getPool().getCurrentUser();
+        username = user.getUserId();
+        if(username != null) {
+            AppHelper.setUser(username);
+            inUsername.setText(user.getUserId());
+            user.getSessionInBackground(authenticationHandler);
+        }
+    }
+
+    private void getUserAuthentication(AuthenticationContinuation continuation, String username) {
+        if(username != null) {
+            this.username = username;
+            AppHelper.setUser(username);
+        }
+        if(this.password == null) {
+            inUsername.setText(username);
+            password = inPassword.getText().toString();
+            if(password == null) {
+                Toast.makeText(LoginActivity.this,inPassword.getHint()+" enter password",Toast.LENGTH_SHORT).show();
+                /*
+                TextView label = (TextView) findViewById(R.id.textViewUserPasswordMessage);
+                label.setText(inPassword.getHint()+" enter password");
+                inPassword.setBackground(getDrawable(R.drawable.text_border_error));
+                */
+                return;
+            }
+
+            if(password.length() < 1) {
+                Toast.makeText(LoginActivity.this,inPassword.getHint()+" enter password",Toast.LENGTH_SHORT).show();
+                /*
+                TextView label = (TextView) findViewById(R.id.textViewUserPasswordMessage);
+                label.setText(inPassword.getHint()+" enter password");
+                inPassword.setBackground(getDrawable(R.drawable.text_border_error));
+                */
+                return;
+            }
+        }
+        AuthenticationDetails authenticationDetails = new AuthenticationDetails(this.username, password, null);
+        continuation.setAuthenticationDetails(authenticationDetails);
+        continuation.continueTask();
+    }
+
+    // initialize app
+    private void initApp() {
+        inUsername = (EditText) findViewById(R.id.txtId);
+        inPassword = (EditText) findViewById(R.id.txtPassword);
+    }
+
+
+    // Callbacks
+    ForgotPasswordHandler forgotPasswordHandler = new ForgotPasswordHandler() {
+        @Override
+        public void onSuccess() {
+            DismissConnectDialog(); //closeWaitDialog();
+            showNotiMsg(LoginActivity.this,"비밀번호를 변경하였습니다.");
+            //showDialogMessage("Password successfully changed!","");
+            inPassword.setText("");
+            inPassword.requestFocus();
+        }
+
+        @Override
+        public void getResetCode(ForgotPasswordContinuation forgotPasswordContinuation) {
+            DismissConnectDialog(); //closeWaitDialog();
+            getForgotPasswordCode(forgotPasswordContinuation);
+        }
+
+        @Override
+        public void onFailure(Exception e) {
+            DismissConnectDialog(); //closeWaitDialog();
+            showNotiMsg(LoginActivity.this,"비밀번호를 변경하지 못하였습니다.");
+            //showDialogMessage("Forgot password failed",AppHelper.formatException(e));
+        }
+    };
+
+    //
+    AuthenticationHandler authenticationHandler = new AuthenticationHandler() {
+        @Override
+        public void onSuccess(CognitoUserSession cognitoUserSession, CognitoDevice device) {
+            DebugLogOutput(TAG, " -- Auth Success");
+            SharedPrefUtil.putLong(LOGIN_TIME, System.currentTimeMillis());
+            AppHelper.setCurrSession(cognitoUserSession);
+            AppHelper.newDevice(device);
+            DismissConnectDialog(); //closeWaitDialog();
+            launchUser();
+        }
+
+        @Override
+        public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String username) {
+            DismissConnectDialog(); //closeWaitDialog();
+            Locale.setDefault(Locale.US);
+            getUserAuthentication(authenticationContinuation, username);
+        }
+
+        @Override
+        public void getMFACode(MultiFactorAuthenticationContinuation multiFactorAuthenticationContinuation) {
+            DismissConnectDialog(); //closeWaitDialog();
+            mfaAuth(multiFactorAuthenticationContinuation);
+        }
+
+        @Override
+        public void onFailure(Exception e) {
+            DismissConnectDialog(); //closeWaitDialog();
+            showNotiMsg(LoginActivity.this,"로그인에 실패하였습니다.");
+            //Toast.makeText(LoginActivity.this,"Sign-in failed",Toast.LENGTH_SHORT).show();
+            /*
+            TextView label = (TextView) findViewById(R.id.textViewUserIdMessage);
+            label.setText("Sign-in failed");
+            inPassword.setBackground(getDrawable(R.drawable.text_border_error));
+
+            label = (TextView) findViewById(R.id.textViewUserIdMessage);
+            label.setText("Sign-in failed");
+            inUsername.setBackground(getDrawable(R.drawable.text_border_error));
+            */
+            //showDialogMessage("Sign-in failed", AppHelper.formatException(e));
+        }
+
+        @Override
+        public void authenticationChallenge(ChallengeContinuation continuation) {
+            /**
+             * For Custom authentication challenge, implement your logic to present challenge to the
+             * user and pass the user's responses to the continuation.
+             */
+            if ("NEW_PASSWORD_REQUIRED".equals(continuation.getChallengeName())) {
+                // This is the first sign-in attempt for an admin created user
+                newPasswordContinuation = (NewPasswordContinuation) continuation;
+                AppHelper.setUserAttributeForDisplayFirstLogIn(newPasswordContinuation.getCurrentUserAttributes(),
+                        newPasswordContinuation.getRequiredAttributes());
+                DismissConnectDialog(); //closeWaitDialog();
+                firstTimeSignIn();
+            } else if ("SELECT_MFA_TYPE".equals(continuation.getChallengeName())) {
+                DismissConnectDialog(); //closeWaitDialog();
+                mfaOptionsContinuation = (ChooseMfaContinuation) continuation;
+                List<String> mfaOptions = mfaOptionsContinuation.getMfaOptions();
+                selectMfaToSignIn(mfaOptions, continuation.getParameters());
+            }
+        }
+    };
+
+    private void clearInput() {
+        inUsername.setText("");
+        inUsername.requestFocus();
+        inPassword.setText("");
+    }
+
+    private void showWaitDialog(String message) {
+        closeWaitDialog();
+        waitDialog = new ProgressDialog(this);
+        waitDialog.setTitle(message);
+        waitDialog.show();
+    }
+
+    private void showDialogMessage(String title, String body) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(title).setMessage(body).setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                } catch (Exception e) {
+                    //
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private void closeWaitDialog() {
+        try {
+            waitDialog.dismiss();
+        }
+        catch (Exception e) {
+            //
+        }
+    }
+
+
+    //GOOGLE AUTH
+    /**
+     * Validates that there is a reasonable server client ID in strings.xml, this is only needed
+     * to make sure users of this sample follow the README.
+     */
+    private void validateServerClientID() {
+        String serverClientId = getString(R.string.google_auth_server_client_id);
+        String suffix = ".apps.googleusercontent.com";
+        if (!serverClientId.trim().endsWith(suffix)) {
+            String message = "Invalid server client ID in strings.xml, must end with " + suffix;
+
+            Log.w(TAG, message);
+            Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private void getAuthCode() {
+        // Start the retrieval process for a server auth code.  If requested, ask for a refresh
+        // token.  Otherwise, only get an access token if a refresh token has been previously
+        // retrieved.  Getting a new access token for an existing grant does not require
+        // user consent.
+        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
+        startActivityForResult(signInIntent, RESULT_GOOGE_GET_AUTH_CODE);
+    }
+
+    private void signOut() {
+        mGoogleSignInClient.signOut().addOnCompleteListener(this, new OnCompleteListener<Void>() {
+            @Override
+            public void onComplete(@NonNull Task<Void> task) {
+                updateUI(null);
+            }
+        });
+    }
+
+    private void revokeAccess() {
+        mGoogleSignInClient.revokeAccess().addOnCompleteListener(this,
+                new OnCompleteListener<Void>() {
+                    @Override
+                    public void onComplete(@NonNull Task<Void> task) {
+                        updateUI(null);
+                    }
+                });
+    }
+
+    private void updateUI(@Nullable GoogleSignInAccount account) {
+        if (account != null) {
+            String authCode = account.getServerAuthCode();
+            ErrorLogOutput(TAG,"Auth Code : " + authCode + "\nEmail : "+ account.getEmail() + "\nGivenName : "+ account.getGivenName() + "\nID : "+ account.getId());
+
+        } else {
+            ErrorLogOutput(TAG,"Auth Code : null");
+        }
+    }
+}

+ 1376 - 0
app/src/main/java/kr/co/icontrols/iotplatform/MainActivity.java

@@ -0,0 +1,1376 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Bundle;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserDetails;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttNewMessageCallback;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.UUID;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.InviteInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.adapter.DeviceListAdapter;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.utils.GetSessionCallback;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_HOME;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_SMART_LIGHT;
+import static kr.co.icontrols.iotplatform.PopupActivity.DEFAULT_POPUP_TIMEOUT;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.cancelToExit;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCognitoSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.goToLoginActivity;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isExpiredSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isRestartAppForBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.requestConfirmUser;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.resetBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.restartApp;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.sendConfirmEmail;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_INFO;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.MQTT_PUBLISH_DELAY_TIME;
+import static kr.co.icontrols.iotplatform.utils.Constants.NOTING;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_OFF;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_ON;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INVITE_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_LOGOUT_SUCCESS;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RESEND_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_UPDATE_DATA;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class MainActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "MainActivity";
+
+    private AlertDialog userDialog;
+    private ProgressDialog waitDialog;
+    private ListView attributesList;
+
+    // Cognito user objects
+    private CognitoUser user;
+    private CognitoUserSession session;
+    private CognitoUserDetails details;
+
+    // User details
+    private String username;
+
+    //SMART LIGHT
+    private ArrayList<DeviceInfo> smartLights = null;
+    private LinearLayout layoutSmartLight[];
+    private ImageView imgSmartLightIcon[];
+    private TextView txtSmartLightName[];
+    private TextView txtSmartLightGroupName[];
+    private int smartLightPower[];
+
+    //HOME DEVICE
+    private ArrayList<DeviceInfo> homeDevices = null;
+    private ArrayList<DeviceInfo> rankDevices = null;
+    private ArrayList<InviteInfo> InviteInfos = null;
+
+    private Handler mConnectedHandler;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        init();
+        if(isExpiredSession()) {
+            getCognitoSession(new GetSessionCallback() {
+                @Override
+                public void successed() {
+                    getUserInfo();
+                }
+
+                @Override
+                public void failed() {
+                    showNotiMsg(MainActivity.this, "세션을 갱신하지 못하였습니다.");
+                    System.exit(0);
+                }
+            });
+        } else {
+            getUserInfo();
+        }
+
+        mConnectedHandler = new Handler() {
+            @Override
+            public void handleMessage(@NonNull Message msg) {
+                super.handleMessage(msg);
+                WarnLogOutput(TAG, "mConnectedHandler : is mqtt connect ? " + MqttController.getInstance().isConneted());
+                if(MqttController.getInstance().isConneted()) {
+                    MqttController.getInstance().allUnsubscribeToTopics();
+                    getSmartLightAndHome();
+                } else {
+                    mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if(isRestartAppForBackgroundWaitTime()) {
+            restartApp(this);
+            return;
+        }
+
+        if(UserInfo.deviceCount > 0) {
+            MqttController.getInstance().allUnsubscribeToTopics();
+            getSmartLightAndHome();
+
+            if(!MqttController.getInstance().isConneted()) {
+                mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+            }
+        }
+
+    }
+
+    @Override
+    protected void onPause() {
+        mConnectedHandler.removeCallbacksAndMessages(null);
+        MqttController.getInstance().allUnsubscribeToTopics();
+        setBackgroundWaitTime();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        resetBackgroundWaitTime();
+
+        if(InviteInfos != null) {
+            InviteInfos.clear();
+        }
+
+        if(smartLights != null) {
+            smartLights.clear();
+        }
+
+        if(homeDevices != null) {
+            homeDevices.clear();
+        }
+
+        if(rankDevices != null) {
+            rankDevices.clear();
+        }
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_INVITE_CONFIRM:
+                if(resultCode == RESULT_OK) {
+                    comfirmInvite(true);
+                } else {
+                    comfirmInvite(false);
+                }
+                break;
+            case RESULT_UPDATE_DATA:
+                if(resultCode == RESULT_OK) {
+                    MqttController.getInstance().allUnsubscribeToTopics();
+                    getSmartLightAndHome();
+                }
+                break;
+            case RESULT_RESEND_CONFIRM:
+                if(resultCode == RESULT_OK) {
+                    requestConfirmUser(MainActivity.this);
+                } else {
+                    goToLoginActivity(MainActivity.this);
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        if(!UserInfo.confirmed) {
+            showNotiMsg(this, "인증 후 사용 가능합니다.");
+            return;
+        }
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                break;
+            case R.id.btnRight:
+                //startActivity(new Intent(this, TestHttpApiActivity.class));
+                //startActivity(new Intent(this, AddDeviceListActivity.class));
+                startActivity(new Intent(this,LightRegisterGuideActivity.class));
+                break;
+            case R.id.btnRegisterDevice:
+                //startActivity(new Intent(this, AddDeviceListActivity.class));
+                startActivity(new Intent(this,LightRegisterGuideActivity.class));
+                break;
+            case R.id.btnRegisterLocation:
+                startActivity(new Intent(this, RegisterLocationActivity.class));
+                break;
+            case R.id.btnAddHome:
+                startActivityForResult(new Intent(this, AddHomeActivity.class),RESULT_UPDATE_DATA);
+                break;
+            case R.id.btnSmartLightMore:
+                findViewById(R.id.layoutMore).setVisibility(View.VISIBLE);
+                break;
+            case R.id.btnCloseMore:
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                break;
+            case R.id.layoutMoreAdd:
+                if(smartLights.size() >= 4){
+                    showNotiMsg(MainActivity.this, "스마트 조명은 4개까지만 등록 가능합니다.");
+                } else {
+                    findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                    startActivityForResult(new Intent(this, AddHomeActivity.class),RESULT_UPDATE_DATA);
+                }
+                break;
+            case R.id.layoutMoreRank:
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                if(smartLights.size() > 1) {
+                    Intent intent = new Intent(MainActivity.this, DeviceChangeRankActivity.class);
+                    intent.putExtra(EXTRA_DEVICE_LIST, smartLights);
+                    intent.putExtra("MODE","SMART_LIGHT");
+                    startActivityForResult(intent,RESULT_UPDATE_DATA);
+                } else {
+                    showNotiMsg(MainActivity.this,"스마트 조명 갯수가 2개 이상에만 변경 가능합니다.");
+                }
+                break;
+            case R.id.layoutMoreDelete: {
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                Intent intent = new Intent(MainActivity.this, DeviceSelectActivity.class);
+                intent.putExtra("MODE", "SMART_LIGHT");
+                intent.putExtra(EXTRA_DEVICE_LIST, smartLights);
+                startActivityForResult(intent,RESULT_UPDATE_DATA);
+                }
+                break;
+            case R.id.btnCloseInfo:
+                findViewById(R.id.layoutInfo).setVisibility(View.GONE);
+                break;
+            case R.id.btnWeatherInfo:
+                findViewById(R.id.layoutInfo).setVisibility(View.VISIBLE);
+                break;
+            case R.id.layoutSmartDevice:
+            case R.id.layoutSmartInfo:
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, "서비스 준비중입니다.");
+                startActivity(intent);
+                break;
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        if(findViewById(R.id.layoutInfo).getVisibility() == View.VISIBLE) {
+            findViewById(R.id.layoutInfo).setVisibility(View.GONE);
+        } else if(findViewById(R.id.layoutMore).getVisibility() == View.VISIBLE) {
+            findViewById(R.id.layoutMore).setVisibility(View.GONE);
+        } else {
+            //super.onBackPressed();
+            cancelToExit(this);
+        }
+    }
+
+    // Initialize this activity
+    private void init() {
+        // Get the user name
+        Bundle extras = getIntent().getExtras();
+        username = AppHelper.getCurrUser();
+        user = AppHelper.getPool().getUser(username);
+
+        layoutSmartLight = new LinearLayout[]{findViewById(R.id.layoutSmartLight01),findViewById(R.id.layoutSmartLight02),findViewById(R.id.layoutSmartLight03),findViewById(R.id.layoutSmartLight04)};
+        imgSmartLightIcon = new ImageView[]{findViewById(R.id.imgIcon01),findViewById(R.id.imgIcon02),findViewById(R.id.imgIcon03),findViewById(R.id.imgIcon04)};
+        txtSmartLightName = new TextView[]{findViewById(R.id.txtName01),findViewById(R.id.txtName02),findViewById(R.id.txtName03),findViewById(R.id.txtName04)};
+        txtSmartLightGroupName = new TextView[]{findViewById(R.id.txtGroupName01),findViewById(R.id.txtGroupName02),findViewById(R.id.txtGroupName03),findViewById(R.id.txtGroupName04)};
+        smartLightPower = new int[]{NOTING,NOTING,NOTING,NOTING};
+
+        //스마트 조명 배경 라운드 처리
+        GradientDrawable drawable= (GradientDrawable) getDrawable(R.drawable.layout_round);
+        findViewById(R.id.imgSmartLight).setBackground(drawable);
+        findViewById(R.id.imgSmartLight).setClipToOutline(true);
+
+        //위치정보
+        findViewById(R.id.layoutLocationInfo).setVisibility(View.GONE);
+        findViewById(R.id.btnRegisterLocation).setVisibility(View.VISIBLE);
+
+        //디바이스
+        findViewById(R.id.layoutRegisterDevice).setVisibility(View.VISIBLE);
+        findViewById(R.id.layoutAddHome).setVisibility(View.GONE);
+        findViewById(R.id.layoutSmartLight).setVisibility(View.GONE);
+        findViewById(R.id.listViewDeviceHome).setVisibility(View.GONE);
+        //TODO - 데모코드
+        findViewById(R.id.layoutSmartInfo).setVisibility(View.GONE);
+        findViewById(R.id.layoutSmartDevice).setVisibility(View.GONE);
+        //TODO - 데모코드(스마트 스마트 조명)
+        findViewById(R.id.layoutSmartDevice01).setEnabled(false);
+        findViewById(R.id.layoutSmartDevice02).setEnabled(false);
+    }
+
+    private void setUi() {
+        //위치 정보
+        if(UserInfo.location.size() == 0) {
+            findViewById(R.id.layoutLocationInfo).setVisibility(View.GONE);
+            findViewById(R.id.btnRegisterLocation).setVisibility(View.VISIBLE);
+        } else {
+            findViewById(R.id.layoutLocationInfo).setVisibility(View.VISIBLE);
+            findViewById(R.id.btnRegisterLocation).setVisibility(View.GONE);
+
+            if(!UserInfo.location.get(0).address.city.isEmpty() || !UserInfo.location.get(0).address.district.isEmpty() || !UserInfo.location.get(0).address.town.isEmpty() ) {
+                ((TextView)findViewById(R.id.txtAddress)).setText(UserInfo.location.get(0).address.city + " " + UserInfo.location.get(0).address.district +" " + UserInfo.location.get(0).address.town);
+            }
+
+            if(UserInfo.weatherInfos.size() > 0) {
+                //하늘 상태 * 10 + 강수 형태
+                switch ((Integer.valueOf(UserInfo.weatherInfos.get(0).sky)*10) + Integer.valueOf(UserInfo.weatherInfos.get(0).pty)) {
+                    case 10: //맑음
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_1_1);
+                        break;
+                    case 11: //비
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_4);
+                        break;
+                    case 12: //진눈개비
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_5);
+                        break;
+                    case 13: //눈
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_6);
+                        break;
+                    case 14: //소나기
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_4);
+                        break;
+                    case 15: //빗방울
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_8);
+                        break;
+                    case 16: //빛방울/눈날림
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_9);
+                        break;
+                    case 17: //눈날림
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_10);
+                        break;
+                    case 30: //구름 많음
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_2);
+                        break;
+                    case 31: //구름 많음 + 비
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_7);
+                        break;
+                    case 32: //구름 많음 + 진눈개비
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_11);
+                        break;
+                    case 33: //구름 많음 + 눈
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_12);
+                        break;
+                    case 34: //구름 많음 + 소나기
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_7);
+                        break;
+                    case 35: //구름 많음 + 빗방울
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_13);
+                        break;
+                    case 36: //구름 많음 + 빛방울/눈날림
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_14);
+                        break;
+                    case 37: //구름 많음 + 눈날림
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_15);
+                        break;
+
+                    case 40: //흐림
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_3);
+                        break;
+                    case 41: //흐림 + 비
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_16);
+                        break;
+                    case 42: //흐림 + 진눈개비
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_17);
+                        break;
+                    case 43: //흐림 + 눈
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_18);
+                        break;
+                    case 44: //흐림 + 소나기
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_16);
+                        break;
+                    case 45: //흐림 + 빗방울
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_19);
+                        break;
+                    case 46: //흐림 + 빛방울/눈날림
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_20);
+                        break;
+                    case 47: //흐림 + 눈날림
+                        ((ImageView) findViewById(R.id.imgWeather)).setImageResource(R.drawable.weather_21);
+                        break;
+                    default://UNKNOWN
+                        ((ImageView) findViewById(R.id.imgWeather)).setVisibility(View.INVISIBLE);
+                        break;
+                }
+
+                //온도
+                if(UserInfo.weatherInfos.get(0).t1h.equals("-999")) {
+                    ((TextView) findViewById(R.id.txtTemp)).setText("NO DATE");
+                } else {
+                    ((TextView) findViewById(R.id.txtTemp)).setText(UserInfo.weatherInfos.get(0).t1h);
+                }
+
+                //습도
+                if(UserInfo.weatherInfos.get(0).reh.equals("-999")) {
+                    ((TextView) findViewById(R.id.txtHumi)).setText("NO DATE");
+                } else {
+                    ((TextView) findViewById(R.id.txtHumi)).setText(UserInfo.weatherInfos.get(0).reh);
+                }
+
+                //미세먼지
+                try {
+                    switch (Integer.valueOf(UserInfo.weatherInfos.get(0).pm10Grade)) {
+                        case 1: //좋음
+                            ((LinearLayout) findViewById(R.id.layoutFindDust)).setBackgroundResource(R.drawable.background_dust_level_good);
+                            break;
+                        case 2: //보통
+                            ((LinearLayout) findViewById(R.id.layoutFindDust)).setBackgroundResource(R.drawable.background_dust_level_normal);
+                            break;
+                        case 3: //나쁨
+                            ((LinearLayout) findViewById(R.id.layoutFindDust)).setBackgroundResource(R.drawable.background_dust_level_bad);
+                            break;
+                        case 4: //매우나쁨
+                            ((LinearLayout) findViewById(R.id.layoutFindDust)).setBackgroundResource(R.drawable.background_dust_level_very_bad);
+                            break;
+                        default://UNKNOWN
+                            ((LinearLayout) findViewById(R.id.layoutFindDust)).setBackgroundResource(R.drawable.background_dust_level_unknown);
+                            break;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+                try {
+                    //초미세먼지지
+                    switch (Integer.valueOf(UserInfo.weatherInfos.get(0).pm25Grade)) {
+                        case 1: //좋음
+                            ((LinearLayout) findViewById(R.id.layoutUltraFindDust)).setBackgroundResource(R.drawable.background_dust_level_good);
+                            break;
+                        case 2: //보통
+                            ((LinearLayout) findViewById(R.id.layoutUltraFindDust)).setBackgroundResource(R.drawable.background_dust_level_normal);
+                            break;
+                        case 3: //나쁨
+                            ((LinearLayout) findViewById(R.id.layoutUltraFindDust)).setBackgroundResource(R.drawable.background_dust_level_bad);
+                            break;
+                        case 4: //매우나쁨
+                            ((LinearLayout) findViewById(R.id.layoutUltraFindDust)).setBackgroundResource(R.drawable.background_dust_level_very_bad);
+                            break;
+                        default://UNKNOWN
+                            ((LinearLayout) findViewById(R.id.layoutUltraFindDust)).setBackgroundResource(R.drawable.background_dust_level_unknown);
+                            break;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                //업데이트 날짜시간,
+                ((TextView)findViewById(R.id.txtWeatherUpdateInfo)).setText(UserInfo.weatherInfos.get(0).adDatetime+"(µg/m2)");
+            }
+        }
+
+        //디바이스
+        if(UserInfo.devicesString.size() > 0) {
+            findViewById(R.id.layoutRegisterDevice).setVisibility(View.GONE);
+            findViewById(R.id.layoutAddHome).setVisibility(View.GONE);
+            findViewById(R.id.layoutSmartLight).setVisibility(View.VISIBLE);
+            findViewById(R.id.listViewDeviceHome).setVisibility(View.VISIBLE);
+            //TODO - 데모코드
+            findViewById(R.id.layoutSmartInfo).setVisibility(View.VISIBLE);
+            findViewById(R.id.layoutSmartDevice).setVisibility(View.VISIBLE);
+        } else {
+            findViewById(R.id.layoutRegisterDevice).setVisibility(View.VISIBLE);
+            findViewById(R.id.layoutAddHome).setVisibility(View.GONE);
+            findViewById(R.id.layoutSmartLight).setVisibility(View.GONE);
+            findViewById(R.id.listViewDeviceHome).setVisibility(View.GONE);
+            //TODO - 데모코드
+            findViewById(R.id.layoutSmartInfo).setVisibility(View.GONE);
+            findViewById(R.id.layoutSmartDevice).setVisibility(View.GONE);
+        }
+
+        getSmartLightAndHome();
+    }
+
+    /*
+    private void clearUserInfo() {
+        UserInfo.location.clear();
+        UserInfo.weatherInfos.clear();
+        UserInfo.devicesString.clear();
+        UserInfo.dgroupsString.clear();
+    }
+    */
+
+    private void getUserInfo() {
+
+        UserInfo.getUserInfo(this, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                //Local DB 스마트 조명과 서버에 등록된 스마트 조명 싱크.
+                syncLocalDB();
+                //날씨 정보 획득
+                if(!UserInfo.confirmed) {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            sendConfirmEmail(MainActivity.this);
+                            //showNotiMsg(MainActivity.this, "인증 후 사용 가능합니다.");
+                        }
+                    });
+                } else if(UserInfo.location.size() == 0) {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            setUi();
+                            if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                                sendPushToken();
+                            }
+
+                            if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                                checkInvite();
+                            }
+                        }
+                    });
+                } else {
+                    getWeatherInfo(UserInfo.location.get(0).locationId);
+                }
+            }
+        });
+
+        /*
+        clearUserInfo();
+        displayProgress(this,null,null);
+        try {
+            HttpApi.getUserInfo(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getUserInfo(onFailure) : " + e.toString());
+                    DismissConnectDialog();
+                    showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getUserInfo(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+
+                            UserInfo.uid = json_data.isNull("uid") ? "" : json_data.getString("uid");
+                            UserInfo.email = json_data.isNull("email") ? "" : json_data.getString("email");
+                            UserInfo.phoneNumber = json_data.isNull("phoneNumber") ? "" : json_data.getString("phoneNumber");
+                            UserInfo.nickname = json_data.isNull("nickname") ? "" : json_data.getString("nickname");
+                            UserInfo.profile = json_data.isNull("profile") ? "" : json_data.getString("profile");
+                            UserInfo.confirmed = json_data.isNull("confirmed") ? false : json_data.getBoolean("confirmed");
+                            UserInfo.pushAccess = json_data.isNull("pushAccess") ? false : json_data.getBoolean("pushAccess");
+                            UserInfo.autoUpdate = json_data.isNull("autoUpdate") ? false : json_data.getBoolean("autoUpdate");
+
+                            if(!json_data.isNull("locations")) {
+                                JSONArray jarrayLocationInfo = new JSONArray(json_data.getString("locations"));
+                                for (int i = 0; i < jarrayLocationInfo.length(); i++) {
+                                    JSONObject jObjectLocationInfo = jarrayLocationInfo.getJSONObject(i);
+                                    LocationInfo locationInfo = new LocationInfo();
+
+                                    locationInfo.locationId = jObjectLocationInfo.isNull("locationId") ? "" : jObjectLocationInfo.getString("locationId");
+                                    locationInfo.latitude = jObjectLocationInfo.isNull("latitude") ? 0 : jObjectLocationInfo.getDouble("latitude");
+                                    locationInfo.longitude = jObjectLocationInfo.isNull("longitude") ? 0 : jObjectLocationInfo.getDouble("longitude");
+
+                                    if (!jObjectLocationInfo.isNull("address")) {
+                                        JSONObject jObjectAddressInfo = new JSONObject(jObjectLocationInfo.getString("address"));
+
+                                        locationInfo.address.city = jObjectAddressInfo.isNull("city") ? "" : jObjectAddressInfo.getString("city");
+                                        locationInfo.address.district = jObjectAddressInfo.isNull("district") ? "" : jObjectAddressInfo.getString("district");
+                                        locationInfo.address.town = jObjectAddressInfo.isNull("town") ? "" : jObjectAddressInfo.getString("town");
+                                    }
+                                    UserInfo.location.add(locationInfo);
+                                }
+                            }
+
+                            if(!json_data.isNull("dgroups")) {
+                                JSONObject jObjectDgroupInfo = new JSONObject(json_data.getString("dgroups"));
+                                if(!jObjectDgroupInfo.isNull("results")) {
+                                    JSONArray arrJson = jObjectDgroupInfo.getJSONArray("results");
+                                    for (int i = 0; i < arrJson.length(); i++) {
+                                        UserInfo.dgroupsString.add(arrJson.getString(i));
+                                    }
+                                }
+
+                                UserInfo.dgroupsCount = jObjectDgroupInfo.isNull("count")?0:jObjectDgroupInfo.getInt("count");
+                            }
+
+                            if(!json_data.isNull("devices")) {
+                                JSONObject jObjectDeviceInfo = new JSONObject(json_data.getString("devices"));
+                                if(!jObjectDeviceInfo.isNull("results")) {
+                                    JSONArray arrJson = jObjectDeviceInfo.getJSONArray("results");
+                                    for (int i = 0; i < arrJson.length(); i++) {
+                                        UserInfo.devicesString.add(arrJson.getString(i));
+                                    }
+                                }
+                                UserInfo.deviceCount = jObjectDeviceInfo.isNull("count")?0:jObjectDeviceInfo.getInt("count");
+                            }
+
+                            //날씨 정보 획득
+                            if(!UserInfo.confirmed) {
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        showNotiMsg(MainActivity.this, "인증 후 사용 가능합니다.");
+                                        DismissConnectDialog();
+                                    }
+                                });
+                            } else if(UserInfo.location.size() == 0) {
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        setUi();
+                                        DismissConnectDialog();
+
+                                        if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                                            sendPushToken();
+                                        }
+
+                                        if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                                            checkInvite();
+                                        }
+                                    }
+                                });
+                            } else {
+                                getWeatherInfo(UserInfo.location.get(0).locationId);
+                            }
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getUserInfo(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                            showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+                        DismissConnectDialog();
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getUserInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getUserInfo(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getUserInfo(Exception) : " + e.toString());
+            showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+            DismissConnectDialog();
+        }
+         */
+    }
+
+    private void getWeatherInfo(final String locationId) {
+        UserInfo.getWeatherInfo(MainActivity.this, locationId, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        setUi();
+                        DismissConnectDialog();
+
+                        if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                            sendPushToken();
+                        }
+
+                        if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                            checkInvite();
+                        }
+                    }
+                });
+            }
+        });
+
+        /*
+        UserInfo.weatherInfos.clear();
+        try {
+            HttpApi.getWeatherInfo(locationId, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onFailure) : " + e.toString());
+                    showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            setUi();
+                            DismissConnectDialog();
+
+                            if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                                sendPushToken();
+                            }
+
+                            if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                                checkInvite();
+                            }
+                        }
+                    });
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+
+                            WeatherInfo weatherInfo = new WeatherInfo();
+
+                            weatherInfo.o3Value = json_data.isNull("o3Value") ? "-1" : json_data.getString("o3Value");
+                            weatherInfo.o3Grad = json_data.isNull("o3Grad") ? "-1" : json_data.getString("o3Grad");
+                            weatherInfo.pm10Value = json_data.isNull("pm10Value") ? "-1" : json_data.getString("pm10Value");
+                            weatherInfo.pm10Grade = json_data.isNull("pm10Grade") ? "-1" : json_data.getString("pm10Grade");
+                            weatherInfo.pm25Value = json_data.isNull("pm25Value") ? "-1" : json_data.getString("pm25Value");
+                            weatherInfo.pm25Grade = json_data.isNull("pm25Grade") ? "-1" : json_data.getString("pm25Grade");
+                            weatherInfo.adDatetime = json_data.isNull("adDatetime") ? "-1" : json_data.getString("adDatetime");
+
+                            weatherInfo.sky = json_data.isNull("sky") ? "-1" : json_data.getString("sky");
+                            weatherInfo.pty = json_data.isNull("pty") ? "-1" : json_data.getString("pty");
+                            weatherInfo.rn1 = json_data.isNull("rn1") ? "-1" : json_data.getString("rn1");
+                            weatherInfo.reh = json_data.isNull("reh") ? "-999" : json_data.getString("reh");
+                            weatherInfo.t1h = json_data.isNull("t1h") ? "-999" : json_data.getString("t1h");
+                            weatherInfo.wdDatetime = json_data.isNull("wdDatetime") ? "" : json_data.getString("wdDatetime");
+
+                            UserInfo.weatherInfos.add(weatherInfo);
+
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    setUi();
+                                    DismissConnectDialog();
+
+                                    if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                                        sendPushToken();
+                                    }
+
+                                    if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                                        checkInvite();
+                                    }
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                            showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    setUi();
+                                    DismissConnectDialog();
+
+                                    if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                                        sendPushToken();
+                                    }
+
+                                    if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                                        checkInvite();
+                                    }
+                                }
+                            });
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+                        DismissConnectDialog();
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(onResponse) JSONException : " + e.toString());
+                        }
+
+                        runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                setUi();
+                                DismissConnectDialog();
+
+                                if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                                    sendPushToken();
+                                }
+
+                                if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                                    checkInvite();
+                                }
+                            }
+                        });
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getWeatherInfo(Exception) : " + e.toString());
+            showNotiMsg(MainActivity.this,getString(R.string.snackbar_error_server));
+            DismissConnectDialog();
+
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    setUi();
+                    DismissConnectDialog();
+
+                    if(!UserInfo.isSendPushToken && UserInfo.confirmed) {
+                        sendPushToken();
+                    }
+
+                    if(!UserInfo.isCheckInvite && UserInfo.confirmed) {
+                        checkInvite();
+                    }
+                }
+            });
+        }
+         */
+    }
+
+    private void syncLocalDB() {
+        smartLights = DBUtil.getAllDeviceInfos(TABLE_SMART_LIGHT);
+        homeDevices = DBUtil.getAllDeviceInfos(TABLE_DEVICE_HOME);
+        rankDevices = DBUtil.getAllDeviceInfos(TABLE_DEVICE_RANK);
+
+        //디바이스 서버와 로컬 유무 체크.(스마트 조명)
+        for(int i = 0 ; i < smartLights.size() ; i++) {
+            boolean isExsit = false;
+            for(int j = 0 ; j < UserInfo.devicesString.size() ; j++) {
+                if(smartLights.get(i).deviceId.equals(UserInfo.devicesString.get(j))){
+                    isExsit = true;
+                }
+            }
+            if(!isExsit && smartLights.get(i).sharedFriendlyName.isEmpty()) {
+                DBUtil.removeDeviceInfo(TABLE_SMART_LIGHT,smartLights.get(i));
+                smartLights.remove(i);
+                i--;
+            }
+        }
+
+        //디바이스 서버와 로컬 유무 체크.(즐겨찾기 홈)
+        for(int i = 0 ; i < homeDevices.size() ; i++) {
+            boolean isExsit = false;
+            for(int j = 0 ; j < UserInfo.devicesString.size() ; j++) {
+                if(homeDevices.get(i).deviceId.equals(UserInfo.devicesString.get(j))){
+                    isExsit = true;
+                }
+            }
+            if(!isExsit && homeDevices.get(i).sharedFriendlyName.isEmpty()) {
+                DBUtil.removeDeviceInfo(TABLE_DEVICE_HOME,homeDevices.get(i));
+                homeDevices.remove(i);
+                i--;
+            }
+        }
+
+        //디바이스 서버와 로컬 유무 체크.(스마트 조명 순서)
+        for(int i = 0 ; i < rankDevices.size() ; i++) {
+            boolean isExsit = false;
+            for(int j = 0 ; j < UserInfo.devicesString.size() ; j++) {
+                if(rankDevices.get(i).deviceId.equals(UserInfo.devicesString.get(j))){
+                    isExsit = true;
+                }
+            }
+            if(!isExsit && rankDevices.get(i).sharedFriendlyName.isEmpty()) {
+                DBUtil.removeDeviceInfo(TABLE_DEVICE_RANK,rankDevices.get(i));
+                rankDevices.remove(i);
+                i--;
+            }
+        }
+    }
+
+    private void getSmartLightAndHome() {
+        smartLights = DBUtil.getAllDeviceInfos(TABLE_SMART_LIGHT);
+        homeDevices = DBUtil.getAllDeviceInfos(TABLE_DEVICE_HOME);
+        rankDevices = DBUtil.getAllDeviceInfos(TABLE_DEVICE_RANK);
+
+        if (UserInfo.deviceCount == 0 && smartLights.size() == 0 && homeDevices.size() ==0) {
+            findViewById(R.id.layoutRegisterDevice).setVisibility(View.VISIBLE);
+            findViewById(R.id.layoutAddHome).setVisibility(View.GONE);
+            findViewById(R.id.layoutSmartLight).setVisibility(View.GONE);
+            findViewById(R.id.listViewDeviceHome).setVisibility(View.GONE);
+            //TODO - 데모코드
+            findViewById(R.id.layoutSmartInfo).setVisibility(View.GONE);
+            findViewById(R.id.layoutSmartDevice).setVisibility(View.GONE);
+        } else {
+            if(smartLights.size() == 0 && homeDevices.size() ==0) {
+                findViewById(R.id.layoutRegisterDevice).setVisibility(View.GONE);
+                findViewById(R.id.layoutAddHome).setVisibility(View.VISIBLE);
+                findViewById(R.id.layoutSmartLight).setVisibility(View.GONE);
+                findViewById(R.id.listViewDeviceHome).setVisibility(View.GONE);
+                //TODO - 데모코드
+                findViewById(R.id.layoutSmartInfo).setVisibility(View.GONE);
+                findViewById(R.id.layoutSmartDevice).setVisibility(View.GONE);
+            } else {
+                findViewById(R.id.layoutRegisterDevice).setVisibility(View.GONE);
+                findViewById(R.id.layoutAddHome).setVisibility(View.GONE);
+
+                if(smartLights.size() > 0) {
+                    findViewById(R.id.layoutSmartLight).setVisibility(View.VISIBLE);
+                    //TODO - 데모코드
+                    findViewById(R.id.layoutSmartInfo).setVisibility(View.VISIBLE);
+                    findViewById(R.id.layoutSmartDevice).setVisibility(View.VISIBLE);
+
+                    for (int i = 0; i < layoutSmartLight.length; i++) {
+                        layoutSmartLight[i].setVisibility(View.INVISIBLE);
+                        layoutSmartLight[i].setEnabled(false);
+                    }
+
+                    for (int i = 0; (i < smartLights.size()) && (i < layoutSmartLight.length); i++) {
+                        final int pos = i;
+                        layoutSmartLight[i].setVisibility(View.VISIBLE);
+                        layoutSmartLight[i].setEnabled(false);
+                        txtSmartLightName[i].setText(smartLights.get(i).friendlyName.isEmpty()?smartLights.get(i).sharedFriendlyName.isEmpty()?getString(R.string.no_name):smartLights.get(i).sharedFriendlyName:smartLights.get(i).friendlyName);
+                        txtSmartLightGroupName[i].setText(smartLights.get(i).dgroupName);
+
+                        final String topicGetP = String.format(getString(R.string.topic_get_status),smartLights.get(i).deviceType,smartLights.get(i).deviceId);
+                        final String topicGetS1 = String.format(getString(R.string.topic_response_status),smartLights.get(i).deviceType,smartLights.get(i).deviceId);
+                        final String topicUpdateP = String.format(getString(R.string.topic_update),smartLights.get(i).deviceId);
+                        final String topicUpdateS1 = String.format(getString(R.string.topic_update_accepted),smartLights.get(i).deviceId);
+                        final String topicUpdateS2 = String.format(getString(R.string.topic_update_delta),smartLights.get(i).deviceId);
+
+                        layoutSmartLight[i].setOnClickListener(new View.OnClickListener() {
+                            @Override
+                            public void onClick(View v) {
+                                if(smartLightPower[pos] == NOTING) {
+                                    showNotiMsg(MainActivity.this,"조명이 연결되지 않았습니다.");
+                                } else if(smartLightPower[pos] == POWER_ON) { //켜진상태
+                                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"power\" : 0}}}");
+                                    layoutSmartLight[pos].setSelected(false);
+                                } else /*if(power == POWER_OFF)*/ { //꺼진상태
+                                    MqttController.getInstance().publish(topicUpdateP,"{\"state\": {\"desired\" : {\"power\" : 1}}}");
+                                    layoutSmartLight[pos].setSelected(true);
+                                }
+                            }
+                        });
+
+                        MqttController.getInstance().subscribeToTopic(topicGetS1, new AWSIotMqttNewMessageCallback() {
+                            @Override
+                            public void onMessageArrived(String topic, byte[] data) {
+                                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                                MqttController.getInstance().unsubscribeToTopic(topicGetS1);
+
+                                int temp_power = NOTING;
+
+                                try {
+                                    JSONObject jsonObject = new JSONObject(new String(data));
+
+                                    if(!jsonObject.isNull("data")) {//Get status topic
+                                        JSONObject jsonState = jsonObject.getJSONObject("data");
+                                        if (!jsonState.isNull("state")) {
+                                            JSONObject jsonReported = jsonState.getJSONObject("state");
+                                            //Power
+                                            if (!jsonReported.isNull("power")) {
+                                                //power off
+                                                temp_power = jsonReported.getInt("power");
+
+                                            }
+                                        }
+                                    }
+                                } catch (JSONException e) {
+                                    e.printStackTrace();
+                                }
+
+                                final int powerFix = temp_power;
+
+                                if(temp_power != NOTING) {
+                                    smartLightPower[pos] = temp_power;
+                                }
+
+                                DebugLogOutput(TAG, "LIGHT_STATUS - power : "+powerFix);
+
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        if(smartLightPower[pos] == NOTING) {
+                                            layoutSmartLight[pos].setEnabled(false);
+                                            layoutSmartLight[pos].setSelected(false);
+                                        } else if(smartLightPower[pos] == POWER_ON) {
+                                            imgSmartLightIcon[pos].setImageResource(R.drawable.icon_home_device_light);
+                                            layoutSmartLight[pos].setEnabled(true);
+                                            layoutSmartLight[pos].setSelected(true);
+                                        } else if(smartLightPower[pos] == POWER_OFF) {
+                                            imgSmartLightIcon[pos].setImageResource(R.drawable.icon_home_device_light);
+                                            layoutSmartLight[pos].setEnabled(true);
+                                            layoutSmartLight[pos].setSelected(false);
+                                        }
+                                    }
+                                });
+                            }
+                        });
+
+                        MqttController.getInstance().subscribeToTopic(topicUpdateS1, new AWSIotMqttNewMessageCallback() {
+                            @Override
+                            public void onMessageArrived(String topic, byte[] data) {
+                                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+
+                                int temp_power = NOTING;
+
+                                try {
+                                    JSONObject jsonObject = new JSONObject(new String(data));
+
+                                    if(!jsonObject.isNull("data")) {//Get status topic
+                                        JSONObject jsonState = jsonObject.getJSONObject("data");
+                                        if (!jsonState.isNull("state")) {
+                                            JSONObject jsonReported = jsonState.getJSONObject("state");
+                                            //Power
+                                            if (!jsonReported.isNull("power")) {
+                                                //power off
+                                                temp_power = jsonReported.getInt("power");
+
+                                            }
+                                        }
+                                    } else if(!jsonObject.isNull("state")) {  //Update topic
+                                        JSONObject jsonState = jsonObject.getJSONObject("state");
+                                        if (!jsonState.isNull("reported")) {
+                                            JSONObject jsonReported = jsonState.getJSONObject("reported");
+                                            //Power
+                                            if (!jsonReported.isNull("power")) {
+                                                //power off
+                                                temp_power = jsonReported.getInt("power");
+
+                                            }
+                                        }
+
+                                        if (!jsonState.isNull("desired")) {
+                                            JSONObject jsonDesired = jsonState.getJSONObject("desired");
+                                            //Power
+                                            if (!jsonDesired.isNull("power")) {
+                                                //power off
+                                                temp_power = jsonDesired.getInt("power");
+
+                                            }
+                                        }
+
+                                        if(!jsonState.isNull("power")) {
+                                            temp_power = jsonState.getInt("power");
+                                        }
+                                    }
+                                } catch (JSONException e) {
+                                    e.printStackTrace();
+                                }
+
+                                final int powerFix = temp_power;
+
+                                if(temp_power != NOTING) {
+                                    smartLightPower[pos] = temp_power;
+                                }
+
+                                DebugLogOutput(TAG, "LIGHT_STATUS - power : "+powerFix);
+
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        if(smartLightPower[pos] == NOTING) {
+                                            layoutSmartLight[pos].setEnabled(false);
+                                            layoutSmartLight[pos].setSelected(false);
+                                        } else if(smartLightPower[pos] == POWER_ON) {
+                                            layoutSmartLight[pos].setEnabled(true);
+                                            layoutSmartLight[pos].setSelected(true);
+                                        } else if(smartLightPower[pos] == POWER_OFF) {
+                                            layoutSmartLight[pos].setEnabled(true);
+                                            layoutSmartLight[pos].setSelected(false);
+                                        }
+                                    }
+                                });
+                            }
+                        });
+
+                        MqttController.getInstance().subscribeToTopic(topicUpdateS2, new AWSIotMqttNewMessageCallback() {
+                            @Override
+                            public void onMessageArrived(String topic, byte[] data) {
+                                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                                //TODO 오류 발생 로직 구현.
+                            }
+                        });
+
+                        new Handler().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                if(smartLights != null && smartLights.size() > pos) {
+
+                                    MqttController.getInstance().publish(topicGetP, "{\"msgId\" : \"" + UUID.randomUUID().toString() + "\"}");
+                                    //MqttController.getInstance().publish(topicGetP, "{\"msgId\" : \"" + smartLights.get(pos).deviceId + "\"}");
+                                }
+                            }
+                        },MQTT_PUBLISH_DELAY_TIME);
+                    }
+                } else {
+                    findViewById(R.id.layoutSmartLight).setVisibility(View.GONE);
+                    //TODO - 데모코드
+                    findViewById(R.id.layoutSmartInfo).setVisibility(View.GONE);
+                    findViewById(R.id.layoutSmartDevice).setVisibility(View.GONE);
+                }
+
+                if(homeDevices.size() > 0) {
+                    findViewById(R.id.listViewDeviceHome).setVisibility(View.VISIBLE);
+                    ListView listView = findViewById(R.id.listViewDeviceHome);
+                    DeviceListAdapter deviceListAdapter = new DeviceListAdapter(this, homeDevices);
+                    listView.setAdapter(deviceListAdapter);
+                    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                        @Override
+                        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                            final DeviceInfo item = (DeviceInfo) parent.getItemAtPosition(position);
+                            Intent intent = new Intent(MainActivity.this,LightDetailActivity.class);
+                            intent.putExtra(EXTRA_DEVICE_INFO, (Parcelable) item);
+                            startActivity(intent);
+                        }
+                    });
+                } else {
+                    findViewById(R.id.listViewDeviceHome).setVisibility(View.GONE);
+                }
+            }
+        }
+    }
+
+    private void sendPushToken() {
+        final String pushToken = SharedPrefUtil.getString(SharedPrefUtil.PUSH_TOKEN,SharedPrefUtil.DEFAULT_PUSH_TOKEN);
+        if(!pushToken.isEmpty()) {
+            try {
+                HttpApi.changeUserInfo("", "", "", UserInfo.pushAccess, UserInfo.autoUpdate, pushToken, new Callback() {
+                    @Override
+                    public void onFailure(Call call, IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onFailure)\n" + e.toString());
+                    }
+
+                    @Override
+                    public void onResponse(Call call, Response response) throws IOException {
+                        if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                            WarnLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) HTTP_NO_CONTENT");
+                            UserInfo.isSendPushToken = true;
+                        } else {
+                            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) body : " + json_data.toString());
+                            } catch (JSONException e) {
+                                ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) JSONException : " + e.toString());
+                            }
+                        }
+                    }
+                });
+            } catch (Exception e) {
+                e.printStackTrace();
+                ErrorLogOutput(TAG, "HttpApi.changeUserInfo(Exception)\n" + e.toString());
+            }
+        }
+    }
+
+
+    private void checkInvite() {
+
+        if(InviteInfos == null) {
+            InviteInfos = new ArrayList<>();
+        } else {
+            InviteInfos.clear();
+        }
+
+        try {
+            HttpApi.getHistoryForInvite(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getHistoryForInvite(onFailure)\n" + e.toString());
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if (response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getHistoryForInvite(onResponse) HTTP_NO_CONTENT");
+                        UserInfo.isCheckInvite = true;
+
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                            for (int i = 0; i < jarrayAll.length(); i++) {
+                                JSONObject jObject = jarrayAll.getJSONObject(i);
+                                InviteInfo item = new InviteInfo(
+                                        jObject.isNull("histId") ? "" : jObject.getString("histId"),
+                                        jObject.isNull("inviteType") ? "" : jObject.getString("inviteType"),
+                                        jObject.isNull("sender") ? "" : jObject.getString("sender"),
+                                        jObject.isNull("target") ? "" : jObject.getString("target"),
+                                        jObject.isNull("inviteDate") ? "" : jObject.getString("inviteDate"),
+                                        jObject.isNull("expireDate") ? "" : jObject.getString("expireDate"));
+
+                                SimpleDateFormat transFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+                                Date expireDate = transFormat.parse(item.expireDate);
+
+                                if(System.currentTimeMillis() < expireDate.getTime()) {
+                                    InviteInfos.add(item);
+                                }
+                            }
+
+                            if(InviteInfos.size() > 0) {
+                                Intent intent = new Intent(MainActivity.this, PopupActivity.class);
+                                intent.putExtra(PopupActivity.POPUP_MODE,PopupActivity.MODE_YESNO);
+                                intent.putExtra(PopupActivity.POPUP_BODY_TEXT,String.format(getString(R.string.popup_invite_you),InviteInfos.get(0).sender));
+                                intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT,getString(R.string.reject));
+                                intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT,getString(R.string.accept));
+                                startActivityForResult(intent,RESULT_INVITE_CONFIRM);
+                            }
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getHistoryForInvite(JSONException)\n" + e.toString());
+                        } catch (ParseException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getHistoryForInvite(ParseException)\n" + e.toString());
+                        }
+
+
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getHistoryForInvite(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getHistoryForInvite(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getHistoryForInvite(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getHistoryForInvite(Exception)\n" + e.toString());
+        }
+    }
+
+    private void comfirmInvite(final boolean yes) {
+        try {
+            HttpApi.acceptToInvite(InviteInfos.get(0).histId, yes, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.acceptToInvite(onFailure)\n" + e.toString());
+
+                    if(yes) {
+                        showNotiMsg(MainActivity.this, "수락을 하지못하였습니다.");
+                    } else {
+                        showNotiMsg(MainActivity.this, "거절을 하지못하였습니다.");
+                    }
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if (response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.acceptToInvite(onResponse) HTTP_NO_CONTENT");
+
+                        if(yes) {
+                            showNotiMsg(MainActivity.this, "수락 하였습니다.");
+                        } else {
+                            showNotiMsg(MainActivity.this, "거절 하였습니다.");
+                        }
+
+                        InviteInfos.remove(0);
+                        if(InviteInfos.size() > 0) {
+                            Intent intent = new Intent(MainActivity.this, PopupActivity.class);
+                            intent.putExtra(PopupActivity.POPUP_MODE,PopupActivity.MODE_YESNO);
+                            intent.putExtra(PopupActivity.POPUP_BODY_TEXT,String.format(getString(R.string.popup_invite_you),InviteInfos.get(0).sender));
+                            intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT,getString(R.string.reject));
+                            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT,getString(R.string.accept));
+                            startActivityForResult(intent,RESULT_INVITE_CONFIRM);
+                        }
+
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.acceptToInvite(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        if(yes) {
+                            showNotiMsg(MainActivity.this, "수락을 하지못하였습니다.");
+                        } else {
+                            showNotiMsg(MainActivity.this, "거절을 하지못하였습니다.");
+                        }
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.acceptToInvite(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.acceptToInvite(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.acceptToInvite(Exception)\n" + e.toString());
+            if(yes) {
+                showNotiMsg(MainActivity.this, "수락을 하지못하였습니다.");
+            } else {
+                showNotiMsg(MainActivity.this, "거절을 하지못하였습니다.");
+            }
+        }
+    }
+
+    private void exit () {
+        Intent intent = new Intent();
+        if(username == null)
+            username = "";
+        intent.putExtra("name",username);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+}

+ 180 - 0
app/src/main/java/kr/co/icontrols/iotplatform/MainMenu.java

@@ -0,0 +1,180 @@
+package kr.co.icontrols.iotplatform;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+
+/**
+ * 메인 메뉴 컴퍼넌트.
+ */
+public class MainMenu extends LinearLayout {
+
+    private String TAG = MainMenu.class.getSimpleName();
+    private static Class<?> mOldActivity = null;
+
+    public static final int MAIN_MENU_HOME = 1;
+    public static final int MAIN_MENU_DEVICE = 2;
+    public static final int MAIN_MENU_MY_GROUP = 3;
+    public static final int MAIN_MENU_PROFILE = 4;
+
+    private int mSelectedIndex = 0;
+    private Context mContext;
+    private static boolean mIsStartNextActivity = false;
+
+    public MainMenu(Context context) {
+        this(context,null);
+        mContext = context;
+    }
+
+    public MainMenu(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+
+        mContext = context;
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MainMenu, 0, 0);
+        mSelectedIndex = a.getInteger(R.styleable.MainMenu_selectedIndex,0);
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.main_menu, this, true);
+
+        initComponents(mSelectedIndex);
+    }
+
+    private void initComponents(int index) {
+        switch (index) {
+            case MAIN_MENU_HOME:
+                findViewById(R.id.layoutMenu01).setSelected(true);
+                findViewById(R.id.layoutMenu02).setSelected(false);
+                findViewById(R.id.layoutMenu03).setSelected(false);
+                findViewById(R.id.layoutMenu04).setSelected(false);
+                findViewById(R.id.txtMenu01).setSelected(true);
+                findViewById(R.id.txtMenu02).setSelected(false);
+                findViewById(R.id.txtMenu03).setSelected(false);
+                findViewById(R.id.txtMenu04).setSelected(false);
+                break;
+            case MAIN_MENU_DEVICE:
+                findViewById(R.id.layoutMenu01).setSelected(false);
+                findViewById(R.id.layoutMenu02).setSelected(true);
+                findViewById(R.id.layoutMenu03).setSelected(false);
+                findViewById(R.id.layoutMenu04).setSelected(false);
+                findViewById(R.id.txtMenu01).setSelected(false);
+                findViewById(R.id.txtMenu02).setSelected(true);
+                findViewById(R.id.txtMenu03).setSelected(false);
+                findViewById(R.id.txtMenu04).setSelected(false);
+                break;
+            case MAIN_MENU_MY_GROUP:
+                findViewById(R.id.layoutMenu01).setSelected(false);
+                findViewById(R.id.layoutMenu02).setSelected(false);
+                findViewById(R.id.layoutMenu03).setSelected(true);
+                findViewById(R.id.layoutMenu04).setSelected(false);
+                findViewById(R.id.txtMenu01).setSelected(false);
+                findViewById(R.id.txtMenu02).setSelected(false);
+                findViewById(R.id.txtMenu03).setSelected(true);
+                findViewById(R.id.txtMenu04).setSelected(false);
+                break;
+            case MAIN_MENU_PROFILE:
+                findViewById(R.id.layoutMenu01).setSelected(false);
+                findViewById(R.id.layoutMenu02).setSelected(false);
+                findViewById(R.id.layoutMenu03).setSelected(false);
+                findViewById(R.id.layoutMenu04).setSelected(true);
+                findViewById(R.id.txtMenu01).setSelected(false);
+                findViewById(R.id.txtMenu02).setSelected(false);
+                findViewById(R.id.txtMenu03).setSelected(false);
+                findViewById(R.id.txtMenu04).setSelected(true);
+                break;
+            default:
+                findViewById(R.id.layoutMenu01).setSelected(false);
+                findViewById(R.id.layoutMenu02).setSelected(false);
+                findViewById(R.id.layoutMenu03).setSelected(false);
+                findViewById(R.id.layoutMenu04).setSelected(false);
+                findViewById(R.id.txtMenu01).setSelected(false);
+                findViewById(R.id.txtMenu02).setSelected(false);
+                findViewById(R.id.txtMenu03).setSelected(false);
+                findViewById(R.id.txtMenu04).setSelected(false);
+                break;
+        }
+
+        findViewById(R.id.layoutMenu01).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if(mSelectedIndex != MAIN_MENU_HOME) {
+                    changeActvityForMainMenu(MAIN_MENU_HOME, getContext());
+                }
+            }
+        });
+        findViewById(R.id.layoutMenu02).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if(mSelectedIndex != MAIN_MENU_DEVICE) {
+                    changeActvityForMainMenu(MAIN_MENU_DEVICE, getContext());
+                }
+            }
+        });
+        findViewById(R.id.layoutMenu03).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if(mSelectedIndex != MAIN_MENU_MY_GROUP) {
+                    changeActvityForMainMenu(MAIN_MENU_MY_GROUP, getContext());
+                }
+            }
+        });
+        findViewById(R.id.layoutMenu04).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if(mSelectedIndex != MAIN_MENU_PROFILE) {
+                    changeActvityForMainMenu(MAIN_MENU_PROFILE, getContext());
+                }
+            }
+        });
+    }
+
+    private void changeActvityForMainMenu(int index, final Context context) {
+        if(!UserInfo.confirmed) {
+            return;
+        }
+
+        mIsStartNextActivity = true;
+        mOldActivity = ((Activity) context).getClass();
+        Intent intent;
+        switch (index) {
+            case MAIN_MENU_HOME:
+                intent = new Intent(context, MainActivity.class);
+                break;
+            case MAIN_MENU_DEVICE:
+                intent = new Intent(context, DeviceActivity.class);
+                break;
+            case MAIN_MENU_MY_GROUP:
+                intent = new Intent(context, MyGroupActivity.class);
+                break;
+            case MAIN_MENU_PROFILE:
+                intent = new Intent(context, ProfileActivity.class);
+                break;
+            default:
+                intent = new Intent(context, MainActivity.class);
+                break;
+        }
+        context.startActivity(intent);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        ((Activity) context).overridePendingTransition(0, 0);
+        new Timer().schedule(new TimerTask() {
+            @Override
+            public void run() {
+                ((Activity) context).finish();
+                mIsStartNextActivity = false;
+            }
+        }, 500);
+
+    }
+}

+ 169 - 0
app/src/main/java/kr/co/icontrols/iotplatform/Mqtt/MqttController.java

@@ -0,0 +1,169 @@
+package kr.co.icontrols.iotplatform.Mqtt;
+
+import android.app.Activity;
+import android.content.Context;
+import android.widget.Toast;
+
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.internal.StaticCredentialsProvider;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttClientStatusCallback;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttManager;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttNewMessageCallback;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttQos;
+import com.amazonaws.regions.Region;
+import com.amazonaws.regions.Regions;
+
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.IotPlatformApplication;
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.TestIotMqttActivity;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+
+public class MqttController {
+
+    private static String TAG = "MqttController";
+    private static String COGNITO_POOL_ID = "ap-northeast-2:0e617292-1b9c-4208-b64b-b2d03729b25b";
+    private static String TOPIC = "androidDemoTopic";
+    private static String ACCOUNT_END_POINT_PREFIX = "a139udwm0q3u52-ats";
+    private static String END_POINT = "wss://a139udwm0q3u52-ats.iot.ap-northeast-2.amazonaws.com";
+    private static String ACCESS_KEY = "AKIAUSTWEOGGE4XFHBWL";
+    private static String SECRET_KEY = "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";
+
+    private static int STATUS_DISCONNECTED = 0;
+    private static int STATUS_CONNECTING = 1;
+    private static int STATUS_RECONNECTING = 2;
+    private static int STATUS_CONNECTED = 3;
+
+    private static MqttController instance;
+
+    private static AWSCredentials awsCredentials;
+    private static AWSCredentialsProvider credentialsProvider;
+    //private static CognitoCachingCredentialsProvider credentialsProvider;
+    private static AWSIotMqttManager mqttManager;
+
+    private ArrayList<String> topicList = new ArrayList<>();
+
+    private static int status_mqtt = STATUS_DISCONNECTED;
+
+    public static MqttController getInstance() {
+        return instance;
+    }
+
+    public static void init(Context context) {
+        if (instance == null) {
+            instance = new MqttController();
+        }
+
+        /*
+        //Amazon Cognito 인증 공급자를 초기화합니다
+        credentialsProvider = new CognitoCachingCredentialsProvider(
+                getApplicationContext(),
+                COGNITO_POOL_ID, // 자격 증명 풀 ID
+                Regions.AP_NORTHEAST_2 // 리전
+        );
+        */
+
+        //Amazon Cognito 인증 공급자를 초기화합니다
+        credentialsProvider = new StaticCredentialsProvider(
+                new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
+
+        //intialize unnqique clientid as client to iot aws
+        Long tsLong = System.currentTimeMillis()/1000;
+        String clientId = tsLong.toString();
+
+        mqttManager = new AWSIotMqttManager(
+                clientId/*"android_iot_app_test_mqtt"*/,
+                Region.getRegion(Regions.AP_NORTHEAST_2),
+                ACCOUNT_END_POINT_PREFIX
+        );
+
+        mqttManager.setCleanSession(true);
+        mqttManager.setKeepAlive(30);
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                awsCredentials = credentialsProvider.getCredentials();
+
+                if(credentialsProvider != null && status_mqtt == STATUS_DISCONNECTED) {
+                    // connect to AWS IoT using credentials provider
+                    mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
+                        @Override
+                        public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
+                            if (status == AWSIotMqttClientStatus.Connecting) {
+                                status_mqtt = STATUS_CONNECTING;
+                                DebugLogOutput(TAG, "connecting");
+                            } else if (status == AWSIotMqttClientStatus.Connected) {
+                                status_mqtt = STATUS_CONNECTED;
+                                DebugLogOutput(TAG, "connected");
+                            } else if (status == AWSIotMqttClientStatus.Reconnecting) {
+                                if (throwable != null) {
+                                    DebugLogOutput(TAG, "Connection error(Reconnecting). / " + throwable);
+                                }
+                                status_mqtt = STATUS_RECONNECTING;
+                            } else if (status == AWSIotMqttClientStatus.ConnectionLost) {
+                                if (throwable != null) {
+                                    DebugLogOutput(TAG, "Connection error(ConnectionLost). / " + throwable);
+                                    throwable.printStackTrace();
+                                }
+                                status_mqtt = STATUS_DISCONNECTED;
+                            } else {
+                                status_mqtt = STATUS_DISCONNECTED;
+                            }
+                        }
+                    });
+                }
+            }
+        }).start();
+    }
+
+    public void subscribeToTopic(final String topic,final AWSIotMqttNewMessageCallback callback) {
+        DebugLogOutput(TAG, "MQTT call subscribeToTopic : " + topic);
+        if(isConneted()) {
+            // subscribe to MQTT topic
+            mqttManager.subscribeToTopic(topic, AWSIotMqttQos.QOS0, callback);
+            topicList.add(topic);
+        } else {
+            DebugLogOutput(TAG, "MQTT NOT CONNECTED(subscribeToTopic)");
+        }
+
+    }
+
+    public void unsubscribeToTopic(String topic) {
+        DebugLogOutput(TAG, "MQTT call unsubscribeToTopic : " + topic);
+        if(isConneted()) {
+            // unsubscribe to MQTT topic
+            mqttManager.unsubscribeTopic(topic);
+            topicList.remove(topic);
+        } else {
+            DebugLogOutput(TAG, "MQTT NOT CONNECTED(unsubscribeToTopic)");
+        }
+    }
+
+    public void allUnsubscribeToTopics() {
+        DebugLogOutput(TAG, "MQTT call allUnsubscribeToTopics");
+        if(isConneted()) {
+            // unsubscribe to MQTT topic
+            for(String s : topicList) {
+                mqttManager.unsubscribeTopic(s);
+            }
+            topicList.clear();
+        } else {
+            DebugLogOutput(TAG, "MQTT NOT CONNECTED(allUnsubscribeToTopics)");
+        }
+    }
+
+    public void publish(final String topic, final String payload){
+
+        mqttManager.publishString(payload, topic, AWSIotMqttQos.QOS0);
+        DebugLogOutput(TAG, "MQTT PUBLISH / " + topic + "\n" + payload);
+    }
+
+    public boolean isConneted() {
+        return status_mqtt == STATUS_CONNECTED;
+    }
+}

+ 688 - 0
app/src/main/java/kr/co/icontrols/iotplatform/MyGroupActivity.java

@@ -0,0 +1,688 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.viewpager.widget.ViewPager;
+
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.google.android.material.tabs.TabLayout;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.adapter.DeviceListAdapter;
+import kr.co.icontrols.iotplatform.adapter.MyGroupTabPagerAdapter;
+import kr.co.icontrols.iotplatform.utils.GetSessionCallback;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static com.amazonaws.mobile.auth.core.internal.util.ThreadUtils.runOnUiThread;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_IMG_INDEX;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.cancelToExit;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCognitoSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupBackgroundString;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageString;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isExpiredSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isRestartAppForBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.resetBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.restartApp;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.DEFAULT_RANK;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_BEFORE_ACTIVITY;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_LIST;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_GROUP_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHANGE_GROUP_IMG;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_GROUP_NAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_UPDATE_DATA;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_UNAUTHORIZED;
+
+public class MyGroupActivity extends AppCompatActivity implements View.OnClickListener {
+    private final String TAG = "MyGroupActivity";
+    private TabLayout mTabGroup;
+    private ViewPager mViewPager;
+    final ArrayList<DeviceInfo> deviceInfoList = new ArrayList<>();
+    private Handler mConnectedHandler;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_my_group);
+
+        mTabGroup = findViewById(R.id.tabGroup);
+        mViewPager = findViewById(R.id.viewPager);
+
+        if(!UserInfo.confirmed) {
+            showNotiMsg(this, "인증 후 사용 가능합니다.");
+        } else if(isExpiredSession()) {
+            getCognitoSession(new GetSessionCallback() {
+                @Override
+                public void successed() {
+                    getMyDeviceGroup();
+                }
+
+                @Override
+                public void failed() {
+                    showNotiMsg(MyGroupActivity.this, "세션을 갱신하지 못하였습니다.");
+                    System.exit(0);
+                }
+            });
+        } else {
+            getMyDeviceGroup();
+        }
+
+        mConnectedHandler = new Handler() {
+            @Override
+            public void handleMessage(@NonNull Message msg) {
+                super.handleMessage(msg);
+                WarnLogOutput(TAG, "mConnectedHandler : is mqtt connect ? " + MqttController.getInstance().isConneted());
+                if(MqttController.getInstance().isConneted()) {
+                    if(mViewPager.getAdapter() != null) {
+                        MqttController.getInstance().allUnsubscribeToTopics();
+                        ((MyGroupTabPagerAdapter) mViewPager.getAdapter()).notifyDataSetChanged();
+                    }
+                } else {
+                    mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if(isRestartAppForBackgroundWaitTime()) {
+            restartApp(this);
+            return;
+        }
+
+        if(mViewPager.getAdapter() != null) {
+            MqttController.getInstance().allUnsubscribeToTopics();
+            ((MyGroupTabPagerAdapter) mViewPager.getAdapter()).notifyDataSetChanged();
+
+            if(!MqttController.getInstance().isConneted()) {
+                mConnectedHandler.sendEmptyMessageDelayed(1,1000);
+            }
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        mConnectedHandler.removeCallbacksAndMessages(null);
+        MqttController.getInstance().allUnsubscribeToTopics();
+        ForceDismissConnectDialog();
+        setBackgroundWaitTime();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        resetBackgroundWaitTime();
+        if(deviceInfoList != null) {
+            deviceInfoList.clear();
+        }
+        super.onDestroy();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_INPUT_GROUP_NAME:
+                if (resultCode == RESULT_OK) {
+                    Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                    if(b != null) {
+                        try {
+                            String newValue = b.getString(PopupActivity.POPUP_INPUT);
+                            if(!newValue.equals(UserInfo.dgroups.get(mViewPager.getCurrentItem()).friendlyName)) {
+                                changeGroup(newValue,"","");
+                            }
+                        } catch (Exception e) {
+                            // Log failure
+                            showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_fail_change_group));
+                        }
+                    } else {
+                        showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_fail_change_group));
+                    }
+                }
+                break;
+            case RESULT_CHANGE_GROUP_IMG:
+                if (resultCode == RESULT_OK) {
+                    final int selected_index = data.getIntExtra(POPUP_IMG_INDEX, 1);
+                    changeGroup("",getGroupImageString(selected_index),getGroupBackgroundString(selected_index));
+                }
+                break;
+            case RESULT_UPDATE_DATA:
+                if (resultCode == RESULT_OK) {
+
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        if(!UserInfo.confirmed) {
+            showNotiMsg(this, "인증 후 사용 가능합니다.");
+            return;
+        }
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnRight:
+                findViewById(R.id.layoutMore).setVisibility(View.VISIBLE);
+                break;
+            case R.id.btnCloseMore:
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                break;
+            case R.id.btnAddGroup:
+            case R.id.layoutMoreAdd: {
+                Intent intent = new Intent(this, AddGroupActivity.class);
+                intent.putExtra(EXTRA_GROUP_LIST, UserInfo.dgroups);
+                intent.putExtra(EXTRA_BEFORE_ACTIVITY,"MyGroupActivity");
+                startActivity(intent);
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                }
+                break;
+            case R.id.layoutMoreRank:
+                if(UserInfo.dgroups.size() > 1) {
+                    Intent intent = new Intent(MyGroupActivity.this, GroupChangeRankActivity.class);
+                    intent.putExtra(EXTRA_GROUP_LIST, UserInfo.dgroups);
+                    startActivity(intent);
+                } else {
+                    showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_error_not_found_group_for_changing));
+                }
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                break;
+            case R.id.layoutMoreDelete:
+                if(UserInfo.dgroups.size() > 1) {
+                    UserInfo.setUnSelectAllGroups();
+                    Intent intent = new Intent(MyGroupActivity.this, GroupSelectActivity.class);
+                    intent.putExtra(EXTRA_GROUP_LIST, UserInfo.dgroups);
+                    intent.putExtra("MODE","DELETE");
+                    startActivity(intent);
+                } else {
+                    showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_error_not_found_group_for_deleting));
+                }
+                findViewById(R.id.layoutMore).setVisibility(View.GONE);
+                break;
+            case R.id.btnCloseGroupSetting:
+                findViewById(R.id.layoutGroupSetting).setVisibility(View.GONE);
+                break;
+            case R.id.layoutGroupSettingAdd:
+                findViewById(R.id.layoutGroupSetting).setVisibility(View.GONE);
+                goToAddDeviceForGroup();
+                break;
+            case R.id.layoutGroupSettingRank:
+                displayProgress(this, "","");
+                deviceInfoList.clear();
+                try{
+                    HttpApi.getDeviceListInDeviceGroup(UserInfo.dgroups.get(mViewPager.getCurrentItem()).dgroupId, new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onFailure)\n" + e.toString());
+                            showNotiMsg(MyGroupActivity.this,"순서 변경에 실패하였습니다.");
+                            DismissConnectDialog();
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HTTP_OK) {
+                                WarnLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) : HTTP_OK");
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    JSONArray jarrayAll = new JSONArray(json_data.getString("devices"));
+
+                                    for (int i = 0; i < jarrayAll.length(); i++) {
+                                        JSONObject jObject = jarrayAll.getJSONObject(i);
+                                        DeviceInfo item = new DeviceInfo(
+                                                jObject.isNull("deviceId") ? "" : jObject.getString("deviceId"),
+                                                jObject.isNull("deviceType") ? "" : jObject.getString("deviceType"),
+                                                jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                                jObject.isNull("sharedFriendlyName") ? "" : jObject.getString("sharedFriendlyName"),
+                                                jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                                jObject.isNull("dgroupName") ? "" : jObject.getString("dgroupName"),
+                                                jObject.isNull("ownerId") ? "" : jObject.getString("ownerId"),
+                                                DEFAULT_RANK,
+                                                DEFAULT_RANK
+                                        );
+
+                                        DeviceInfo localDbItem = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK, item.deviceId);
+                                        if (localDbItem != null) {
+                                            item.rank = localDbItem.rank;
+                                            item.groupRank = localDbItem.groupRank;
+                                        }
+
+                                        deviceInfoList.add(item);
+                                    }
+
+                                    if(deviceInfoList.size() > 1) {
+                                        for(int i = 0 ; i < deviceInfoList.size()-1 ; i ++) {
+                                            for(int j = i+1 ; j < deviceInfoList.size() ; j ++) {
+                                                if(deviceInfoList.get(i).groupRank > deviceInfoList.get(j).groupRank) {
+                                                    DeviceInfo item = deviceInfoList.get(j);
+                                                    deviceInfoList.remove(j);
+                                                    deviceInfoList.add(i,item);
+                                                }
+                                            }
+                                        }
+
+                                        Intent intent = new Intent(MyGroupActivity.this, DeviceChangeRankActivity.class);
+                                        intent.putExtra(EXTRA_DEVICE_LIST, deviceInfoList);
+                                        intent.putExtra("MODE","GROUP_RANK");
+                                        startActivityForResult(intent,RESULT_UPDATE_DATA);
+                                    } else {
+                                        showNotiMsg(MyGroupActivity.this,"순서 변경할 스마트 조명이 존재하지 않습니다.");
+                                    }
+                                } catch (JSONException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(JSONException)\n" + e.toString());
+                                    showNotiMsg(MyGroupActivity.this,"순서 변경에 실패하였습니다.");
+                                    DismissConnectDialog();
+                                }
+                            } else if(response.code() == HTTP_UNAUTHORIZED) {
+                                showNotiMsg(MyGroupActivity.this, getString(R.string.snackbar_expired_token));
+                                restartApp(MyGroupActivity.this);
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) JSONException : " + e.toString());
+                                }
+                                showNotiMsg(MyGroupActivity.this,"순서 변경에 실패하였습니다.");
+                                DismissConnectDialog();
+                            }
+                        }
+                    });
+                } catch(Exception e){
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(Exception)\n" + e.toString());
+                    DismissConnectDialog();
+                }
+                findViewById(R.id.layoutGroupSetting).setVisibility(View.GONE);
+                break;
+            case R.id.layoutGroupSettingMove:
+                findViewById(R.id.layoutGroupSetting).setVisibility(View.GONE);
+                break;
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        if(findViewById(R.id.layoutMore).getVisibility() == View.VISIBLE) {
+            findViewById(R.id.layoutMore).setVisibility(View.GONE);
+        } else {
+            //super.onBackPressed();
+            cancelToExit(this);
+        }
+    }
+
+    private void showSettingMenu() {
+        ((TextView)findViewById(R.id.txtGroupSettingTitle)).setText(UserInfo.dgroups.get(mViewPager.getCurrentItem()).friendlyName+" "+getString(R.string.edit));
+        findViewById(R.id.layoutGroupSetting).setVisibility(View.VISIBLE);
+    }
+
+    private void goToAddDeviceForGroup() {
+        UserInfo.getDevice(this, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        UserInfo.setUnSelectAllDevices();
+                        Intent intent = new Intent(MyGroupActivity.this, DeviceSelectActivity.class);
+                        intent.putExtra("MODE", "MOVE_GROUP");
+                        intent.putExtra(EXTRA_DEVICE_LIST, UserInfo.devices);
+                        intent.putExtra(EXTRA_GROUP_ID, UserInfo.dgroups.get(mViewPager.getCurrentItem()).dgroupId);
+                        intent.putExtra(EXTRA_GROUP_NAME, UserInfo.dgroups.get(mViewPager.getCurrentItem()).friendlyName);
+                        startActivityForResult(intent,RESULT_UPDATE_DATA);
+                    }
+                });
+            }
+        });
+
+        /*
+        if(UserInfo.devices.size() > 0) {
+            Intent intent = new Intent(MyGroupActivity.this, DeviceSelectActivity.class);
+            intent.putExtra("MODE", "MOVE_GROUP");
+            intent.putExtra(EXTRA_DEVICE_LIST, UserInfo.devices);
+            intent.putExtra(EXTRA_GROUP_ID, UserInfo.dgroups.get(mViewPager.getCurrentItem()).dgroupId);
+            intent.putExtra(EXTRA_GROUP_NAME, UserInfo.dgroups.get(mViewPager.getCurrentItem()).friendlyName);
+            startActivityForResult(intent,RESULT_UPDATE_DATA);
+        } else {
+            UserInfo.devices.clear();
+            displayProgress(this, null,null);
+            try{
+                HttpApi.getAllDevice(new Callback() {
+                    @Override
+                    public void onFailure(Call call, IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.getAllDevice(onFailure)\n" + e.toString());
+                        DismissConnectDialog();
+                    }
+
+                    @Override
+                    public void onResponse(Call call, Response response) throws IOException {
+                        if(response.code() == HTTP_OK) {
+                            WarnLogOutput(TAG, "HttpApi.getAllDevice(onResponse) : HTTP_OK");
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+
+                                UserInfo.deviceCount = json_data.isNull("count") ? 0 : json_data.getInt("count");
+
+                                if(UserInfo.deviceCount > 0) {
+                                    JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                                    for (int i = 0; i < jarrayAll.length(); i++) {
+                                        JSONObject jObject = jarrayAll.getJSONObject(i);
+                                        DeviceInfo item = new DeviceInfo(
+                                                jObject.isNull("deviceId") ? "" : jObject.getString("deviceId"),
+                                                jObject.isNull("deviceType") ? "" : jObject.getString("deviceType"),
+                                                jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                                jObject.isNull("sharedFriendlyName") ? "" : jObject.getString("sharedFriendlyName"),
+                                                jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                                jObject.isNull("dgroupName") ? "" : jObject.getString("dgroupName"),
+                                                jObject.isNull("ownerId") ? "" : jObject.getString("ownerId"),
+                                                DEFAULT_RANK
+                                        );
+
+                                        DeviceInfo localDbItem = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK, item.deviceId);
+                                        if (localDbItem == null) {
+                                            DBUtil.putDeviceInfo(TABLE_DEVICE_RANK, item);
+                                        } else {
+                                            item.rank = localDbItem.rank;
+                                            DBUtil.updateDeviceItem(TABLE_DEVICE_RANK, item);
+                                        }
+                                    }
+
+                                    ArrayList<DeviceInfo> deviceInfos = DBUtil.getAllDeviceInfos(TABLE_DEVICE_RANK);
+                                    for (DeviceInfo d : deviceInfos) {
+                                        UserInfo.devices.add(d);
+                                    }
+                                    deviceInfos.clear();
+                                }
+
+                                Intent intent = new Intent(MyGroupActivity.this, DeviceSelectActivity.class);
+                                intent.putExtra("MODE", "MOVE_GROUP");
+                                intent.putExtra(EXTRA_DEVICE_LIST, UserInfo.devices);
+                                intent.putExtra(EXTRA_GROUP_ID, UserInfo.dgroups.get(mViewPager.getCurrentItem()).dgroupId);
+                                intent.putExtra(EXTRA_GROUP_NAME, UserInfo.dgroups.get(mViewPager.getCurrentItem()).friendlyName);
+                                startActivityForResult(intent,RESULT_UPDATE_DATA);
+
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.getAllDevice(JSONException)\n" + e.toString());
+                                DismissConnectDialog();
+                            }
+                        } else {
+                            ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                            DismissConnectDialog();
+                            try {
+                                JSONObject json_data = new JSONObject(response.body().string());
+                                ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) body : " + json_data.toString());
+                            } catch (JSONException e) {
+                                ErrorLogOutput(TAG, "HttpApi.getAllDevice(onResponse) JSONException : " + e.toString());
+                            }
+                        }
+                    }
+                });
+            } catch(Exception e){
+                e.printStackTrace();
+                ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(Exception)\n" + e.toString());
+                DismissConnectDialog();
+            }
+        }
+         */
+    }
+
+    public void getMyDeviceGroup(){
+        UserInfo.getGroup(this, new UserInfo.CallbackForUpdate() {
+            @Override
+            public void updateData() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (UserInfo.dgroups.size() == 0) {
+                            ErrorLogOutput(TAG, "mGroupList size : " + UserInfo.dgroups.size());
+                        } else {
+                            mViewPager.setAdapter(new MyGroupTabPagerAdapter(MyGroupActivity.this, UserInfo.dgroups, new MyGroupTabPagerAdapter.PressSettingButton() {
+                                @Override
+                                public void showGroupSettingMenu() {
+                                    showSettingMenu();
+                                }
+
+                                @Override
+                                public void goToAddDevice() {
+                                    goToAddDeviceForGroup();
+                                }
+                            }));
+                            mTabGroup.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+                                @Override
+                                public void onTabSelected(TabLayout.Tab tab) {
+                                    LinearLayout tabLayout = (LinearLayout) ((ViewGroup) mTabGroup.getChildAt(0)).getChildAt(tab.getPosition());
+                                    TextView tabTextView = (TextView) tabLayout.getChildAt(1);
+                                    tabTextView.setTypeface(tabTextView.getTypeface(), Typeface.BOLD);
+                                    mViewPager.setCurrentItem(tab.getPosition());
+                                }
+
+                                @Override
+                                public void onTabUnselected(TabLayout.Tab tab) {
+                                    LinearLayout tabLayout = (LinearLayout) ((ViewGroup) mTabGroup.getChildAt(0)).getChildAt(tab.getPosition());
+                                    TextView tabTextView = (TextView) tabLayout.getChildAt(1);
+                                    tabTextView.setTypeface(tabTextView.getTypeface(), Typeface.NORMAL);
+                                }
+
+                                @Override
+                                public void onTabReselected(TabLayout.Tab tab) {
+                                }
+                            });
+                            for (DeviceGroupInfo info : UserInfo.dgroups) {
+                                mTabGroup.addTab(mTabGroup.newTab().setText(info.friendlyName));
+                            }
+                            mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabGroup));
+                        }
+                    }
+                });
+            }
+        });
+
+        /*
+        UserInfo.dgroups.clear();
+        displayProgress(this,"","");
+        try {
+            HttpApi.getDeviceGroupList(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onFailure)\n" + e.toString());
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) : HTTP_OK");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            JSONArray jarrayAll = new JSONArray(json_data.getString("results"));
+                            for (int i = 0; i < jarrayAll.length(); i++) {
+                                JSONObject jObject = jarrayAll.getJSONObject(i);
+                                DeviceGroupInfo item = new DeviceGroupInfo(jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                        jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                        jObject.isNull("icon") ? "" : jObject.getString("icon"),
+                                        jObject.isNull("index") ? 99 : jObject.getInt("index"));
+
+                                UserInfo.dgroups.add(item);
+                            }
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if(UserInfo.dgroups.size() == 0) {
+                                        ErrorLogOutput(TAG, "mGroupList size : " + UserInfo.dgroups.size());
+                                    } else {
+                                        mViewPager.getAdapter().notifyDataSetChanged();
+                                        for(DeviceGroupInfo info : UserInfo.dgroups) {
+                                            mTabGroup.addTab(mTabGroup.newTab().setText(info.friendlyName));
+                                        }
+                                    }
+                                    DismissConnectDialog();
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(JSONException)\n" + e.toString());
+                            DismissConnectDialog();
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(onResponse) JSONException : " + e.toString());
+                        }
+                        DismissConnectDialog();
+                    }
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceGroupList(Exception)\n" + e.toString());
+            DismissConnectDialog();
+        }
+         */
+    }
+
+    private void changeGroup(final String name,final String icon, String background) {
+        displayProgress(this,"","");
+        try {
+            HttpApi.modifyDeviceGroup(UserInfo.dgroups.get(mViewPager.getCurrentItem()).dgroupId,
+                    name,
+                    icon,
+                    background,
+                    new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.modifyDeviceGroup(onFailure)\n" + e.toString());
+                            showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_fail_change_group));
+                            DismissConnectDialog();
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                                WarnLogOutput(TAG, "HttpApi.modifyDeviceGroup(onResponse) HTTP_NO_CONTENT");
+
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        runOnUiThread(new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_successed_change_group));
+                                                if(!name.isEmpty()) {
+                                                    UserInfo.dgroups.get(mViewPager.getCurrentItem()).friendlyName = name;
+                                                    //TODO - 그룹 이름 갱신 로직 구현해야함.
+                                                }
+                                                if(!icon.isEmpty()) {
+                                                    UserInfo.dgroups.get(mViewPager.getCurrentItem()).icon = icon;
+                                                    //TODO - 그룹 이미지 갱신 로직 구현해야함.
+                                                }
+                                                if(!background.isEmpty()) {
+                                                    //TODO - 백그라운드 이미지 저장
+                                                }
+
+                                                DismissConnectDialog();
+                                                ((MyGroupTabPagerAdapter)mViewPager.getAdapter()).notifyDataSetChanged();
+                                            }
+                                        });
+                                    }
+                                });
+                            } else if(response.code() == HTTP_UNAUTHORIZED) {
+                                showNotiMsg(MyGroupActivity.this, getString(R.string.snackbar_expired_token));
+                                restartApp(MyGroupActivity.this);
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.modifyDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_fail_change_group));
+                                DismissConnectDialog();
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.modifyDeviceGroup(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.modifyDeviceGroup(onResponse) JSONException : " + e.toString());
+                                }
+                            }
+                        }
+                    });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.modifyDeviceGroup(Exception)\n" + e.toString());
+            showNotiMsg(MyGroupActivity.this,getString(R.string.snackbar_fail_change_group));
+            DismissConnectDialog();
+        }
+    }
+}

+ 182 - 0
app/src/main/java/kr/co/icontrols/iotplatform/NewPasswordActivity.java

@@ -0,0 +1,182 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserAttributes;
+
+import kr.co.icontrols.iotplatform.adapter.FirstTimeLoginAttributesDisplayAdapter;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isPasswordValid;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setupUI;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+
+public class NewPasswordActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private EditText txtPassword, txtComfirmPassword;
+    private AlertDialog userDialog;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_new_password);
+
+        setupUI(this,findViewById(R.id.layoutMain));
+
+        txtPassword = findViewById(R.id.txtPassword);
+        txtComfirmPassword = findViewById(R.id.txtComfirmPassword);
+
+        refreshItemsDisplayed();
+    }
+
+    @Override
+    public void onBackPressed() {
+        exit(false);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnOk:
+                if(txtPassword == null || txtPassword.getText().toString().isEmpty()) {
+                    Toast.makeText(this,getString(R.string.input_password),Toast.LENGTH_SHORT).show();
+                } else if(txtComfirmPassword == null || txtComfirmPassword.getText().toString().isEmpty()) {
+                    Toast.makeText(this,getString(R.string.input_password_comfirm),Toast.LENGTH_SHORT).show();
+                } else if(!(txtPassword.getText().toString().equals(txtComfirmPassword.getText().toString()))) {
+                    Toast.makeText(this,getString(R.string.not_same_password),Toast.LENGTH_SHORT).show();
+                } else if(!isPasswordValid(txtPassword.getText().toString())) {
+                    Toast.makeText(this,getString(R.string.invalid_password),Toast.LENGTH_SHORT).show();
+                } else {
+                    String newUserPassword = txtPassword.getText().toString();
+                    if (newUserPassword != null) {
+                        AppHelper.setPasswordForFirstTimeLogin(newUserPassword);
+                        if (checkAttributes()) {
+                            exit(true);
+                        }
+                    }
+                    showNotiMsg(NewPasswordActivity.this, "비밀번호 변경 중 오류가 발생하였습니다."); //showDialogMessage("Error", "Enter all required attributed", false);
+                }
+                break;
+        }
+    }
+
+    private void refreshItemsDisplayed() {
+        final FirstTimeLoginAttributesDisplayAdapter attributesAdapter =
+                new FirstTimeLoginAttributesDisplayAdapter(getApplicationContext());
+        final ListView displayListView;
+        displayListView = (ListView) findViewById(R.id.listViewCurrentUserDetails);
+        displayListView.setAdapter(attributesAdapter);
+        displayListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                TextView data = (TextView) view.findViewById(R.id.editTextUserDetailInput);
+                String attributeType = data.getHint().toString();
+                String attributeValue = data.getText().toString();
+                showAttributeDetail(attributeType, attributeValue);
+            }
+        });
+    }
+
+    private void showAttributeDetail(final String attributeType, final String attributeValue) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(attributeType);
+        final EditText input = new EditText(NewPasswordActivity.this);
+        input.setText(attributeValue);
+
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.MATCH_PARENT);
+
+        input.setLayoutParams(lp);
+        input.requestFocus();
+        builder.setView(input);
+
+        builder.setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    String newValue = input.getText().toString();
+                    if (!newValue.equals(attributeValue)) {
+                        AppHelper.setUserAttributeForFirstTimeLogin(attributeType, newValue);
+                        refreshItemsDisplayed();
+                    }
+                    userDialog.dismiss();
+                } catch (Exception e) {
+                    // Log failure
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private boolean checkAttributes() {
+        // Check if all required attributes have values
+        return true;
+    }
+
+    private void showDialogMessage(String title, String body, final boolean exit) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(title).setMessage(body).setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                    if (exit) {
+                        exit(false);
+                    }
+                } catch (Exception e) {
+                    exit(false);
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private void exit(Boolean continueWithSignIn) {
+        Intent intent = new Intent();
+        intent.putExtra("continueSignIn", continueWithSignIn);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+}

+ 47 - 0
app/src/main/java/kr/co/icontrols/iotplatform/NotificationActivity.java

@@ -0,0 +1,47 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+import android.view.View;
+
+public class NotificationActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_notification);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+        }
+    }
+}

+ 147 - 0
app/src/main/java/kr/co/icontrols/iotplatform/PermissionActivity.java

@@ -0,0 +1,147 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.View;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.PERMISSIONS;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.PERMISSION_ALL;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_PERMISSION_AGREE;
+
+public class PermissionActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_permission);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if(hasPermissions(this, PERMISSIONS)){
+            goToLoginActivity();
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_PERMISSION_AGREE:
+                if(resultCode == RESULT_OK) {
+                    Intent intent = new Intent();
+                    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                    Uri uri = Uri.fromParts("package", getPackageName(), null);
+                    intent.setData(uri);
+                    startActivity(intent);
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        if(requestCode == PERMISSION_ALL){
+            boolean isGoToLoginActivity = true;
+            for(int i = 0;i<permissions.length;i++){
+                if(permissions[i].equals(android.Manifest.permission.CALL_PHONE) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(Manifest.permission.READ_EXTERNAL_STORAGE) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(android.Manifest.permission.CAMERA) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(android.Manifest.permission.ACCESS_FINE_LOCATION) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                }
+            }
+
+            if(isGoToLoginActivity){
+                goToLoginActivity();
+            } else {
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE,PopupActivity.MODE_YESNO);
+                intent.putExtra(PopupActivity.POPUP_BODY_TEXT,getString(R.string.popup_set_permission));
+                intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT,getString(R.string.setting));
+                startActivityForResult(intent,RESULT_PERMISSION_AGREE);
+            }
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e){
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+
+
+        switch (v.getId()) {
+            case R.id.btnPermission:
+                if(needRequestRuntimePermissions()) {
+                    if (!hasPermissions(this, PERMISSIONS)) {
+                        ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
+                    } else {
+                        goToLoginActivity();
+                    }
+                } else {
+                    goToLoginActivity();
+                }
+                break;
+        }
+    }
+
+    private boolean needRequestRuntimePermissions() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
+    }
+
+    public static boolean hasPermissions(Context context, String... permissions) {
+        if (context != null && permissions != null) {
+            for (String permission : permissions) {
+                if (ActivityCompat.checkSelfPermission(context, permission) != PERMISSION_GRANTED) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private void goToLoginActivity(){
+        Intent intent = new Intent(this, LoginActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        finish();
+    }
+}

+ 146 - 0
app/src/main/java/kr/co/icontrols/iotplatform/PhoneNumChangeActivity.java

@@ -0,0 +1,146 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static android.content.Intent.EXTRA_PHONE_NUMBER;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setupUI;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+
+public class PhoneNumChangeActivity extends AppCompatActivity implements View.OnClickListener, TextWatcher {
+
+    private String TAG = "PhoneNumChangeActivity";
+    private EditText txtPhoneNum;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_phone_num_change);
+
+        setupUI(this,findViewById(R.id.layoutMain));
+
+        txtPhoneNum = findViewById(R.id.txtPhoneNum);
+        txtPhoneNum.addTextChangedListener(this);
+        txtPhoneNum.setText(getIntent().getStringExtra(EXTRA_PHONE_NUMBER));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnOk:
+                changePhoneNum(txtPhoneNum.getText().toString());
+                break;
+        }
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        if(txtPhoneNum.length() > 0) {
+            findViewById(R.id.btnOk).setEnabled(true);
+        } else {
+            findViewById(R.id.btnOk).setEnabled(false);
+        }
+    }
+
+    private void changePhoneNum(final String phoneNumber) {
+        try {
+            displayProgress(PhoneNumChangeActivity.this, "","");
+            HttpApi.changeUserInfo("", "", phoneNumber, UserInfo.pushAccess, UserInfo.autoUpdate, "", new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onFailure)\n" + e.toString());
+                    showNotiMsg(PhoneNumChangeActivity.this, getString(R.string.snackbar_fail_change_hp));
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) HTTP_NO_CONTENT");
+                        UserInfo.phoneNumber = phoneNumber;
+                        showNotiMsg(PhoneNumChangeActivity.this, getString(R.string.snackbar_successed_change_hp),true);
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) JSONException : " + e.toString());
+                        }
+                        showNotiMsg(PhoneNumChangeActivity.this, getString(R.string.snackbar_fail_change_hp));
+                    }
+                    DismissConnectDialog();
+                }
+            });
+        } catch (Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(Exception)\n" + e.toString());
+            DismissConnectDialog();
+            showNotiMsg(PhoneNumChangeActivity.this, getString(R.string.snackbar_fail_change_hp));
+        }
+    }
+}

+ 574 - 0
app/src/main/java/kr/co/icontrols/iotplatform/PopupActivity.java

@@ -0,0 +1,574 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.InputFilter;
+import android.text.Spannable;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.ForegroundColorSpan;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCircleBitmap;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageResource;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setupUI;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_01;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_02;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_03;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_04;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_05;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_06;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_07;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_08;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_09;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_00_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_01_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_02_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_03_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_04_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_05_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_06_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_07_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_08_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_09_STRING;
+
+public class PopupActivity extends AppCompatActivity implements View.OnClickListener {
+    /**
+     * 일반적인 팝업
+     */
+    public final static int MODE_NORMAL = 1;
+    /**
+     * Yes, No 버튼이 포함된 팝업
+     */
+    public final static int MODE_YESNO = 2;
+    /**
+     * 버튼 없는 팝업
+     */
+    public final static int MODE_NO_BUTTON = 3;
+    /**
+     * 일반적인 팝업(아이콘 표시)
+     */
+    public final static int MODE_NORMAL_WITH_ICON = 4;
+    /**
+     * Yes, No 버튼이 포함된 팝업(아이콘 표시)
+     */
+    public final static int MODE_YESNO_WITH_ICON = 5;
+    /**
+     * Yes, No 버튼이 포함된 팝업(체크박스 표시)
+     */
+    public final static int MODE_YESNO_WITH_CHK = 6;
+    /**
+     * 자동 로그인 팝업
+     */
+    public final static int MODE_AUTO_LOGIN = 7;
+    /**
+     * 입력박스 팝업
+     */
+    public final static int MODE_INPUT = 8;
+    /**
+     * 그룹이미지 팝업
+     */
+    public final static int MODE_SELECT_GROUP_IMG = 9;
+
+    /**
+     * Bluetooth ON 팝업
+     */
+    public final static int MODE_ENABLE_BT = 10;
+
+    /**
+     * 재실모드 시간 설정 팝업
+     */
+    public final static int MODE_SET_JAESIL_TIME = 11;
+
+    public final static String POPUP_MODE = "popup_mode";
+    public final static String POPUP_TITLE_TEXT = "popup_title_text";
+    public final static String POPUP_BODY_TEXT = "popup_body_text";
+    public final static String POPUP_BODY_RED_MARK = "popup_body_mark";
+    public final static String POPUP_OK_BUTTON_TEXT = "popup_ok_button_text";
+    public final static String POPUP_CANCEL_BUTTON_TEXT = "popup_cancel_button_text";
+    public final static String POPUP_TIMEOUT = "popup_timeout";
+    public final static String POPUP_BUNDLE = "popup_bundle";
+    public final static String POPUP_INPUT = "popup_input";
+    public final static String POPUP_INPUT_MIN = "popup_input_min";
+    public final static String POPUP_INPUT_MAX = "popup_input_max";
+    public final static String POPUP_IMG_INDEX = "popup_img_index";
+    public final static String POPUP_TIME_INDEX = "popup_time_index";
+
+    public final static int DEFAULT_POPUP_TIMEOUT = (2 * 1000);
+
+
+    private Timer mTimer;
+    private Handler mHandler;
+
+    private int mEntryMode;
+    private Activity mActivity;
+    private RelativeLayout mConfirmBtn;
+    private RelativeLayout mCancelBtn;
+    private TextView mTxt_popup_ok, mTxt_popup_cancel;
+    private String mBodyText;
+    private String mTitleText;
+    private EditText mTxtInput;
+    private int mTimeout;
+    private Bundle mBundle;
+    private int mSelectedImgIndex = 0;
+    private int mSelectedTime = 0;
+    private int inputMinLenght = 0;
+    private int inputMaxLenght = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mEntryMode = 0;
+        mActivity = this;
+
+        Intent intent = getIntent();
+        mEntryMode = intent.getIntExtra(POPUP_MODE, 0);
+        mTimeout = intent.getIntExtra(POPUP_TIMEOUT, 0);
+        mBundle = intent.getBundleExtra(POPUP_BUNDLE);
+
+        if(mEntryMode == MODE_NORMAL || mEntryMode == MODE_YESNO || mEntryMode == MODE_NO_BUTTON || mEntryMode == MODE_NORMAL_WITH_ICON || mEntryMode == MODE_YESNO_WITH_ICON || mEntryMode == MODE_YESNO_WITH_CHK) {
+            setContentView(R.layout.activity_popup);
+
+            mTitleText = intent.getStringExtra(POPUP_TITLE_TEXT);
+            TextView txtTitle = (TextView) findViewById(R.id.txtTitle);
+            if(mTitleText == null || mTitleText.isEmpty()){
+                txtTitle.setVisibility(View.GONE);
+            } else {
+                txtTitle.setVisibility(View.VISIBLE);
+                txtTitle.setText(mTitleText);
+            }
+            mBodyText = intent.getStringExtra(POPUP_BODY_TEXT);
+            TextView txtBody = (TextView) findViewById(R.id.txtBody);
+            if(mBodyText == null || mBodyText.isEmpty()){
+                txtBody.setVisibility(View.GONE);
+            } else {
+                txtBody.setVisibility(View.VISIBLE);
+                txtBody.setText(mBodyText);
+            }
+
+            if(mEntryMode == MODE_NORMAL_WITH_ICON || mEntryMode == MODE_YESNO_WITH_ICON) {
+                findViewById(R.id.imgWarning).setVisibility(View.VISIBLE);
+                findViewById(R.id.chkDontShow).setVisibility(View.GONE);
+            } else if(mEntryMode == MODE_YESNO_WITH_CHK) {
+                findViewById(R.id.chkDontShow).setVisibility(View.VISIBLE);
+                findViewById(R.id.imgWarning).setVisibility(View.GONE);
+            } else {
+                findViewById(R.id.imgWarning).setVisibility(View.GONE);
+                findViewById(R.id.chkDontShow).setVisibility(View.GONE);
+            }
+
+            if(mTitleText == null || mTitleText.length() == 0) {
+                txtTitle.setVisibility(View.GONE);
+            } else {
+                txtTitle.setVisibility(View.VISIBLE);
+            }
+        } else if(mEntryMode == MODE_AUTO_LOGIN) {
+            setContentView(R.layout.activity_popup_auto_login);
+
+            final LinearLayout layoutAutoLogin = findViewById(R.id.layoutAutoLogin);
+            final ImageView imgAutoLogin = findViewById(R.id.imgAutoLogin);
+
+            layoutAutoLogin.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    boolean isAutoLogin = SharedPrefUtil.getBoolean(SharedPrefUtil.AUTO_LOGIN, SharedPrefUtil.DEFAULT_AUTO_LOGIN);
+                    if(isAutoLogin) {
+                        layoutAutoLogin.setBackgroundResource(R.drawable.button_white_line_gray);
+                        imgAutoLogin.setImageResource(R.drawable.ui_chk);
+                    } else {
+                        layoutAutoLogin.setBackgroundResource(R.drawable.button_yellow);
+                        imgAutoLogin.setImageResource(R.drawable.ui_chk_chked);
+                    }
+                    SharedPrefUtil.putBoolean(SharedPrefUtil.AUTO_LOGIN,!isAutoLogin);
+                }
+            });
+
+            if(SharedPrefUtil.getBoolean(SharedPrefUtil.AUTO_LOGIN, SharedPrefUtil.DEFAULT_AUTO_LOGIN)) {
+                layoutAutoLogin.setBackgroundResource(R.drawable.button_yellow);
+                imgAutoLogin.setImageResource(R.drawable.ui_chk_chked);
+            } else {
+                layoutAutoLogin.setBackgroundResource(R.drawable.button_white_line_gray);
+                imgAutoLogin.setImageResource(R.drawable.ui_chk);
+            }
+        } else if(MODE_INPUT == mEntryMode){
+            setContentView(R.layout.activity_popup_input);
+            //setupUI(this, findViewById(R.id.layoutMain));
+
+            inputMinLenght = intent.getIntExtra(POPUP_INPUT_MIN, 0);
+            inputMaxLenght = intent.getIntExtra(POPUP_INPUT_MAX,0);
+
+            mTxtInput = findViewById(R.id.txtInput);
+
+            mTitleText = intent.getStringExtra(POPUP_TITLE_TEXT);
+            TextView txtTitle = (TextView) findViewById(R.id.txtTitle);
+            if(mTitleText == null || mTitleText.isEmpty()){
+                txtTitle.setVisibility(View.GONE);
+            } else {
+                txtTitle.setVisibility(View.VISIBLE);
+                txtTitle.setText(mTitleText);
+            }
+            mBodyText = intent.getStringExtra(POPUP_BODY_TEXT);
+            TextView txtBody = (TextView) findViewById(R.id.txtBody);
+            if(mBodyText == null || mBodyText.isEmpty()){
+                txtBody.setVisibility(View.GONE);
+            } else {
+                txtBody.setVisibility(View.VISIBLE);
+                txtBody.setText(mBodyText);
+            }
+
+            if(mBundle!=null) {
+                String strInput = mBundle.getString(POPUP_INPUT);
+                mTxtInput.setText(strInput);
+            } else {
+                mTxtInput.setText("");
+            }
+        } else if(MODE_SELECT_GROUP_IMG == mEntryMode) {
+            setContentView(R.layout.activity_popup_select_group_img);
+
+            ((ImageView)findViewById(R.id.img01)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_01_STRING)));
+            ((ImageView)findViewById(R.id.img02)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_02_STRING)));
+            ((ImageView)findViewById(R.id.img03)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_03_STRING)));
+            ((ImageView)findViewById(R.id.img04)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_04_STRING)));
+            ((ImageView)findViewById(R.id.img05)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_05_STRING)));
+            ((ImageView)findViewById(R.id.img06)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_06_STRING)));
+            ((ImageView)findViewById(R.id.img07)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_07_STRING)));
+            ((ImageView)findViewById(R.id.img08)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_08_STRING)));
+            ((ImageView)findViewById(R.id.img09)).setImageBitmap(getCircleBitmap(getGroupImageResource(GROUP_IMG_09_STRING)));
+
+
+            mTitleText = intent.getStringExtra(POPUP_TITLE_TEXT);
+            TextView txtTitle = (TextView) findViewById(R.id.txtTitle);
+            if(mTitleText == null || mTitleText.isEmpty()){
+                txtTitle.setVisibility(View.GONE);
+            } else {
+                txtTitle.setVisibility(View.VISIBLE);
+                txtTitle.setText(mTitleText);
+            }
+            mBodyText = intent.getStringExtra(POPUP_BODY_TEXT);
+            TextView txtBody = (TextView) findViewById(R.id.txtBody);
+            if(mBodyText == null || mBodyText.isEmpty()){
+                txtBody.setVisibility(View.GONE);
+            } else {
+                txtBody.setVisibility(View.VISIBLE);
+                txtBody.setText(mBodyText);
+            }
+
+            mSelectedImgIndex = intent.getIntExtra(POPUP_IMG_INDEX,1);
+            changeSelectedImg(mSelectedImgIndex);
+        } else if(mEntryMode == MODE_ENABLE_BT) {
+            setContentView(R.layout.activity_popup);
+
+            mTitleText = getString(R.string.bt_on_for_use_str);
+            TextView txtTitle = (TextView) findViewById(R.id.txtTitle);
+            txtTitle.setVisibility(View.VISIBLE);
+            txtTitle.setText(mTitleText);
+
+            TextView txtBody = (TextView) findViewById(R.id.txtBody);
+            txtBody.setVisibility(View.GONE);
+
+            if(mEntryMode == MODE_NORMAL_WITH_ICON || mEntryMode == MODE_YESNO_WITH_ICON) {
+                findViewById(R.id.imgWarning).setVisibility(View.VISIBLE);
+                findViewById(R.id.chkDontShow).setVisibility(View.GONE);
+            } else if(mEntryMode == MODE_YESNO_WITH_CHK) {
+                findViewById(R.id.chkDontShow).setVisibility(View.VISIBLE);
+                findViewById(R.id.imgWarning).setVisibility(View.GONE);
+            } else {
+                findViewById(R.id.imgWarning).setVisibility(View.GONE);
+                findViewById(R.id.chkDontShow).setVisibility(View.GONE);
+            }
+        } else if(mEntryMode == MODE_SET_JAESIL_TIME) {
+            setContentView(R.layout.activity_popup_set_jaesil_time);
+            mSelectedTime = intent.getIntExtra(POPUP_TIME_INDEX,60);
+            changeSelectTime(mSelectedTime);
+        }
+
+        if (mTimeout > 0) {
+            mHandler = new Handler();
+            startDelayTimer(mTimeout);
+        }
+
+        mConfirmBtn = findViewById(R.id.popup_ok);
+        mCancelBtn =  findViewById(R.id.popup_cancel);
+        mTxt_popup_ok = findViewById(R.id.txt_popup_ok);
+        mTxt_popup_cancel = findViewById(R.id.txt_popup_cancel);
+
+        if (mEntryMode == MODE_NORMAL || mEntryMode == MODE_NORMAL_WITH_ICON) {
+            mCancelBtn.setVisibility(View.GONE);
+            findViewById(R.id.layout_round_ok).setVisibility(View.GONE);
+            findViewById(R.id.button_line).setVisibility(View.GONE);
+        } else if(mEntryMode == MODE_NO_BUTTON) {
+            findViewById(R.id.popup_button_layout).setVisibility(View.GONE);
+        } else if (mEntryMode == MODE_ENABLE_BT) {
+            mCancelBtn.setVisibility(View.GONE);
+            findViewById(R.id.layout_round_ok).setVisibility(View.GONE);
+            findViewById(R.id.button_line).setVisibility(View.GONE);
+            mTxt_popup_ok.setText(getString(R.string.bt_on_for_use_confirm_str));
+        }
+
+        if (intent.getStringExtra(POPUP_OK_BUTTON_TEXT) != null) {
+            mTxt_popup_ok.setText(intent.getStringExtra(POPUP_OK_BUTTON_TEXT));
+        }
+        if (intent.getStringExtra(POPUP_CANCEL_BUTTON_TEXT) != null) {
+            mTxt_popup_cancel.setText(intent.getStringExtra(POPUP_CANCEL_BUTTON_TEXT));
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if(mTimer != null){
+            mTimer.cancel();
+            mTimer = null;
+        }
+    }
+
+    /**
+     * 버튼 클릭에 대해 처리루틴 함수
+     * @param view
+     */
+    @Override
+    public void onClick(View view) {
+        final View fv = view;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                mActivity.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e){
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+
+        switch (view.getId()) {
+            case R.id.popup_ok:
+                if(mEntryMode == MODE_INPUT){
+                    if(mTxtInput.getText().toString().length() == 0 || mTxtInput.getText().toString().isEmpty()) {
+                        showNotiMsg(PopupActivity.this,"입력란이 비어있습니다.");
+                        return;
+                    } else if(inputMinLenght != 0 && mTxtInput.getText().toString().length() < inputMinLenght) {
+                        showNotiMsg(PopupActivity.this,"최소 "+inputMinLenght+"자 이상을 입력해야합니다.");
+                        return;
+                    } else if(inputMaxLenght != 0 && mTxtInput.getText().toString().length() > inputMaxLenght) {
+                        showNotiMsg(PopupActivity.this,"최대 "+inputMaxLenght+"자 이하를 입력해야합니다.");
+                        return;
+                    }
+
+                    if(mBundle!=null) {
+                        mBundle.putString(POPUP_INPUT,mTxtInput.getText().toString());
+                    }
+                } else if(mEntryMode == MODE_SELECT_GROUP_IMG) {
+                    getIntent().putExtra(POPUP_IMG_INDEX,mSelectedImgIndex);
+                } else if(mEntryMode == MODE_SET_JAESIL_TIME) {
+                    getIntent().putExtra(POPUP_TIME_INDEX,mSelectedTime);
+                }
+                setResultAndFinish();
+                break;
+
+            case R.id.popup_cancel:
+                setResult(RESULT_CANCELED, null);
+                finish();
+                break;
+
+            case R.id.img01:
+                changeSelectedImg(GROUP_01);
+                break;
+            case R.id.img02:
+                changeSelectedImg(GROUP_02);
+                break;
+            case R.id.img03:
+                changeSelectedImg(GROUP_03);
+                break;
+            case R.id.img04:
+                changeSelectedImg(GROUP_04);
+                break;
+            case R.id.img05:
+                changeSelectedImg(GROUP_05);
+                break;
+            case R.id.img06:
+                changeSelectedImg(GROUP_06);
+                break;
+            case R.id.img07:
+                changeSelectedImg(GROUP_07);
+                break;
+            case R.id.img08:
+                changeSelectedImg(GROUP_08);
+                break;
+            case R.id.img09:
+                changeSelectedImg(GROUP_09);
+                break;
+            case R.id.txtTime1:
+                changeSelectTime(120);
+                break;
+            case R.id.txtTime2:
+                changeSelectTime(90);
+                break;
+            case R.id.txtTime3:
+                changeSelectTime(60);
+                break;
+            case R.id.txtTime4:
+                changeSelectTime(50);
+                break;
+            case R.id.txtTime5:
+                changeSelectTime(40);
+                break;
+            case R.id.txtTime6:
+                changeSelectTime(30);
+                break;
+            case R.id.txtTime7:
+                changeSelectTime(20);
+                break;
+            case R.id.txtTime8:
+                changeSelectTime(10);
+                break;
+            case R.id.txtTime9:
+                changeSelectTime(5);
+                break;
+        }
+    }
+
+    private void setResultAndFinish() {
+        Intent intent = getIntent();
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+
+    /**
+     * 일정 시간 후에 팝업 사라게하는 함수
+     * @param period
+     */
+    private void startDelayTimer(int period){
+        if(mTimer != null){
+            mTimer.cancel();
+        }
+        mTimer = new Timer();
+        mTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        DismissConnectDialog();
+                        Intent intent = getIntent();
+                        setResult(RESULT_OK, intent);
+                        finish();
+                    }
+                });
+            }
+        }, period);
+    }
+
+    /**
+     * 이미지 팝업에서 이미지 선택시 UI 변경
+     * @param index
+     */
+    private void changeSelectedImg(int index) {
+        mSelectedImgIndex = index;
+        ImageView[] imgList = new ImageView[]{
+                null,
+                findViewById(R.id.img01),
+                findViewById(R.id.img02),
+                findViewById(R.id.img03),
+                findViewById(R.id.img04),
+                findViewById(R.id.img05),
+                findViewById(R.id.img06),
+                findViewById(R.id.img07),
+                findViewById(R.id.img08),
+                findViewById(R.id.img09),
+        };
+
+        String groupImgString[] = {GROUP_IMG_00_STRING,
+                GROUP_IMG_01_STRING,GROUP_IMG_02_STRING,GROUP_IMG_03_STRING,
+                GROUP_IMG_04_STRING,GROUP_IMG_05_STRING,GROUP_IMG_06_STRING,
+                GROUP_IMG_07_STRING,GROUP_IMG_08_STRING,GROUP_IMG_09_STRING,
+        };
+
+        for(int i = 1 ; i < imgList.length ; i++) {
+            imgList[i].setImageBitmap(getCircleBitmap(getGroupImageResource(groupImgString[i]),mSelectedImgIndex==i));
+        }
+    }
+
+    private void changeSelectTime(int time) {
+        mSelectedTime = time;
+        TextView textView[] = new TextView[]{
+                findViewById(R.id.txtTime1),
+                findViewById(R.id.txtTime2),
+                findViewById(R.id.txtTime3),
+                findViewById(R.id.txtTime4),
+                findViewById(R.id.txtTime5),
+                findViewById(R.id.txtTime6),
+                findViewById(R.id.txtTime7),
+                findViewById(R.id.txtTime8),
+                findViewById(R.id.txtTime9),
+        };
+
+        int index = -1;
+        switch (mSelectedTime) {
+            case 120:
+                index = 0;
+                break;
+            case 90:
+                index = 1;
+                break;
+            case 60:
+                index = 2;
+                break;
+            case 50:
+                index = 3;
+                break;
+            case 40:
+                index = 4;
+                break;
+            case 30:
+                index = 5;
+                break;
+            case 20:
+                index = 6;
+                break;
+            case 10:
+                index = 7;
+                break;
+            case 5:
+                index = 8;
+                break;
+            default:
+                mSelectedTime = 60;
+                index = 2;
+                break;
+        }
+
+        for(int i = 0 ; i < textView.length ; i++) {
+            if(index == i) {
+                textView[i].setSelected(true);
+            } else {
+                textView[i].setSelected(false);
+            }
+        }
+    }
+}

+ 54 - 0
app/src/main/java/kr/co/icontrols/iotplatform/PrivacyPolicyActivity.java

@@ -0,0 +1,54 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.webkit.WebView;
+
+public class PrivacyPolicyActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_privacy_policy);
+
+        WebView webView = findViewById(R.id.webView);
+        webView.loadUrl("file:///android_asset/bestinhome-agree.html");
+        webView.getSettings().setJavaScriptEnabled(true);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnClose:
+            case R.id.btnOk:
+                finish();
+                break;
+        }
+    }
+}

+ 330 - 0
app/src/main/java/kr/co/icontrols/iotplatform/ProfileActivity.java

@@ -0,0 +1,330 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserDetails;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.GenericHandler;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.GetDetailsHandler;
+
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.cognito.ItemToDisplay;
+import kr.co.icontrols.iotplatform.utils.DownloadImageTask;
+import kr.co.icontrols.iotplatform.utils.GetSessionCallback;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.cancelToExit;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCognitoSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isExpiredSession;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isRestartAppForBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.resetBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.restartApp;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_NICKNAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CALL_CENTER;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_UPDATE_DATA;
+
+public class ProfileActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG="ProfileActivity";
+
+    private TextView txtName, txtLocation;
+
+    private AlertDialog userDialog;
+    private ProgressDialog waitDialog;
+
+    // Cognito user objects
+    private CognitoUser user;
+    private CognitoUserSession session;
+    private CognitoUserDetails details;
+
+    // User details
+    private String username;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_profile);
+
+        txtName = findViewById(R.id.txtName);
+        txtLocation = findViewById(R.id.txtLocation);
+
+        // Get the user name
+        Bundle extras = getIntent().getExtras();
+        username = AppHelper.getCurrUser();
+        user = AppHelper.getPool().getUser(username);
+
+        setUi();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if(isRestartAppForBackgroundWaitTime()) {
+            restartApp(this);
+            return;
+        }
+
+        if(isExpiredSession()) {
+            getCognitoSession(new GetSessionCallback() {
+                @Override
+                public void successed() {
+                    getDetails();
+                }
+
+                @Override
+                public void failed() {
+                    showNotiMsg(ProfileActivity.this, "세션을 갱신하지 못하였습니다.");
+                    System.exit(0);
+                }
+            });
+        } else {
+            getDetails();
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        //super.onBackPressed();
+        cancelToExit(this);
+    }
+
+    @Override
+    protected void onPause() {
+        setBackgroundWaitTime();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        resetBackgroundWaitTime();
+        super.onDestroy();
+    }
+
+    @SuppressLint("MissingPermission")
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_UPDATE_DATA:
+                if(resultCode == RESULT_OK) {
+                    setUi();
+                }
+                break;
+            case RESULT_CALL_CENTER:
+                if(resultCode == RESULT_OK) {
+                    Intent intent = new Intent(Intent.ACTION_CALL);
+                    intent.setData(Uri.parse("tel:" + getString(R.string.service_center_hp)));
+                    startActivity(intent);
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        if(!UserInfo.confirmed && (v.getId() != R.id.btnEditUser)) {
+            showNotiMsg(this, "인증 후 사용 가능합니다.");
+            return;
+        }
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnEditUser:
+                startActivityForResult(new Intent(this,ProfileUserActivity.class),RESULT_UPDATE_DATA);
+                break;
+            case R.id.layoutLocation:
+                startActivity(new Intent(this,RegisterLocationActivity.class));
+                break;
+            case R.id.layoutMember:
+                startActivity(new Intent(this,AdminMemberActivity.class));
+                break;
+            case R.id.layoutSwitchMapper:
+                startActivity(new Intent(this,SwitchMapperRegisterGuideActivity.class));
+                break;
+            case R.id.layoutSetting:
+                startActivity(new Intent(this,SettingActivity.class));
+                break;
+            case R.id.layoutFaq:
+                startActivity(new Intent(this,FaqActivity.class));
+                break;
+            case R.id.layoutNoti:
+                startActivity(new Intent(this,NotificationActivity.class));
+                break;
+            case R.id.txtCallCenter: {
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_call_center));
+                intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.call));
+                startActivityForResult(intent, RESULT_CALL_CENTER);
+                }
+                break;
+            case R.id.btnGoToMall: {
+                Uri uriUrl = Uri.parse("https://bestin.hdc-smart.com/main/index");
+                Intent launchBrowser = new Intent(Intent.ACTION_VIEW, uriUrl);
+                startActivity(launchBrowser);
+                }
+                break;
+            case R.id.txtKakao: {
+                Uri uriUrl = Uri.parse("https://pf.kakao.com/_yxmPxbxb/chat");
+                Intent launchBrowser = new Intent(Intent.ACTION_VIEW, uriUrl);
+                startActivity(launchBrowser);
+                }
+                break;
+        }
+    }
+
+
+    private void setUi() {
+        // show The Image in a ImageView
+        if(UserInfo.profile != null && !UserInfo.profile.isEmpty()) {
+            new DownloadImageTask(findViewById(R.id.imgProfile)).execute(UserInfo.profile);
+        } else {
+            ((ImageView)findViewById(R.id.imgProfile)).setImageResource(R.drawable.profile_thum_176);
+        }
+
+        if(UserInfo.nickname != null && !UserInfo.nickname.isEmpty()) {
+            txtName.setText(UserInfo.nickname);
+        }
+
+        if(UserInfo.location != null && UserInfo.location.size() > 0) {
+            txtLocation.setText(UserInfo.location.get(0).address.city + " " + UserInfo.location.get(0).address.district +" " + UserInfo.location.get(0).address.town);
+        } else {
+            ((TextView)findViewById(R.id.txtLocation)).setText(getString(R.string.hint_input_location));
+        }
+    }
+
+    // Get user details from CIP service
+    private void getDetails() {
+        AppHelper.getPool().getUser(username).getDetailsInBackground(detailsHandler);
+    }
+
+    // Show user attributes from CIP service
+    private void showAttributes() {
+        int count = AppHelper.getItemCount();
+        for(int i = 0 ; i < count ; i++) {
+            ItemToDisplay it = AppHelper.getItemForDisplay(i);
+            if(it.getLabelText().equals(COGNITO_NICKNAME)) {
+                //txtName.setText(it.getDataText());
+            }
+        }
+    }
+
+    GetDetailsHandler detailsHandler = new GetDetailsHandler() {
+        @Override
+        public void onSuccess(CognitoUserDetails cognitoUserDetails) {
+            closeWaitDialog();
+            // Store details in the AppHandler
+            AppHelper.setUserDetails(cognitoUserDetails);
+            showAttributes();
+        }
+
+        @Override
+        public void onFailure(Exception exception) {
+            closeWaitDialog();
+            showDialogMessage("Could not fetch user details!", AppHelper.formatException(exception), true);
+        }
+    };
+
+    GenericHandler trustedDeviceHandler = new GenericHandler() {
+        @Override
+        public void onSuccess() {
+            // Close wait dialog
+            closeWaitDialog();
+        }
+
+        @Override
+        public void onFailure(Exception exception) {
+            closeWaitDialog();
+            showDialogMessage("Failed to update device status", AppHelper.formatException(exception), true);
+        }
+    };
+
+    private void updateDeviceStatus(CognitoDevice device) {
+        device.rememberThisDeviceInBackground(trustedDeviceHandler);
+    }
+
+    private void showDialogMessage(String title, String body, final boolean exit) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(title).setMessage(body).setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                    if(exit) {
+                        exit();
+                    }
+                } catch (Exception e) {
+                    // Log failure
+                    Log.e(TAG," -- Dialog dismiss failed");
+                    if(exit) {
+                        exit();
+                    }
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private void closeWaitDialog() {
+        try {
+            waitDialog.dismiss();
+        }
+        catch (Exception e) {
+            //
+        }
+    }
+
+    private void exit () {
+        Intent intent = new Intent();
+        if(username == null)
+            username = "";
+        intent.putExtra("name",username);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+}

+ 971 - 0
app/src/main/java/kr/co/icontrols/iotplatform/ProfileUserActivity.java

@@ -0,0 +1,971 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserDetails;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession;
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener;
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferNetworkLossHandler;
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferObserver;
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferState;
+import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility;
+import com.amazonaws.services.s3.model.ObjectMetadata;
+import com.theartofdev.edmodo.cropper.CropImage;
+import com.theartofdev.edmodo.cropper.CropImageView;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import kr.co.icontrols.iotplatform.AWS.Util;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.utils.DownloadImageTask;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static android.content.Intent.EXTRA_PHONE_NUMBER;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.DATABASE_NAME;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCircleBitmap;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_ID;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_HP;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_NICKNAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RESEND_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_SIGN_OUT_CONFIRM;
+
+public class ProfileUserActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private static final String TAG = "ProfileUserActivity";
+
+    private TextView txtName, txtEmail, txtHp, txtEmailCertification, txtAutoLoginDescription;
+    private ImageView imgProfile;
+    private boolean isChangeData;
+
+    //private CustomDialog customDialog;
+    private AlertDialog userDialog;
+    private ProgressDialog waitDialog;
+
+    // Cognito user objects
+    private CognitoUser user;
+    private CognitoUserSession session;
+    private CognitoUserDetails details;
+
+    // User details
+    private String username;
+
+    // Reference to the utility class
+    static Util util;
+    // The TransferUtility is the primary class for managing transfer to S3
+    private static TransferUtility transferUtility;
+    private static TransferObserver observer;
+    private String saveFileName;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_profile_user);
+
+        isChangeData = false;
+
+        // Initializes TransferUtility, always do this before using it.
+        observer = null;
+        util = new Util();
+        transferUtility = util.getTransferUtility(this);
+        TransferNetworkLossHandler.getInstance(this);
+
+        txtName = findViewById(R.id.txtName);
+        txtEmail = findViewById(R.id.txtEmail);
+        txtHp = findViewById(R.id.txtHp);
+        txtAutoLoginDescription = findViewById(R.id.txtAutoLoginDescription);
+        txtEmailCertification = findViewById(R.id.txtEmailCertification);
+
+        imgProfile = findViewById(R.id.imgProfile);
+
+        // Get the user name
+        Bundle extras = getIntent().getExtras();
+        username = AppHelper.getCurrUser();
+        user = AppHelper.getPool().getUser(username);
+        getDetails();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (SharedPrefUtil.getBoolean(SharedPrefUtil.AUTO_LOGIN, SharedPrefUtil.DEFAULT_AUTO_LOGIN)) {
+            txtAutoLoginDescription.setText(getString(R.string.selected));
+        } else {
+            txtAutoLoginDescription.setText(getString(R.string.unselected));
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        if(observer != null) {
+            observer.cleanTransferListener();
+        }
+        super.onPause();
+    }
+
+    @Override
+    public void onBackPressed() {
+        if(isChangeData) {
+            setResult(RESULT_OK);
+            finish();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.layoutEmail:
+                if(UserInfo.confirmed) {
+                    showNotiMsg(ProfileUserActivity.this,getString(R.string.snackbar_already_certification));
+                } else {
+                    Intent intent = new Intent(this, PopupActivity.class);
+                    intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                    intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, "인증 요청 메일을 전송하시겠습니까?");
+                    startActivityForResult(intent, RESULT_RESEND_CONFIRM);
+                }
+                break;
+            case R.id.layoutChangePassword:
+                startActivity(new Intent(this, ChangePasswordActivity.class));
+                break;
+            case R.id.layoutHp: {
+                Intent intent = new Intent(this, PhoneNumChangeActivity.class);
+                intent.putExtra(EXTRA_PHONE_NUMBER,txtHp.getText().toString());
+                startActivityForResult(intent, RESULT_INPUT_HP);
+                /*
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+                Bundle b = new Bundle();
+                if (txtHp.getText().toString().isEmpty()) {
+                    b.putString(PopupActivity.POPUP_INPUT, "");
+                } else {
+                    b.putString(PopupActivity.POPUP_INPUT, txtHp.getText().toString());
+                }
+                intent.putExtra(POPUP_BUNDLE, b);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.change_hp));
+                startActivityForResult(intent, RESULT_INPUT_HP);
+                 */
+                }
+                break;
+            case R.id.layoutAutoLogin: {
+                Intent autologinIntent = new Intent(this, PopupActivity.class);
+                autologinIntent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_AUTO_LOGIN);
+                startActivity(autologinIntent);
+                }
+                break;
+            case R.id.layoutLogout:
+                UserInfo.clear();
+                SharedPrefUtil.putBoolean(SharedPrefUtil.AUTO_LOGIN,false);
+                user.signOut();
+                restartApp();
+                break;
+            case R.id.layoutSignOut:{
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_sign_out_confirm));
+                intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.signout2));
+                startActivityForResult(intent, RESULT_SIGN_OUT_CONFIRM);
+                /*
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+                Bundle b = new Bundle();
+                b.putString(PopupActivity.POPUP_INPUT, "");
+                intent.putExtra(POPUP_BUNDLE, b);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_sign_out_confirm));
+                startActivityForResult(intent, RESULT_SIGN_OUT_CONFIRM);
+                 */
+                }
+                break;
+            case R.id.imgProfile:
+                selectImage();
+                break;
+            case R.id.txtName:
+                //showUserDetail(COGNITO_NICKNAME, txtName.getText().toString());
+                {
+                Intent intent = new Intent(this, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+                intent.putExtra(PopupActivity.POPUP_INPUT_MIN, 4);
+                intent.putExtra(PopupActivity.POPUP_INPUT_MAX, 8);
+                Bundle b = new Bundle();
+                if (txtName.getText().toString().isEmpty()) {
+                    b.putString(PopupActivity.POPUP_INPUT, "");
+                } else {
+                    b.putString(PopupActivity.POPUP_INPUT, txtName.getText().toString());
+                }
+                intent.putExtra(POPUP_BUNDLE, b);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.change_nickname));
+                startActivityForResult(intent, RESULT_INPUT_NICKNAME);
+                }
+                break;
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        // handle result of CropImageActivity
+        if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
+            CropImage.ActivityResult result = CropImage.getActivityResult(data);
+            if (resultCode == RESULT_OK) {
+                //((ImageView) findViewById(R.id.imgProfile)).setImageURI(result.getUri());
+                Bitmap bitmap = null;
+                try {
+                    bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), result.getUri());
+                    ((ImageView) findViewById(R.id.imgProfile)).setImageBitmap(getCircleBitmap(bitmap));
+
+                    Date day = new Date();
+                    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.KOREA);
+                    saveFileName = String.valueOf("user_profile_"+sdf.format(day)+".jpg");
+                    String targetDir = Environment.getExternalStorageDirectory().toString();
+                    File file = new File(targetDir+"/"+saveFileName);
+                    file.createNewFile();
+                    OutputStream  out = new FileOutputStream(file);
+                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
+                    beginUpload(file);
+                    out.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    ((ImageView) findViewById(R.id.imgProfile)).setImageURI(result.getUri());
+                    showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_profile_img));
+                }
+            } else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
+                showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_profile_img));
+            }
+        } else if(requestCode == RESULT_INPUT_NICKNAME) {
+            if (resultCode == RESULT_OK) {
+                Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                if(b != null) {
+                    try {
+                        String newValue = b.getString(PopupActivity.POPUP_INPUT);
+                        if(!newValue.equals(UserInfo.nickname)) {
+                            changeProfile("",newValue,"");
+                        }
+                    } catch (Exception e) {
+                        // Log failure
+                        showNotiMsg(ProfileUserActivity.this,getString(R.string.snackbar_fail_change_nickname));
+                    }
+                } else {
+                    showNotiMsg(ProfileUserActivity.this,getString(R.string.snackbar_fail_change_nickname));
+                }
+            }
+        } else if(requestCode == RESULT_INPUT_HP) {
+            if (resultCode == RESULT_OK) {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        isChangeData = true;
+                        txtHp.setText(UserInfo.phoneNumber);
+                    }
+                });
+                /*
+                Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                if(b != null) {
+                    try {
+                        String newValue = b.getString(PopupActivity.POPUP_INPUT);
+                        if(!newValue.equals(UserInfo.phoneNumber)) {
+                            changeProfile("","",newValue);
+                        }
+                    } catch (Exception e) {
+                        // Log failure
+                        showNotiMsg(ProfileUserActivity.this,getString(R.string.snackbar_fail_change_hp));
+                    }
+                } else {
+                    showNotiMsg(ProfileUserActivity.this,getString(R.string.snackbar_fail_change_hp));
+                }
+                 */
+            }
+        } else if(requestCode == RESULT_SIGN_OUT_CONFIRM) {
+            if (resultCode == RESULT_OK) {
+                /*
+                Bundle b = data.getBundleExtra(POPUP_BUNDLE);
+                if(b != null) {
+                    try {
+                        String newValue = b.getString(PopupActivity.POPUP_INPUT);
+                        signOut(newValue);
+                    } catch (Exception e) {
+                        // Log failure
+                        showNotiMsg(ProfileUserActivity.this,"회원 탈퇴에 실패하였습니다.");
+                    }
+                } else {
+                    showNotiMsg(ProfileUserActivity.this,"회원 탈퇴에 실패하였습니다.");
+                }
+                 */
+                UserInfo.clear();
+                signOut("");
+            }
+        } else if(requestCode == RESULT_RESEND_CONFIRM) {
+            if (resultCode == RESULT_OK) {
+                requestConfirmUser();
+            }
+        }
+    }
+
+    private void signOut(final String password) {
+        try{
+            displayProgress(ProfileUserActivity.this, "","");
+            HttpApi.deleteUserInfo(UserInfo.uid, password, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.deleteUserInfo(onFailure)\n" + e.toString());
+                    showNotiMsg(ProfileUserActivity.this, "회원 탈퇴에 실패하였습니다.");
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.deleteUserInfo(onResponse) HTTP_NO_CONTENT");
+                        showNotiMsg(ProfileUserActivity.this, "회원 탈퇴을 하였습니다.");
+                        SharedPrefUtil.reset();
+                        DBUtil.removeAll();
+                        deleteDatabase(DATABASE_NAME);
+                        IotPlatformApplication.getInstance().clearApplicationData();
+                        user.signOut();
+                        restartApp();
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.deleteUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(ProfileUserActivity.this, "회원 탈퇴에 실패하였습니다.");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.deleteUserInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.deleteUserInfo(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                    DismissConnectDialog();
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.deleteUserInfo(Exception)\n" + e.toString());
+            showNotiMsg(ProfileUserActivity.this, "회원 탈퇴에 실패하였습니다.");
+            DismissConnectDialog();
+        }
+    }
+
+    private void requestConfirmUser() {
+        try {
+            displayProgress(ProfileUserActivity.this, "","");
+            HttpApi.requestConfirmUser(true, true, UserInfo.email, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onFailure)\n" + e.toString());
+                    showNotiMsg(ProfileUserActivity.this, "인증 요청에 실패하였습니다.");
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) HTTP_NO_CONTENT");
+                        showNotiMsg(ProfileUserActivity.this, "인증을 요청을 하였습니다.");
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(ProfileUserActivity.this, "인증 요청에 실패하였습니다.");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                    DismissConnectDialog();
+                }
+            });
+        } catch (Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(Exception)\n" + e.toString());
+            showNotiMsg(ProfileUserActivity.this, "인증 요청에 실패하였습니다.");
+            DismissConnectDialog();
+        }
+    }
+
+    private void changeProfile(final String profile, final String nickName, final String phoneNumber) {
+        try {
+            displayProgress(ProfileUserActivity.this, "","");
+            HttpApi.changeUserInfo(profile, nickName, phoneNumber, UserInfo.pushAccess, UserInfo.autoUpdate, "", new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onFailure)\n" + e.toString());
+                    if(!profile.isEmpty()) {
+                        showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_profile_img));
+                    } else if(!nickName.isEmpty()) {
+                        showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_nickname));
+                    } else if(!phoneNumber.isEmpty()) {
+                        showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_hp));
+                    }
+                    DismissConnectDialog();
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) HTTP_NO_CONTENT");
+                        isChangeData = true;
+                        if(!profile.isEmpty()) {
+                            UserInfo.profile = profile;
+                            showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_successed_change_profile_img));
+                        } else if(!nickName.isEmpty()) {
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    txtName.setText(nickName);
+                                }
+                            });
+                            UserInfo.nickname = nickName;
+                            showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_successed_change_nickname));
+                        } else if(!phoneNumber.isEmpty()) {
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    txtHp.setText(phoneNumber);
+                                }
+                            });
+                            UserInfo.phoneNumber = phoneNumber;
+                            showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_successed_change_hp));
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(onResponse) JSONException : " + e.toString());
+                        }
+                        if(!profile.isEmpty()) {
+                            showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_profile_img));
+                        } else if(!nickName.isEmpty()) {
+                            showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_nickname));
+                        } else if(!phoneNumber.isEmpty()) {
+                            showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_hp));
+                        }
+                    }
+                    DismissConnectDialog();
+                }
+            });
+        } catch (Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.changeUserInfo(Exception)\n" + e.toString());
+            DismissConnectDialog();
+            if(!profile.isEmpty()) {
+                showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_profile_img));
+            } else if(!nickName.isEmpty()) {
+                showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_nickname));
+            } else if(!phoneNumber.isEmpty()) {
+                showNotiMsg(ProfileUserActivity.this, getString(R.string.snackbar_fail_change_hp));
+            }
+        }
+    }
+
+
+    // Get user details from CIP serviceN
+    private void getDetails() {
+        //AppHelper.getPool().getUser(username).getDetailsInBackground(detailsHandler);
+
+        // show The Image in a ImageView
+        if(UserInfo.profile != null && !UserInfo.profile.isEmpty()) {
+            new DownloadImageTask(findViewById(R.id.imgProfile)).execute(UserInfo.profile);
+        } else {
+            ((ImageView)findViewById(R.id.imgProfile)).setImageResource(R.drawable.profile_thum_176);
+        }
+
+        // set Value for component
+        txtEmail.setText(UserInfo.email);
+        txtHp.setText(UserInfo.phoneNumber);
+        txtName.setText(UserInfo.nickname);
+        if (UserInfo.confirmed) {
+            txtEmailCertification.setText(getString(R.string.cert));
+        } else {
+            txtEmailCertification.setText(getString(R.string.no_cert));
+        }
+    }
+
+    /*
+    // Show user attributes from CIP service
+    private void showAttributes() {
+        int count = AppHelper.getItemCount();
+        for (int i = 0; i < count; i++) {
+            ItemToDisplay it = AppHelper.getItemForDisplay(i);
+            if (it.getLabelText().equals(COGNITO_EMAIL)) {
+                txtEmail.setText(it.getDataText());
+                if (AppHelper.isEmailVerified()) {
+                    txtEmailCertification.setText(getString(R.string.cert));
+                } else {
+                    txtEmailCertification.setText(getString(R.string.no_cert));
+                }
+            } else if (it.getLabelText().equals(COGNITO_PHONENUMBER)) {
+                txtHp.setText(it.getDataText());
+            } else if (it.getLabelText().equals(COGNITO_NICKNAME)) {
+                txtName.setText(it.getDataText());
+            }
+        }
+    }
+
+    // Update attributes
+    private void updateAttribute(String attributeType, String attributeValue) {
+
+        if(attributeType == null || attributeType.length() < 1) {
+            return;
+        }
+        CognitoUserAttributes updatedUserAttributes = new CognitoUserAttributes();
+        updatedUserAttributes.addAttribute(attributeType, attributeValue);
+        Toast.makeText(getApplicationContext(), attributeType + ": " + attributeValue, Toast.LENGTH_LONG);
+        showWaitDialog("Updating...");
+        AppHelper.getPool().getUser(AppHelper.getCurrUser()).updateAttributesInBackground(updatedUserAttributes, updateHandler);
+    }
+
+    // Delete attribute
+    private void deleteAttribute(String attributeName) {
+        showWaitDialog("Deleting...");
+        List<String> attributesToDelete = new ArrayList<>();
+        attributesToDelete.add(attributeName);
+        AppHelper.getPool().getUser(AppHelper.getCurrUser()).deleteAttributesInBackground(attributesToDelete, deleteHandler);
+    }
+
+    GetDetailsHandler detailsHandler = new GetDetailsHandler() {
+        @Override
+        public void onSuccess(CognitoUserDetails cognitoUserDetails) {
+            closeWaitDialog();
+            // Store details in the AppHandler
+            AppHelper.setUserDetails(cognitoUserDetails);
+            showAttributes();
+            // Trusted devices?
+            handleTrustedDevice();
+        }
+
+        @Override
+        public void onFailure(Exception exception) {
+            closeWaitDialog();
+            showDialogMessage("Could not fetch user details!", AppHelper.formatException(exception), true);
+        }
+    };
+
+    UpdateAttributesHandler updateHandler = new UpdateAttributesHandler() {
+        @Override
+        public void onSuccess(List<CognitoUserCodeDeliveryDetails> attributesVerificationList) {
+            // Update successful
+            if(attributesVerificationList.size() > 0) {
+                showDialogMessage("Updated", "The updated attributes has to be verified",  false);
+            }
+            getDetails();
+        }
+
+        @Override
+        public void onFailure(Exception exception) {
+            // Update failed
+            closeWaitDialog();
+            showDialogMessage("Update failed", AppHelper.formatException(exception), false);
+        }
+    };
+
+    GenericHandler deleteHandler = new GenericHandler() {
+        @Override
+        public void onSuccess() {
+            closeWaitDialog();
+            // Attribute was deleted
+            Toast.makeText(getApplicationContext(), "Deleted", Toast.LENGTH_SHORT);
+
+            // Fetch user details from the the service
+            getDetails();
+        }
+
+        @Override
+        public void onFailure(Exception e) {
+            closeWaitDialog();
+            // Attribute delete failed
+            showDialogMessage("Delete failed", AppHelper.formatException(e), false);
+
+            // Fetch user details from the service
+            getDetails();
+        }
+    };
+
+    GenericHandler trustedDeviceHandler = new GenericHandler() {
+        @Override
+        public void onSuccess() {
+            // Close wait dialog
+            closeWaitDialog();
+        }
+
+        @Override
+        public void onFailure(Exception exception) {
+            closeWaitDialog();
+            showDialogMessage("Failed to update device status", AppHelper.formatException(exception), true);
+        }
+    };
+
+    private void handleTrustedDevice() {
+        CognitoDevice newDevice = AppHelper.getNewDevice();
+        if (newDevice != null) {
+            AppHelper.newDevice(null);
+            trustedDeviceDialog(newDevice);
+        }
+    }
+
+    private void updateDeviceStatus(CognitoDevice device) {
+        device.rememberThisDeviceInBackground(trustedDeviceHandler);
+    }
+
+    private void trustedDeviceDialog(final CognitoDevice newDevice) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle("Remember this device?");
+        //final EditText input = new EditText(UserActivity.this);
+
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.MATCH_PARENT);
+
+        //input.setLayoutParams(lp);
+        //input.requestFocus();
+        //builder.setView(input);
+
+        builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    //String newValue = input.getText().toString();
+                    showWaitDialog("Remembering this device...");
+                    updateDeviceStatus(newDevice);
+                    userDialog.dismiss();
+                } catch (Exception e) {
+                    // Log failure
+                }
+            }
+        }).setNegativeButton("No", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                } catch (Exception e) {
+                    // Log failure
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+
+    private void showUserDetail(final String attributeType, final String attributeValue) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(attributeType);
+        final EditText input = new EditText(ProfileUserActivity.this);
+        input.setText(attributeValue);
+
+        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.MATCH_PARENT,
+                LinearLayout.LayoutParams.MATCH_PARENT);
+
+        input.setLayoutParams(lp);
+        input.requestFocus();
+        builder.setView(input);
+
+        builder.setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    String newValue = input.getText().toString();
+                    if(!newValue.equals(attributeValue)) {
+                        showWaitDialog("Updating...");
+                        updateAttribute(AppHelper.getSignUpFieldsC2O().get(attributeType), newValue);
+                    }
+                    userDialog.dismiss();
+                } catch (Exception e) {
+                    // Log failure
+                }
+            }
+        }).setPositiveButton("Delete", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                    deleteAttribute(AppHelper.getSignUpFieldsC2O().get(attributeType));
+                } catch (Exception e) {
+                    // Log failure
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private void showWaitDialog(String message) {
+        closeWaitDialog();
+        waitDialog = new ProgressDialog(this);
+        waitDialog.setTitle(message);
+        waitDialog.show();
+    }
+
+    private void showDialogMessage(String title, String body, final boolean exit) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(title).setMessage(body).setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                    if (exit) {
+                        exit();
+                    }
+                } catch (Exception e) {
+                    // Log failure
+                    Log.e(TAG, " -- Dialog dismiss failed");
+                    if (exit) {
+                        exit();
+                    }
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private void closeWaitDialog() {
+        try {
+            waitDialog.dismiss();
+        } catch (Exception e) {
+            //
+        }
+    }
+
+    private void exit() {
+        Intent intent = new Intent();
+        if (username == null)
+            username = "";
+        intent.putExtra("name", username);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+    */
+
+    private void restartApp() {
+        Intent intent = new Intent(this, SplashActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        finish();
+    }
+
+    private void selectImage() {
+        CropImage.activity()
+                .setGuidelines(CropImageView.Guidelines.ON)
+                .setActivityTitle("My Crop")
+                .setCropShape(CropImageView.CropShape.OVAL)
+                .setCropMenuCropButtonTitle("Done")
+                .setRequestedSize(400, 400)
+                .setCropMenuCropButtonIcon(R.drawable.icon_crop)
+                .setAspectRatio(1,1)
+                .setScaleType(CropImageView.ScaleType.FIT_CENTER)
+                .start(this);
+    }
+
+    public static String getPath(final Context context, final Uri uri) {
+        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+        // DocumentProvider
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+                // ExternalStorageProvider
+                if (isExternalStorageDocument(uri)) {
+                    final String docId = DocumentsContract.getDocumentId(uri);
+                    final String[] split = docId.split(":");
+                    final String type = split[0];
+                    if ("primary".equalsIgnoreCase(type)) {
+                        return Environment.getExternalStorageDirectory() + "/" + split[1];
+                    }
+                }
+                // DownloadsProvider
+                else if (isDownloadsDocument(uri)) {
+                    final String id = DocumentsContract.getDocumentId(uri);
+                    final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+                    return getDataColumn(context, contentUri, null, null);
+                }
+                // MediaProvider
+                else if (isMediaDocument(uri)) {
+                    final String docId = DocumentsContract.getDocumentId(uri);
+                    final String[] split = docId.split(":");
+                    final String type = split[0];
+                    Uri contentUri = null;
+                    if ("image".equals(type)) {
+                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+                    } else if ("video".equals(type)) {
+                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+                    } else if ("audio".equals(type)) {
+                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+                    }
+                    final String selection = "_id=?";
+                    final String[] selectionArgs = new String[]{
+                            split[1]
+                    };
+                    return getDataColumn(context, contentUri, selection, selectionArgs);
+                }
+            }
+            // MediaStore (and general)
+            else if ("content".equalsIgnoreCase(uri.getScheme())) {
+                return getDataColumn(context, uri, null, null);
+            }
+            // File
+            else if ("file".equalsIgnoreCase(uri.getScheme())) {
+                return uri.getPath();
+            }
+        }
+        return null;
+    }
+
+    public static boolean isExternalStorageDocument(Uri uri) {
+        return "com.android.externalstorage.documents".equals(uri.getAuthority());
+    }
+
+    public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
+        Cursor cursor = null;
+        final String column = "_data";
+        final String[] projection = {column};
+        try {
+            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+            if (cursor != null && cursor.moveToFirst()) {
+                final int column_index = cursor.getColumnIndexOrThrow(column);
+                return cursor.getString(column_index);
+            }
+        } finally {
+            if (cursor != null)
+                cursor.close();
+        }
+        return null;
+    }
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is MediaProvider.
+     */
+    public static boolean isMediaDocument(Uri uri) {
+        return "com.android.providers.media.documents".equals(uri.getAuthority());
+    }
+
+    /**
+     * @param uri The Uri to check.
+     * @return Whether the Uri authority is DownloadsProvider.
+     */
+    public static boolean isDownloadsDocument(Uri uri) {
+        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+    }
+
+    /*
+     * A TransferListener class that can listen to a upload task and be notified
+     * when the status changes.
+     */
+    class UploadListener implements TransferListener {
+
+        // Simply updates the UI list when notified.
+        @Override
+        public void onError(int id, Exception e) {
+            ErrorLogOutput(TAG, "S3->Error during upload: " + id + "\ne : " + e);
+        }
+
+        @Override
+        public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
+            DebugLogOutput(TAG, String.format("S3->onProgressChanged: %d, total: %d, current: %d",
+                    id, bytesTotal, bytesCurrent));
+        }
+
+        @Override
+        public void onStateChanged(int id, TransferState newState) {
+            DebugLogOutput(TAG, "S3->onStateChanged: " + id + ", " + newState);
+            if(newState == TransferState.COMPLETED){
+                changeProfile("https://bi-home-admin-factory-app-314854109580.s3.ap-northeast-2.amazonaws.com/user_profile/"+UserInfo.uid+"/"+saveFileName,"","");
+            }
+        }
+    }
+
+    private void beginUpload(File file) {
+
+        if(observer != null) {
+            observer.cleanTransferListener();
+        }
+
+        observer = transferUtility.upload(
+                "bi-home-admin-factory-app-314854109580" + "/user_profile/" + UserInfo.uid,
+                file.getName(),
+                file,
+                new ObjectMetadata(),
+                null,
+                new UploadListener()
+        );
+
+        /*
+         * Note that usually we set the transfer listener after initializing the
+         * transfer. However it isn't required in this sample app. The flow is
+         * click upload button -> start an activity for image selection
+         * startActivityForResult -> onActivityResult -> beginUpload -> onResume
+         * -> set listeners to in progress transfers.
+         */
+        // observer.setTransferListener(new UploadListener());
+    }
+}

+ 19 - 0
app/src/main/java/kr/co/icontrols/iotplatform/Protocol/SHA256.java

@@ -0,0 +1,19 @@
+package kr.co.icontrols.iotplatform.Protocol;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class SHA256 {
+    public static String sha256(String str) {
+        String SHA = "";
+        try{
+            MessageDigest sh = MessageDigest.getInstance("SHA-256");
+            sh.update(str.getBytes());
+            byte byteData[] = sh.digest();
+            StringBuffer sb = new StringBuffer();
+            for(int i = 0 ; i < byteData.length ; i++) sb.append(Integer.toString((byteData[i]&0xff) + 0x100, 16).substring(1));
+            SHA = sb.toString();
+        }catch(NoSuchAlgorithmException e) { e.printStackTrace(); SHA = null; }
+        return SHA;
+    }
+}

+ 84 - 0
app/src/main/java/kr/co/icontrols/iotplatform/Protocol/bleProtocol.java

@@ -0,0 +1,84 @@
+package kr.co.icontrols.iotplatform.Protocol;
+
+public class bleProtocol {
+    public static final byte BLE_HEADER                                                 = (byte) 0xF2;
+
+    public static final byte DEVICE_ID_TYPE                                             = (byte) 0x01;
+    public static final byte MODEL_ID_TYPE                                              = (byte) 0x02;
+    public static final byte MAC_TYPE                                                   = (byte) 0x03;
+    public static final byte NO_ERROR_TYPE                                              = (byte) 0x00;
+
+    public static final byte BLE_COMMAND_LTOA_BLINK_LIGHT_RSP_TO_APP                    = (byte) 0xA0;
+    public static final byte BLE_COMMAND_LTOA_FACTORY_RESET_RSP_TO_APP                  = (byte) 0xA1;
+    public static final byte BLE_COMMAND_LTOA_AWS_ONBOARDING_RSP_TO_APP                 = (byte) 0xA2;
+    public static final byte BLE_COMMAND_ATOL_AWS_ONBOARDING_INFO_RSP_TO_LIGHT          = (byte) 0xA3;
+    public static final byte BLE_COMMAND_LTOA_USER_REGISTERATION_RSP_TO_APP             = (byte) 0xA4;
+    public static final byte BLE_COMMAND_ATOL_USER_REGISTERATION_COMPLETE_RSP_TO_LIGHT  = (byte) 0xA5;
+    public static final byte BLE_COMMAND_LTOA_LIGHT_VERSION_RSP_TO_APP                  = (byte) 0xA6;
+    public static final byte BLE_COMMAND_LTOA_LIGHT_STATUS_RSP_TO_APP                   = (byte) 0xA7;
+    public static final byte BLE_COMMAND_ATOL_APP_STATUS_RESPONSE_TO_LIGHT                  = (byte) 0xA8;
+    public static final byte BLE_COMMAND_ATOL_APP_MAPPING_READY_TO_LIGHT                = (byte) 0xA9;
+
+    public static final byte BLE_COMMAND_STOA_BUZZER_PLAY_RSP_TO_APP                    = (byte) 0xC0;
+    public static final byte BLE_COMMAND_STOA_FACTORYRESET_RSP_TO_APP                   = (byte) 0xC1;
+    public static final byte BLE_COMMAND_STOA_MAC_ADDR_RSP_TO_APP                       = (byte) 0xC2;
+    public static final byte BLE_COMMAND_STOA_VERSION_RSP_TO_APP                        = (byte) 0xC3;
+
+    public static final byte BLE_COMMAND_LTOA_LIGHT_GROUPING_RSP_TO_APP                 = (byte) 0xE0;
+    public static final byte BLE_COMMAND_STOA_SWITCH_SCANRSP_TO_APP                     = (byte) 0xE1;
+    public static final byte BLE_COMMAND_STOA_SWITCH_GROUPING_RSP_TO_APP                = (byte) 0xE2;
+    public static final byte BLE_COMMAND_LTOA_INIT_LIGHT_GROUPING_RSP_TO_APP            = (byte) 0xE3;
+    public static final byte BLE_COMMAND_STOA_INIT_SWITCH_GROUPING_RSP_TO_APP           = (byte) 0xE4;
+
+    public static final byte BLE_COMMAND_ATOL_BLINK_LIGHT_REQ_TO_LIGHT                  = (byte) 0x20;
+    public static final byte BLE_COMMAND_ATOL_FACTORY_RESET_REQ_TO_LIGHT                = (byte) 0x21;
+    public static final byte BLE_COMMAND_ATOL_AWS_ONBOARDING_REQ_TO_LIGHT               = (byte) 0x22;
+    public static final byte BLE_COMMAND_LTOA_AWS_ONBOARDING_INFO_REQ_TO_APP            = (byte) 0x23;
+    public static final byte BLE_COMMAND_ATOL_USER_REGISTERATION_REQ_TO_LIGHT           = (byte) 0x24;
+    public static final byte BLE_COMMAND_LTOA_USER_REGISTERATION_COMPLETE_REQ_TO_APP    = (byte) 0x25;
+    public static final byte BLE_COMMAND_ATOL_LIGHT_VERSION_REQ_TO_LIGHT                = (byte) 0x26;
+    public static final byte BLE_COMMAND_ATOL_LIGHT_STATUS_REQ_TO_LIGHT                 = (byte) 0x27;
+    public static final byte BLE_COMMAND_ATOL_APP_STATUS_REQ_TO_LIGHT                   = (byte) 0x28;
+    public static final byte BLE_COMMAND_LTOA_LIGHT_MAPPING_READY_TO_APP                = (byte) 0x29;
+
+    public static final byte BLE_COMMAND_ATOS_BUZZER_PLAY_REQ_TO_SWITCH                 = (byte) 0x40;
+    public static final byte BLE_COMMAND_ATOS_FACTORYRESET_REQ_TO_SWITCH                = (byte) 0x41;
+    public static final byte BLE_COMMAND_ATOS_MAC_ADDR_REQ_TO_SWITCH                    = (byte) 0x42;
+    public static final byte BLE_COMMAND_ATOS_VERSION_REQ_TO_SWITCH                     = (byte) 0x43;
+
+    public static final byte BLE_COMMAND_ATOL_APP_GROUPING_REQ_TO_LIGHT                 = (byte) 0x60;
+    public static final byte BLE_COMMAND_ATOS_APP_SCAN_REQ_TO_SWITCH                    = (byte) 0x61;
+
+    public static final byte BLE_COMMAND_ATOL_APP_INIT_GROUPING_REQ_TO_LIGHT            = (byte) 0x63;
+    public static final byte BLE_COMMAND_ATOS_APP_INIT_GROUPING_REQ_TO_SWITCH           = (byte) 0x64;
+
+
+    public static final byte[] blinkLightProtocol                   = {BLE_HEADER, BLE_COMMAND_ATOL_BLINK_LIGHT_REQ_TO_LIGHT, (byte) (0x01), (byte) (0x03)};
+    public static final byte[] lightVersionProtocol                 = {BLE_HEADER, BLE_COMMAND_ATOL_LIGHT_VERSION_REQ_TO_LIGHT, (byte) (0x00)};
+    public static final byte[] FactoryResetLightProtocol            = {BLE_HEADER, BLE_COMMAND_ATOL_FACTORY_RESET_REQ_TO_LIGHT, (byte) (0x00)};
+    public static final byte[] onBoardingFailProtocol               = {BLE_HEADER, (byte) (0x81), (byte) (0x01), (byte) (0x02)};
+    public static final byte[] onBoardingRequestProtocol            = {BLE_HEADER, BLE_COMMAND_ATOL_AWS_ONBOARDING_REQ_TO_LIGHT, (byte) (0x00)};
+    public static final byte[] onBoardingGetIdDataSuccessProtocol   = {BLE_HEADER, BLE_COMMAND_ATOL_AWS_ONBOARDING_INFO_RSP_TO_LIGHT, (byte) (0x01), (byte) (0x01)};
+    public static final byte[] onBoardingGetIdDataFailProtocol      = {BLE_HEADER, BLE_COMMAND_ATOL_AWS_ONBOARDING_INFO_RSP_TO_LIGHT, (byte) (0x01), (byte) (0x02)};
+    public static final byte[] userRegisterRequestProtocol          = {BLE_HEADER, BLE_COMMAND_ATOL_USER_REGISTERATION_REQ_TO_LIGHT, (byte) (0x00)};
+    public static final byte[] userRegisterCompleteSuccessProtocol  = {BLE_HEADER, BLE_COMMAND_ATOL_USER_REGISTERATION_COMPLETE_RSP_TO_LIGHT, (byte) (0x01), (byte) (0x01)};
+    public static final byte[] userRegisterCompleteFailProtocol     = {BLE_HEADER, BLE_COMMAND_ATOL_USER_REGISTERATION_COMPLETE_RSP_TO_LIGHT, (byte) (0x01), (byte) (0x02)};
+    public static final byte[] buzzerSwitchProtocol                 = {BLE_HEADER, BLE_COMMAND_ATOS_BUZZER_PLAY_REQ_TO_SWITCH, (byte) (0x01), (byte) (0x01)};
+    public static final byte[] FactoryResetSwitchProtocol           = {BLE_HEADER, BLE_COMMAND_ATOS_MAC_ADDR_REQ_TO_SWITCH, (byte) (0x00)};
+    public static final byte[] getMacSwitchProtocol                 = {BLE_HEADER, BLE_COMMAND_ATOS_MAC_ADDR_REQ_TO_SWITCH, (byte) (0x00)};
+    public static final byte[] switchVersionProtocol                = {BLE_HEADER, BLE_COMMAND_ATOS_VERSION_REQ_TO_SWITCH, (byte) (0x00)};
+    public static       byte[] scanRequestSwitchProtocol            = {BLE_HEADER, BLE_COMMAND_ATOS_APP_SCAN_REQ_TO_SWITCH, (byte) (0x01), (byte) (0x00)};
+    public static final byte[] request_provion_status               = {BLE_HEADER, BLE_COMMAND_ATOL_LIGHT_STATUS_REQ_TO_LIGHT, (byte) (0x00)};
+    public static final byte[] userMappingReadyRequestProtocol      = {BLE_HEADER, BLE_COMMAND_ATOL_APP_STATUS_REQ_TO_LIGHT, (byte) (0x00)};
+    public static final byte[] userMappingReadySuccessProtocol      = {BLE_HEADER, BLE_COMMAND_ATOL_APP_MAPPING_READY_TO_LIGHT, (byte) (0x01), (byte) (0x01)};
+    public static final byte[] userMappingReadyFailProtocol         = {BLE_HEADER, BLE_COMMAND_ATOL_APP_MAPPING_READY_TO_LIGHT, (byte) (0x01), (byte) (0x02)};
+
+    public static final byte[] LightFactoryReset_Success_Command    = {BLE_HEADER, (byte)(0xA9), (byte) (0x01), (byte) (0x01)};
+    public static final byte[] LightFactoryReset_Fail_Command       = {BLE_HEADER, (byte)(0xA9), (byte) (0x01), (byte) (0x01)};
+
+    public static final byte[] lightStatusSuccessResponseProtocol   = {BLE_HEADER, BLE_COMMAND_ATOL_APP_STATUS_RESPONSE_TO_LIGHT, (byte) (0x01), (byte) (0x01)};
+    public static final byte[] lightStatusFailResponseProtocol      = {BLE_HEADER, BLE_COMMAND_ATOL_APP_STATUS_RESPONSE_TO_LIGHT, (byte) (0x01), (byte) (0x02)};
+
+    public static final byte[] initGroupDateSwitchProtocol          = {BLE_HEADER, BLE_COMMAND_ATOS_APP_INIT_GROUPING_REQ_TO_SWITCH, (byte) (0x00)};
+    public static final byte[] initGroupDateLightProtocol           = {BLE_HEADER, BLE_COMMAND_ATOL_APP_INIT_GROUPING_REQ_TO_LIGHT, (byte) (0x00)};
+}

+ 99 - 0
app/src/main/java/kr/co/icontrols/iotplatform/Protocol/bleProtocolState.java

@@ -0,0 +1,99 @@
+package kr.co.icontrols.iotplatform.Protocol;
+
+public class bleProtocolState {
+
+    public static final int PROVISIONING_BLE_CONNECTED          = 11;
+    public static final int PROVISIONING_BLE_DISCONNECTED       = 12;
+    public static final int PROVISIONING_LIST_NETWORK_SUCCESS   = 13;
+    public static final int PROVISIONING_LIST_NETWORK_FAIL      = 14;
+    public static final int PROVISIONING_SAVE_NETWORK_SUCCESS   = 15;
+    public static final int PROVISIONING_SAVE_NETWORK_FAIL      = 16;
+
+    public static final int ON_BOARDING_BLE_CONNECTED           = 20;
+    public static final int ON_BOARDING_BLE_DISCONNECTED        = 21;
+    public static final int ON_BOARDING_BLE_NOT_CONNECTED       = 22;
+    public static final int ON_BOARDING_REQUEST                 = 23;
+    public static final int ON_BOARDING_GET_REQUEST_SUCCESS     = 24;
+    public static final int ON_BOARDING_GET_REQUEST_FAIL        = 25;
+    public static final int ON_BOARDING_GET_ID_DATA             = 26;
+    public static final int ON_BOARDING_GET_ID_DATA_ERROR       = 27;
+    public static final int ON_BOARDING_SUCCESS                 = 28;
+    public static final int ON_BOARDING_TIMEOUT                 = 29;
+    public static final int ON_BOARDING_FAIL                    = 30;
+
+    public static final int BLUETOOTH_CONNECTED                 = 31;
+    public static final int BLUETOOTH_DISCONNECTED              = 32;
+    public static final int BLUETOOTH_NOT_CONNECTED             = 33;
+    public static final int BLUETOOTH_WRITE_CHARACTERISTIC      = 34;
+    public static final int BLUETOOTH_WRITE_DESCRIPTOR          = 35;
+    public static final int BLUETOOTH_GET_NOTIFICATION          = 36;
+    public static final int BLUETOOTH_GET_NOTIFICATION_FAIL     = 37;
+
+    public static final int BLINK_AND_BUZZER_PROTOCOL_SUCCESS   = 41;
+    public static final int BLINK_AND_BUZZER_PROTOCOL_FAIL      = 42;
+
+    public static final int FACTORY_RESET_PROTOCOL_SUCCESS      = 54;
+    public static final int FACTORY_RESET_PROTOCOL_FAIL         = 55;
+    public static final int FACTORY_RESET_PROTOCOL_TIMEOUT      = 56;
+
+    public static final int SCAN_RESULT_PROTOCOL_SUCCESS        = 61;
+    public static final int SCAN_RESULT_PROTOCOL_FAIL           = 62;
+
+    public static final int GROUPING_PROTOCOL_SUCCESS           = 71;
+    public static final int GROUPING_PROTOCOL_FAIL              = 72;
+
+    public static final int REQUEST_PROTOCOL_SUCCESS            = 81;
+    public static final int REQUEST_PROTOCOL_FAIL               = 82;
+
+    public static final int USER_REGISTER_REQUEST_SUCCESS       = 90;
+    public static final int USER_REGISTER_REQUEST_FAIL          = 91;
+    public static final int USER_REGISTER_REQUEST_TIMEOUT       = 92;
+    public static final int USER_REGISTER_COMPLETE_SUCCESS      = 93;
+    public static final int USER_REGISTER_COMPLETE_FAIL         = 94;
+    public static final int USER_REGISTER_FAIL                  = 95;
+    public static final int USER_REGISTER_END                   = 96;
+    public static final int USER_AWS_PROVISION_REQUEST          = 97;
+    public static final int USER_AWS_PROVISION_SUCCESS          = 98;
+    public static final int USER_MAPPING_READY_REQUEST_SUCCESS  = 99;
+    public static final int USER_MAPPING_READY_REQUEST_FAIL     = 100;
+    public static final int USER_MAPPING_READY_TIMEOUT          = 101;
+    public static final int USER_MAPPING_READY_RESPONSE         = 102;
+    public static final int USER_MAPPING_READY_COMPLETE         = 103;
+
+    public static final int HDC_ICON_LIGHT_REGISTER_SUCCESS     = 111;
+    public static final int HDC_ICON_SWITCH_REGISTER_SUCCESS    = 112;
+
+    public static final int HDC_ICON_LS_VERSION_CHECK_SUCCESS   = 121;
+    public static final int HDC_ICON_LM_VERSION_CHECK_SUCCESS   = 122;
+    public static final int HDC_GATT_CONNECT				  	= 123;
+
+
+    public static final int DELAY_ONE_SEC       			  	= 150;
+
+    public static final int RSP_BLINK_PROTOCOL_SUCCESS          = 900;
+    public static final int RSP_BLINK_PROTOCOL_FAIL             = 901;
+    public static final int RSP_BLINK_PROTOCOL_TIMEOUT          = 902;
+    public static final int RSP_USER_REGISTER_GET_ID_DATA       = 903;
+    public static final int REQ_REGISTER_DEVICE_ID              = 904;
+    public static final int RSP_BUZZER_PROTOCOL_SUCCESS         = 905;
+    public static final int RSP_BUZZER_PROTOCOL_FAIL            = 906;
+    public static final int RSP_BUZZER_PROTOCOL_TIMEOUT         = 907;
+    public static final int RSP_GROUP_SWITCH_SUCCESS            = 908;
+    public static final int RSP_GROUP_SWITCH_FAIL               = 909;
+    public static final int RSP_GROUP_SWITCH_TIMEOUT            = 910;
+    public static final int RSP_GROUP_LIGHT_SUCCESS             = 911;
+    public static final int RSP_GROUP_LIGHT_FAIL                = 912;
+    public static final int RSP_GROUP_LIGHT_TIMEOUT             = 913;
+    public static final int RSP_GROUP_END                       = 914;
+    public static final int RSP_INIT_GROUP_SWITCH_SUCCES        = 915;
+    public static final int RSP_INIT_GROUP_SWITCH_FAIL          = 916;
+    public static final int RSP_INIT_GROUP_SWITCH_TIMEOUT       = 917;
+    public static final int RSP_INIT_GROUP_LIGHT_SUCCES         = 918;
+    public static final int RSP_INIT_GROUP_LIGHT_FAIL           = 919;
+    public static final int RSP_INIT_GROUP_LIGHT_TIMEOUT        = 920;
+    public static final int RSP_GET_WIFI_STATUS_SUCCESS         = 921;
+    public static final int RSP_GET_WIFI_STATUS_FAIL            = 922;
+    public static final int RSP_GET_WIFI_STATUS_TIMEOUT         = 923;
+    public static final int RSP_INIT_GROUP_SWITCH_FAIL_OVER_LIGHT = 924;
+}
+

+ 337 - 0
app/src/main/java/kr/co/icontrols/iotplatform/RegisterLocationActivity.java

@@ -0,0 +1,337 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.KakaoLocationInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.adapter.KaKaoLoctionAdapter;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ForceDismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setupUI;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_CONFLICT;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_CREATED;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_NO_CONTENT;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class RegisterLocationActivity extends AppCompatActivity implements View.OnClickListener {
+    private final String TAG = "RegisterLocationActivity";
+    private KaKaoLoctionAdapter mKaKaoLoctionAdapter = new KaKaoLoctionAdapter();
+    private KakaoLocationInfo kakaoLocationInfo = null;
+    private Button btnRegister;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_register_location);
+        setupUI(this,findViewById(R.id.layoutMain));
+
+        btnRegister = findViewById(R.id.btnRegister);
+
+
+        ListView listView = findViewById(R.id.listView);
+        listView.setAdapter(mKaKaoLoctionAdapter);
+        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                kakaoLocationInfo = (KakaoLocationInfo) parent.getItemAtPosition(position);
+                mKaKaoLoctionAdapter.setSelected(position);
+                btnRegister.setVisibility(View.VISIBLE);
+            }
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        ForceDismissConnectDialog();
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+
+        if(mKaKaoLoctionAdapter!=null) {
+            mKaKaoLoctionAdapter.removeAllItem();
+        }
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnSearch:
+                btnRegister.setVisibility(View.INVISIBLE);
+                kakaoLocationInfo = null;
+                mKaKaoLoctionAdapter.removeAllItem();
+                mKaKaoLoctionAdapter.notifyDataSetChanged();
+
+                final String input = ((TextView)findViewById(R.id.txtInput)).getText().toString();
+                try {
+                    displayProgress(RegisterLocationActivity.this,"","");
+                    HttpApi.getLocationInfo(input, new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getLocationInfo(onFailure)\n" + e.toString());
+                            DismissConnectDialog();
+                            showNotiMsg(RegisterLocationActivity.this,"지역 검색을 실패하였습니다.");
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HTTP_OK) {
+                                WarnLogOutput(TAG, "HttpApi.getLocationInfo(onResponse) : HTTP_OK");
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    WarnLogOutput(TAG, "HttpApi.getLocationInfo(onResponse) body : " + json_data.toString());
+
+                                    JSONArray jarrayAll = new JSONArray(json_data.getString("documents"));
+
+                                    for (int i = 0; i < jarrayAll.length(); i++) {
+                                        JSONObject jObject = jarrayAll.getJSONObject(i);
+
+                                        KakaoLocationInfo item = new KakaoLocationInfo();
+                                        if(!jObject.isNull("address")) {
+                                            JSONObject subJObject = new JSONObject(jObject.getString("address"));
+                                            item.address_name = jObject.isNull("address_name") ? "" : jObject.getString("address_name");
+                                            item.b_code = subJObject.isNull("b_code") ? "" : subJObject.getString("b_code");
+                                            item.h_code = subJObject.isNull("h_code") ? "" : subJObject.getString("h_code");
+                                            item.main_address_no = subJObject.isNull("main_address_no") ? "" : subJObject.getString("main_address_no");
+                                            item.mountain_yn = subJObject.isNull("mountain_yn") ? "" : subJObject.getString("mountain_yn");
+                                            item.region_1depth_name = subJObject.isNull("region_1depth_name") ? "" : subJObject.getString("region_1depth_name");
+                                            item.region_2depth_name = subJObject.isNull("region_2depth_name") ? "" : subJObject.getString("region_2depth_name");
+                                            item.region_3depth_h_name = subJObject.isNull("region_3depth_h_name") ? "" : subJObject.getString("region_3depth_h_name");
+                                            item.region_3depth_name = subJObject.isNull("region_3depth_name") ? "" : subJObject.getString("region_3depth_name");
+                                            item.sub_address_no = subJObject.isNull("sub_address_no") ? "" : subJObject.getString("sub_address_no");
+                                            item.x = jObject.isNull("x") ? "" : jObject.getString("x");
+                                            item.y = jObject.isNull("y") ? "" : jObject.getString("y");
+                                            item.address_type = jObject.isNull("address_type") ? "" : jObject.getString("address_type");
+                                            item.road_address = jObject.isNull("road_address") ? "" : jObject.getString("road_address");
+                                        } else if(!jObject.isNull("road_address")) {
+                                            JSONObject subJObject = new JSONObject(jObject.getString("road_address"));
+                                            item.address_name = jObject.isNull("address_name") ? "" : jObject.getString("address_name");
+                                            item.region_1depth_name = subJObject.isNull("region_1depth_name") ? "" : subJObject.getString("region_1depth_name");
+                                            item.region_2depth_name = subJObject.isNull("region_2depth_name") ? "" : subJObject.getString("region_2depth_name");
+                                            item.region_3depth_h_name = subJObject.isNull("region_3depth_h_name") ? "" : subJObject.getString("region_3depth_h_name");
+                                            item.region_3depth_name = subJObject.isNull("region_3depth_name") ? "" : subJObject.getString("region_3depth_name");
+
+                                            //동이 있는 경우 동 추가.
+                                            item.address_name += item.region_3depth_name.isEmpty()?"":"("+item.region_3depth_name+")";
+
+                                            item.x = jObject.isNull("x") ? "" : jObject.getString("x");
+                                            item.y = jObject.isNull("y") ? "" : jObject.getString("y");
+                                        }
+
+                                        if(!item.region_3depth_name.isEmpty() || !item.region_3depth_h_name.isEmpty()) {
+                                            mKaKaoLoctionAdapter.addItem(item);
+                                        }
+                                    }
+
+                                    //UI 업데이트
+                                    runOnUiThread(new Runnable() {
+                                        @Override
+                                        public void run() {
+                                            if(mKaKaoLoctionAdapter.getCount() == 0) {
+                                                showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_error_not_found_location));
+                                            } else {
+                                                mKaKaoLoctionAdapter.notifyDataSetChanged();
+                                            }
+                                            DismissConnectDialog();
+                                        }
+                                    });
+                                } catch (JSONException e) {
+                                    e.printStackTrace();
+                                    ErrorLogOutput(TAG, "HttpApi.getLocationInfo(JSONException)\n" + e.toString());
+                                    showNotiMsg(RegisterLocationActivity.this,"지역 검색을 실패하였습니다.");
+                                    DismissConnectDialog();
+                                }
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.getLocationInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                DismissConnectDialog();
+                                showNotiMsg(RegisterLocationActivity.this,"지역 검색을 실패하였습니다.");
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.getLocationInfo(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.getLocationInfo(onResponse) JSONException : " + e.toString());
+                                }
+                            }
+                        }
+                    });
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    DismissConnectDialog();
+                    showNotiMsg(RegisterLocationActivity.this,"지역 검색을 실패하였습니다.");
+                    ErrorLogOutput(TAG, "HttpApi.getLocationInfo(Exception)\n" + e.toString());
+                }
+                break;
+            case R.id.btnDeleteInput:
+                ((TextView)findViewById(R.id.txtInput)).setText("");
+                break;
+            case R.id.btnRegister:
+                if(kakaoLocationInfo != null) {
+                    if (UserInfo.location.size() > 0) {
+                        modifyLocation(kakaoLocationInfo);
+                    } else {
+                        registerLocation(kakaoLocationInfo);
+                    }
+                }
+                break;
+        }
+    }
+
+    private void registerLocation(final KakaoLocationInfo item) {
+        try {
+            displayProgress(RegisterLocationActivity.this,"","");
+            HttpApi.registerLocationInfo(item.y,
+                    item.x,
+                    item.region_1depth_name,
+                    item.region_2depth_name,
+                    item.region_3depth_name.isEmpty()?item.region_3depth_h_name:item.region_3depth_name,
+                    new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.registerLocationInfo(onFailure)\n" + e.toString());
+                            showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_error_reg_location));
+                            DismissConnectDialog();
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HTTP_NO_CONTENT) {
+                                WarnLogOutput(TAG, "HttpApi.registerLocationInfo(onResponse) : HTTP_CREATED");
+                                UserInfo.updateTimeUserInfo = 0;
+                                UserInfo.updateTimeWeatherInfo = 0;
+                                showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_successed_reg_location),MainActivity.class);
+                            } else if(response.code() == HTTP_CONFLICT) {
+
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.registerLocationInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.registerLocationInfo(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.registerLocationInfo(onResponse) JSONException : " + e.toString());
+                                }
+                                showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_error_reg_location));
+                            }
+                            DismissConnectDialog();
+                        }
+                    });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.registerLocationInfo(Exception)\n" + e.toString());
+            showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_error_reg_location));
+            DismissConnectDialog();
+        }
+    }
+
+    private void modifyLocation(final KakaoLocationInfo item) {
+        try {
+            displayProgress(RegisterLocationActivity.this,"","");
+            HttpApi.modifyLocationInfo(UserInfo.location.get(0).locationId,
+                    item.y,
+                    item.x,
+                    item.region_1depth_name,
+                    item.region_2depth_name,
+                    item.region_3depth_name.isEmpty()?item.region_3depth_h_name:item.region_3depth_name,
+                    new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.modifyLocationInfo(onFailure)\n" + e.toString());
+                            showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_error_reg_location));
+                            DismissConnectDialog();
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            if(response.code() == HTTP_NO_CONTENT) {
+                                WarnLogOutput(TAG, "HttpApi.modifyLocationInfo(onResponse) : HTTP_CREATED");
+                                UserInfo.updateTimeUserInfo = 0;
+                                UserInfo.updateTimeWeatherInfo = 0;
+                                showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_successed_reg_location),MainActivity.class);
+                            } else if(response.code() == HTTP_CONFLICT) {
+
+                            } else {
+                                ErrorLogOutput(TAG, "HttpApi.modifyLocationInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                try {
+                                    JSONObject json_data = new JSONObject(response.body().string());
+                                    ErrorLogOutput(TAG, "HttpApi.modifyLocationInfo(onResponse) body : " + json_data.toString());
+                                } catch (JSONException e) {
+                                    ErrorLogOutput(TAG, "HttpApi.modifyLocationInfo(onResponse) JSONException : " + e.toString());
+                                }
+                                showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_error_reg_location));
+                            }
+                            DismissConnectDialog();
+                        }
+                    });
+        } catch (Exception e) {
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.modifyLocationInfo(Exception)\n" + e.toString());
+            showNotiMsg(RegisterLocationActivity.this,getString(R.string.snackbar_error_reg_location));
+            DismissConnectDialog();
+        }
+    }
+}

+ 155 - 0
app/src/main/java/kr/co/icontrols/iotplatform/Service/MessagingService.java

@@ -0,0 +1,155 @@
+package kr.co.icontrols.iotplatform.Service;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.media.RingtoneManager;
+
+import androidx.core.app.NotificationCompat;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
+import kr.co.icontrols.iotplatform.PopupActivity;
+import kr.co.icontrols.iotplatform.R;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.PUSH_TOKEN;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.putString;
+
+/**
+ * Push 서비스 이용을 위한 Service
+ */
+public class MessagingService extends FirebaseMessagingService {
+    private final String TAG = "MessagingService";
+    private final String channelId = "HDC_IOT";
+    private final String channelName = "HDC_IOT_APP";
+    @Override
+    public void onNewToken(String s) {
+        super.onNewToken(s);
+        DebugLogOutput(TAG, "onNewToken: " + s);
+        putString(PUSH_TOKEN,s);
+    }
+
+    @Override
+    public void onMessageReceived(RemoteMessage remoteMessage) {
+
+        // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
+        DebugLogOutput(TAG, "From: " + remoteMessage.getFrom());
+
+        // Check if message contains a data payload.
+        if (remoteMessage.getData().size() > 0) {
+            DebugLogOutput(TAG, "Message data payload: " + remoteMessage.getData());
+
+            if (/* Check if data needs to be processed by long running job */ true) {
+                // For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
+                //scheduleJob();
+            } else {
+                // Handle message within 10 seconds
+                //handleNow();
+            }
+        }
+
+        if (remoteMessage.getNotification() != null) {
+            DebugLogOutput(TAG, "Message Notification Title: " + remoteMessage.getNotification().getTitle());
+            DebugLogOutput(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
+            showNotification(remoteMessage);
+        } else if(remoteMessage.getData() != null) {
+            DebugLogOutput(TAG, "Message Data Title: " + remoteMessage.getData().get("title"));
+            DebugLogOutput(TAG, "Message Data Body: " + remoteMessage.getData().get("body"));
+            showData(remoteMessage);
+        }
+
+        // Also if you intend on generating your own notifications as a result of a received FCM
+        // message, here is where that should be initiated. See sendNotification method below.
+    }
+
+    /**
+     * 사용자에게 Push 메시지 표시하는 함수
+     * @param remoteMessage
+     */
+    private void showNotification(RemoteMessage remoteMessage) {
+
+        NotificationManager notifManager = (NotificationManager) getSystemService  (Context.NOTIFICATION_SERVICE);
+
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+            int importance = NotificationManager.IMPORTANCE_HIGH;
+            NotificationChannel mChannel = new NotificationChannel(channelId, channelName, importance);
+            notifManager.createNotificationChannel(mChannel);
+        }
+
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), channelId);
+        Intent notificationIntent = new Intent(getApplicationContext(), PopupActivity.class);
+        notificationIntent.putExtra(PopupActivity.POPUP_MODE,PopupActivity.MODE_NORMAL);
+        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        notificationIntent.putExtra(PopupActivity.POPUP_TITLE_TEXT,remoteMessage.getNotification().getTitle());
+        notificationIntent.putExtra(PopupActivity.POPUP_BODY_TEXT,remoteMessage.getNotification().getBody());
+
+        int requestID = (int) System.currentTimeMillis();
+
+        PendingIntent pendingIntent
+                = PendingIntent.getActivity(getApplicationContext()
+                , requestID
+                , notificationIntent
+                , PendingIntent.FLAG_UPDATE_CURRENT);
+
+        builder.setContentTitle(remoteMessage.getNotification().getTitle()) // required
+                .setContentText(remoteMessage.getNotification().getBody())  // required
+                .setDefaults(Notification.DEFAULT_ALL) // 알림, 사운드 진동 설정
+                .setAutoCancel(true) // 알림 터치시 반응 후 삭제
+                .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
+                .setColor(getResources().getColor(R.color.colorPrimary))
+                .setSmallIcon(R.drawable.app_icon_png_02)
+                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
+                //.setBadgeIconType(R.drawable.msg_icon)
+                .setContentIntent(pendingIntent);
+
+        notifManager.notify(0, builder.build());
+    }
+
+    /**
+     * 사용자에게 Push 메시지 표시하는 함수
+     * @param remoteMessage
+     */
+    private void showData(RemoteMessage remoteMessage) {
+
+        NotificationManager notifManager = (NotificationManager) getSystemService  (Context.NOTIFICATION_SERVICE);
+
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+            int importance = NotificationManager.IMPORTANCE_HIGH;
+            NotificationChannel mChannel = new NotificationChannel(channelId, channelName, importance);
+            notifManager.createNotificationChannel(mChannel);
+        }
+
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), channelId);
+        Intent notificationIntent = new Intent(getApplicationContext(), PopupActivity.class);
+        notificationIntent.putExtra(PopupActivity.POPUP_MODE,PopupActivity.MODE_NORMAL);
+        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        notificationIntent.putExtra(PopupActivity.POPUP_BODY_TEXT, remoteMessage.getData().get("title") + "\n\n" + remoteMessage.getData().get("body"));
+
+        int requestID = (int) System.currentTimeMillis();
+
+        PendingIntent pendingIntent
+                = PendingIntent.getActivity(getApplicationContext()
+                , requestID
+                , notificationIntent
+                , PendingIntent.FLAG_UPDATE_CURRENT);
+
+        builder.setContentTitle(remoteMessage.getData().get("title")) // required
+                .setContentText(remoteMessage.getData().get("body"))  // required
+                .setDefaults(Notification.DEFAULT_ALL) // 알림, 사운드 진동 설정
+                .setAutoCancel(true) // 알림 터치시 반응 후 삭제
+                .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
+                .setColor(getResources().getColor(R.color.colorPrimary))
+                .setSmallIcon(R.drawable.app_icon_png_02)
+                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
+                //.setBadgeIconType(R.drawable.msg_icon)
+                .setContentIntent(pendingIntent);
+
+        notifManager.notify(0, builder.build());
+    }
+}

+ 70 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SettingActivity.java

@@ -0,0 +1,70 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INIT_APP_CONFIRM;
+
+public class SettingActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_setting);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if(UserInfo.pushAccess/*SharedPrefUtil.getBoolean(SharedPrefUtil.NOTI_ALL,SharedPrefUtil.DEFAULT_NOTI_ALL)*/) {
+            ((TextView)findViewById(R.id.txtAlarmOnOff)).setText(getString(R.string.on));
+        } else {
+            ((TextView)findViewById(R.id.txtAlarmOnOff)).setText(getString(R.string.off));
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnAlarmSetting:
+                startActivity(new Intent(this, AlarmSettingActivity.class));
+                break;
+            case R.id.btnAppInfo:
+                startActivity(new Intent(this, AppInfoActivity.class));
+                break;
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+        }
+    }
+}

+ 52 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SignUpCertificationActivity.java

@@ -0,0 +1,52 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class SignUpCertificationActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_sign_up_certification);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnEmailCertification:
+                break;
+            case R.id.btnHpCertification:
+                break;
+        }
+    }
+}

+ 76 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SignUpFinishActivity.java

@@ -0,0 +1,76 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.getBoolean;
+
+public class SignUpFinishActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_sign_up_finish);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onBackPressed() {
+        //super.onBackPressed();
+        goToLoginActivity();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+            case R.id.btnLogin:
+                goToLoginActivity();
+                break;
+        }
+    }
+
+    /**
+     * 로그인 화면 이동.
+     */
+    private void goToLoginActivity(){
+        Intent intent;
+        intent = new Intent(this, LoginActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        finish();
+    }
+}

+ 310 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SignUpInputActivity.java

@@ -0,0 +1,310 @@
+package kr.co.icontrols.iotplatform;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserAttributes;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.SignUpHandler;
+import com.amazonaws.services.cognitoidentityprovider.model.CodeDeliveryDetailsType;
+import com.amazonaws.services.cognitoidentityprovider.model.SignUpResult;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import kr.co.icontrols.iotplatform.DataClass.KakaoLocationInfo;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isPasswordValid;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.setupUI;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_NICKNAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_PHONENUMBER;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class SignUpInputActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private String TAG = "SignUpInputActivity";
+
+    private EditText txtId, txtPassword, txtComfirmPassword, txtNickName, txtHp;
+
+    private AlertDialog userDialog;
+    private ProgressDialog waitDialog;
+    private String usernameInput;
+    private String userpasswdInput;
+    private String userNickName;
+    private String userHp;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_sign_up_input);
+
+        setupUI(this,findViewById(R.id.layoutMain));
+
+        txtId = findViewById(R.id.txtId);
+        txtPassword = findViewById(R.id.txtPassword);
+        txtComfirmPassword = findViewById(R.id.txtComfirmPassword);
+        txtNickName = findViewById(R.id.txtNickName);
+        txtHp = findViewById(R.id.txtHp);
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnOk:
+                if(!((CheckBox)findViewById(R.id.chkAgreeService)).isChecked()) {
+                    showNotiMsg(this, "약관 동의 후 가능합니다.");
+                } else if(txtId == null || txtId.getText().toString().isEmpty()) {
+                    showNotiMsg(this,getString(R.string.input_id));
+                } else if(txtPassword == null || txtPassword.getText().toString().isEmpty()) {
+                    showNotiMsg(this,getString(R.string.input_password));
+                } else if(txtComfirmPassword == null || txtComfirmPassword.getText().toString().isEmpty()) {
+                    showNotiMsg(this,getString(R.string.input_password_comfirm));
+                } else if(!(txtPassword.getText().toString().equals(txtComfirmPassword.getText().toString()))) {
+                    showNotiMsg(this,getString(R.string.not_same_password));
+                } else if(!isPasswordValid(txtPassword.getText().toString())) {
+                    showNotiMsg(this,getString(R.string.invalid_password));
+                } else if(txtNickName == null || txtNickName.getText().toString().isEmpty()) {
+                    showNotiMsg(this,getString(R.string.input_nickname));
+                } else if(txtNickName == null || txtNickName.getText().toString().length() < 4) {
+                    showNotiMsg(this,getString(R.string.short_nickname));
+                } else if(txtHp == null || txtHp.getText().toString().isEmpty()) {
+                    showNotiMsg(this,getString(R.string.input_phone_number));
+                } else {
+                    usernameInput = txtId.getText().toString();
+                    userpasswdInput = txtPassword.getText().toString();
+                    userNickName = txtNickName.getText().toString();
+                    userHp = txtHp.getText().toString();
+
+                    try {
+                        displayProgress(SignUpInputActivity.this,"","");
+                        HttpApi.isValidateUserInfo(usernameInput, userNickName, userHp, new Callback() {
+                            @Override
+                            public void onFailure(Call call, IOException e) {
+                                e.printStackTrace();
+                                ErrorLogOutput(TAG, "HttpApi.isValidateUserInfo(onFailure)\n" + e.toString());
+                                DismissConnectDialog();
+                            }
+
+                            @Override
+                            public void onResponse(Call call, Response response) throws IOException {
+                                if(response.code() == HTTP_OK) {
+                                    WarnLogOutput(TAG, "HttpApi.isValidateUserInfo(onResponse) : HTTP_OK");
+                                    try {
+                                        JSONObject json_data = new JSONObject(response.body().string());
+                                        WarnLogOutput(TAG, "body : "+json_data.toString());
+
+                                        final String email = json_data.isNull("email") ? "" : json_data.getString("email");
+                                        final String nickname = json_data.isNull("nickname") ? "" : json_data.getString("nickname");
+                                        final String phoneNumber = json_data.isNull("phoneNumber") ? "" : json_data.getString("phoneNumber");
+
+                                        if(email.equals("VALID") && nickname.equals("VALID") && phoneNumber.equals("VALID")) {
+                                            runOnUiThread(new Runnable() {
+                                                @Override
+                                                public void run() {
+                                                    CognitoUserAttributes userAttributes = new CognitoUserAttributes();
+                                                    userAttributes.addAttribute(AppHelper.getSignUpFieldsC2O().get(COGNITO_NICKNAME).toString(), txtNickName.getText().toString());
+                                                    userAttributes.addAttribute(AppHelper.getSignUpFieldsC2O().get(COGNITO_PHONENUMBER).toString(), "+82"+txtHp.getText().toString().substring(1));
+
+                                                    //showWaitDialog("Signing up...");
+                                                    AppHelper.getPool().signUpInBackground(usernameInput, userpasswdInput, userAttributes, null, signUpHandler);
+                                                }
+                                            });
+                                        } else {
+                                            //UI 업데이트
+                                            runOnUiThread(new Runnable() {
+                                                @Override
+                                                public void run() {
+                                                    String msg = "";
+                                                    if(email.equals("EXIST")) {
+                                                        msg = (msg.isEmpty()?"":"\n")+"이메일이 존재합니다.";
+                                                    } else if(email.equals("INVALID")) {
+                                                        msg = (msg.isEmpty()?"":"\n")+"이메일이 유효하지 않습니다.";
+                                                    } else if(nickname.equals("EXIST")) {
+                                                        msg = (msg.isEmpty()?"":"\n")+"별명이 존재합니다. ";
+                                                    } else if(nickname.equals("INVALID")) {
+                                                        msg = (msg.isEmpty()?"":"\n")+"별명이 유효하지 않습니다.";
+                                                    } else if(phoneNumber.equals("EXIST")) {
+                                                        msg = (msg.isEmpty()?"":"\n")+"휴대포번호가 존재합니다.";
+                                                    } else if(phoneNumber.equals("INVALID")) {
+                                                        msg = (msg.isEmpty()?"":"\n")+"휴대포번호가 존재합니다.";
+                                                    }
+                                                   showNotiMsg(SignUpInputActivity.this, msg);
+                                                   DismissConnectDialog();
+                                                }
+                                            });
+                                        }
+                                    } catch (JSONException e) {
+                                        e.printStackTrace();
+                                        ErrorLogOutput(TAG, "HttpApi.isValidateUserInfo(JSONException)\n" + e.toString());
+                                        DismissConnectDialog();
+                                    }
+                                } else {
+                                    ErrorLogOutput(TAG, "HttpApi.isValidateUserInfo(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                                    DismissConnectDialog();
+                                    try {
+                                        JSONObject json_data = new JSONObject(response.body().string());
+                                        ErrorLogOutput(TAG, "HttpApi.isValidateUserInfo(onResponse) body : " + json_data.toString());
+                                    } catch (JSONException e) {
+                                        ErrorLogOutput(TAG, "HttpApi.isValidateUserInfo(onResponse) JSONException : " + e.toString());
+                                    }
+                                }
+                            }
+                        });
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                        ErrorLogOutput(TAG, "HttpApi.isValidateUserInfo(IOException)\n" + e.toString());
+                        DismissConnectDialog();
+                    }
+                }
+
+                //startActivity(new Intent(this, SignUpCertificationActivity.class));
+                break;
+            case R.id.btnShowDetail:
+                startActivity(new Intent(this, PrivacyPolicyActivity.class));
+                break;
+        }
+    }
+
+    SignUpHandler signUpHandler = new SignUpHandler() {
+        @Override
+        public void onSuccess(CognitoUser user, SignUpResult signUpResult) {
+            // Check signUpConfirmationState to see if the user is already confirmed
+            DismissConnectDialog();//closeWaitDialog();
+            Boolean regState = signUpResult.getUserConfirmed();
+            if (regState) {
+                // User is already confirmed
+                //showDialogMessage("Sign up successful!",usernameInput+" has been Confirmed", true);
+                //showNotiMsg(SignUpInputActivity.this,"회원가입을 완료하였습니다.");
+                startActivity(new Intent(SignUpInputActivity.this,SignUpFinishActivity.class));
+                finish();
+            }
+            else {
+                // User is not confirmed
+                confirmSignUp(signUpResult.getCodeDeliveryDetails());
+            }
+        }
+
+        @Override
+        public void onFailure(Exception exception) {
+            DismissConnectDialog();//closeWaitDialog();
+//            TextView label = (TextView) findViewById(R.id.textViewRegUserIdMessage);
+//            label.setText("Sign up failed");
+//            username.setBackground(getDrawable(R.drawable.text_border_error));
+            //showDialogMessage("Sign up failed",AppHelper.formatException(exception),false);
+            showNotiMsg(SignUpInputActivity.this,"회원가입을 실패하였습니다.");
+        }
+    };
+
+    private void confirmSignUp(CodeDeliveryDetailsType cognitoUserCodeDeliveryDetails) {
+        Intent intent = new Intent(this, SignUpCertificationActivity.class);
+        intent.putExtra("source","signup");
+        intent.putExtra("name", usernameInput);
+        intent.putExtra("destination", cognitoUserCodeDeliveryDetails.getDestination());
+        intent.putExtra("deliveryMed", cognitoUserCodeDeliveryDetails.getDeliveryMedium());
+        intent.putExtra("attribute", cognitoUserCodeDeliveryDetails.getAttributeName());
+        startActivityForResult(intent, 10);
+    }
+
+    private void showDialogMessage(String title, String body, final boolean exit) {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(title).setMessage(body).setNeutralButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                try {
+                    userDialog.dismiss();
+                    if(exit) {
+                        exit(usernameInput);
+                    }
+                } catch (Exception e) {
+                    if(exit) {
+                        exit(usernameInput);
+                    }
+                }
+            }
+        });
+        userDialog = builder.create();
+        userDialog.show();
+    }
+
+    private void showWaitDialog(String message) {
+        closeWaitDialog();
+        waitDialog = new ProgressDialog(this);
+        waitDialog.setTitle(message);
+        waitDialog.show();
+    }
+
+    private void closeWaitDialog() {
+        try {
+            waitDialog.dismiss();
+        }
+        catch (Exception e) {
+            //
+        }
+    }
+
+    private void exit(String uname) {
+        exit(uname, null);
+    }
+
+    private void exit(String uname, String password) {
+        Intent intent = new Intent();
+        if (uname == null) {
+            uname = "";
+        }
+        if (password == null) {
+            password = "";
+        }
+        intent.putExtra("name", uname);
+        intent.putExtra("password", password);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+}

+ 181 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SmartGuideActivity.java

@@ -0,0 +1,181 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.ViewFlipper;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+public class SmartGuideActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private CheckBox chkDontShow;
+
+    private View indicator1;
+    private View indicator2;
+    private View indicator3;
+
+    private ViewFlipper mViewFlipper;
+    private int m_nPreTouchPosX = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_smart_guide);
+
+        indicator1 = findViewById(R.id.indicator1);
+        indicator2 = findViewById(R.id.indicator2);
+        indicator3 = findViewById(R.id.indicator3);
+        mViewFlipper = findViewById(R.id.viewFlipper);
+        chkDontShow = findViewById(R.id.chkDontShow);
+
+        changeHelpPage();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnClose:
+                SharedPrefUtil.putBoolean(SharedPrefUtil.SHOW_SMART_GUIDE,!chkDontShow.isChecked());
+                Intent intent = new Intent(this, LoginActivity.class);
+                startActivity(intent);
+                finish();
+                break;
+        }
+    }
+
+    private void changeHelpPage(){
+        /**
+         * ViewFlipper에 도움말 페이지 추가.
+         */
+        mViewFlipper.removeAllViews();
+        int main_img[] = {R.drawable.img_smartguide_01,R.drawable.img_smartguide_02,R.drawable.img_smartguide_03};
+        String main_txtTitle[] = getResources().getStringArray(R.array.list_smart_guide_title);
+        String main_txtSubTitle[] = getResources().getStringArray(R.array.list_smart_guide_subtitle);
+        for(int i = 0;i<main_img.length;i++) {
+            View layoutGuide = LayoutInflater.from(this).inflate(R.layout.layout_guide,null);
+            ((ImageView)layoutGuide.findViewById(R.id.img)).setImageResource(main_img[i]);
+            ((TextView)layoutGuide.findViewById(R.id.txtTitle)).setText(main_txtTitle[i]);
+            ((TextView)layoutGuide.findViewById(R.id.txtSubTitle)).setText(main_txtSubTitle[i]);
+            mViewFlipper.addView(layoutGuide);
+        }
+        mViewFlipper.setOnTouchListener(MyTouchListener);
+        updateIndicators(mViewFlipper.getDisplayedChild());
+    }
+
+
+    /**
+     * 화면에 터치이벤트 추가.
+     */
+    View.OnTouchListener MyTouchListener = new View.OnTouchListener()
+    {
+        public boolean onTouch(View v, MotionEvent event)
+        {
+            if (event.getAction() == MotionEvent.ACTION_DOWN)
+            {
+                m_nPreTouchPosX = (int)event.getX();
+            }
+            if (event.getAction() == MotionEvent.ACTION_UP)
+            {
+                int nTouchPosX = (int)event.getX();
+                if (nTouchPosX < m_nPreTouchPosX)
+                {
+                    if(mViewFlipper.getDisplayedChild() < mViewFlipper.getChildCount()-1)
+                        MoveNextView();
+                }
+                else if (nTouchPosX > m_nPreTouchPosX)
+                {
+                    if(mViewFlipper.getDisplayedChild()>0)
+                        MovewPreviousView();
+                }
+                m_nPreTouchPosX = nTouchPosX;
+            }
+            updateIndicators(mViewFlipper.getDisplayedChild());
+            return true;
+        }
+
+    };
+
+    /**
+     * 다음 페이지 이동 함수
+     */
+    private void MoveNextView()
+    {
+        mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.appear_from_right));
+        mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.disappear_to_left));
+        mViewFlipper.showNext();
+    }
+
+    /**
+     * 이전 페이지 이동 함수
+     */
+    private void MovewPreviousView()
+    {
+        mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.appear_from_left));
+        mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.disappear_to_right));
+        mViewFlipper.showPrevious();
+    }
+
+    /**
+     * 페이지 인디게이터 갱신 함수
+     * @param position
+     */
+    public void updateIndicators(int position) {
+        switch (position) {
+            case 0:
+                indicator1.setBackgroundResource(R.drawable.paging_guide_on);
+                indicator2.setBackgroundResource(R.drawable.paging_guide_off);
+                indicator3.setBackgroundResource(R.drawable.paging_guide_off);
+                break;
+
+            case 1:
+                indicator1.setBackgroundResource(R.drawable.paging_guide_off);
+                indicator2.setBackgroundResource(R.drawable.paging_guide_on);
+                indicator3.setBackgroundResource(R.drawable.paging_guide_off);
+                break;
+
+            case 2:
+                indicator1.setBackgroundResource(R.drawable.paging_guide_off);
+                indicator2.setBackgroundResource(R.drawable.paging_guide_off);
+                indicator3.setBackgroundResource(R.drawable.paging_guide_on);
+                break;
+        }
+    }
+}

+ 260 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SplashActivity.java

@@ -0,0 +1,260 @@
+package kr.co.icontrols.iotplatform;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Base64;
+import android.widget.Toast;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.security.MessageDigest;
+
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.PERMISSIONS;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.PERMISSION_ALL;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.resetBackgroundWaitTime;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHECK_VERSION;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.BACKGROUND_WAIT_TIME;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.getBoolean;
+
+public class SplashActivity extends AppCompatActivity {
+
+    private String TAG = SplashActivity.class.getSimpleName();
+
+    private Context mAppContext = null;
+    private Handler mHandler = new Handler();
+    private static final int SPLASH_DELAY_MILLIS = 1500;
+    private String mCurrentVersion;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_splash);
+        mAppContext = getApplicationContext();
+        resetBackgroundWaitTime();
+    }
+
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+        // show the main UI after SPLASH_DELAY_MILLIS
+        try {
+            mCurrentVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
+            //TODO - 버전 표시해야함.
+            //((TextView)findViewById(R.id.txtVersion)).setText("Ver : " + mCurrentVersion);
+        } catch (PackageManager.NameNotFoundException e){
+            e.printStackTrace();
+            mCurrentVersion= "";
+        }
+
+        GetVersionCode getVersionCode = new GetVersionCode();
+        getVersionCode.execute();
+        //mHandler.postDelayed(mStartMainUIActivityRunnable, SPLASH_DELAY_MILLIS);
+        //getAppKeyHash();
+    }
+
+
+    private void getAppKeyHash() {
+        try {
+            PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
+            for (Signature signature : info.signatures) {
+                MessageDigest md;
+                md = MessageDigest.getInstance("SHA");
+                md.update(signature.toByteArray());
+                String something = new String(Base64.encode(md.digest(), 0));
+                ErrorLogOutput("Hash key", something);
+            }
+        } catch (Exception e) {
+            ErrorLogOutput("name not found", e.toString());
+        }
+    }
+
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_CHECK_VERSION:
+                if(resultCode == Activity.RESULT_OK) {
+                    final String appPackageName = getPackageName();
+                    try {
+                        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName)));
+                    } catch (android.content.ActivityNotFoundException anfe) {
+                        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName)));
+                    }
+                } else {
+                    mHandler.postDelayed(mStartMainUIActivityRunnable, SPLASH_DELAY_MILLIS);
+                }
+                break;
+        }
+    }
+
+    /**
+     * 퍼미션 요청이 필요 유무 체크 함수
+     * @return
+     */
+    private boolean needRequestRuntimePermissions() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
+    }
+
+    /**
+     * 퍼미션 수락 상태 체크 함수
+     * @param context
+     * @param permissions 퍼미션 List
+     * @return
+     */
+    public static boolean hasPermissions(Context context, String... permissions) {
+        if (context != null && permissions != null) {
+            for (String permission : permissions) {
+                if (ActivityCompat.checkSelfPermission(context, permission) != PERMISSION_GRANTED) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 스플레시 일정 시간 후 로그인 화면 또는
+     */
+    private Runnable mStartMainUIActivityRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if(needRequestRuntimePermissions()) {
+                if (!hasPermissions(SplashActivity.this, PERMISSIONS)) {
+                    ActivityCompat.requestPermissions(SplashActivity.this, PERMISSIONS, PERMISSION_ALL);
+                } else {
+                    goToLoginActivity();
+                }
+            } else {
+                goToLoginActivity();
+            }
+        }
+    };
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        if(requestCode == PERMISSION_ALL){
+            boolean isGoToLoginActivity = true;
+            for(int i = 0;i<permissions.length;i++){
+                if(permissions[i].equals(android.Manifest.permission.CALL_PHONE) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(Manifest.permission.READ_EXTERNAL_STORAGE) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(android.Manifest.permission.CAMERA) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                } else if(permissions[i].equals(android.Manifest.permission.ACCESS_FINE_LOCATION) && grantResults[i] != PERMISSION_GRANTED){
+                    isGoToLoginActivity = false;
+                }
+            }
+
+            if(isGoToLoginActivity){
+                goToLoginActivity();
+            } else {
+                goToPermissionActivity();
+            }
+        }
+    }
+
+    /**
+     * 로그인 화면 이동.
+     */
+    private void goToLoginActivity(){
+        Intent intent;
+        if(getBoolean(SharedPrefUtil.SHOW_SMART_GUIDE,SharedPrefUtil.DEFAULT_SHOW_SMART_GUIDE)) {
+            intent = new Intent(mAppContext, SmartGuideActivity.class);
+        } else {
+            intent = new Intent(mAppContext, LoginActivity.class);
+        }
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        finish();
+    }
+
+    /**
+     * 권한 요청 화면 이동
+     */
+    private void goToPermissionActivity(){
+        /*
+        Intent intent = new Intent(mAppContext, PermissionActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+         */
+        Toast.makeText(this, "권한 설정 후 사용이 가능합니다.", Toast.LENGTH_SHORT).show();
+        finish();
+    }
+
+    /**
+     * 앱 업데이트 팝업 표시.
+     */
+    private void showPopupGoToMarket(){
+        Intent intent = new Intent(SplashActivity.this,PopupActivity.class);
+        intent.putExtra(PopupActivity.POPUP_MODE,PopupActivity.MODE_YESNO);
+        intent.putExtra(PopupActivity.POPUP_BODY_TEXT,getString(R.string.popup_upgrade_app));
+        intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT,getString(R.string.upgrage));
+        intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT,getString(R.string.late));
+        startActivityForResult(intent,RESULT_CHECK_VERSION);
+    }
+
+    /**
+     * 앱 버전 체크 함수
+     */
+    public class GetVersionCode extends AsyncTask<Void, String, String> {
+        @Override
+        protected String doInBackground(Void... voids) {
+            String newVersion = null;
+            try {
+                Document doc = Jsoup
+                        .connect("https://play.google.com/store/apps/details?id="+getPackageName())
+                        .get();
+                Elements Version = doc.select(".htlgb").eq(6);
+                for (Element mElement : Version) {
+                    newVersion = mElement.text().trim();
+                }
+                return newVersion;
+            } catch (Exception e) {
+                return newVersion;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(String onlineVersion) {
+            super.onPostExecute(onlineVersion);
+            if (!mCurrentVersion.isEmpty() && onlineVersion != null && !onlineVersion.isEmpty()) {
+                if (mCurrentVersion.equals(onlineVersion)) {
+                    mHandler.postDelayed(mStartMainUIActivityRunnable, SPLASH_DELAY_MILLIS);
+                } else {
+                    showPopupGoToMarket();
+                }
+            } else {
+                //showPopupGoToMarket();
+                mHandler.postDelayed(mStartMainUIActivityRunnable, SPLASH_DELAY_MILLIS);
+            }
+            WarnLogOutput(TAG, "Current version " + mCurrentVersion + " / playstore version " + onlineVersion);
+        }
+    }
+}

+ 1394 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SwitchMapperRegisterActivity.java

@@ -0,0 +1,1394 @@
+package kr.co.icontrols.iotplatform;
+
+import android.Manifest;
+import android.app.Activity;
+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.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import kr.co.icontrols.iotplatform.AWS.AmazonBLEinfo;
+import kr.co.icontrols.iotplatform.Protocol.bleProtocol;
+import kr.co.icontrols.iotplatform.Protocol.bleProtocolState;
+import kr.co.icontrols.iotplatform.utils.hdcUUID;
+
+import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
+import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_BLINK_PROTOCOL_TIMEOUT;
+import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_BUZZER_PROTOCOL_TIMEOUT;
+import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT;
+import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT;
+import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT;
+import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT;
+import static kr.co.icontrols.iotplatform.Protocol.bleProtocolState.SCAN_RESULT_PROTOCOL_FAIL;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DismissConnectDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.IsRunningProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.appendByteArray;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.byteArrayToHex;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.displayProgress;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getProgressDialog;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isBlueToothEnable;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.isPasswordValid;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_ENABLE_BT_MAIN;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_LIGHT_BLINK_SUCCESS;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_BUT_NO_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RETRY_SCAN_DEVICE;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_SWITCH_MAPPER_BUZZER_SUCCESS;
+
+public class SwitchMapperRegisterActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private final String TAG = "SwitchMapperRegister";
+
+    private final int  SCAN_TYPE_NOTHING = -1;
+    private final int  SCAN_TYPE_SWITCH = 0;
+    private final int  SCAN_TYPE_LIGHT = 1;
+    private int scanType = SCAN_TYPE_SWITCH;
+
+    // Switch scan ui
+    private LinearLayout layoutSwitchList;
+    private TextView txtSwitch;
+    private ListView listViewSwitch;
+    private DeviceArrayAdapter switchArrayAdapter;
+    private List<AmazonBLEinfo> swithList = new ArrayList<>();
+
+
+    // Light scan ui
+    private LinearLayout layoutLightList;
+    private TextView txtLight;
+    private ListView listViewLight;
+    private DeviceArrayAdapter lightArrayAdapter;
+    private List<AmazonBLEinfo> lightList = new ArrayList<>();
+    private List<String> lightListSelected = new ArrayList<>();
+    private List<String> lightScanList = new ArrayList<>();
+
+    // completed ui
+    private LinearLayout layoutCompleted;
+    private TextView txtCompleted;
+
+    // Bluetooth
+    private static final int REQUEST_ENABLE_BT = 1;
+    private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
+
+    // ble
+    private Handler handler;
+    //private BluetoothGatt bluetoothGatt;
+    private BluetoothAdapter bluetoothAdapter;
+    private BluetoothGattCharacteristic bluetoothGattCharacteristic;
+    private Map<String, BluetoothGatt> connectedDeviceMap;
+
+    //status
+    private final int UI_STATUS_LOADING = 0;
+    private final int UI_STATUS_SHOW_SWITCH_LIST = 1;
+    private final int UI_STATUS_SHOW_LIGHT_LIST = 3;
+    private final int UI_STATUS_SHOW_FAIL = 4;
+    private final int UI_STATUS_SHOW_COMPLETED = 5;
+    private final int UI_STATUS_SHOW_ETC = 6;
+
+    private final int PROCESS_STATUS_NOTHING = 0;
+    private final int PROCESS_STATUS_BUZZER = 1;
+    private final int PROCESS_STATUS_BLINK = 2;
+    private final int PROCESS_STATUS_SCAN_LIGHT = 3;
+    private final int PROCESS_STATUS_GROUPING_SWITCH = 4;
+    private final int PROCESS_STATUS_GROUPING_LIGHT = 5;
+    private final int PROCESS_STATUS_INIT_SWITCH = 6;
+    private final int PROCESS_STATUS_INIT_LIGHT = 7;
+
+    //retry to scan device
+    private boolean isRetry = false;
+
+    private int status_process = PROCESS_STATUS_NOTHING;
+    private int selectedSwitchPosition = 0;
+    private int selectedLightPosition = 0;
+    private int sendIndexLight = 0;
+    private String selectedSwitchMac = "";
+    private String selectedLightMac = "";
+    private Handler switchRegisterHandler;
+    private static final long SCAN_PERIOD = 5000;
+    private int FIRST_SCAN_PERIOD = 500;
+    private int SCAN_COUNT = 3;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_switch_mapper_register);
+
+        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+        requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_FINE_LOCATION);
+
+        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+        if (bluetoothManager != null) {
+            bluetoothAdapter = bluetoothManager.getAdapter();
+        }
+
+        setUI();
+
+        // Switch Device Selected & alarm buzzer
+        listViewSwitch.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final AmazonBLEinfo item = (AmazonBLEinfo) parent.getItemAtPosition(position);
+                selectedSwitchMac = item.getMacAddr();
+                selectedSwitchPosition = position;
+                startSwitchBuzzer();
+            }
+        });
+
+        listViewLight.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final AmazonBLEinfo item = (AmazonBLEinfo) parent.getItemAtPosition(position);
+                selectedLightMac = item.getMacAddr();
+                selectedLightPosition = position;
+
+                if(lightListSelected.contains(selectedLightMac)) {
+                    lightListSelected.remove(selectedLightMac);
+                    lightArrayAdapter.notifyDataSetChanged();
+                    selectedLightPosition = -1;
+                } else {
+                    startBlinkLight();
+                }
+            }
+        });
+
+        switchRegisterHandler = new Handler(Looper.getMainLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                DebugLogOutput(TAG, "[handleMessage what] : " + msg.what);
+                switch (msg.what) {
+                    case bleProtocolState.BLUETOOTH_CONNECTED:
+                        //Toast.makeText(SwitchMapperRegisterActivity.this, "BLE Connected", Toast.LENGTH_SHORT).show();
+                        DebugLogOutput(TAG, "[Switch Register Protocol] Switch Device Connect");
+                        break;
+
+                    case bleProtocolState.BLUETOOTH_DISCONNECTED:
+                        //Toast.makeText(SwitchMapperRegisterActivity.this, "Switch Device Disconnect", Toast.LENGTH_SHORT).show();
+                        DebugLogOutput(TAG, "[Buzzer Protocol] Switch Device Disconnect");
+                        break;
+
+                    case bleProtocolState.BLUETOOTH_NOT_CONNECTED:
+                        //Toast.makeText(SwitchMapperRegisterActivity.this, "BLE Connect Fail", Toast.LENGTH_SHORT).show();
+                        showNotiMsg(SwitchMapperRegisterActivity.this,"블루투스 연결을 실패하였습니다.");
+                        DebugLogOutput(TAG, "[Switch Register Protocol] BLE Connect Fail");
+                        if(layoutLightList.getVisibility() != View.VISIBLE) {
+                            retryScanDevice();
+                        } else {
+                            if(IsRunningProgress()){
+                                DismissConnectDialog();
+                            }
+                        }
+                        break;
+
+                    case bleProtocolState.BLUETOOTH_GET_NOTIFICATION:
+                        switchRegisterHandler.removeMessages(bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL);
+                        //Toast.makeText(SwitchMapperRegisterActivity.this, "Mac : " + msg.obj, Toast.LENGTH_SHORT).show();
+                        DebugLogOutput(TAG, "[Switch Register Protocol] Get MAC Success");
+                        bleDisconnect(selectedSwitchMac);
+                        break;
+
+                    case bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL:
+                        //Toast.makeText(SwitchMapperRegisterActivity.this, "Time Over 5' Sec", Toast.LENGTH_SHORT).show();
+                        DebugLogOutput(TAG, "[Switch Register Protocol] BLUETOOTH_GET_NOTIFICATION_FAIL");
+                        bleDisconnect(selectedSwitchMac);
+                        break;
+                    case bleProtocolState.RSP_BUZZER_PROTOCOL_SUCCESS:{
+                        switchRegisterHandler.removeMessages(RSP_BUZZER_PROTOCOL_TIMEOUT);
+                        //bleDisconnect(selectedSwitchMac);
+                        changeUI(UI_STATUS_SHOW_SWITCH_LIST);
+                        Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
+                        intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_confirm_register_switch_mapper_title));
+                        intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_confirm_register_switch_mapper_description));
+                        intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.yes));
+                        intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.re_select));
+                        startActivityForResult(intent, RESULT_SWITCH_MAPPER_BUZZER_SUCCESS);
+                        }
+                        break;
+                    case bleProtocolState.RSP_BUZZER_PROTOCOL_FAIL:
+                        switchRegisterHandler.removeMessages(RSP_BUZZER_PROTOCOL_TIMEOUT);
+                        //bleDisconnect(selectedSwitchMac);
+                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"스마트 스위치 연결음 송신에 실패하였습니다.");
+                        break;
+                    case RSP_BUZZER_PROTOCOL_TIMEOUT:
+                        //bleDisconnect(selectedSwitchMac);
+                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"스마트 스위치 연결시간이 초과되었습니다.");
+                        break;
+                    case bleProtocolState.RSP_BLINK_PROTOCOL_SUCCESS:{
+                        switchRegisterHandler.removeMessages(RSP_BLINK_PROTOCOL_TIMEOUT);
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST);
+                        Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
+                        intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_confirm_register_light_title));
+                        intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_confirm_register_light_description));
+                        intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.yes));
+                        intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.re_select));
+                        startActivityForResult(intent, RESULT_LIGHT_BLINK_SUCCESS);
+                        }
+                        break;
+                    case bleProtocolState.RSP_BLINK_PROTOCOL_FAIL:
+                        switchRegisterHandler.removeMessages(RSP_BLINK_PROTOCOL_TIMEOUT);
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 깜빡임 요청 중에 오류가 발생하였습니다.");
+                        //TODO 스마트 조명 깜빡임 실패시 로직 구현해야함.
+                        break;
+                    case RSP_BLINK_PROTOCOL_TIMEOUT:
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 깜빡임 요청시간을 초과되었습니다.");
+                        //TODO 스마트 조명 깜빡임 Timeout 로직 구현해야함.
+                        break;
+                    case bleProtocolState.SCAN_RESULT_PROTOCOL_SUCCESS:
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"등록하실 스마트 조명를 선택해 주세요\n선택하신 스마트 조명이 깜빡입니다.");
+                        switchRegisterHandler.removeMessages(bleProtocolState.SCAN_RESULT_PROTOCOL_FAIL);
+                        //TODO - 현재 스마트 스위치에서 스캔된 모듈 정보와 단말기에서 스캔한 모듈 정보 둘 다 존재할 경우로 작업했음.(차후 스마트 스위치에서 스캔된 모듈로 표시해야 될수도 있음)
+                        for(int i = 0 ; i < lightList.size() ; i++) {
+                            boolean isExist = false;
+                            for(String s : lightScanList) {
+                                if(lightList.get(i).getMacAddr().equals(s)){
+                                    isExist = true;
+                                }
+                            }
+                            if(!isExist) {
+                                lightList.remove(i--);
+                            }
+                        }
+                        lightArrayAdapter.notifyDataSetChanged();
+                        break;
+                    case bleProtocolState.SCAN_RESULT_PROTOCOL_FAIL:
+                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"스마트 스위치의 검색 요청이 실패하였습니다.");
+                        //TODO 스마트 스위치에서 스마트 조명 스캔 실패시.
+                        break;
+                    case bleProtocolState.RSP_GROUP_SWITCH_SUCCESS:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT);
+                        sendIndexLight = 0;
+                        startGroupForLight(sendIndexLight++);
+                        //initGroupDataForLight(sendIndexLight++);  //스마트 조명 그루핑 초기화 과정 제외.
+                        break;
+                    case bleProtocolState.RSP_GROUP_SWITCH_FAIL:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT);
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치의 그룹 요청이 실패하였습니다.");
+                        break;
+                    case bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT:
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치의 그룹 요청시간을 초과하였습니다.");
+                        break;
+                    case bleProtocolState.RSP_GROUP_LIGHT_SUCCESS:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT);
+                        if(lightListSelected.size() > sendIndexLight) {
+                            startGroupForLight(sendIndexLight++);
+                        } else {
+                            RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_END, null);
+                        }
+                        break;
+                    case bleProtocolState.RSP_GROUP_LIGHT_FAIL:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT);
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 요청이 실패하였습니다.");
+                        break;
+                    case bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT:
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 요청시간을 초과하였습니다.");
+                        break;
+                    case bleProtocolState.RSP_GROUP_END:
+                        changeUI(UI_STATUS_SHOW_COMPLETED,"스마트 스위치 등록이 완료되었습니다.");
+                        for(String key : connectedDeviceMap.keySet()){
+                            DebugLogOutput(TAG, "onSuccessed] connectedDeviceMap bluetoothGatt disconnect / MAC :"+key);
+                            connectedDeviceMap.get(key).disconnect();
+                            connectedDeviceMap.get(key).close();
+                        }
+                        connectedDeviceMap.clear();
+                        break;
+                    case bleProtocolState.RSP_INIT_GROUP_SWITCH_SUCCES:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT);
+                        startGroupForSwitch();
+                        break;
+                    case bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT);
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치 그룹 초기화 요청이 실패하였습니다.");
+                        break;
+                    case bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL_OVER_LIGHT:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT);
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치 초기화 버튼(독서버튼 5초)을 눌러 초기화 하세요.");
+                        break;
+                    case RSP_INIT_GROUP_SWITCH_TIMEOUT:
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 스위치 그룹 초기화 시간을 초과하였습니다.");
+                        break;
+                    case bleProtocolState.RSP_INIT_GROUP_LIGHT_SUCCES:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT);
+                        if(lightListSelected.size() > sendIndexLight) {
+                            initGroupDataForLight(sendIndexLight++);
+                        } else {
+                            sendIndexLight = 0;
+                            startGroupForLight(sendIndexLight++);
+                        }
+                        break;
+                    case bleProtocolState.RSP_INIT_GROUP_LIGHT_FAIL:
+                        switchRegisterHandler.removeMessages(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT);
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 초기화 요청이 실패하였습니다.");
+                        break;
+                    case RSP_INIT_GROUP_LIGHT_TIMEOUT:
+                        changeUI(UI_STATUS_SHOW_LIGHT_LIST,"스마트 조명 그룹 초기화 시간을 초과하였습니다.");
+                        break;
+                    default:
+                        throw new IllegalStateException("Unexpected value: " + msg.what);
+                }
+            }
+        };
+        checkEnableBluetooth();
+    }
+
+    private void startSwitchBuzzer() {
+        changeUI(UI_STATUS_LOADING,"스마트 스위치로 연결음 요청 중입니다...");
+        status_process = PROCESS_STATUS_BUZZER;
+        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            RequestHandlerSendMessage(RSP_BUZZER_PROTOCOL_TIMEOUT,null);
+            switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.buzzerSwitchProtocol);
+            DebugLogOutput(TAG, "buzzerSwitchProtocol : " + byteArrayToHex(bleProtocol.buzzerSwitchProtocol));
+        }
+    }
+
+    private void startSwitchScanLight() {
+        changeUI(UI_STATUS_LOADING,"스마트 스위치의 검색 범위 내 스마트 조명을 찾고 있습니다...");
+        status_process = PROCESS_STATUS_SCAN_LIGHT;
+        lightScanList.clear();
+        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            RequestHandlerSendMessage(SCAN_RESULT_PROTOCOL_FAIL,null);
+            DebugLogOutput(TAG, String.format("Before scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
+            bleProtocol.scanRequestSwitchProtocol[3] = (byte) (SCAN_COUNT & 0xff);
+            DebugLogOutput(TAG, String.format("After scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
+            switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.scanRequestSwitchProtocol);
+            DebugLogOutput(TAG, "scanRequestSwitchProtocol : " + byteArrayToHex(bleProtocol.scanRequestSwitchProtocol));
+        }
+    }
+
+    private void startBlinkLight() {
+        changeUI(UI_STATUS_LOADING,"선택된 스마트 조명에 깜빡임을 요청중입니다...");
+        status_process = PROCESS_STATUS_BLINK;
+        if (!connectedDeviceMap.containsKey(selectedLightMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedLightMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedLightMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback, TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedLightMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedLightMac)) {
+            RequestHandlerSendMessage(RSP_BLINK_PROTOCOL_TIMEOUT, null);
+            lightBLEGattWriteRequestCommand(selectedLightMac,bleProtocol.blinkLightProtocol);
+            DebugLogOutput(TAG, "blinkLightProtocol : " + byteArrayToHex(bleProtocol.blinkLightProtocol));
+        }
+    }
+
+    private void initGroupDataForSwitch() {
+        changeUI(UI_STATUS_LOADING,"스마트 스위치 그룹 초기화 요청 중입니다.");
+        status_process = PROCESS_STATUS_INIT_SWITCH;
+        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT, null);
+            switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.initGroupDateSwitchProtocol);
+            DebugLogOutput(TAG, "initGroupDateSwitchProtocol : " + byteArrayToHex(bleProtocol.initGroupDateSwitchProtocol));
+        }
+    }
+
+    private void initGroupDataForLight(final int index) {
+        changeUI(UI_STATUS_LOADING,"스마트 조명 그룹 초기화 요청 중입니다.");
+        status_process = PROCESS_STATUS_INIT_LIGHT;
+        final String mac = lightListSelected.get(index);
+        if (!connectedDeviceMap.containsKey(mac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(mac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback, TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(mac)) {
+            RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT, null);
+             lightBLEGattWriteRequestCommand(mac,bleProtocol.initGroupDateLightProtocol);
+            DebugLogOutput(TAG, "initGroupDateLightProtocol : " + byteArrayToHex(bleProtocol.initGroupDateLightProtocol));
+        }
+    }
+
+    private void startGroupForSwitch() {
+        changeUI(UI_STATUS_LOADING,"스마트 스위치 그룹 요청 중입니다.");
+        status_process = PROCESS_STATUS_GROUPING_SWITCH;
+        if (!connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(selectedSwitchMac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback, TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(selectedSwitchMac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, swithGattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(selectedSwitchMac)) {
+            RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT, null);
+            switchBLEGattWriteRequestCommand(selectedSwitchMac,getGroupProtocolForSwitch());
+            DebugLogOutput(TAG, "groupSwitchProtocol : " + byteArrayToHex(getGroupProtocolForSwitch()));
+        }
+    }
+
+    private void startGroupForLight(final int index) {
+        status_process = PROCESS_STATUS_GROUPING_LIGHT;
+        final String mac = lightListSelected.get(index);
+        changeUI(UI_STATUS_LOADING,"스마트 조명 그룹 요청 중입니다.("+index+"/"+lightListSelected.size()+")");
+        if (!connectedDeviceMap.containsKey(mac)) {
+            DebugLogOutput(TAG, "onClick: bluetoothGatt is null");
+            BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(mac);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback, TRANSPORT_LE));
+            } else {
+                connectedDeviceMap.put(mac, bluetoothDevice.connectGatt(SwitchMapperRegisterActivity.this, false, lightGattCallback));
+            }
+        } else if (connectedDeviceMap.containsKey(mac)) {
+            RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT, null);
+            lightBLEGattWriteRequestCommand(mac,getGroupProtocolForLight());
+            DebugLogOutput(TAG, "groupLightProtocol : " + byteArrayToHex(getGroupProtocolForLight()));
+        }
+    }
+
+    private byte[] getGroupProtocolForSwitch() {
+        int switchSize = (lightListSelected.size() * 6) + 4;
+        byte[] switchProtocol = new byte[switchSize];
+        switchProtocol[0] = (byte) (0xF2 & 0xff);
+        switchProtocol[1] = (byte) (0x62 & 0xff);
+        switchProtocol[2] = (byte) (((lightListSelected.size() * 6) + 1) & 0xff);
+        switchProtocol[3] = (byte) (lightListSelected.size() & 0xff);
+        for (int i = 0; i < lightListSelected.size(); i++) {
+            String lightMac = lightListSelected.get(i);
+            String[] lightMacParts = lightMac.split(":");
+            byte[] toMacPartsAddress = new byte[6];
+            for (int b = 0; b < 6; b++) {
+                Integer hex = Integer.parseInt(lightMacParts[b], 16);
+                toMacPartsAddress[b] = hex.byteValue();
+            }
+            switchProtocol = appendByteArray(switchProtocol, toMacPartsAddress, 4 + (i * 6));
+        }
+        return switchProtocol;
+    }
+
+    private byte[] getGroupProtocolForLight() {
+        int switchCount = 1;
+        int lightSize = (switchCount * 6) + 4;
+        byte[] lightProtocol = new byte[lightSize];
+
+        lightProtocol[0] = (byte) (0xF2 & 0xff);
+        lightProtocol[1] = (byte) (0x60 & 0xff);
+        lightProtocol[2] = (byte) (((switchCount * 6) + 1) & 0xff); //Length(Header, Command, Length 는 제외)
+        lightProtocol[3] = (byte) (0x01 & 0xff); //그루핑 스마트 조명 스위치 개수.
+
+        for (int i = 0; i < switchCount; i++) {
+            String lightMac = selectedSwitchMac;
+            String[] lightMacParts = lightMac.split(":");
+            byte[] toMacPartsAddress = new byte[6];
+            for (int b = 0; b < 6; b++) {
+                Integer hex = Integer.parseInt(lightMacParts[b], 16);
+                toMacPartsAddress[b] = hex.byteValue();
+            }
+            lightProtocol = appendByteArray(lightProtocol, toMacPartsAddress, 4 + (i * 6));
+        }
+        return lightProtocol;
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnGroup:
+                if(lightListSelected.size() == 0) {
+                    showNotiMsg(SwitchMapperRegisterActivity.this,"스마트 조명을 하나 이상 선택해주세요.");
+                } else {
+                    startGroupForSwitch();
+                    //initGroupDataForSwitch();
+                }
+                break;
+            case R.id.btnCompleted:
+                actvityExit();
+                break;
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case RESULT_ENABLE_BT_MAIN:
+                checkEnableBluetooth2();
+                break;
+            case RESULT_RETRY_BUT_NO_DEVICE:
+                finish();
+                break;
+            case RESULT_RETRY_SCAN_DEVICE:
+                if (resultCode == Activity.RESULT_OK) {
+                    isRetry= true;
+                    startScanningDevice();
+                } else {
+                    finish();
+                }
+                break;
+            case RESULT_SWITCH_MAPPER_BUZZER_SUCCESS:
+                if (resultCode == Activity.RESULT_OK) {
+                    startSwitchScanLight();
+                } else {
+                    selectedSwitchPosition = -1;
+                    changeUI(UI_STATUS_SHOW_SWITCH_LIST);
+                }
+                break;
+            case RESULT_LIGHT_BLINK_SUCCESS:
+                if (resultCode == Activity.RESULT_OK) {
+                    if(!lightListSelected.contains(selectedLightMac)) {
+                        lightListSelected.add(selectedLightMac);
+                        lightArrayAdapter.notifyDataSetChanged();
+                    }
+                } else {
+                    selectedLightPosition = -1;
+                    changeUI(UI_STATUS_SHOW_LIGHT_LIST);
+                }
+                break;
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        
+        DebugLogOutput(TAG, "bleDisconnect: Call");
+        for(String key : connectedDeviceMap.keySet()){
+            DebugLogOutput(TAG, "onDestroy] connectedDeviceMap bluetoothGatt disconnect / MAC :"+key);
+            connectedDeviceMap.get(key).disconnect();
+            connectedDeviceMap.get(key).close();
+        }
+        connectedDeviceMap.clear();
+
+        if(swithList != null) {
+            swithList.clear();
+        }
+
+        if(lightList != null) {
+            lightList.clear();
+        }
+
+        if(lightListSelected != null) {
+            lightListSelected.clear();
+        }
+
+        if(lightScanList != null) {
+            lightScanList.clear();
+        }
+    }
+
+    private void activityExit() {
+        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
+    private void setUI() {
+
+        connectedDeviceMap = new HashMap<String, BluetoothGatt>();
+
+        // Scan Device
+        listViewSwitch = findViewById(R.id.listViewSwitch);
+        switchArrayAdapter = new DeviceArrayAdapter(swithList,SCAN_TYPE_SWITCH);
+        listViewSwitch.setAdapter(switchArrayAdapter);
+        listViewSwitch.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+
+        // Scan Wifi
+        listViewLight = findViewById(R.id.listViewLight);
+        lightArrayAdapter = new DeviceArrayAdapter(lightList,SCAN_TYPE_LIGHT);
+        listViewLight.setAdapter(lightArrayAdapter);
+        listViewLight.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
+
+        // ui
+        layoutSwitchList = findViewById(R.id.layoutSwitchList);
+        layoutLightList = findViewById(R.id.layoutLightList);
+        layoutCompleted = findViewById(R.id.layoutCompleted);
+        txtSwitch = findViewById(R.id.txtSwitch);
+        txtLight = findViewById(R.id.txtLight);
+        txtCompleted = findViewById(R.id.txtCompleted);
+    }
+
+    private void changeUI(final int status) {
+        changeUI(status, null, -1);
+    }
+
+    private void changeUI(final int status,final String msg) {
+        changeUI(status, msg, -1);
+    }
+
+    private void changeUI(final int status,final String msg, final long hideTime) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                switch (status) {
+                    case UI_STATUS_LOADING:
+                        if(IsRunningProgress()){
+                            getProgressDialog().showTitle(msg);
+                        } else {
+                            if(hideTime > 0) {
+                                displayProgress(SwitchMapperRegisterActivity.this, msg, null, hideTime);
+                            } else {
+                                displayProgress(SwitchMapperRegisterActivity.this, msg, null, 10000);
+                            }
+                        }
+                        break;
+                    case UI_STATUS_SHOW_SWITCH_LIST:
+                        layoutSwitchList.setVisibility(View.VISIBLE);
+                        layoutLightList.setVisibility(View.GONE);
+                        layoutCompleted.setVisibility(View.GONE);
+                        if(msg!=null) {
+                            txtSwitch.setText(msg);
+                        }
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                    case UI_STATUS_SHOW_LIGHT_LIST:
+                        layoutSwitchList.setVisibility(View.GONE);
+                        layoutLightList.setVisibility(View.VISIBLE);
+                        layoutCompleted.setVisibility(View.GONE);
+                        if(msg!=null) {
+                            txtLight.setText(msg);
+                        }
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                    case UI_STATUS_SHOW_COMPLETED:
+                        findViewById(R.id.btnBack).setVisibility(View.INVISIBLE);
+                        layoutSwitchList.setVisibility(View.GONE);
+                        layoutLightList.setVisibility(View.GONE);
+                        layoutCompleted.setVisibility(View.VISIBLE);
+                        if(msg!=null) {
+                            txtCompleted.setText(msg);
+                        }
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                    case UI_STATUS_SHOW_FAIL:
+                        showNotiMsg(SwitchMapperRegisterActivity.this,msg);
+                        new Handler().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                actvityExit();
+                            }
+                        },3000);
+                        if(IsRunningProgress()) {
+                            DismissConnectDialog();
+                        }
+                        break;
+                }
+            }
+        });
+    }
+
+    private void actvityExit() {
+        Intent intent = new Intent(getApplicationContext(), ProfileActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                | Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
+    private void bleDisconnect(String mac) {
+        if(connectedDeviceMap.containsKey(mac)) {
+            if(connectedDeviceMap.get(mac) !=null) {
+                connectedDeviceMap.get(mac).disconnect();
+                connectedDeviceMap.get(mac).close();
+            }
+            connectedDeviceMap.remove(mac);
+        }
+    }
+
+    /**
+     * Ble 검색
+     */
+    private void startScanningDevice() {
+        changeUI(UI_STATUS_LOADING,"등록할 스마트 조명을 찾고 있습니다.");
+        scanLeDevice(true);
+    }
+
+    private void scanLeDevice(final boolean enable) {
+        handler = new Handler();
+        if (enable) {
+            swithList.clear();
+            lightList.clear();
+            lightListSelected.clear();
+            // Stops scanning after a pre-defined scan period.
+            handler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    bluetoothAdapter.stopLeScan(leScanCallback);
+
+                    if(switchArrayAdapter == null || lightArrayAdapter == null || (switchArrayAdapter.getCount() == 0) || (lightArrayAdapter.getCount() == 0)) {
+                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"검색된 장치가 없습니다.");
+                        retryScanDevice();
+                    } else {
+                        switchArrayAdapter.notifyDataSetChanged();
+                        lightArrayAdapter.notifyDataSetChanged();
+                        changeUI(UI_STATUS_SHOW_SWITCH_LIST,"등록하실 스마트 스위치를 \n" +
+                                "선택해 주세요.\n" +
+                                "선택하신 스마트 스위치의 \n" +
+                                "진동(소리)이 울립니다.\n");
+                    }
+                }
+            }, SCAN_PERIOD);
+            bluetoothAdapter.startLeScan(leScanCallback);
+        } else {
+            bluetoothAdapter.stopLeScan(leScanCallback);
+        }
+    }
+
+    private final BluetoothAdapter.LeScanCallback leScanCallback =
+            new BluetoothAdapter.LeScanCallback() {
+                @Override
+                public void onLeScan(final BluetoothDevice device, int rssi,
+                                     final byte[] scanRecord) {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (device.getName() != null && device.getName().toUpperCase().contains("ICON_LS")) {
+                                DebugLogOutput(TAG, "Scan Switch Register Record :" + byteArrayToHex(scanRecord));
+                                AmazonBLEinfo thisDevice = new AmazonBLEinfo(device.getName(),
+                                        device.getAddress(), device);
+                                if (!swithList.contains(thisDevice)) {
+                                    swithList.add(thisDevice);
+                                }
+                            } else if (device.getName() != null && device.getName().toUpperCase().contains("ICON_LM")) {
+                                DebugLogOutput(TAG, "Scan Light Register Record :" + byteArrayToHex(scanRecord));
+                                AmazonBLEinfo thisDevice = new AmazonBLEinfo(device.getName(),
+                                        device.getAddress(), device);
+                                if (!lightList.contains(thisDevice)) {
+                                    lightList.add(thisDevice);
+                                }
+                            }
+                        }
+                    });
+                }
+            };
+
+    private final BluetoothGattCallback swithGattCallback = new BluetoothGattCallback() {
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+            super.onConnectionStateChange(gatt, status, newState);
+
+            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "BluetoothGatt.STATE_CONNECTED State from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_CONNECTED, null);
+
+                gatt.discoverServices();
+
+            } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "bluetoothGatt.STATE_DISCONNECTED State from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_DISCONNECTED, null);
+
+            } else if (status != BluetoothGatt.GATT_SUCCESS) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "bluetoothGatt.GATT_FAIL from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+                bleDisconnect(gatt.getDevice().getAddress());
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_NOT_CONNECTED, null);
+            }
+        }
+
+        @Override
+        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+            super.onServicesDiscovered(gatt, status);
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onServicesDiscovered State from %s (%s) - status %d",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status
+            ));
+
+            BluetoothGattService deviceGattService = gatt.getService(hdcUUID.SWITCH_GATT_SERVICE_UUID);
+            if (deviceGattService == null) {
+                DebugLogOutput(TAG, "Switch # device No Gatt Service !!");
+                return;
+            }
+
+            bluetoothGattCharacteristic = deviceGattService.getCharacteristic(hdcUUID.SWITCH_NOTIFICATION_CHARACTERISTIC_UUID);
+            if (bluetoothGattCharacteristic == null) {
+                DebugLogOutput(TAG, "Switch # device No Gatt Notification Service !!");
+                return;
+            }
+
+            DebugLogOutput(TAG, "Switch/Set on Notification");
+            gatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);
+
+            if (hdcUUID.SWITCH_NOTIFICATION_CHARACTERISTIC_UUID.equals(bluetoothGattCharacteristic.getUuid())) {
+                DebugLogOutput(TAG, "Switch/Write Descriptor");
+                BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(hdcUUID.SWITCH_GATT_DESCRIPTORS_UUID);
+                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+                gatt.writeDescriptor(descriptor);
+            }
+        }
+
+        @Override
+        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+            super.onCharacteristicWrite(gatt, characteristic, status);
+            byte[] data = characteristic.getValue();
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onCharacteristicWrite State from %s (%s) - status %d - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status,
+                    byteArrayToHex(data)
+            ));
+        }
+
+        @Override
+        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+            super.onCharacteristicChanged(gatt, characteristic);
+            byte[] data = characteristic.getValue();
+
+            // non use notification
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onCharacteristicChanged State from %s (%s) - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    byteArrayToHex(data)
+            ));
+
+            if (data[0] == (byte) (bleProtocol.BLE_HEADER) && data[1] == (byte) (bleProtocol.BLE_COMMAND_STOA_MAC_ADDR_RSP_TO_APP)) {
+                DebugLogOutput(TAG, "SWITCH # get Notification");
+
+                try {
+                    String mac = "";
+                    mac = String.format("%02x", data[3] & 0xff).toUpperCase() + ":" +
+                            String.format("%02x", data[4] & 0xff).toUpperCase() + ":" +
+                            String.format("%02x", data[5] & 0xff).toUpperCase() + ":" +
+                            String.format("%02x", data[6] & 0xff).toUpperCase() + ":" +
+                            String.format("%02x", data[7] & 0xff).toUpperCase() + ":" +
+                            String.format("%02x", data[8] & 0xff).toUpperCase();
+
+                    DebugLogOutput(TAG, "onCharacteristicChanged: MAC : " + mac);
+
+                    RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_GET_NOTIFICATION, mac);
+
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            } else if (data[0] == (byte) (bleProtocol.BLE_HEADER) && data[1] == (byte) (bleProtocol.BLE_COMMAND_STOA_BUZZER_PLAY_RSP_TO_APP)) {
+                if(data[3] == (byte)0x01) {
+                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_SUCCESS");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_BUZZER_PROTOCOL_SUCCESS, null);
+                } else {
+                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_BUZZER_PROTOCOL_FAIL, null);
+                }
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_STOA_SWITCH_SCANRSP_TO_APP) {
+                int num = data[3];
+                int len = num * 8;
+                for (int i = 4; i < len; i += 8) {
+                    String scanResultInfo = String.format("ICON_LM_%02X%02X%02X", data[i + 3], data[i + 4], data[i + 5]);
+                    DebugLogOutput(TAG, "scanResultInfo[" + scanResultInfo + "]");
+                    lightScanList.add(String.format("%02X:%02X:%02X:%02X:%02X:%02X", data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5]));
+                }
+                RequestHandlerSendMessage(bleProtocolState.SCAN_RESULT_PROTOCOL_SUCCESS,null);
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_STOA_SWITCH_GROUPING_RSP_TO_APP) {
+                DebugLogOutput(TAG, "Switch/Write Protocol(GROUP) : " + byteArrayToHex(characteristic.getValue()));
+                if(data[4] == (byte)0x01) {
+                    DebugLogOutput(TAG, "get RSP_GROUP_SWITCH_SUCCESS");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_SUCCESS, null);
+                } else {
+                    DebugLogOutput(TAG, "get RSP_GROUP_SWITCH_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_FAIL, null);
+                }
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_STOA_INIT_SWITCH_GROUPING_RSP_TO_APP) {
+                if(data[3] == (byte)0x01) {
+                    DebugLogOutput(TAG, "get RSP_INIT_GROUP_SWITCH_SUCCES");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_SUCCES, null);
+                } else if(data[3] == (byte)0x03) {
+                    DebugLogOutput(TAG, "get RSP_INIT_GROUP_SWITCH_FAIL_OVER_LIGHT");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL_OVER_LIGHT, null);
+                } else {
+                    DebugLogOutput(TAG, "get RSP_INIT_GROUP_SWITCH_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_FAIL, null);
+                }
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+            super.onDescriptorWrite(gatt, descriptor, status);
+            byte[] data = descriptor.getValue();
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onDescriptorWrite State from %s (%s) - status %d - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status,
+                    byteArrayToHex(data)
+            ));
+            if(status_process == PROCESS_STATUS_BUZZER) {
+                DebugLogOutput(TAG, "스마트 스위치 소리 요청\nonDescriptorWrite: status_process = " + status_process);
+                RequestHandlerSendMessage(RSP_BUZZER_PROTOCOL_TIMEOUT, null);
+                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(), bleProtocol.buzzerSwitchProtocol);
+            } else if(status_process == PROCESS_STATUS_SCAN_LIGHT) {
+                DebugLogOutput(TAG, String.format("Before scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
+                bleProtocol.scanRequestSwitchProtocol[3] = (byte) (SCAN_COUNT & 0xff);
+                DebugLogOutput(TAG, String.format("After scanRequestSwitchProtocol : %s", byteArrayToHex(bleProtocol.scanRequestSwitchProtocol)));
+                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(),bleProtocol.scanRequestSwitchProtocol);
+                RequestHandlerSendMessage(SCAN_RESULT_PROTOCOL_FAIL,null);
+            } else if(status_process == PROCESS_STATUS_GROUPING_SWITCH) {
+                DebugLogOutput(TAG, "스마트 스위치 그룹 요청\nonDescriptorWrite: status_process = " + status_process);
+                RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_SWITCH_TIMEOUT, null);
+                switchBLEGattWriteRequestCommand(selectedSwitchMac,getGroupProtocolForSwitch());
+            } else if(status_process == PROCESS_STATUS_INIT_SWITCH) {
+                DebugLogOutput(TAG, "스마트 스위치 그룹 정보 초기화\nonDescriptorWrite: status_process = " + status_process);
+                RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_SWITCH_TIMEOUT, null);
+                switchBLEGattWriteRequestCommand(selectedSwitchMac,bleProtocol.initGroupDateSwitchProtocol);
+            } else {
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL, null);
+                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(), bleProtocol.getMacSwitchProtocol);
+            }
+
+        }
+    };
+
+    private final BluetoothGattCallback lightGattCallback = new BluetoothGattCallback() {
+        @Override
+        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+            super.onConnectionStateChange(gatt, status, newState);
+
+            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "BluetoothGatt.STATE_CONNECTED State from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_CONNECTED, null);
+
+                gatt.discoverServices();
+
+            } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "bluetoothGatt.STATE_DISCONNECTED State from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_DISCONNECTED, null);
+
+            } else if (status != BluetoothGatt.GATT_SUCCESS) {
+                DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                        "bluetoothGatt.GATT_FAIL from %s (%s) - status %d - state %d",
+                        gatt.getDevice().getName(),
+                        gatt.getDevice().getAddress(),
+                        status,
+                        newState
+                ));
+                bleDisconnect(gatt.getDevice().getAddress());
+                RequestHandlerSendMessage(bleProtocolState.BLUETOOTH_NOT_CONNECTED, null);
+            }
+        }
+
+        @Override
+        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+            super.onServicesDiscovered(gatt, status);
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onServicesDiscovered State from %s (%s) - status %d",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status
+            ));
+
+            BluetoothGattService deviceGattService = gatt.getService(hdcUUID.LIGHT_GATT_SERVICE_UUID);
+            if (deviceGattService == null) {
+                DebugLogOutput(TAG, "Light # device No Gatt Service !!");
+                return;
+            }
+
+            bluetoothGattCharacteristic = deviceGattService.getCharacteristic(hdcUUID.LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID);
+            if (bluetoothGattCharacteristic == null) {
+                DebugLogOutput(TAG, "Light # device No Gatt Notification Service !!");
+                return;
+            }
+
+            DebugLogOutput(TAG, "Light/Set on Notification");
+            gatt.setCharacteristicNotification(bluetoothGattCharacteristic, true);
+
+            if (hdcUUID.LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID.equals(bluetoothGattCharacteristic.getUuid())) {
+                DebugLogOutput(TAG, "Light/Write Descriptor");
+                BluetoothGattDescriptor descriptor = bluetoothGattCharacteristic.getDescriptor(hdcUUID.LIGHT_GATT_DESCRIPTORS_UUID);
+                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+                gatt.writeDescriptor(descriptor);
+            }
+        }
+
+        @Override
+        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+            super.onCharacteristicWrite(gatt, characteristic, status);
+            byte[] data = characteristic.getValue();
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onCharacteristicWrite State from %s (%s) - status %d - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status,
+                    byteArrayToHex(data)
+            ));
+        }
+
+        @Override
+        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+            super.onCharacteristicChanged(gatt, characteristic);
+            byte[] data = characteristic.getValue();
+
+            // non use notification
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onCharacteristicChanged State from %s (%s) - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    byteArrayToHex(data)
+            ));
+
+            if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_BLINK_LIGHT_RSP_TO_APP && data[2] == (byte)0x01) {
+                if (data[3] == (byte) 0x01) {
+                    DebugLogOutput(TAG, "get RSP_BLINK_PROTOCOL_SUCCESS");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_SUCCESS, null);
+                } else if (data[3] == (byte) 0x02) {
+                    DebugLogOutput(TAG, "get RSP_BLINK_PROTOCOL_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_BLINK_PROTOCOL_FAIL, null);
+                }
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_LIGHT_GROUPING_RSP_TO_APP && data[2] == (byte)0x03) {
+                DebugLogOutput(TAG, "Light/Write Protocol(GROUP) : " + byteArrayToHex(characteristic.getValue()));
+                /*
+                Error Code
+                0x01: Success, 1 번째 그룹스위치 설정 (최종 1 개)
+                0x02: Success, 2 번째 그룹스위치 설정 (최종 2 개)
+                0x03: Error, 3 번째 그룹스위치 설정 설정 (이미 2 개 설정되어 있음)
+                0x0A: Sucess, 이미 등록된 그룹스위치 설정 (재설정됨)
+                */
+                if(data[4] == (byte)0x01 || data[4] == (byte)0x02 || data[4] == (byte)0x0A) {
+                    DebugLogOutput(TAG, "get RSP_GROUP_LIGHT_SUCCESS");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_SUCCESS, null);
+                } else {
+                    DebugLogOutput(TAG, "get RSP_GROUP_LIGHT_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_FAIL, null);
+                }
+            } else if (data[0] == bleProtocol.BLE_HEADER && data[1] == bleProtocol.BLE_COMMAND_LTOA_INIT_LIGHT_GROUPING_RSP_TO_APP) {
+                if(data[3] == (byte)0x01) {
+                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_SUCCESS");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_SUCCES, null);
+                } else {
+                    DebugLogOutput(TAG, "get RSP_BUZZER_PROTOCOL_FAIL");
+                    RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_FAIL, null);
+                }
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
+            super.onDescriptorWrite(gatt, descriptor, status);
+            byte[] data = descriptor.getValue();
+
+            DebugLogOutput(TAG, String.format(Locale.getDefault(),
+                    "onDescriptorWrite State from %s (%s) - status %d - data %s",
+                    gatt.getDevice().getName(),
+                    gatt.getDevice().getAddress(),
+                    status,
+                    byteArrayToHex(data)
+            ));
+            if(status_process == PROCESS_STATUS_BLINK) {
+                DebugLogOutput(TAG, "조명 깜빡임 요청\nonDescriptorWrite: status_process = " + status_process);
+                RequestHandlerSendMessage(RSP_BLINK_PROTOCOL_TIMEOUT, null);
+                lightBLEGattWriteRequestCommand(gatt.getDevice().getAddress(), bleProtocol.blinkLightProtocol);
+            } else if(status_process == PROCESS_STATUS_GROUPING_LIGHT) {
+                DebugLogOutput(TAG, "조명 그룹 요청\nonDescriptorWrite: status_process = " + status_process);
+                RequestHandlerSendMessage(bleProtocolState.RSP_GROUP_LIGHT_TIMEOUT, null);
+                lightBLEGattWriteRequestCommand(gatt.getDevice().getAddress(),getGroupProtocolForLight());
+                DebugLogOutput(TAG, "groupLightProtocol : " + byteArrayToHex(getGroupProtocolForLight()));
+            } else if(status_process == PROCESS_STATUS_INIT_LIGHT) {
+                DebugLogOutput(TAG, "조명 그룹 초기화 요청\nonDescriptorWrite: status_process = " + status_process);
+                RequestHandlerSendMessage(bleProtocolState.RSP_INIT_GROUP_LIGHT_TIMEOUT, null);
+                switchBLEGattWriteRequestCommand(gatt.getDevice().getAddress(),bleProtocol.initGroupDateLightProtocol);
+                DebugLogOutput(TAG, "initGroupDateLightProtocol : " + byteArrayToHex(bleProtocol.initGroupDateLightProtocol));
+            }
+        }
+    };
+
+    private void RequestHandlerSendMessage(int deviceState, String mac) {
+        Message msg = new Message();
+        msg.what = deviceState;
+        if (deviceState == bleProtocolState.BLUETOOTH_GET_NOTIFICATION_FAIL) {
+            switchRegisterHandler.sendMessageDelayed(msg, 5000);
+        } else if (deviceState == bleProtocolState.BLUETOOTH_GET_NOTIFICATION) {
+            msg.obj = mac;
+            switchRegisterHandler.sendMessage(msg);
+        } else if(deviceState == RSP_BUZZER_PROTOCOL_TIMEOUT) {
+            switchRegisterHandler.sendMessageDelayed(msg, 5000);
+        } else if (deviceState == SCAN_RESULT_PROTOCOL_FAIL) {
+            switchRegisterHandler.sendMessageDelayed(msg, (SCAN_COUNT + 3) * 1000);
+        } else if (deviceState == RSP_GROUP_SWITCH_TIMEOUT) {
+            switchRegisterHandler.sendMessageDelayed(msg, 10000);
+        } else if (deviceState == RSP_GROUP_LIGHT_TIMEOUT) {
+            switchRegisterHandler.sendMessageDelayed(msg, 10000);
+        } else if(deviceState == RSP_INIT_GROUP_SWITCH_TIMEOUT) {
+            switchRegisterHandler.sendMessageDelayed(msg, 10000);
+        } else if(deviceState == RSP_INIT_GROUP_LIGHT_TIMEOUT) {
+            switchRegisterHandler.sendMessageDelayed(msg, 10000);
+        } else if(deviceState == RSP_BLINK_PROTOCOL_TIMEOUT) {
+            switchRegisterHandler.sendMessageDelayed(msg, 10000);
+        } else {
+            switchRegisterHandler.sendMessage(msg);
+        }
+    }
+
+    private void switchBLEGattWriteRequestCommand(String address, byte[] protocol) {
+        bluetoothGattCharacteristic = connectedDeviceMap.get(address).getService(hdcUUID.SWITCH_GATT_SERVICE_UUID)
+                .getCharacteristic(hdcUUID.SWITCH_GATT_WRITE_CHARACTERISTIC_UUID);
+        bluetoothGattCharacteristic.setValue(protocol);
+        connectedDeviceMap.get(address).writeCharacteristic(bluetoothGattCharacteristic);
+    }
+
+    private void lightBLEGattWriteRequestCommand(String address, byte[] protocol) {
+        bluetoothGattCharacteristic = connectedDeviceMap.get(address).getService(hdcUUID.LIGHT_GATT_SERVICE_UUID)
+                .getCharacteristic(hdcUUID.LIGHT_GATT_WRITE_CHARACTERISTIC_UUID);
+        bluetoothGattCharacteristic.setValue(protocol);
+        connectedDeviceMap.get(address).writeCharacteristic(bluetoothGattCharacteristic);
+    }
+
+    private void retryScanDevice() {
+        if(isRetry) {
+            Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL_WITH_ICON);
+            if(swithList.size() == 0 && lightList.size() == 0) {
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_light_device_title));
+            } else if(swithList.size() == 0) {
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_device_title));
+            } else {
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_light_device_title));
+            }
+            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_not_found_light_device_description_2));
+            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.ok));
+            startActivityForResult(intent, RESULT_RETRY_BUT_NO_DEVICE);
+        } else {
+            Intent intent = new Intent(SwitchMapperRegisterActivity.this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO_WITH_ICON);
+            if(swithList.size() == 0 && lightList.size() == 0) {
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_light_device_title));
+            } else if(swithList.size() == 0) {
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_switch_device_title));
+            } else {
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, getString(R.string.popup_not_found_light_device_title));
+            }
+            intent.putExtra(PopupActivity.POPUP_BODY_TEXT, getString(R.string.popup_not_found_light_device_description_1));
+            intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, getString(R.string.retry));
+            intent.putExtra(PopupActivity.POPUP_CANCEL_BUTTON_TEXT, getString(R.string.cancel));
+            startActivityForResult(intent, RESULT_RETRY_SCAN_DEVICE);
+        }
+    }
+
+    private void checkEnableBluetooth() {
+        if(!isBlueToothEnable(this)){
+            Intent intent = new Intent(this, PopupActivity.class);
+            intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_ENABLE_BT);
+            startActivityForResult(intent, RESULT_ENABLE_BT_MAIN);
+        } else {
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    startScanningDevice();
+                }
+            }, FIRST_SCAN_PERIOD);
+        }
+    }
+
+    private void checkEnableBluetooth2() {
+        if(!isBlueToothEnable(this)){
+            showNotiMsg(this,"블루투스 활성화를 실패하였습니다.",true);
+        } else {
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    startScanningDevice();
+                }
+            }, FIRST_SCAN_PERIOD);
+        }
+    }
+
+    /**
+     * Ble 리스트 아답터
+     */
+    public class DeviceArrayAdapter extends BaseAdapter {
+        // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+        private List<AmazonBLEinfo> listViewItemList;
+        private int deviceType = 0;
+
+        // ListViewAdapter의 생성자
+        public DeviceArrayAdapter(List<AmazonBLEinfo> listItem, int type) {
+            listViewItemList = listItem;
+            deviceType = type;
+        }
+
+        // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+        @Override
+        public int getCount() {
+            return listViewItemList==null?0:listViewItemList.size() ;
+        }
+
+        // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+        @Override
+        public View getView(final int position, View convertView, ViewGroup parent) {
+            final int pos = position;
+            final Context context = parent.getContext();
+
+            // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+            if (convertView == null) {
+                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                convertView = inflater.inflate(R.layout.listview_add_device_list, parent, false);
+            }
+
+            // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
+            LinearLayout layoutDevice = convertView.findViewById(R.id.layoutDevice);
+            TextView txtName = convertView.findViewById(R.id.txtName);
+            ImageView imgIcon = convertView.findViewById(R.id.imgIcon);
+
+            // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
+            if(deviceType == SCAN_TYPE_SWITCH) {
+                imgIcon.setImageResource(R.drawable.icon_add_device_switch_mapper);
+            } else if(deviceType == SCAN_TYPE_LIGHT) {
+                imgIcon.setImageResource(R.drawable.icon_add_device_light);
+                layoutDevice.setSelected(false);
+
+                for(String s : lightListSelected) {
+                    if(listViewItemList.get(position).getMacAddr().equals(s)) {
+                        layoutDevice.setSelected(true);
+                    }
+                }
+            }
+            txtName.setText(listViewItemList.get(position).getName());
+
+            return convertView;
+        }
+
+        // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+        @Override
+        public long getItemId(int position) {
+            return position ;
+        }
+
+        // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
+        @Override
+        public Object getItem(int position) {
+            return listViewItemList.get(position) ;
+        }
+
+        public void removeAll() {
+            listViewItemList.clear();
+        }
+    }
+}

+ 56 - 0
app/src/main/java/kr/co/icontrols/iotplatform/SwitchMapperRegisterGuideActivity.java

@@ -0,0 +1,56 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+public class SwitchMapperRegisterGuideActivity extends AppCompatActivity implements View.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_switch_mapper_register_guide);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnRegister:
+                startActivity(new Intent(this,SwitchMapperRegisterActivity.class));;
+                break;
+        }
+    }
+}

+ 356 - 0
app/src/main/java/kr/co/icontrols/iotplatform/TestHttpApiActivity.java

@@ -0,0 +1,356 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.utils.CommonUtils;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import kr.co.icontrols.iotplatform.utils.SharedPrefUtil;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_09_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_09_STRING;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_NO_CONTENT;
+
+public class TestHttpApiActivity extends AppCompatActivity implements View.OnClickListener {
+    private String TAG = "TestHttpApiActivity";
+
+    final int U_1 = 0;
+    final int U_2 = 1;
+    final int U_3 = 2;
+    final int U_4 = 3;
+    final int U_5 = 4;
+    final int U_6 = 5;
+    final int M_1 = 6;
+    final int M_2 = 7;
+    final int M_3 = 8;
+    final int M_4 = 9;
+    final int M_5 = 10;
+    final int M_6 = 11;
+    final int M_7 = 12;
+    final int M_8 = 13;
+    final int M_9 = 14;
+    final int M_10 = 15;
+    final int G_1 = 16;
+    final int G_2 = 17;
+    final int G_3 = 18;
+    final int G_4 = 19;
+    final int G_5 = 20;
+    final int G_6 = 21;
+    final int G_7 = 22;
+    final int G_8 = 23;
+    final int G_9 = 24;
+    final int D_1 = 25;
+    final int D_2 = 26;
+    final int D_3 = 27;
+    final int D_4 = 28;
+    final int D_5 = 29;
+    final int D_6 = 30;
+    final int D_7 = 31;
+    final int H_1 = 32;
+    final int H_2 = 33;
+    final int H_3 = 34;
+    final int H_4 = 35;
+    final int H_5 = 36;
+    final int E_1 = 37;
+
+    Spinner mSpinnerType;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_test_http_api);
+
+        String[] arrayList = {
+                "(1) 회원정보 조회",
+                "(2) 회원정보 수정",
+                "(3) 회원정보 검증",
+                "(4) 회원 비밀번호 수정",
+                "(5) 인증 요청",
+                "(6) 회원탈퇴",
+                "(1) (마스터) 게스트 멤버추가",
+                "(2) (마스터) 게스트 초대이력 조회",
+                "(3) (마스터) 게스트 초대이력 삭제",
+                "(4) (마스터) 게스트 멤버삭제",
+                "(5) (마스터) 게스트 멤버 목록 조회",
+                "(6) (마스터) 마스터에서 탈퇴",
+                "(7) (게스트) 초대 이력 목록 조회",
+                "(8) (게스트) 초대 승락/거절",
+                "(9) (게스트) 마스터 정보 조회",
+                "(10) (게스트) 멤버탈퇴",
+                "(1) 디바이스 그룹 등록",
+                "(2) 디바이스 그룹 조회",
+                "(3) 디바이스 그룹 수정",
+                "(4) 디바이스 그룹 삭제",
+                "(5) 디바이스 그룹 목록 조회",
+                "(6) 디바이스 그룹 목록 순서 변경",
+                "(7) 디바이스 그룹 목록 별 디바이스 목록",
+                "(8) 디바이스 그룹에 디바이스 연결",
+                "(9) 디바이스 그룹에 디바이스 삭제",
+                "(1) 디바이스 매핑",
+                "(2) 디바이스 상세정보 조회",
+                "(3) 디바이스 상세정보 수정",
+                "(4) 디바이스 삭제(디바이스 초기화)",
+                "(5) 디바이스 디바이스 최신 펌웨어 정보 조회",
+                "(6) 디바이스 최신 펌웨어 업데이트",
+                "(7) 디바이스 전체 목록 조회",
+                "(1) 위치 정보 등록",
+                "(2) 위치 정보 조회",
+                "(3) 위치 정보 수정",
+                "(4) 위치 정보 삭제",
+                "(5) 날씨 정보 조회",
+                "(1) 데이터 암호화",
+        };
+
+
+
+
+        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getApplicationContext(),android.R.layout.simple_spinner_dropdown_item,arrayList);
+        mSpinnerType = findViewById(R.id.spinnerType);
+        mSpinnerType.setAdapter(arrayAdapter);
+
+
+        String msg = "";
+        msg += "ID Token : "+ AppHelper.getCurrSession().getIdToken().getJWTToken() + "\n";
+        msg += "AC Token : "+ AppHelper.getCurrSession().getAccessToken().getJWTToken() + "\n";
+        msg += "UserName : "+ AppHelper.getCurrSession().getUsername() + "\n";
+        msg += "RF Token : "+ AppHelper.getCurrSession().getRefreshToken().getToken() + "\n";
+        ((TextView)findViewById(R.id.txtResult)).setText(msg);
+
+        CommonUtils.ErrorLogOutput(TAG, AppHelper.getCurrSession().getIdToken().getJWTToken());
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnSend:
+                ((TextView)findViewById(R.id.txtResult)).setText("");
+                try {
+                    switch (mSpinnerType.getSelectedItemPosition()) {
+                        case U_1:
+                            HttpApi.getUserInfo(mCallback);
+                            break;
+                        case U_2:
+                            HttpApi.changeUserInfo("","테스트","01030477403",false,false, "", mCallback);
+                            break;
+                        case U_3:
+                            HttpApi.isValidateUserInfo("red0aqua@naver.com","일명","01030477403", mCallback);
+                            break;
+                        case U_4:
+                            //liM81102!
+                            HttpApi.changeUserPassword(AppHelper.getCurrSession().getUsername(),"AQICAHjbIZwaQRyoV4KR8B7jV1DXHoxakY1ucl7Keca1obZv+QGVpZa2yFdPAHSjdY64iSfiAAAAZzBlBgkqhkiG9w0BBwagWDBWAgEAMFEGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMv79gC22hObsD2YR8AgEQgCQyUMRtrG6Zosisxe35cTK8jMtUFV6asX0mt74UORWp+UivRBU=",mCallback);
+                            break;
+                        case U_5:
+                            HttpApi.requestConfirmUser(true,true,"red0aqua@naver.com",mCallback);
+                            break;
+                        case U_6:
+                            HttpApi.deleteUserInfo(AppHelper.getCurrSession().getUsername(),"liM811021",mCallback);
+                            break;
+                        case M_1:
+                            HttpApi.inviteGuestForMaster(true,"common.gnst@gmail.com",mCallback);
+                            break;
+                        case M_2:
+                            HttpApi.getHistoryForInviteGuest(mCallback);
+                            break;
+                        case M_3:
+                            HttpApi.deleteHistoryForInviteGuest("39772b61-dc47-450a-9648-bae4d086bace",mCallback);
+                            break;
+                        case M_4:
+                            //미진행 작업.
+                            ((TextView)findViewById(R.id.txtResult)).setText("미진행 작업.");
+                            //HttpApi.deleteGuest("",mCallback);
+                            break;
+                        case M_5:
+                            HttpApi.getGuestList(mCallback);
+                            break;
+                        case M_6:
+                            HttpApi.deleteMasterInfo(mCallback);
+                            break;
+                        case M_7:
+                            HttpApi.getHistoryForInvite(mCallback);
+                            break;
+                        case M_8:
+                            HttpApi.acceptToInvite("47aacad0-4ec2-4686-9ef9-be8d130bba24OK",true,mCallback);
+                            break;
+                        case M_9:
+                            HttpApi.getMasterInfo(mCallback);
+                        case M_10:
+                            //미진행 작업.
+                            ((TextView)findViewById(R.id.txtResult)).setText("미진행 작업.");
+                            //HttpApi.deleteGuestInfo("",mCallback);
+                            break;
+                        case G_1:
+                            //기능 구현 완료.
+                            ((TextView)findViewById(R.id.txtResult)).setText("기능 구현 완료");
+                            break;
+                        case G_2:
+                            //기본그룹
+                            HttpApi.getDeviceGroup("d8fcf171-396f-45b2-a8d1-24cc5dfe4d0f", mCallback);
+                            break;
+                        case G_3:
+                            HttpApi.modifyDeviceGroup("a0c46d4a-f2f5-4ab8-b79a-978c0bdd58e9","테스트9999",GROUP_IMG_09_STRING,GROUP_BACKGROUND_09_STRING,mCallback);
+                            break;
+                        case G_4:
+                            //기능 구현 완료
+                            ((TextView)findViewById(R.id.txtResult)).setText("기능 구현 완료");
+                            break;
+                        case G_5:
+                            HttpApi.getDeviceGroupList(mCallback);
+                            break;
+                        case G_6:
+                            //기능 구현 완료.
+                            ((TextView)findViewById(R.id.txtResult)).setText("기능 구현 완료");
+                            break;
+                        case G_7:
+                            //기본그룹
+                            HttpApi.getDeviceListInDeviceGroup("d8fcf171-396f-45b2-a8d1-24cc5dfe4d0f",mCallback);
+                            break;
+                        case G_8:
+                            HttpApi.connectDeviceInDeviceGroup("d8fcf171-396f-45b2-a8d1-24cc5dfe4d0f","a12020081800004303",mCallback);
+                        break;
+                        case G_9:
+                            //미진행 작업.
+                            ((TextView)findViewById(R.id.txtResult)).setText("미진행 작업.");
+                            break;
+                        case D_1:
+                            //HttpApi.mappingDevice("a12020091710000015",mCallback);
+                            HttpApi.mappingDevice("a12020081800004403",mCallback);
+                            //HttpApi.mappingDevice("a12020081800004302","테스트조명09","icon_01","background_01",mCallback);
+                            break;
+                        case D_2:
+                            HttpApi.getDeviceInfo("a12020081800004403",mCallback);
+                            break;
+                        case D_3:
+                            HttpApi.modifyDeviceInfo("a12020081800004403","우리집조명13",mCallback);
+                            break;
+                        case D_4:
+                            HttpApi.deleteDevice("a12020081800004403",mCallback);
+                            break;
+                        case D_5:
+                            HttpApi.getFirmwareInfoAtDevice("a12020081800004403",mCallback);
+                            break;
+                        case D_6:
+                            HttpApi.updateFirmwareInfoAtDevice("a12020081800004403",mCallback);
+                            break;
+                        case D_7:
+                            HttpApi.getAllDevice(mCallback);
+                            break;
+                        case H_1:
+                            HttpApi.registerLocationInfo("37.6642357913395","127.040679390304","서울","도봉구","방학1동",mCallback);
+                            break;
+                        case H_2:
+                            HttpApi.getLocationInfo(mCallback);
+                            break;
+                        case H_3:
+                            HttpApi.modifyLocationInfo("e46e4d34-16f5-46b2-9579-8a8948cedf03","37.6642357913395","127.040679390304","서울","도봉구","방학1동",mCallback);
+                            break;
+                        case H_4:
+                            HttpApi.deleteLocationInfo("e46e4d34-16f5-46b2-9579-8a8948cedf03",mCallback);
+                            break;
+                        case H_5:
+                            HttpApi.getWeatherInfo("e46e4d34-16f5-46b2-9579-8a8948cedf03",mCallback);
+                            break;
+                        case E_1:
+                            HttpApi.encryptData("liM81102!",mCallback);
+                            break;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    final String msg =  "HttpApi.TEST(Exception)\n" + e.toString();
+                    ErrorLogOutput(TAG, msg);
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            ((TextView)findViewById(R.id.txtResult)).setText(msg);
+                        }
+                    });
+                }
+                break;
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+        }
+    }
+
+    Callback mCallback = new Callback() {
+        @Override
+        public void onFailure(Call call, IOException e) {
+            e.printStackTrace();
+            final String msg =  "HttpApi.TEST(onFailure)\n" + e.toString();
+            ErrorLogOutput(TAG, msg);
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    ((TextView)findViewById(R.id.txtResult)).setText(msg);
+                }
+            });
+        }
+
+        @Override
+        public void onResponse(Call call, Response response) throws IOException {
+            String temp = "HttpApi.TEST(onResponse) Response code : " + response.code() + "\nresponse : " + response.toString()+"\n";
+            ErrorLogOutput(TAG, "HttpApi.TEST(onResponse) Response code : " + response.code() + "\nresponse : " + response.toString());
+            try {
+                JSONObject json_data = new JSONObject(response.body().string());
+                ErrorLogOutput(TAG, "HttpApi.TEST(onResponse) body : " + json_data.toString());
+                temp += "HttpApi.TEST(onResponse) body : " + json_data.toString();
+            } catch (JSONException e) {
+                ErrorLogOutput(TAG, "HttpApi.TEST(onResponse) JSONException : " + e.toString());
+                temp += "HttpApi.TEST(onResponse) JSONException : " + e.toString();
+            }
+
+            final String msg = temp;
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    ((TextView)findViewById(R.id.txtResult)).setText(msg);
+                }
+            });
+        }
+    };
+}

+ 240 - 0
app/src/main/java/kr/co/icontrols/iotplatform/TestIotMqttActivity.java

@@ -0,0 +1,240 @@
+package kr.co.icontrols.iotplatform;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.auth.CognitoCachingCredentialsProvider;
+import com.amazonaws.internal.StaticCredentialsProvider;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttClientStatusCallback;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttManager;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttNewMessageCallback;
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttQos;
+import com.amazonaws.regions.Region;
+import com.amazonaws.regions.Regions;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+
+public class TestIotMqttActivity extends AppCompatActivity implements View.OnClickListener {
+
+    private String TAG = "TestIotMqttActivity";
+    private String COGNITO_POOL_ID = "ap-northeast-2:0e617292-1b9c-4208-b64b-b2d03729b25b";
+    //"a12020091710000015"
+    private String TOPIC1 = "$aws/things/a12020091710000015/shadow/get"; //publish
+    private String TOPIC2 = "$aws/things/a12020091710000015/shadow/get/accepted";
+    private String TOPIC3 = "$aws/things/a12020091710000015/shadow/get/rejected";
+    private String TOPIC4 = "$aws/things/a12020091710000015/shadow/update"; //publish
+    private String TOPIC5 = "$aws/things/a12020091710000015/shadow/update/accepted";
+    private String TOPIC6 = "$aws/things/a12020091710000015/shadow/update/delta";
+    private String ACCOUNT_END_POINT_PREFIX = "a139udwm0q3u52-ats";
+    private String END_POINT = "wss://a139udwm0q3u52-ats.iot.ap-northeast-2.amazonaws.com";
+    private String ACCESS_KEY = "AKIAUSTWEOGGE4XFHBWL";
+    private String SECRET_KEY = "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";
+
+    private EditText txtInput;
+    private TextView txtResult;
+
+    private AWSCredentials awsCredentials;
+    private static AWSCredentialsProvider credentialsProvider;
+    //private static CognitoCachingCredentialsProvider credentialsProvider;
+    private static AWSIotMqttManager mqttManager;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_test_iot_mqtt);
+
+        txtInput = findViewById(R.id.txtInput);
+        txtResult = findViewById(R.id.txtResult);
+
+        txtResult.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                txtResult.setText("");
+                return false;
+            }
+        });
+
+        /*
+        //Amazon Cognito 인증 공급자를 초기화합니다
+        credentialsProvider = new CognitoCachingCredentialsProvider(
+                getApplicationContext(),
+                COGNITO_POOL_ID, // 자격 증명 풀 ID
+                Regions.AP_NORTHEAST_2 // 리전
+        );
+        */
+
+        //Amazon Cognito 인증 공급자를 초기화합니다
+        credentialsProvider = new StaticCredentialsProvider(
+                new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY));
+
+        //intialize unnqique clientid as client to iot aws
+        Long tsLong = System.currentTimeMillis()/1000;
+        String clientId = tsLong.toString();
+
+        mqttManager = new AWSIotMqttManager(
+                clientId/*"android_iot_app_test_mqtt"*/,
+                Region.getRegion(Regions.AP_NORTHEAST_2),
+                ACCOUNT_END_POINT_PREFIX
+        );
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                awsCredentials = credentialsProvider.getCredentials();
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        findViewById(R.id.btnConnect).setEnabled(true);
+                        Toast.makeText(TestIotMqttActivity.this, "credentials ok?", Toast.LENGTH_SHORT).show();
+                    }
+                });
+            }
+        }).start();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onClick(View v) {
+        /*
+        final View fv = v;
+        fv.setEnabled(false);
+        Timer buttonTimer = new Timer();
+        buttonTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            fv.setEnabled(true);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        } finally {
+
+                        }
+                    }
+                });
+            }
+        }, 500);
+        */
+
+        switch (v.getId()) {
+            case R.id.btnBack:
+                onBackPressed();
+                break;
+            case R.id.btnSend:
+                try {
+                    if(txtInput.getText().toString().isEmpty()) {
+                        mqttManager.publishString(/*"{\n" +
+                            "    \"state\": {\n" +
+                            "        \"desired\" : {\n" +
+                            "            \"power\" : 1,\n" +
+                            "            \"dimm\" : 20\n" +
+                            "        }\n" +
+                            "    }\n" +
+                            "}"+*/txtInput.getText().toString(), TOPIC1, AWSIotMqttQos.QOS0);
+                    } else {
+                        mqttManager.publishString("{\n" +
+                            "    \"state\": {\n" +
+                            "        \"desired\" : {\n" +
+                            "            \"power\" : 1,\n" +
+                            "            \"dimm\" : "+txtInput.getText().toString()+"\n" +
+                            "        }\n" +
+                            "    }\n" +
+                            "}", TOPIC4, AWSIotMqttQos.QOS0);
+                    }
+
+                } catch (Exception e) {
+                    ErrorLogOutput(TAG, "Publish error. / " + e);
+                }
+                txtInput.setText("");
+                break;
+            case R.id.btnConnect:
+                if(credentialsProvider != null) {
+                    // connect to AWS IoT using credentials provider
+                    mqttManager.connect(credentialsProvider, new AWSIotMqttClientStatusCallback() {
+                        @Override
+                        public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if (status == AWSIotMqttClientStatus.Connecting) {
+                                        txtResult.setText("Connecting..."+"\n"+txtResult.getText());
+                                    } else if (status == AWSIotMqttClientStatus.Connected) {
+                                        txtResult.setText("Connected"+"\n"+txtResult.getText());
+
+                                        // subscribe to MQTT topic
+                                        mqttManager.subscribeToTopic(TOPIC2, AWSIotMqttQos.QOS0, new AWSIotMqttNewMessageCallback() {
+                                            @Override
+                                            public void onMessageArrived(String topic, byte[] data) {
+                                                DebugLogOutput(TAG, "Client persistent-client-id-1 received a message on topic:" + topic);
+                                                DebugLogOutput(TAG, "onMessageArrived / "+TOPIC5+"\n"+new String(data));
+                                                txtResult.setText(new String(data) + "\n" + txtResult.getText());
+                                            }
+                                        });
+
+                                        mqttManager.subscribeToTopic(TOPIC3, AWSIotMqttQos.QOS0, new AWSIotMqttNewMessageCallback() {
+                                            @Override
+                                            public void onMessageArrived(String topic, byte[] data) {
+                                                DebugLogOutput(TAG, "Client persistent-client-id-1 received a message on topic:" + topic);
+                                                DebugLogOutput(TAG, "onMessageArrived / "+TOPIC6+"\n"+new String(data));
+                                                txtResult.setText(new String(data) + "\n" + txtResult.getText());
+                                            }
+                                        });
+
+                                        mqttManager.subscribeToTopic(TOPIC5, AWSIotMqttQos.QOS0, new AWSIotMqttNewMessageCallback() {
+                                            @Override
+                                            public void onMessageArrived(String topic, byte[] data) {
+                                                DebugLogOutput(TAG, "Client persistent-client-id-1 received a message on topic:" + topic);
+                                                DebugLogOutput(TAG, "onMessageArrived / "+TOPIC5+"\n"+new String(data));
+                                                txtResult.setText("" + new String(data) + "\n" + txtResult.getText());
+                                            }
+                                        });
+
+                                        mqttManager.subscribeToTopic(TOPIC6, AWSIotMqttQos.QOS0, new AWSIotMqttNewMessageCallback() {
+                                            @Override
+                                            public void onMessageArrived(String topic, byte[] data) {
+                                                DebugLogOutput(TAG, "Client persistent-client-id-1 received a message on topic:" + topic);
+                                                DebugLogOutput(TAG, "onMessageArrived / "+TOPIC6+"\n"+new String(data));
+                                                txtResult.setText(new String(data) + "\n" + txtResult.getText());
+                                            }
+                                        });
+
+                                    } else if (status == AWSIotMqttClientStatus.Reconnecting) {
+                                        if (throwable != null) {
+                                            DebugLogOutput(TAG, "Connection error. / " + throwable);
+                                        }
+                                        txtResult.setText("Reconnecting"+"\n"+txtResult.getText());
+                                    } else if (status == AWSIotMqttClientStatus.ConnectionLost) {
+                                        if (throwable != null) {
+                                            DebugLogOutput(TAG, "Connection error. / " + throwable);
+                                            throwable.printStackTrace();
+                                        }
+                                        txtResult.setText("Disconnected"+"\n"+txtResult.getText());
+                                    } else {
+                                        txtResult.setText("Disconnected"+"\n"+txtResult.getText());
+                                    }
+                                }
+                            });
+                        }
+                    });
+                } else {
+                    Toast.makeText(TestIotMqttActivity.this, "credentials is null", Toast.LENGTH_SHORT).show();
+                }
+                break;
+        }
+    }
+}

+ 321 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/DeviceListAdapter.java

@@ -0,0 +1,321 @@
+package kr.co.icontrols.iotplatform.adapter;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.amazonaws.mobileconnectors.iot.AWSIotMqttNewMessageCallback;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.logging.LogRecord;
+
+import kr.co.icontrols.iotplatform.AWS.AmazonBLEinfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.IotPlatformApplication;
+import kr.co.icontrols.iotplatform.Mqtt.MqttController;
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.utils.CommonUtils;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.Constants.MQTT_PUBLISH_DELAY_TIME;
+import static kr.co.icontrols.iotplatform.utils.Constants.NOTING;
+import static kr.co.icontrols.iotplatform.utils.Constants.NOT_CONNECTED;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_OFF;
+import static kr.co.icontrols.iotplatform.utils.Constants.POWER_ON;
+
+public class DeviceListAdapter extends BaseAdapter {
+
+    private String TAG = "DeviceListAdapter";
+
+    // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+    private List<DeviceInfo> listViewItemList;
+    private ArrayList<Integer> listOnOff = new ArrayList<>();
+    private Activity activity;
+    private String groupName;
+
+    // ListViewAdapter의 생성자
+    public DeviceListAdapter(Activity activity, List<DeviceInfo> listItem, String groupName) {
+        this.activity = activity;
+        listViewItemList = listItem;
+        this.groupName = groupName;
+
+        for(DeviceInfo d : listViewItemList) {
+            listOnOff.add(NOT_CONNECTED);
+        }
+    }
+
+    public DeviceListAdapter(Activity activity, List<DeviceInfo> listItem) {
+        this.activity = activity;
+        listViewItemList = listItem;
+        groupName = "";
+        for(DeviceInfo d : listViewItemList) {
+            listOnOff.add(NOT_CONNECTED);
+        }
+    }
+
+    // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+    @Override
+    public int getCount() {
+        return listViewItemList==null?0:listViewItemList.size() ;
+    }
+
+    // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        final int pos = position;
+        final Context context = parent.getContext();
+
+        // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+        if (convertView == null) {
+
+        }
+
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        convertView = inflater.inflate(R.layout.listview_device_list, parent, false);
+
+        // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
+        final LinearLayout layoutDevice = convertView.findViewById(R.id.layoutDevice);
+        final TextView txtName = convertView.findViewById(R.id.txtName);
+        final TextView txtGroupName = convertView.findViewById(R.id.txtGroupName);
+        final TextView txtStatus = convertView.findViewById(R.id.txtStatus);
+        final ImageView imgIcon = convertView.findViewById(R.id.imgIcon);
+
+        // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
+        txtName.setText(listViewItemList.get(position).friendlyName.isEmpty()?listViewItemList.get(position).sharedFriendlyName.isEmpty()?IotPlatformApplication.getInstance().getString(R.string.no_name):listViewItemList.get(position).sharedFriendlyName:listViewItemList.get(position).friendlyName);
+        txtGroupName.setText(listViewItemList.get(position).dgroupName.isEmpty()?groupName:listViewItemList.get(position).dgroupName);
+        txtStatus.setText(activity.getString(R.string.not_connected));
+        txtStatus.setVisibility(View.INVISIBLE);
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if(txtStatus.getVisibility() != View.VISIBLE) {
+                    txtStatus.setVisibility(View.VISIBLE);
+                }
+            }
+        },2000);
+
+        layoutDevice.setSelected(false);
+
+        final String topicP = String.format(context.getString(R.string.topic_get_status),listViewItemList.get(position).deviceType,listViewItemList.get(position).deviceId);
+        final String topicS1 = String.format(context.getString(R.string.topic_response_status),listViewItemList.get(position).deviceType,listViewItemList.get(position).deviceId);
+        final String topicS2 = String.format(context.getString(R.string.topic_update_accepted),listViewItemList.get(position).deviceId);
+
+
+        MqttController.getInstance().subscribeToTopic(topicS1, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+                int power = NOT_CONNECTED;
+                int dimming = NOTING;
+                int colorTemp = NOTING;
+
+                try {
+                    JSONObject jsonObject = new JSONObject(new String(data));
+                    if(!jsonObject.isNull("data")) {
+                        JSONObject jsonState = jsonObject.getJSONObject("data");
+                        if (!jsonState.isNull("state")) {
+                            JSONObject jsonReported = jsonState.getJSONObject("state");
+                            //Power
+                            if (!jsonReported.isNull("power")) {
+                                //power off
+                                power = jsonReported.getInt("power");
+
+                            }
+                            //dimming
+                            if (!jsonReported.isNull("dimm")) {
+                                dimming = jsonReported.getInt("dimm");
+                            }
+
+                            //colorTemp
+                            if (!jsonReported.isNull("temp")) {
+                                colorTemp = jsonReported.getInt("temp");
+                            }
+                        }
+                    }
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+
+                final int powerFix = power;
+                final int dimmingFix = dimming;
+                final int colorTempFix = colorTemp;
+
+                listOnOff.remove(pos);
+                listOnOff.add(pos,powerFix);
+
+                activity.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        imgIcon.setImageResource(R.drawable.icon_add_device_light);
+                        imgIcon.setBackgroundResource(R.drawable.background_circle_add_device_icon);
+                        if(powerFix == 0) {
+                            layoutDevice.setSelected(false);
+                            txtStatus.setVisibility(View.VISIBLE);
+                            txtStatus.setText(activity.getString(R.string.off));
+                        } else if(powerFix == 1) {
+                            layoutDevice.setSelected(true);
+                            txtStatus.setVisibility(View.VISIBLE);
+                            txtStatus.setText(activity.getString(R.string.on));
+                        }
+
+                        if(dimmingFix != -1) {
+
+                        }
+
+                        if(colorTempFix != -1) {
+
+                        }
+                    }
+                });
+            }
+        });
+
+        MqttController.getInstance().subscribeToTopic(topicS2, new AWSIotMqttNewMessageCallback() {
+            @Override
+            public void onMessageArrived(String topic, byte[] data) {
+                DebugLogOutput(TAG, "MQTT SUB / "+topic + "\n" + new String(data));
+
+                int temp_power = NOT_CONNECTED;
+
+                try {
+                    JSONObject jsonObject = new JSONObject(new String(data));
+
+                    if(!jsonObject.isNull("data")) {//Get status topic
+                        JSONObject jsonState = jsonObject.getJSONObject("data");
+                        if (!jsonState.isNull("state")) {
+                            JSONObject jsonReported = jsonState.getJSONObject("state");
+                            //Power
+                            if (!jsonReported.isNull("power")) {
+                                //power off
+                                temp_power = jsonReported.getInt("power");
+
+                            }
+                        }
+                    } else if(!jsonObject.isNull("state")) {  //Update topic
+                        JSONObject jsonState = jsonObject.getJSONObject("state");
+                        if (!jsonState.isNull("reported")) {
+                            JSONObject jsonReported = jsonState.getJSONObject("reported");
+                            //Power
+                            if (!jsonReported.isNull("power")) {
+                                //power off
+                                temp_power = jsonReported.getInt("power");
+
+                            }
+                        }
+
+                        if (!jsonState.isNull("desired")) {
+                            JSONObject jsonDesired = jsonState.getJSONObject("desired");
+                            //Power
+                            if (!jsonDesired.isNull("power")) {
+                                //power off
+                                temp_power = jsonDesired.getInt("power");
+
+                            }
+                        }
+
+                        if(!jsonState.isNull("power")) {
+                            temp_power = jsonState.getInt("power");
+                        }
+                    }
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+
+                final int powerFix = temp_power;
+
+                listOnOff.remove(pos);
+                listOnOff.add(pos,powerFix);
+
+                DebugLogOutput(TAG, "LIGHT_STATUS - power : "+powerFix);
+                if(powerFix != NOT_CONNECTED) {
+                    activity.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            imgIcon.setImageResource(R.drawable.icon_add_device_light);
+                            imgIcon.setBackgroundResource(R.drawable.background_circle_add_device_icon);
+                            if(powerFix == 0) {
+                                layoutDevice.setSelected(false);
+                                txtStatus.setVisibility(View.VISIBLE);
+                                txtStatus.setText(activity.getString(R.string.off));
+                            } else if(powerFix == 1) {
+                                layoutDevice.setSelected(true);
+                                txtStatus.setVisibility(View.VISIBLE);
+                                txtStatus.setText(activity.getString(R.string.on));
+                            }
+                        }
+                    });
+                }
+            }
+        });
+
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                MqttController.getInstance().publish(topicP,"{\"msgId\" : \""+UUID.randomUUID().toString()+"\"}");
+                //MqttController.getInstance().publish(topicP,"{\"msgId\" : \""+listViewItemList.get(position).deviceId+"\"}");
+            }
+        },MQTT_PUBLISH_DELAY_TIME);
+
+        return convertView;
+    }
+
+    // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+    @Override
+    public long getItemId(int position) {
+        return position ;
+    }
+
+    // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
+    @Override
+    public Object getItem(int position) {
+        return listViewItemList.get(position) ;
+    }
+
+    public boolean isAllOn() {
+        boolean resault = true;
+        for(Integer b : listOnOff){
+            if(b == POWER_OFF){
+                resault = false;
+            }
+        }
+        return resault;
+    }
+
+    public void setOnOff() {
+        long delayTime = 10;
+        boolean b = isAllOn();
+
+        for(int i = 0 ; i < listViewItemList.size() ; i++) {
+            final int pos = i;
+            if (listOnOff.get(pos) != NOT_CONNECTED) {
+                new Handler().postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        final String topicUpdateP = String.format(activity.getString(R.string.topic_update), listViewItemList.get(pos).deviceId);
+                        MqttController.getInstance().publish(topicUpdateP, "{\"state\": {\"desired\" : {\"power\" : " + (b ? "0" : "1") + "}}}");
+                    }
+                }, delayTime);
+                delayTime += 5;
+            }
+        }
+    }
+
+    public void removeAll() {
+        listViewItemList.clear();
+        listOnOff.clear();
+    }
+}

+ 88 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/DeviceSelectAdapter.java

@@ -0,0 +1,88 @@
+package kr.co.icontrols.iotplatform.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.IotPlatformApplication;
+import kr.co.icontrols.iotplatform.R;
+
+public class DeviceSelectAdapter extends BaseAdapter {
+    // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+    private List<DeviceInfo> listViewItemList;
+
+    // ListViewAdapter의 생성자
+    public DeviceSelectAdapter(List<DeviceInfo> listItem) {
+        listViewItemList = listItem;
+    }
+
+    // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+    @Override
+    public int getCount() {
+        return listViewItemList==null?0:listViewItemList.size() ;
+    }
+
+    // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        final int pos = position;
+        final Context context = parent.getContext();
+
+        // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+        if (convertView == null) {
+            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            convertView = inflater.inflate(R.layout.listview_device_select_list_item, parent, false);
+        }
+
+        // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
+        TextView txtDeviceName = convertView.findViewById(R.id.txtDeviceName);
+        TextView txtGroupName = convertView.findViewById(R.id.txtGroupName);
+        ImageView imgIcon = convertView.findViewById(R.id.imgIcon);
+        ImageView imgChk = convertView.findViewById(R.id.imgChk);
+
+        // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
+        txtDeviceName.setText(listViewItemList.get(position).friendlyName.isEmpty()?listViewItemList.get(position).sharedFriendlyName.isEmpty()?IotPlatformApplication.getInstance().getString(R.string.no_name):listViewItemList.get(position).sharedFriendlyName:listViewItemList.get(position).friendlyName);
+        txtGroupName.setText(listViewItemList.get(position).dgroupName);
+        imgChk.setSelected(listViewItemList.get(position).isSelected());
+
+        return convertView;
+    }
+
+    // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+    @Override
+    public long getItemId(int position) {
+        return position ;
+    }
+
+    // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
+    @Override
+    public Object getItem(int position) {
+        return listViewItemList.get(position) ;
+    }
+
+    public void setSelected(int position) {
+        listViewItemList.get(position).isSelected = (listViewItemList.get(position).isSelected()?0:1);
+    }
+
+    public boolean isSelected() {
+        boolean resault = false;
+
+        for(DeviceInfo d : listViewItemList) {
+            if(d.isSelected()) {
+                resault = true;
+            }
+        }
+        return resault;
+    }
+
+    public void removeAll() {
+        listViewItemList.clear();
+    }
+}

+ 93 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/FirstTimeLoginAttributesDisplayAdapter.java

@@ -0,0 +1,93 @@
+/*
+ * Copyright 2013-2017 Amazon.com,
+ * Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Amazon Software License (the "License").
+ * You may not use this file except in compliance with the
+ * License. A copy of the License is located at
+ *
+ *      http://aws.amazon.com/asl/
+ *
+ * or in the "license" file accompanying this file. This file is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, express or implied. See the License
+ * for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kr.co.icontrols.iotplatform.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.cognito.ItemToDisplay;
+
+public class FirstTimeLoginAttributesDisplayAdapter extends BaseAdapter {
+    private Context context;
+    private int count;
+    private static LayoutInflater layoutInflater;
+
+    public FirstTimeLoginAttributesDisplayAdapter(Context context) {
+        this.context = context;
+
+        count = AppHelper.getFirstTimeLogInItemsCount();
+
+        layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+    @Override
+    public int getCount() {
+        return count;
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return position;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        Holder holder;
+
+        if(convertView == null) {
+            convertView = layoutInflater.inflate(R.layout.fields_generic, null);
+            holder = new Holder();
+            holder.label = (TextView) convertView.findViewById(R.id.textViewUserDetailLabel);
+            holder.data = (TextView) convertView.findViewById(R.id.editTextUserDetailInput);
+            holder.message = (TextView) convertView.findViewById(R.id.textViewUserDetailMessage);
+
+            convertView.setTag(holder);
+        }
+        else {
+            holder = (Holder) convertView.getTag();
+        }
+
+        ItemToDisplay item = AppHelper.getUserAttributeForFirstLogInCheck(position);
+        holder.label.setText(item.getLabelText());
+        holder.label.setTextColor(item.getLabelColor());
+        holder.data.setHint(item.getLabelText());
+        holder.data.setText(item.getDataText());
+        holder.data.setTextColor(item.getDataColor());
+        holder.message.setText(item.getMessageText());
+        holder.message.setTextColor(item.getMessageColor());
+
+        return convertView;
+    }
+
+    // Helper class to recycle View's
+    static class Holder {
+        TextView label;
+        TextView data;
+        TextView message;
+    }
+}

+ 108 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/GroupSelectAdapter.java

@@ -0,0 +1,108 @@
+package kr.co.icontrols.iotplatform.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.R;
+
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCircleBitmap;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageResource;
+
+public class GroupSelectAdapter extends BaseAdapter {
+    // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+    private List<DeviceGroupInfo> listViewItemList;
+
+    // ListViewAdapter의 생성자
+    public GroupSelectAdapter(List<DeviceGroupInfo> listItem) {
+        listViewItemList = listItem;
+    }
+
+    public GroupSelectAdapter(List<DeviceGroupInfo> listItem, String selectGroupId) {
+        listViewItemList = listItem;
+
+        for(int i = 0; i < listViewItemList.size() ; i++) {
+            if(selectGroupId.equals(listViewItemList.get(i).dgroupId)) {
+                listViewItemList.get(i).isSelected = 1;
+            }
+        }
+    }
+
+    // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+    @Override
+    public int getCount() {
+        return listViewItemList==null?0:listViewItemList.size() ;
+    }
+
+    // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        final int pos = position;
+        final Context context = parent.getContext();
+
+        // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+        if (convertView == null) {
+            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            convertView = inflater.inflate(R.layout.listview_group_select_list_item, parent, false);
+        }
+
+        // 화면에 표시될 View(Layout이 inflate된)으로부터 위젯에 대한 참조 획득
+        TextView txtGroupName = convertView.findViewById(R.id.txtGroupName);
+        TextView txtDeviceCount = convertView.findViewById(R.id.txtDeviceCount);
+        ImageView imgIcon = convertView.findViewById(R.id.imgIcon);
+        ImageView imgChk = convertView.findViewById(R.id.imgChk);
+
+        // Data Set(listViewItemList)에서 position에 위치한 데이터 입력
+        txtGroupName.setText(listViewItemList.get(position).friendlyName);
+        txtDeviceCount.setText(listViewItemList.get(position).deviceCount + "개 스마트 조명");
+        imgIcon.setImageBitmap(getCircleBitmap(getGroupImageResource(listViewItemList.get(position).icon)));
+        imgChk.setSelected(listViewItemList.get(position).isSelected());
+
+        return convertView;
+    }
+
+    // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+    @Override
+    public long getItemId(int position) {
+        return position ;
+    }
+
+    // 지정한 위치(position)에 있는 데이터 리턴 : 필수 구현
+    @Override
+    public Object getItem(int position) {
+        return listViewItemList.get(position) ;
+    }
+
+    public void setSelected(int position) {
+        listViewItemList.get(position).isSelected = listViewItemList.get(position).isSelected()?0:1;
+    }
+
+    public void setSelectedForSingle(int position) {
+        for(int i = 0 ; i < listViewItemList.size() ; i ++) {
+            listViewItemList.get(i).isSelected = (position==i?1:0);
+        }
+    }
+
+    public boolean isSelected() {
+        boolean resault = false;
+
+        for(DeviceGroupInfo d : listViewItemList) {
+            if(d.isSelected()) {
+                resault = true;
+            }
+        }
+        return resault;
+    }
+
+    public void removeAll() {
+        listViewItemList.clear();
+    }
+}

+ 100 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/KaKaoLoctionAdapter.java

@@ -0,0 +1,100 @@
+package kr.co.icontrols.iotplatform.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.KakaoLocationInfo;
+import kr.co.icontrols.iotplatform.R;
+
+public class KaKaoLoctionAdapter extends BaseAdapter {
+    // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+    private ArrayList<KakaoLocationInfo> listViewItem = new ArrayList<>();
+    private int selectedIndex;
+
+    // ListviewTotalAdapter 생성자
+    public KaKaoLoctionAdapter() {
+        selectedIndex = -1;
+    }
+
+    // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+    @Override
+    public int getCount() {
+        return listViewItem.size();
+    }
+
+    // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        final int pos = position;
+        final Context context = parent.getContext();
+
+        // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+        if (convertView == null) {
+
+        }
+
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        convertView = inflater.inflate(R.layout.listview_kakao_location_info_item, parent, false);
+
+        TextView txtAddressName = convertView.findViewById(R.id.txtAddressName);
+        txtAddressName.setText(listViewItem.get(position).address_name);
+
+        if(position == selectedIndex) {
+            txtAddressName.setSelected(true);
+        } else {
+            txtAddressName.setSelected(false);
+        }
+
+        return convertView;
+    }
+
+    // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    /**
+     * 지정한 포지션의 아이템 획득.
+     * @param position
+     * @return
+     */
+    @Override
+    public Object getItem(int position) {
+        return listViewItem.get(position);
+    }
+
+    public ArrayList<KakaoLocationInfo> getAllItem() {
+        return listViewItem;
+    }
+
+    /**
+     * 아이템 추가 함수.
+     * @param item
+     */
+    public void addItem(KakaoLocationInfo item) {
+        listViewItem.add(item);
+    }
+
+    public void setSelected(int pos) {
+        selectedIndex = pos;
+        notifyDataSetChanged();
+    }
+
+    /**
+     * 리스트에 활당된 모든 아이템에 대한 메모리 반환.
+     */
+    public void removeAllItem() {
+        selectedIndex = -1;
+
+        if (listViewItem != null) {
+            listViewItem.clear();
+        }
+    }
+}

+ 129 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/MemberAdapter.java

@@ -0,0 +1,129 @@
+package kr.co.icontrols.iotplatform.adapter;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.AdminMemberActivity;
+import kr.co.icontrols.iotplatform.DataClass.MemberInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.PopupActivity;
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.utils.DownloadImageTask;
+
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CONFIRM_DELETE_MEMBER;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CONFIRM_SEND_INVITE_MSG;
+
+public class MemberAdapter extends BaseAdapter {
+
+    private Context context;
+
+    // Adapter에 추가된 데이터를 저장하기 위한 ArrayList
+    private ArrayList<MemberInfo> listViewItem = new ArrayList<MemberInfo>();
+
+    // ListviewTotalAdapter 생성자
+    public MemberAdapter(Context context) {
+        this.context = context;
+    }
+
+    // Adapter에 사용되는 데이터의 개수를 리턴. : 필수 구현
+    @Override
+    public int getCount() {
+        return listViewItem.size();
+    }
+
+    // position에 위치한 데이터를 화면에 출력하는데 사용될 View를 리턴. : 필수 구현
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        final int pos = position;
+        final Context context = parent.getContext();
+
+        // "listview_item" Layout을 inflate하여 convertView 참조 획득.
+        if (convertView == null) {
+
+        }
+
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        convertView = inflater.inflate(R.layout.listview_memberinfo_item, parent, false);
+
+        final TextView txtName = convertView.findViewById(R.id.txtName);
+        TextView txtDelete = convertView.findViewById(R.id.txtDelete);
+        ImageView imgProfile = convertView.findViewById(R.id.imgProfile);
+
+        txtName.setText(listViewItem.get(position).nickname);
+        if(listViewItem.get(position).profile != null && !listViewItem.get(position).profile.isEmpty()) {
+            new DownloadImageTask(imgProfile).execute(listViewItem.get(position).profile);
+        } else {
+            imgProfile.setImageResource(R.drawable.profile_thum_176);
+        }
+        txtDelete.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent intent = new Intent(context, PopupActivity.class);
+                intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_YESNO);
+                intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, txtName.getText().toString() + "님을 멤버에서 삭제하시겠습니까?");
+                AdminMemberActivity.sSelectedMemberId = listViewItem.get(pos).customerId;
+                ((Activity)context).startActivityForResult(intent, RESULT_CONFIRM_DELETE_MEMBER);
+            }
+        });
+
+        return convertView;
+    }
+
+    // 지정한 위치(position)에 있는 데이터와 관계된 아이템(row)의 ID를 리턴. : 필수 구현
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    /**
+     * 지정한 포지션의 아이템 획득.
+     * @param position
+     * @return
+     */
+    @Override
+    public Object getItem(int position) {
+        return listViewItem.get(position);
+    }
+
+    public ArrayList<MemberInfo> getAllItem() {
+        return listViewItem;
+    }
+
+    /**
+     * 아이템 추가 함수.
+     * @param item
+     */
+    public void addItem(MemberInfo item) {
+        listViewItem.add(item);
+    }
+
+    /**
+     * 리스트에 특정 아이템 삭제.
+     */
+    public void removeItem(String customerId) {
+        for(int i = 0 ; i < listViewItem.size() ; i++) {
+            if(listViewItem.get(i).customerId.equals(customerId)) {
+                listViewItem.remove(i);
+                i--;
+            }
+        }
+    }
+
+    /**
+     * 리스트에 활당된 모든 아이템에 대한 메모리 반환.
+     */
+    public void removeAllItem() {
+        if (listViewItem != null) {
+            listViewItem.clear();
+        }
+    }
+}

+ 318 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/MyGroupTabPagerAdapter.java

@@ -0,0 +1,318 @@
+package kr.co.icontrols.iotplatform.adapter;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.viewpager.widget.PagerAdapter;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.DataClass.DeviceInfo;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.Database.DBUtil;
+import kr.co.icontrols.iotplatform.LightDetailActivity;
+import kr.co.icontrols.iotplatform.LightRegisterDeviceActivity;
+import kr.co.icontrols.iotplatform.PopupActivity;
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.utils.HttpApi;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static com.amazonaws.mobile.auth.core.internal.util.ThreadUtils.runOnUiThread;
+import static kr.co.icontrols.iotplatform.Database.DatabaseHelper.TABLE_DEVICE_RANK;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_BUNDLE;
+import static kr.co.icontrols.iotplatform.PopupActivity.POPUP_IMG_INDEX;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.DebugLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getCircleBitmap;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupBackgournResourceWithIcon;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageIndex;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.getGroupImageResource;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.showNotiMsg;
+import static kr.co.icontrols.iotplatform.utils.Constants.DEFAULT_RANK;
+import static kr.co.icontrols.iotplatform.utils.Constants.EXTRA_DEVICE_INFO;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_CHANGE_GROUP_IMG;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_INPUT_GROUP_NAME;
+import static kr.co.icontrols.iotplatform.utils.HttpApi.HTTP_OK;
+
+public class MyGroupTabPagerAdapter extends PagerAdapter {
+    private final String TAG = "MyGroupTabPagerAdapter";
+
+    private ArrayList<DeviceGroupInfo> mListGroup;
+    private Activity mActivity;
+
+    public interface PressSettingButton {
+        void showGroupSettingMenu();
+        void goToAddDevice();
+    }
+
+    private PressSettingButton pressSettingButton;
+
+    public MyGroupTabPagerAdapter(Activity activity, ArrayList<DeviceGroupInfo> list, PressSettingButton pressSettingButton) {
+        this.mActivity = activity;
+        this.mListGroup = list;
+        this.pressSettingButton = pressSettingButton;
+    }
+
+    @Override
+    public int getCount() {
+        return mListGroup.size();
+    }
+
+    @Override
+    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
+        return (view ==(View)object);
+    }
+
+    @Override
+    public Object instantiateItem(ViewGroup container, final int position) {
+        ViewGroup viewGroup = (ViewGroup) mActivity.getLayoutInflater().inflate(
+                R.layout.layout_my_group_tab_viewpager, null);
+
+        final RelativeLayout layoutTopRelativeLayout = viewGroup.findViewById(R.id.layoutTop);
+        final LinearLayout layoutRegisterDevice = viewGroup.findViewById(R.id.layoutRegisterDevice);
+        final LinearLayout btnRegisterDevice = viewGroup.findViewById(R.id.btnRegisterDevice);
+        final LinearLayout layoutListView = viewGroup.findViewById(R.id.layoutListView);
+        final LinearLayout layoutGroupName = viewGroup.findViewById(R.id.layoutGroupName);
+        final LinearLayout layoutAllOnOff = viewGroup.findViewById(R.id.layoutAllOnOff);
+        final ListView listView = viewGroup.findViewById(R.id.listView);
+        final ImageView imgGroup = viewGroup.findViewById(R.id.imgGroup);
+        final TextView txtGroupName = viewGroup.findViewById(R.id.txtGroupName);
+        final TextView txtDeviceCount = viewGroup.findViewById(R.id.txtDeviceCount);
+        final TextView txtAddDevice = viewGroup.findViewById(R.id.txtAddDevice);
+        final TextView txtAllOnOff = viewGroup.findViewById(R.id.txtAllOnOff);
+        final ImageButton btnSetting = viewGroup.findViewById(R.id.btnSetting);
+
+
+        if(UserInfo.deviceCount > 0) {
+            txtAddDevice.setText(mActivity.getString(R.string.add_device_for_group));
+            btnRegisterDevice.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if(pressSettingButton != null) {
+                        pressSettingButton.goToAddDevice();
+                    }
+                }
+            });
+        } else {
+            txtAddDevice.setText(mActivity.getString(R.string.add_device));
+            btnRegisterDevice.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mActivity.startActivity(new Intent(mActivity, LightRegisterDeviceActivity.class));
+                }
+            });
+        }
+
+        btnSetting.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if(pressSettingButton != null) {
+                    pressSettingButton.showGroupSettingMenu();
+                }
+            }
+        });
+
+        layoutTopRelativeLayout.setBackgroundResource(getGroupBackgournResourceWithIcon(mListGroup.get(position).icon));
+        imgGroup.setImageBitmap(getCircleBitmap(getGroupImageResource(mListGroup.get(position).icon)));
+        txtGroupName.setText(mListGroup.get(position).friendlyName);
+        final int pos = position;
+        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final DeviceInfo item = (DeviceInfo) parent.getItemAtPosition(position);
+                if(item.dgroupName.isEmpty() && item.dgroupId.isEmpty()) {
+                    item.dgroupName = mListGroup.get(pos).friendlyName;
+                    item.dgroupId = mListGroup.get(pos).dgroupId;
+                }
+                Intent intent = new Intent(mActivity, LightDetailActivity.class);
+                intent.putExtra(EXTRA_DEVICE_INFO, (Parcelable) item);
+                mActivity.startActivity(intent);
+            }
+        });
+
+        if(position != 0) {
+            layoutGroupName.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Intent intent = new Intent(mActivity, PopupActivity.class);
+                    intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_INPUT);
+                    Bundle b = new Bundle();
+                    if (txtGroupName.getText().toString().isEmpty()) {
+                        b.putString(PopupActivity.POPUP_INPUT, "");
+                    } else {
+                        b.putString(PopupActivity.POPUP_INPUT, txtGroupName.getText().toString());
+                    }
+                    intent.putExtra(POPUP_BUNDLE, b);
+                    intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, mActivity.getString(R.string.change_group_name));
+                    mActivity.startActivityForResult(intent, RESULT_INPUT_GROUP_NAME);
+                }
+            });
+        }
+
+        imgGroup.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                {
+                    Intent intent = new Intent(mActivity, PopupActivity.class);
+                    intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_SELECT_GROUP_IMG);
+                    intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, mActivity.getString(R.string.group_image));
+                    intent.putExtra(POPUP_IMG_INDEX, getGroupImageIndex(mListGroup.get(position).icon));
+                    mActivity.startActivityForResult(intent, RESULT_CHANGE_GROUP_IMG);
+                }
+            }
+        });
+
+
+        try{
+            HttpApi.getDeviceListInDeviceGroup(mListGroup.get(position).dgroupId, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onFailure)\n" + e.toString());
+                    showNotiMsg(mActivity,"스마트 조명 목록 조회에 실패하였습니다");
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HTTP_OK) {
+                        WarnLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) : HTTP_OK");
+                        final ArrayList<DeviceInfo> deviceInfoList = new ArrayList<>();
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            JSONArray jarrayAll = new JSONArray(json_data.getString("devices"));
+
+                            for (int i = 0; i < jarrayAll.length(); i++) {
+                                JSONObject jObject = jarrayAll.getJSONObject(i);
+                                DeviceInfo item = new DeviceInfo(
+                                        jObject.isNull("deviceId") ? "" : jObject.getString("deviceId"),
+                                        jObject.isNull("deviceType") ? "" : jObject.getString("deviceType"),
+                                        jObject.isNull("friendlyName") ? "" : jObject.getString("friendlyName"),
+                                        jObject.isNull("sharedFriendlyName") ? "" : jObject.getString("sharedFriendlyName"),
+                                        jObject.isNull("dgroupId") ? "" : jObject.getString("dgroupId"),
+                                        jObject.isNull("dgroupName") ? "" : jObject.getString("dgroupName"),
+                                        jObject.isNull("ownerId") ? "" : jObject.getString("ownerId"),
+                                        DEFAULT_RANK,
+                                        DEFAULT_RANK
+                                );
+
+                                DeviceInfo localDbItem = DBUtil.getDeviceInfoForId(TABLE_DEVICE_RANK, item.deviceId);
+                                if (localDbItem != null) {
+                                    item.rank = localDbItem.rank;
+                                    item.groupRank = localDbItem.groupRank;
+                                }
+
+                                deviceInfoList.add(item);
+                            }
+
+                            if(deviceInfoList.size() > 1) {
+                                for(int i = 0 ; i < deviceInfoList.size()-1 ; i ++) {
+                                    for(int j = i+1 ; j < deviceInfoList.size() ; j ++) {
+                                        if(deviceInfoList.get(i).groupRank > deviceInfoList.get(j).groupRank) {
+                                            DeviceInfo item = deviceInfoList.get(j);
+                                            deviceInfoList.remove(j);
+                                            deviceInfoList.add(i,item);
+                                        }
+                                    }
+                                }
+                            }
+
+                            final int deviceCount = json_data.isNull("count") ? 0 : json_data.getInt("count");
+
+                            for(int i = 0 ; i < UserInfo.dgroups.size() ; i++) {
+                                if(UserInfo.dgroups.get(i).dgroupId.equals(mListGroup.get(position).dgroupId)){
+                                    UserInfo.dgroups.get(i).deviceCount = deviceCount;
+                                }
+                            }
+
+                            //UI 업데이트
+                            runOnUiThread(new Runnable() {
+                                @Override
+                                public void run() {
+                                    if(deviceInfoList.size() == 0) {
+                                        layoutRegisterDevice.setVisibility(View.VISIBLE);
+                                        layoutListView.setVisibility(View.GONE);
+                                    } else {
+                                        layoutRegisterDevice.setVisibility(View.GONE);
+                                        layoutListView.setVisibility(View.VISIBLE);
+                                        final DeviceListAdapter deviceListAdapter = new DeviceListAdapter(mActivity, deviceInfoList,mListGroup.get(position).friendlyName);
+                                        listView.setAdapter(deviceListAdapter);
+
+                                        layoutAllOnOff.setOnClickListener(new View.OnClickListener() {
+                                            @Override
+                                            public void onClick(View v) {
+                                                deviceListAdapter.setOnOff();
+                                            }
+                                        });
+                                    }
+                                    txtDeviceCount.setText(deviceCount+"개 스마트 조명 등록");
+
+                                }
+                            });
+
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(JSONException)\n" + e.toString());
+                        }
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(mActivity,"스마트 조명 목록 조회에 실패하였습니다" + " : " + response.code());
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                }
+            });
+        } catch(Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.getDeviceListInDeviceGroup(Exception)\n" + e.toString());
+            showNotiMsg(mActivity,"스마트 조명 목록 조회에 실패하였습니다");
+        }
+
+        container.addView(viewGroup);
+
+        return viewGroup;
+    }
+        @Override
+    public int getItemPosition(Object object) {
+        return POSITION_NONE;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup collection, int position, Object view) {
+        //must be overridden else throws exception as not overridden.
+        DebugLogOutput(TAG, "destroyItem : " + collection.getChildCount()+"");
+        collection.removeView((View) view);
+    }
+
+    public void removeAll() {
+        if(mListGroup != null) {
+            mListGroup.clear();
+        }
+    }
+}

+ 105 - 0
app/src/main/java/kr/co/icontrols/iotplatform/adapter/UserAttributesAdapter.java

@@ -0,0 +1,105 @@
+/*
+ * Copyright 2013-2017 Amazon.com,
+ * Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Amazon Software License (the "License").
+ * You may not use this file except in compliance with the
+ * License. A copy of the License is located at
+ *
+ *      http://aws.amazon.com/asl/
+ *
+ * or in the "license" file accompanying this file. This file is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, express or implied. See the License
+ * for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kr.co.icontrols.iotplatform.adapter;
+
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import kr.co.icontrols.iotplatform.cognito.ItemToDisplay;
+
+public class UserAttributesAdapter extends BaseAdapter {
+    private String TAG = "UserAttributesAdapter";
+    private Context context;
+    private int count;
+    private static LayoutInflater layoutInflater;
+
+    public UserAttributesAdapter(Context context) {
+        this.context = context;
+
+        count = AppHelper.getItemCount();
+
+        layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+    @Override
+    public int getCount() {
+        return count;
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return position;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        Holder holder;
+
+        if(convertView == null) {
+            convertView = layoutInflater.inflate(R.layout.fields_generic, null);
+            holder = new Holder();
+            holder.label = (TextView) convertView.findViewById(R.id.textViewUserDetailLabel);
+            holder.data = (TextView) convertView.findViewById(R.id.editTextUserDetailInput);
+            holder.message = (TextView) convertView.findViewById(R.id.textViewUserDetailMessage);
+
+            convertView.setTag(holder);
+        }
+        else {
+            holder = (Holder) convertView.getTag();
+        }
+
+        ItemToDisplay item = AppHelper.getItemForDisplay(position);
+        holder.label.setText(item.getLabelText());
+        holder.label.setTextColor(item.getLabelColor());
+        holder.data.setHint(item.getLabelText());
+        holder.data.setText(item.getDataText());
+        holder.data.setTextColor(item.getDataColor());
+        int resID = 0;
+        if(item.getDataDrawable() != null) {
+            if(item.getDataDrawable().equals("checked")) {
+                resID = R.drawable.ui_chk_chked;
+            }
+            else if(item.getDataDrawable().equals("not_checked")) {
+                resID = R.drawable.ui_chk;
+            }
+        }
+        holder.data.setCompoundDrawablesWithIntrinsicBounds(0, 0, resID, 0);
+        holder.message.setText(item.getMessageText());
+        holder.message.setTextColor(item.getMessageColor());
+
+        return convertView;
+    }
+
+    // Helper class to recycle View's
+    static class Holder {
+        TextView label;
+        TextView data;
+        TextView message;
+    }
+}

+ 588 - 0
app/src/main/java/kr/co/icontrols/iotplatform/cognito/AppHelper.java

@@ -0,0 +1,588 @@
+/*
+ * Copyright 2013-2017 Amazon.com,
+ * Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Amazon Software License (the "License").
+ * You may not use this file except in compliance with the
+ * License. A copy of the License is located at
+ *
+ *      http://aws.amazon.com/asl/
+ *
+ * or in the "license" file accompanying this file. This file is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, express or implied. See the License
+ * for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kr.co.icontrols.iotplatform.cognito;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.Log;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserAttributes;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserDetails;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserPool;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession;
+import com.amazonaws.regions.Regions;
+import com.amazonaws.services.cognitoidentityprovider.model.AttributeType;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_AUTO_UPDATE;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_CONFIRMED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_EMAIL;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_EMAIL_VERIFIED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_AUTO_UPDATE;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_CONFIRMED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_EMAIL;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_EMAIL_VERIFIED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_INVITED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_NICKNAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_PHONENUMBER;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_PHONENUMBER_VERIFIED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_PROFILE;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_PUSH_ACCESS;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_FIELD_UID;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_INVITED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_NICKNAME;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_PHONENUMBER;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_PHONENUMBER_VERIFIED;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_PROFILE;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_PUSH_ACCESS;
+import static kr.co.icontrols.iotplatform.utils.Constants.COGNITO_UID;
+
+public class AppHelper {
+    private static final String TAG = "AppHelper";
+    // App settings
+
+    private static List<String> attributeDisplaySeq;
+    private static Map<String, String> signUpFieldsC2O;
+    private static Map<String, String> signUpFieldsO2C;
+
+    private static AppHelper appHelper;
+    private static CognitoUserPool userPool;
+    private static String user;
+    private static CognitoDevice newDevice;
+
+    private static CognitoUserAttributes attributesChanged;
+    private static List<AttributeType> attributesToDelete;
+
+    private static List<ItemToDisplay> currDisplayedItems;
+    private static  int itemCount;
+
+    private static List<ItemToDisplay> trustedDevices;
+    private static int trustedDevicesCount;
+    private static List<CognitoDevice> deviceDetails;
+    private static CognitoDevice thisDevice;
+    private static boolean thisDeviceTrustState;
+
+    private static List<ItemToDisplay> firstTimeLogInDetails;
+    private static Map<String, String> firstTimeLogInUserAttributes;
+    private static List<String> firstTimeLogInRequiredAttributes;
+    private static int firstTimeLogInItemsCount;
+    private static Map<String, String> firstTimeLogInUpDatedAttributes;
+    private static String firstTimeLoginNewPassword;
+
+    private static List<ItemToDisplay> mfaOptions;
+    private static List<String> mfaAllOptionsCode;
+
+    // The lines below allow app users to register in your Cognito User Pool. Use
+    // a Cognito Identity Pool to control the user's access to your account resources.
+    // https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html
+
+    // Change the next three lines of code to run this demo on your user pool
+
+    /**
+     * Add your pool id here
+     */
+    private static final String userPoolId = "ap-northeast-2_uH9o0zBGj";
+
+    /**
+     * Add you app id
+     */
+    private static final String clientId = "12fj066eb79942cgrflu999udh";
+
+    /**
+     * App secret associated with your app id - if the App id does not have an associated App secret,
+     * set the App secret to null.
+     * e.g. clientSecret = null;
+     */
+    private static final String clientSecret = null;
+
+    /**
+     * Set Your User Pools region.
+     * e.g. if your user pools are in US East (N Virginia) then set cognitoRegion = Regions.US_EAST_1.
+     */
+    private static final Regions cognitoRegion = Regions.AP_NORTHEAST_2;
+
+    // User details from the service
+    private static CognitoUserSession currSession;
+    private static CognitoUserDetails userDetails;
+
+    // User details to display - they are the current values, including any local modification
+    private static boolean phoneVerified;
+    private static boolean emailVerified;
+
+    private static boolean phoneAvailable;
+    private static boolean emailAvailable;
+
+    private static Set<String> currUserAttributes;
+
+    public static void init(Context context) {
+        setData();
+
+        if (appHelper != null && userPool != null) {
+            return;
+        }
+
+        if (appHelper == null) {
+            appHelper = new AppHelper();
+        }
+
+        if (userPool == null) {
+
+            // Create a user pool with default ClientConfiguration
+            userPool = new CognitoUserPool(context, userPoolId, clientId, clientSecret, cognitoRegion);
+
+            // This will also work
+            /*
+            ClientConfiguration clientConfiguration = new ClientConfiguration();
+            AmazonCognitoIdentityProvider cipClient = new AmazonCognitoIdentityProviderClient(new AnonymousAWSCredentials(), clientConfiguration);
+            cipClient.setRegion(Region.getRegion(cognitoRegion));
+            userPool = new CognitoUserPool(context, userPoolId, clientId, clientSecret, cipClient);
+            */
+
+
+        }
+
+        phoneVerified = false;
+        phoneAvailable = false;
+        emailVerified = false;
+        emailAvailable = false;
+
+        currUserAttributes = new HashSet<String>();
+        currDisplayedItems = new ArrayList<ItemToDisplay>();
+        trustedDevices = new ArrayList<ItemToDisplay>();
+        firstTimeLogInDetails = new ArrayList<ItemToDisplay>();
+        firstTimeLogInUpDatedAttributes= new HashMap<String, String>();
+
+        newDevice = null;
+        thisDevice = null;
+        thisDeviceTrustState = false;
+
+        mfaOptions = new ArrayList<ItemToDisplay>();
+    }
+
+    public static CognitoUserPool getPool() {
+        return userPool;
+    }
+
+    public static Map<String, String> getSignUpFieldsC2O() {
+        return signUpFieldsC2O;
+    }
+
+    public static Map<String, String> getSignUpFieldsO2C() {
+        return signUpFieldsO2C;
+    }
+
+    public static List<String> getAttributeDisplaySeq() {
+        return attributeDisplaySeq;
+    }
+
+    public static void setCurrSession(CognitoUserSession session) {
+        currSession = session;
+    }
+
+    public static  CognitoUserSession getCurrSession() {
+        return currSession;
+    }
+
+    public static void setUserDetails(CognitoUserDetails details) {
+        userDetails = details;
+        refreshWithSync();
+    }
+
+    public static  CognitoUserDetails getUserDetails() {
+        return userDetails;
+    }
+
+    public static String getCurrUser() {
+        return user;
+    }
+
+    public static void setUser(String newUser) {
+        user = newUser;
+    }
+
+    public static boolean isPhoneVerified() {
+        return phoneVerified;
+    }
+
+    public static boolean isEmailVerified() {
+        return emailVerified;
+    }
+
+    public static boolean isPhoneAvailable() {
+        return phoneAvailable;
+    }
+
+    public static boolean isEmailAvailable() {
+        return emailAvailable;
+    }
+
+    public static void setPhoneVerified(boolean phoneVerif) {
+        phoneVerified = phoneVerif;
+    }
+
+    public static void setEmailVerified(boolean emailVerif) {
+        emailVerified = emailVerif;
+    }
+
+    public static void setPhoneAvailable(boolean phoneAvail) {
+        phoneAvailable = phoneAvail;
+    }
+
+    public static void setEmailAvailable(boolean emailAvail) {
+        emailAvailable = emailAvail;
+    }
+
+    public static void clearCurrUserAttributes() {
+        currUserAttributes.clear();
+    }
+
+    public static void addCurrUserattribute(String attribute) {
+        currUserAttributes.add(attribute);
+    }
+
+    public static List<String> getNewAvailableOptions() {
+        List<String> newOption = new ArrayList<String>();
+        for(String attribute : attributeDisplaySeq) {
+            if(!(currUserAttributes.contains(attribute))) {
+                newOption.add(attribute);
+            }
+        }
+        return  newOption;
+    }
+
+    public static String formatException(Exception exception) {
+        String formattedString = "Internal Error";
+        Log.e(TAG, " -- Error: "+exception.toString());
+        Log.getStackTraceString(exception);
+
+        String temp = exception.getMessage();
+
+        if(temp != null && temp.length() > 0) {
+            formattedString = temp.split("\\(")[0];
+            if(temp != null && temp.length() > 0) {
+                return formattedString;
+            }
+        }
+
+        return  formattedString;
+    }
+
+    public  static  int getItemCount() {
+        return itemCount;
+    }
+
+    public static int getDevicesCount() {
+        return trustedDevicesCount;
+    }
+
+    public static int getFirstTimeLogInItemsCount() {
+        return  firstTimeLogInItemsCount;
+    }
+
+    public  static ItemToDisplay getItemForDisplay(int position) {
+        return  currDisplayedItems.get(position);
+    }
+
+    public static ItemToDisplay getDeviceForDisplay(int position) {
+        if (position >= trustedDevices.size()) {
+            return new ItemToDisplay(" ", " ", " ", Color.BLACK, Color.DKGRAY, Color.parseColor("#37A51C"), 0, null);
+        }
+        return trustedDevices.get(position);
+    }
+
+    public static ItemToDisplay getUserAttributeForFirstLogInCheck(int position) {
+        return firstTimeLogInDetails.get(position);
+    }
+
+    public static void setUserAttributeForDisplayFirstLogIn(Map<String, String> currAttributes, List<String> requiredAttributes) {
+        firstTimeLogInUserAttributes = currAttributes;
+        firstTimeLogInRequiredAttributes = requiredAttributes;
+        firstTimeLogInUpDatedAttributes = new HashMap<String, String>();
+        refreshDisplayItemsForFirstTimeLogin();
+    }
+
+    public static void setUserAttributeForFirstTimeLogin(String attributeName, String attributeValue) {
+        if (firstTimeLogInUserAttributes ==  null) {
+            firstTimeLogInUserAttributes = new HashMap<String, String>();
+        }
+        firstTimeLogInUserAttributes.put(attributeName, attributeValue);
+        firstTimeLogInUpDatedAttributes.put(attributeName, attributeValue);
+        refreshDisplayItemsForFirstTimeLogin();
+    }
+
+    public static Map<String, String> getUserAttributesForFirstTimeLogin() {
+        return firstTimeLogInUpDatedAttributes;
+    }
+
+    public static void setPasswordForFirstTimeLogin(String password) {
+        firstTimeLoginNewPassword = password;
+    }
+
+    public static String getPasswordForFirstTimeLogin() {
+        return firstTimeLoginNewPassword;
+    }
+
+    private static void refreshDisplayItemsForFirstTimeLogin() {
+        firstTimeLogInItemsCount = 0;
+        firstTimeLogInDetails = new ArrayList<ItemToDisplay>();
+
+        for(Map.Entry<String, String> attr: firstTimeLogInUserAttributes.entrySet()) {
+            if ("phone_number_verified".equals(attr.getKey()) || "email_verified".equals(attr.getKey())) {
+                continue;
+            }
+            String message = "";
+            if ((firstTimeLogInRequiredAttributes != null) && (firstTimeLogInRequiredAttributes.contains(attr.getKey()))) {
+                message = "Required";
+            }
+            ItemToDisplay item = new ItemToDisplay(attr.getKey(), attr.getValue(), message, Color.BLACK, Color.DKGRAY, Color.parseColor("#329AD6"), 0, null);
+            firstTimeLogInDetails.add(item);
+            firstTimeLogInRequiredAttributes.size();
+            firstTimeLogInItemsCount++;
+        }
+
+        for (String attr: firstTimeLogInRequiredAttributes) {
+            if (!firstTimeLogInUserAttributes.containsKey(attr)) {
+                ItemToDisplay item = new ItemToDisplay(attr, "", "Required", Color.BLACK, Color.DKGRAY, Color.parseColor("#329AD6"), 0, null);
+                firstTimeLogInDetails.add(item);
+                firstTimeLogInItemsCount++;
+            }
+        }
+    }
+
+    public static void newDevice(CognitoDevice device) {
+        newDevice = device;
+    }
+
+    public static void setDevicesForDisplay(List<CognitoDevice> devicesList) {
+        trustedDevicesCount = 0;
+        thisDeviceTrustState = false;
+        deviceDetails = devicesList;
+        trustedDevices = new ArrayList<ItemToDisplay>();
+        for(CognitoDevice device: devicesList) {
+            if (thisDevice != null && thisDevice.getDeviceKey().equals(device.getDeviceKey())) {
+                thisDeviceTrustState = true;
+            } else {
+                ItemToDisplay item = new ItemToDisplay("", device.getDeviceName(), device.getCreateDate().toString(), Color.BLACK, Color.DKGRAY, Color.parseColor("#329AD6"), 0, null);
+                item.setDataDrawable("checked");
+                trustedDevices.add(item);
+                trustedDevicesCount++;
+            }
+        }
+    }
+
+    public static CognitoDevice getDeviceDetail(int position) {
+        if (position <= trustedDevicesCount) {
+            return deviceDetails.get(position);
+        } else {
+            return null;
+        }
+    }
+
+    public static void setMfaOptionsForDisplay(List<String> options, Map<String, String> parameters) {
+        mfaAllOptionsCode = options;
+        mfaOptions = new ArrayList<ItemToDisplay>();
+        String textToDisplay = "";
+        for (String option: options) {
+            if ("SMS_MFA".equals(option)) {
+                textToDisplay = "Send SMS";
+                if (parameters.containsKey("CODE_DELIVERY_DESTINATION")) {
+                    textToDisplay = textToDisplay + " to "+ parameters.get("CODE_DELIVERY_DESTINATION");
+                }
+            } else if ("SOFTWARE_TOKEN_MFA".equals(option)) {
+                textToDisplay = "Use TOTP";
+                if (parameters.containsKey("FRIENDLY_DEVICE_NAME")) {
+                    textToDisplay = textToDisplay + ": " + parameters.get("FRIENDLY_DEVICE_NAME");
+                }
+            }
+            ItemToDisplay item = new ItemToDisplay("", textToDisplay, "", Color.BLACK, Color.DKGRAY, Color.parseColor("#329AD6"), 0, null);
+            mfaOptions.add(item);
+            textToDisplay = "Unsupported MFA";
+        }
+    }
+
+    public static List<String> getAllMfaOptions() {
+        return mfaAllOptionsCode;
+    }
+
+    public static String getMfaOptionCode(int position) {
+        return mfaAllOptionsCode.get(position);
+    }
+
+    public static ItemToDisplay getMfaOptionForDisplay(int position) {
+        if (position >= mfaOptions.size()) {
+            return new ItemToDisplay(" ", " ", " ", Color.BLACK, Color.DKGRAY, Color.parseColor("#37A51C"), 0, null);
+        }
+        return mfaOptions.get(position);
+    }
+
+    public static int getMfaOptionsCount() {
+        return mfaOptions.size();
+    }
+
+    //public static
+
+    public static CognitoDevice getNewDevice() {
+        return newDevice;
+    }
+
+    public static CognitoDevice getThisDevice() {
+        return thisDevice;
+    }
+
+    public static void setThisDevice(CognitoDevice device) {
+        thisDevice = device;
+    }
+
+    public static boolean getThisDeviceTrustState() {
+        return thisDeviceTrustState;
+    }
+
+    private static void setData() {
+        // Set attribute display sequence
+        attributeDisplaySeq = new ArrayList<String>();
+        attributeDisplaySeq.add(COGNITO_FIELD_NICKNAME);
+        attributeDisplaySeq.add(COGNITO_FIELD_PHONENUMBER);
+        attributeDisplaySeq.add(COGNITO_FIELD_EMAIL);
+        //custom att
+        attributeDisplaySeq.add(COGNITO_FIELD_UID);
+        attributeDisplaySeq.add(COGNITO_FIELD_PROFILE);
+        attributeDisplaySeq.add(COGNITO_FIELD_CONFIRMED);
+        attributeDisplaySeq.add(COGNITO_FIELD_PUSH_ACCESS);
+        attributeDisplaySeq.add(COGNITO_FIELD_AUTO_UPDATE);
+        attributeDisplaySeq.add(COGNITO_FIELD_INVITED);
+
+        signUpFieldsC2O = new HashMap<String, String>();
+        signUpFieldsC2O.put(COGNITO_NICKNAME, COGNITO_FIELD_NICKNAME);
+        signUpFieldsC2O.put(COGNITO_PHONENUMBER, COGNITO_FIELD_PHONENUMBER);
+        signUpFieldsC2O.put(COGNITO_PHONENUMBER_VERIFIED, COGNITO_FIELD_PHONENUMBER_VERIFIED);
+        signUpFieldsC2O.put(COGNITO_EMAIL_VERIFIED, COGNITO_FIELD_EMAIL_VERIFIED);
+        signUpFieldsC2O.put(COGNITO_EMAIL,COGNITO_FIELD_EMAIL);
+        signUpFieldsC2O.put(COGNITO_UID,COGNITO_FIELD_UID);
+        signUpFieldsC2O.put(COGNITO_PROFILE,COGNITO_FIELD_PROFILE);
+        signUpFieldsC2O.put(COGNITO_CONFIRMED,COGNITO_FIELD_CONFIRMED);
+        signUpFieldsC2O.put(COGNITO_PUSH_ACCESS,COGNITO_FIELD_PUSH_ACCESS);
+        signUpFieldsC2O.put(COGNITO_AUTO_UPDATE,COGNITO_FIELD_AUTO_UPDATE);
+        signUpFieldsC2O.put(COGNITO_INVITED,COGNITO_FIELD_INVITED);
+
+        signUpFieldsO2C = new HashMap<String, String>();
+        signUpFieldsO2C.put(COGNITO_FIELD_NICKNAME, COGNITO_NICKNAME);
+        signUpFieldsO2C.put(COGNITO_FIELD_PHONENUMBER, COGNITO_PHONENUMBER);
+        signUpFieldsO2C.put(COGNITO_FIELD_PHONENUMBER_VERIFIED, COGNITO_PHONENUMBER_VERIFIED);
+        signUpFieldsO2C.put(COGNITO_FIELD_EMAIL_VERIFIED, COGNITO_EMAIL_VERIFIED);
+        signUpFieldsO2C.put(COGNITO_FIELD_EMAIL, COGNITO_EMAIL);
+        signUpFieldsO2C.put(COGNITO_FIELD_UID, COGNITO_UID);
+        signUpFieldsO2C.put(COGNITO_FIELD_PROFILE, COGNITO_PROFILE);
+        signUpFieldsO2C.put(COGNITO_FIELD_CONFIRMED, COGNITO_CONFIRMED);
+        signUpFieldsO2C.put(COGNITO_FIELD_PUSH_ACCESS, COGNITO_PUSH_ACCESS);
+        signUpFieldsO2C.put(COGNITO_FIELD_AUTO_UPDATE, COGNITO_AUTO_UPDATE);
+        signUpFieldsO2C.put(COGNITO_FIELD_INVITED, COGNITO_INVITED);
+    }
+
+    private static void refreshWithSync() {
+        // This will refresh the current items to display list with the attributes fetched from service
+        List<String> tempKeys = new ArrayList<>();
+        List<String> tempValues = new ArrayList<>();
+
+        emailVerified = false;
+        phoneVerified = false;
+
+        emailAvailable = false;
+        phoneAvailable = false;
+
+        currDisplayedItems = new ArrayList<ItemToDisplay>();
+        currUserAttributes.clear();
+        itemCount = 0;
+
+        for(Map.Entry<String, String> attr: userDetails.getAttributes().getAttributes().entrySet()) {
+
+            tempKeys.add(attr.getKey());
+            tempValues.add(attr.getValue());
+
+            if(attr.getKey().contains(COGNITO_FIELD_EMAIL_VERIFIED)) {
+                emailVerified = attr.getValue().contains("true");
+            }
+            else if(attr.getKey().contains(COGNITO_FIELD_PHONENUMBER_VERIFIED)) {
+                phoneVerified = attr.getValue().contains("true");
+            }
+
+            if(attr.getKey().equals(COGNITO_FIELD_EMAIL)) {
+                emailAvailable = true;
+            }
+            else if(attr.getKey().equals(COGNITO_FIELD_PHONENUMBER)) {
+                phoneAvailable = true;
+            }
+        }
+
+        // Arrange the input attributes per the display sequence
+        Set<String> keySet = new HashSet<>(tempKeys);
+        for(String det: attributeDisplaySeq) {
+            if(keySet.contains(det)) {
+                // Adding items to display list in the required sequence
+
+                ItemToDisplay item = new ItemToDisplay(signUpFieldsO2C.get(det), tempValues.get(tempKeys.indexOf(det)), "",
+                        Color.BLACK, Color.DKGRAY, Color.parseColor("#37A51C"),
+                        0, null);
+
+                if(det.contains("email")) {
+                    if(emailVerified) {
+                        item.setDataDrawable("checked");
+                        item.setMessageText("Email verified");
+                    }
+                    else {
+                        item.setDataDrawable("not_checked");
+                        item.setMessageText("Email not verified");
+                        item.setMessageColor(Color.parseColor("#E94700"));
+                    }
+                }
+
+                if(det.contains("phone_number")) {
+                    if(phoneVerified) {
+                        item.setDataDrawable("checked");
+                        item.setMessageText("Phone number verified");
+                    }
+                    else {
+                        item.setDataDrawable("not_checked");
+                        item.setMessageText("Phone number not verified");
+                        item.setMessageColor(Color.parseColor("#E94700"));
+                    }
+                }
+                
+                currDisplayedItems.add(item);
+                currUserAttributes.add(det);
+                itemCount++;
+            }
+        }
+    }
+
+    private static void modifyAttribute(String attributeName, String newValue) {
+        //
+
+    }
+
+    private static void deleteAttribute(String attributeName) {
+
+    }
+}
+

+ 128 - 0
app/src/main/java/kr/co/icontrols/iotplatform/cognito/ItemToDisplay.java

@@ -0,0 +1,128 @@
+/*
+ * Copyright 2013-2017 Amazon.com,
+ * Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Amazon Software License (the "License").
+ * You may not use this file except in compliance with the
+ * License. A copy of the License is located at
+ *
+ *      http://aws.amazon.com/asl/
+ *
+ * or in the "license" file accompanying this file. This file is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, express or implied. See the License
+ * for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kr.co.icontrols.iotplatform.cognito;
+
+public class ItemToDisplay {
+    // Text for display
+    private String labelText;
+    private String dataText;
+    private String messageText;
+
+    // Display text colors
+    private int labelColor;
+    private int dataColor;
+    private int messageColor;
+
+    // Data box background
+    private int dataBackground;
+
+    // Data box drawable
+    private String dataDrawable;
+
+    // Constructor
+    protected ItemToDisplay(String labelText, String dataText, String messageText,
+                            int labelColor, int dataColor, int messageColor,
+                            int dataBackground, String dataDrawable) {
+        this.labelText = labelText;
+        this.dataText = dataText;
+        this.messageText = messageText;
+        this.labelColor = labelColor;
+        this.dataColor = dataColor;
+        this.messageColor = messageColor;
+        this.dataBackground = dataBackground;
+        this.dataDrawable = dataDrawable;
+    }
+
+    public String getLabelText() {
+        return labelText;
+    }
+
+    public void setLabelText(String labelText) {
+        this.labelText = labelText;
+    }
+
+    public String getDataText() {
+        return dataText;
+    }
+
+    public void setDataText(String dataText) {
+        this.dataText = dataText;
+    }
+
+    public String getMessageText() {
+        return messageText;
+    }
+
+    public void setMessageText(String messageText) {
+        this.messageText = messageText;
+    }
+
+    public int getLabelColor() {
+        return labelColor;
+    }
+
+    public void setLabelColor(int labelColor) {
+        this.labelColor = labelColor;
+    }
+
+    public int getDataColor() {
+        return dataColor;
+    }
+
+    public void setDataColor(int dataColor) {
+        this.dataColor = dataColor;
+    }
+
+    public int getMessageColor() {
+        return messageColor;
+    }
+
+    public void setMessageColor(int messageColor) {
+        this.messageColor = messageColor;
+    }
+
+    public int getDataBackground() {
+        return dataBackground;
+    }
+
+    public void setDataBackground(int dataBackground) {
+        this.dataBackground = dataBackground;
+    }
+
+    public String getDataDrawable() {
+        return dataDrawable;
+    }
+
+    public void setDataDrawable(String dataDrawable) {
+        this.dataDrawable = dataDrawable;
+    }
+
+    @Override
+    public String toString() {
+        return "ItemToDisplay{" +
+                "labelText='" + labelText + '\'' +
+                ", dataText='" + dataText + '\'' +
+                ", messageText='" + messageText + '\'' +
+                ", labelColor=" + labelColor +
+                ", dataColor=" + dataColor +
+                ", messageColor=" + messageColor +
+                ", dataBackground=" + dataBackground +
+                ", dataDrawable='" + dataDrawable + '\'' +
+                '}';
+    }
+}

+ 994 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/CommonUtils.java

@@ -0,0 +1,994 @@
+package kr.co.icontrols.iotplatform.utils;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Debug;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoDevice;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUserSession;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.AuthenticationContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChallengeContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.ChooseMfaContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.MultiFactorAuthenticationContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.continuations.NewPasswordContinuation;
+import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.AuthenticationHandler;
+import com.google.android.material.snackbar.Snackbar;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import kr.co.icontrols.iotplatform.BuildConfig;
+import kr.co.icontrols.iotplatform.DataClass.UserInfo;
+import kr.co.icontrols.iotplatform.IotPlatformApplication;
+import kr.co.icontrols.iotplatform.LoginActivity;
+import kr.co.icontrols.iotplatform.PopupActivity;
+import kr.co.icontrols.iotplatform.ProfileUserActivity;
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.SplashActivity;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.app.Activity.RESULT_OK;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_00_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_01_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_02_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_03_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_04_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_05_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_06_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_07_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_08_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_BACKGROUND_09_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_00_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_01_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_02_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_03_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_04_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_05_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_06_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_07_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_08_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.GROUP_IMG_09_STRING;
+import static kr.co.icontrols.iotplatform.utils.Constants.RESULT_RESEND_CONFIRM;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.AUTO_LOGIN;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.BACKGROUND_WAIT_TIME;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_AUTO_LOGIN;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_BACKGROUND_WAIT_TIME;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.DEFAULT_LOGIN_TIME;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.LOGIN_TIME;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.getLong;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.putBoolean;
+import static kr.co.icontrols.iotplatform.utils.SharedPrefUtil.putLong;
+
+public class CommonUtils {
+
+    private final static String TAG = "CommonUtils";
+
+    // 로그 출력 옵션
+    private final static boolean DEBUG_LOG_ON = BuildConfig.DEBUG?true:false;
+    private final static boolean ERROR_LOG_ON = BuildConfig.DEBUG?true:false;
+    private final static boolean WARN_LOG_ON = BuildConfig.DEBUG?true:false;
+    private final static boolean INFO_LOG_ON = BuildConfig.DEBUG?true:false;
+
+    public static void DebugLogOutput(String tag, String s) {
+        if (DEBUG_LOG_ON) Log.d(tag, s);
+    }
+
+    public static void ErrorLogOutput(String tag, String s) {
+        if (ERROR_LOG_ON) Log.e(tag, s);
+    }
+
+    public static void WarnLogOutput(String tag, String s) {
+        if (WARN_LOG_ON) Log.w(tag, s);
+    }
+
+    public static void InfoLogOutput(String tag, String s) {
+        if (INFO_LOG_ON) Log.i(tag, s);
+    }
+
+    public static final  int PERMISSION_ALL = 1;
+    public static final  String[] PERMISSIONS = {
+            android.Manifest.permission.CALL_PHONE,
+            android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
+            android.Manifest.permission.READ_EXTERNAL_STORAGE,
+            android.Manifest.permission.CAMERA,
+            Manifest.permission.ACCESS_FINE_LOCATION,
+    };
+
+    private static final int EXIT_TIME = 2000;
+    private static boolean mIsCancelToExit;
+
+    private static CustomProgressDialog mConnectProgressDialog;
+    private final static int DEFUALT_HIDEN_PROGRESSBAR_DELAY_TIME = 5000;
+    private static Timer mDialogTimer;
+    private static long mShowProgressTime = 0;
+
+    private static long BACKGROUND_WAIT_EXIT_TIME = 30*60*1000;
+
+    /**
+     * 로딩화면 시작 함수
+     *
+     * @param activity
+     * @param str
+     * @param str2
+     */
+    public static void displayProgress(final Activity activity, String str, String str2){
+        displayProgress(activity, str, str2, DEFUALT_HIDEN_PROGRESSBAR_DELAY_TIME);
+    }
+
+    public static void displayProgress(final Activity activity, String str, String str2, long hideTime) {
+        if(mConnectProgressDialog != null)
+            return;
+        mShowProgressTime = System.currentTimeMillis();
+        mConnectProgressDialog = new CustomProgressDialog(activity, str, str2);
+        mConnectProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+            @Override
+            public void onCancel(DialogInterface dialog) {
+
+            }
+        });
+
+        mConnectProgressDialog.setCancelable(false);
+        if (mDialogTimer != null) {
+            mDialogTimer.cancel();
+            mDialogTimer.purge();
+        }
+        mDialogTimer = new Timer();
+        mDialogTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                if (mConnectProgressDialog != null)
+                    activity.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            if(mConnectProgressDialog != null) {
+                                //mConnectProgressDialog.showTitle();
+                                //mConnectProgressDialog.showSubTitle();
+                                DismissConnectDialog();
+                            }
+                        }
+                    });
+            }
+        }, hideTime);
+        mConnectProgressDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+
+            @Override
+            public void onDismiss(DialogInterface dialog) {
+                DismissConnectDialog();
+            }
+        });
+
+        //mConnectProgressDialog.show();
+
+        try {
+            mConnectProgressDialog.show();
+        } catch (Exception e) {
+            e.printStackTrace();
+            mConnectProgressDialog = null;
+        }
+    }
+
+    /**
+     * 로딩화면 종료 함수
+     */
+    public static void DismissConnectDialog() {
+        long delay = System.currentTimeMillis()-500;
+        if(mShowProgressTime > delay) {
+            Timer delayHideProgressTimer = new Timer();
+            delayHideProgressTimer.schedule(new TimerTask() {
+                @Override
+                public void run() {
+                    DismissConnectDialog();
+                }
+            },mShowProgressTime - delay + 100);
+            return;
+        }
+
+        if (mDialogTimer != null) {
+            mDialogTimer.cancel();
+            mDialogTimer.purge();
+        }
+        if (mConnectProgressDialog != null) {
+            try {
+                mConnectProgressDialog.dismiss();
+            }
+            catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+            mConnectProgressDialog = null;
+        }
+    }
+    public static void ForceDismissConnectDialog() {
+        if (mDialogTimer != null) {
+            mDialogTimer.cancel();
+            mDialogTimer.purge();
+        }
+        if (mConnectProgressDialog != null) {
+            try {
+                mConnectProgressDialog.dismiss();
+            }
+            catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+            mConnectProgressDialog = null;
+        }
+    }
+
+    public static boolean IsRunningProgress(){
+        return (mConnectProgressDialog != null);
+    }
+
+    public static CustomProgressDialog getProgressDialog() {
+        return mConnectProgressDialog;
+    }
+
+    /**
+     * 로딩화면 타이틀 문구 표시
+     *
+     * @param str
+     */
+    public static void progressDialogShowTitle(String str) {
+        if (mConnectProgressDialog != null) {
+            mConnectProgressDialog.showTitle(str);
+        }
+    }
+
+    /**
+     * 로딩화면 서버타이틀 문구 표시
+     *
+     * @param str
+     */
+    public static void progressDialogShowSubTitle(String str) {
+        if (mConnectProgressDialog != null) {
+            mConnectProgressDialog.showSubTitle(str);
+        }
+    }
+
+
+    /**
+     * 이메일 형식 유효성 체크 함수.
+     * @param email
+     * @return
+     */
+    public static boolean isEmailValid(String email) {
+        String expression = "^[\\w\\.-]+@([\\w\\-]+\\.)+[\\w\\-]+$";
+        Pattern pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
+        Matcher matcher = pattern.matcher(email);
+        return matcher.matches();
+    }
+
+    /**
+     * 암호 설정을 위한 유효성 체크 함수
+     * @param s
+     * @return
+     */
+    public static boolean isPasswordValid(String s){
+        /*
+        if(s.matches("[0-9]+")){
+            return false;
+        } else if(s.matches("[^0-9]+")){
+            return false;
+        } else if(s.matches("[\\w-]+")){
+            return  true;
+        }
+        return false;
+         */
+
+        return s.matches("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*\\W)(?=.\\S+$).{8,20}$");
+    }
+
+    public static void cancelToExit(final Activity activity) {
+        if(mIsCancelToExit){
+            activity.finish();
+            System.exit(0);
+        } else {
+            mIsCancelToExit = true;
+            Toast.makeText(activity,activity.getString(R.string.cancel_exit_app),Toast.LENGTH_SHORT).show();
+            Timer exitTimer = new Timer();
+            exitTimer.schedule(new TimerTask() {
+                @Override
+                public void run() {
+                    activity.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            try {
+
+                            } catch (Exception e){
+                                e.printStackTrace();
+                            } finally {
+                                mIsCancelToExit = false;
+                            }
+                        }
+                    });
+                }
+            }, EXIT_TIME);
+        }
+    }
+
+    //Hide Keypad
+    public static void setupUI(final Activity activity, View view) {
+        //Set up touch listener for non-text box views to hide keyboard.
+        if(!(view instanceof EditText)) {
+            view.setOnTouchListener(new View.OnTouchListener() {
+                public boolean onTouch(View v, MotionEvent event) {
+                    hideSoftKeyboard(activity);
+                    return false;
+                }
+            });
+        }
+
+        //If a layout container, iterate over children and seed recursion.
+        if (view instanceof ViewGroup) {
+
+            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
+
+                View innerView = ((ViewGroup) view).getChildAt(i);
+
+                setupUI(activity, innerView);
+            }
+        }
+    }
+
+    /**
+     * 소프트 키보드 숨김 함수
+     * @param activity
+     */
+    public static void hideSoftKeyboard(Activity activity) {
+        if(activity != null && activity.getCurrentFocus() != null && activity.getCurrentFocus().getWindowToken()!= null) {
+            InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
+            inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
+        }
+    }
+
+    public static int getGroupImageResource(String s) {
+        int rscID = R.drawable.img_group_01;
+        switch (s) {
+            case GROUP_IMG_00_STRING:
+                rscID = R.drawable.img_group_01;
+                break;
+            case GROUP_IMG_01_STRING:
+                rscID = R.drawable.img_group_01;
+                break;
+            case GROUP_IMG_02_STRING:
+                rscID = R.drawable.img_group_02;
+                break;
+            case GROUP_IMG_03_STRING:
+                rscID = R.drawable.img_group_03;
+                break;
+            case GROUP_IMG_04_STRING:
+                rscID = R.drawable.img_group_04;
+                break;
+            case GROUP_IMG_05_STRING:
+                rscID = R.drawable.img_group_05;
+                break;
+            case GROUP_IMG_06_STRING:
+                rscID = R.drawable.img_group_06;
+                break;
+            case GROUP_IMG_07_STRING:
+                rscID = R.drawable.img_group_07;
+                break;
+            case GROUP_IMG_08_STRING:
+                rscID = R.drawable.img_group_08;
+                break;
+            case GROUP_IMG_09_STRING:
+                rscID = R.drawable.img_group_09;
+                break;
+        }
+
+        return rscID;
+    }
+
+    public static int getGroupImageIndex(String s) {
+        int index = 1;
+        switch (s) {
+            case GROUP_IMG_00_STRING:
+                index = 1;
+                break;
+            case GROUP_IMG_01_STRING:
+                index = 1;
+                break;
+            case GROUP_IMG_02_STRING:
+                index = 2;
+                break;
+            case GROUP_IMG_03_STRING:
+                index = 3;
+                break;
+            case GROUP_IMG_04_STRING:
+                index = 4;
+                break;
+            case GROUP_IMG_05_STRING:
+                index = 5;
+                break;
+            case GROUP_IMG_06_STRING:
+                index = 6;
+                break;
+            case GROUP_IMG_07_STRING:
+                index = 7;
+                break;
+            case GROUP_IMG_08_STRING:
+                index = 8;
+                break;
+            case GROUP_IMG_09_STRING:
+                index = 9;
+                break;
+        }
+        return index;
+    }
+
+    public static String getGroupImageString(int index) {
+        String str = GROUP_IMG_00_STRING;
+        switch (index) {
+            case 0:
+                str = GROUP_IMG_01_STRING;
+                break;
+            case 1:
+                str = GROUP_IMG_01_STRING;
+                break;
+            case 2:
+                str = GROUP_IMG_02_STRING;
+                break;
+            case 3:
+                str = GROUP_IMG_03_STRING;
+                break;
+            case 4:
+                str = GROUP_IMG_04_STRING;
+                break;
+            case 5:
+                str = GROUP_IMG_05_STRING;
+                break;
+            case 6:
+                str = GROUP_IMG_06_STRING;
+                break;
+            case 7:
+                str = GROUP_IMG_07_STRING;
+                break;
+            case 8:
+                str = GROUP_IMG_08_STRING;
+                break;
+            case 9:
+                str = GROUP_IMG_09_STRING;
+                break;
+        }
+        return str;
+    }
+
+    public static int getGroupBackgournResourceWithIcon(String s) {
+        int rscID = R.drawable.bg_group_home_01;
+        switch (s) {
+            case GROUP_IMG_00_STRING:
+                rscID = R.drawable.bg_group_home_01;
+                break;
+            case GROUP_IMG_01_STRING:
+                rscID = R.drawable.bg_group_home_01;
+                break;
+            case GROUP_IMG_02_STRING:
+                rscID = R.drawable.bg_group_home_02;
+                break;
+            case GROUP_IMG_03_STRING:
+                rscID = R.drawable.bg_group_home_03;
+                break;
+            case GROUP_IMG_04_STRING:
+                rscID = R.drawable.bg_group_home_04;
+                break;
+            case GROUP_IMG_05_STRING:
+                rscID = R.drawable.bg_group_home_05;
+                break;
+            case GROUP_IMG_06_STRING:
+                rscID = R.drawable.bg_group_home_06;
+                break;
+            case GROUP_IMG_07_STRING:
+                rscID = R.drawable.bg_group_home_07;
+                break;
+            case GROUP_IMG_08_STRING:
+                rscID = R.drawable.bg_group_home_08;
+                break;
+            case GROUP_IMG_09_STRING:
+                rscID = R.drawable.bg_group_home_09;
+                break;
+        }
+
+        return rscID;
+    }
+
+    public static String getGroupBackgroundString(int index) {
+        String str = GROUP_BACKGROUND_00_STRING;
+        switch (index) {
+            case 0:
+                str = GROUP_BACKGROUND_01_STRING;
+                break;
+            case 1:
+                str = GROUP_BACKGROUND_01_STRING;
+                break;
+            case 2:
+                str = GROUP_BACKGROUND_02_STRING;
+                break;
+            case 3:
+                str = GROUP_BACKGROUND_03_STRING;
+                break;
+            case 4:
+                str = GROUP_BACKGROUND_04_STRING;
+                break;
+            case 5:
+                str = GROUP_BACKGROUND_05_STRING;
+                break;
+            case 6:
+                str = GROUP_BACKGROUND_06_STRING;
+                break;
+            case 7:
+                str = GROUP_BACKGROUND_07_STRING;
+                break;
+            case 8:
+                str = GROUP_BACKGROUND_08_STRING;
+                break;
+            case 9:
+                str = GROUP_BACKGROUND_09_STRING;
+                break;
+        }
+        return str;
+    }
+
+    public static int getGroupBackgournResource(String s) {
+        int rscID = R.color.gray;
+        switch (s) {
+            case GROUP_BACKGROUND_00_STRING:
+                rscID = R.color.gray;
+                break;
+            case GROUP_BACKGROUND_01_STRING:
+                rscID = R.drawable.bg_group_home_01;
+                break;
+            case GROUP_BACKGROUND_02_STRING:
+                rscID = R.drawable.bg_group_home_02;
+                break;
+            case GROUP_BACKGROUND_03_STRING:
+                rscID = R.drawable.bg_group_home_03;
+                break;
+            case GROUP_BACKGROUND_04_STRING:
+                rscID = R.drawable.bg_group_home_04;
+                break;
+            case GROUP_BACKGROUND_05_STRING:
+                rscID = R.drawable.bg_group_home_05;
+                break;
+            case GROUP_BACKGROUND_06_STRING:
+                rscID = R.drawable.bg_group_home_06;
+                break;
+            case GROUP_BACKGROUND_07_STRING:
+                rscID = R.drawable.bg_group_home_07;
+                break;
+            case GROUP_BACKGROUND_08_STRING:
+                rscID = R.drawable.bg_group_home_08;
+                break;
+            case GROUP_BACKGROUND_09_STRING:
+                rscID = R.drawable.bg_group_home_09;
+                break;
+        }
+
+        return rscID;
+    }
+
+    public static Bitmap getCircleBitmap(int rscID) {
+        return getCircleBitmap(((BitmapDrawable) IotPlatformApplication.getInstance().getApplicationContext().getDrawable(rscID)).getBitmap(),false);
+    }
+
+    public static Bitmap getCircleBitmap(Bitmap bitmap) {
+        return getCircleBitmap(bitmap, false);
+    }
+
+    public static Bitmap getCircleBitmap(int rscID, boolean selected) {
+        return getCircleBitmap(((BitmapDrawable) IotPlatformApplication.getInstance().getApplicationContext().getDrawable(rscID)).getBitmap(),selected);
+    }
+
+    public static Bitmap getCircleBitmap(Bitmap bitmap, boolean selected) {
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+
+        if (width < height) {
+            height = width;
+        } else {
+            width = height;
+        }
+
+        Bitmap outPut = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(outPut);
+
+
+        final Rect rect = new Rect(2, 2, width - 2, height - 2);
+        final RectF rectf = new RectF(rect);
+        Paint paint = new Paint();
+        paint.setAntiAlias(true);
+
+
+        canvas.drawARGB(0, 0, 0, 0);
+        canvas.drawArc(rectf, 0, 360, false, paint);
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+        canvas.drawBitmap(bitmap, rect, rect, paint);
+
+        Paint drawLine = new Paint();
+        drawLine.setAntiAlias(true);
+        drawLine.setStyle(Paint.Style.STROKE);
+
+        if(selected) {
+            drawLine.setStrokeWidth(4);
+            drawLine.setColor(0xFF271449);
+        } else {
+            drawLine.setStrokeWidth(2);
+            drawLine.setColor(Color.TRANSPARENT);
+        }
+
+        canvas.drawArc(rectf, 0, 360, false, drawLine);
+        return outPut;
+    }
+
+    public static String byteArrayToHex(byte[] a) {
+        StringBuilder sb = new StringBuilder();
+        for (final byte b : a)
+            sb.append(String.format("%02x ", b & 0xff));
+        return sb.toString();
+    }
+
+    public static String bssidToString(byte[] bssid) {
+        StringBuilder sb = new StringBuilder(18);
+        for (byte b : bssid) {
+            if (sb.length() > 0)
+                sb.append(':');
+            sb.append(String.format("%02x", b));
+        }
+        return sb.toString();
+    }
+
+    public static  byte[] appendByteArray(byte[] protocol, byte[] mac, int position) {
+        byte[] temp = new byte[protocol.length];
+        System.arraycopy(protocol, 0, temp, 0, protocol.length);
+        System.arraycopy(mac, 0, temp, position, mac.length);
+        return temp;
+    }
+
+    public static void showNotiMsg(Activity activity, String msg) {
+        showNotiMsg(activity, msg, false);
+    }
+    public static void showNotiMsg(Activity activity, String msg, boolean isExist) {
+        /*
+        Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content),msg,Snackbar.LENGTH_LONG);
+        if(isExist) {
+            snackbar.addCallback(new Snackbar.Callback() {
+                @Override
+                public void onDismissed(Snackbar snackbar, int event) {
+                    if (event == DISMISS_EVENT_TIMEOUT) {
+                        // Some work to be done.
+                        activity.finish();
+                    }
+                }
+            });
+        }
+        snackbar.show();
+         */
+        activity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                LayoutInflater inflater = activity.getLayoutInflater();
+                View toastDesign = inflater.inflate(R.layout.custom_toast, null/*activity.findViewById(R.id.layoutToast)*/);
+
+                TextView text = toastDesign.findViewById(R.id.txt);
+                text.setText(msg); // toast_design.xml 파일에서 직접 텍스트를 지정 가능
+
+                Toast toast = new Toast(activity);
+                toast.setGravity(Gravity.BOTTOM | Gravity.HORIZONTAL_GRAVITY_MASK, 0, 0);
+                toast.setDuration(Toast.LENGTH_SHORT);
+                toast.setView(toastDesign);
+                toast.show();
+                if(isExist) {
+                    activity.setResult(RESULT_OK);
+                    activity.finish();
+                }
+            }
+        });
+    }
+
+    public static void showNotiMsg(Activity activity, String msg, Class<?> cls) {
+        /*
+        Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content),msg,Snackbar.LENGTH_LONG);
+        snackbar.addCallback(new Snackbar.Callback() {
+
+            @Override
+            public void onDismissed(Snackbar snackbar, int event) {
+                if (event == DISMISS_EVENT_TIMEOUT) {
+                    // Some work to be done.
+                    Intent intent = new Intent(activity, cls);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                            | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    activity.startActivity(intent);
+                }
+            }
+        });
+        snackbar.show();
+         */
+        activity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                LayoutInflater inflater = activity.getLayoutInflater();
+                View toastDesign = inflater.inflate(R.layout.custom_toast, null);
+
+                TextView text = toastDesign.findViewById(R.id.txt);
+                text.setText(msg); // toast_design.xml 파일에서 직접 텍스트를 지정 가능
+
+                Toast toast = new Toast(activity);
+                toast.setGravity(Gravity.BOTTOM | Gravity.HORIZONTAL_GRAVITY_MASK, 0, 0);
+                toast.setDuration(Toast.LENGTH_SHORT);
+                toast.setView(toastDesign);
+                toast.show();
+
+                Intent intent = new Intent(activity, cls);
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                        | Intent.FLAG_ACTIVITY_NEW_TASK);
+                activity.startActivity(intent);
+            }
+        });
+    }
+
+    public static void showNotiMsg(final Activity activity, String msg, final Class<?> cls, final int flag) {
+        /*
+        Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content),msg,Snackbar.LENGTH_LONG);
+        snackbar.addCallback(new Snackbar.Callback() {
+            @Override
+            public void onDismissed(Snackbar snackbar, int event) {
+                if (event == DISMISS_EVENT_TIMEOUT) {
+                    // Some work to be done.
+                    Intent intent = new Intent(activity, cls);
+                    intent.setFlags(flag);
+                    activity.startActivity(intent);
+                }
+            }
+        });
+        snackbar.show();
+         */
+
+        activity.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                LayoutInflater inflater = activity.getLayoutInflater();
+                View toastDesign = inflater.inflate(R.layout.custom_toast, null);
+
+                TextView text = toastDesign.findViewById(R.id.txt);
+                text.setText(msg); // toast_design.xml 파일에서 직접 텍스트를 지정 가능
+
+                Toast toast = new Toast(activity);
+                toast.setGravity(Gravity.BOTTOM | Gravity.HORIZONTAL_GRAVITY_MASK, 0, 0);
+                toast.setDuration(Toast.LENGTH_SHORT);
+                toast.setView(toastDesign);
+                toast.show();
+
+                Intent intent = new Intent(activity, cls);
+                intent.setFlags(flag);
+                activity.startActivity(intent);
+            }
+        });
+    }
+
+    public static void requestConfirmUser(Activity activity) {
+        try {
+            displayProgress(activity, "","");
+            HttpApi.requestConfirmUser(true, true, UserInfo.email, new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    e.printStackTrace();
+                    ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onFailure)\n" + e.toString());
+                    showNotiMsg(activity, "인증 이메일 전송 요청에 실패하였습니다.");
+                    DismissConnectDialog();
+                    goToLoginActivity(activity);
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    if(response.code() == HttpApi.HTTP_NO_CONTENT) {
+                        WarnLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) HTTP_NO_CONTENT");
+                        showNotiMsg(activity, "인증 이메일 전송을 요청을 하였습니다.");
+                        goToLoginActivity(activity);
+                    } else {
+                        ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) Error code : " + response.code() + "\nresponse : " + response.toString());
+                        showNotiMsg(activity, "인증 이메일 전송 요청에 실패하였습니다.");
+                        try {
+                            JSONObject json_data = new JSONObject(response.body().string());
+                            ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) body : " + json_data.toString());
+                        } catch (JSONException e) {
+                            ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(onResponse) JSONException : " + e.toString());
+                        }
+                    }
+                    DismissConnectDialog();
+                    goToLoginActivity(activity);
+                }
+            });
+        } catch (Exception e){
+            e.printStackTrace();
+            ErrorLogOutput(TAG, "HttpApi.requestConfirmUser(Exception)\n" + e.toString());
+            showNotiMsg(activity, "인증 이메일 전송 요청에 실패하였습니다.");
+            DismissConnectDialog();
+            goToLoginActivity(activity);
+        }
+    }
+
+    public static void sendConfirmEmail(Activity activity) {
+        Intent intent = new Intent(activity, PopupActivity.class);
+        intent.putExtra(PopupActivity.POPUP_MODE, PopupActivity.MODE_NORMAL);
+        intent.putExtra(PopupActivity.POPUP_TITLE_TEXT, "이메일 인증이 진행되지 않았습니다. 이메일 인증후 서비스를 이용해주시기 바랍니다.");
+        intent.putExtra(PopupActivity.POPUP_OK_BUTTON_TEXT, "이메일 인증 받기");
+        activity.startActivityForResult(intent, RESULT_RESEND_CONFIRM);
+    }
+
+    public static void goToLoginActivity(Activity activity){
+        putBoolean(AUTO_LOGIN, DEFAULT_AUTO_LOGIN);
+        Intent intent;
+        intent = new Intent(activity, LoginActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        activity.startActivity(intent);
+        activity.finish();
+    }
+
+    public static boolean isSupportedBluetooth(Context mContext){
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+    }
+
+    @SuppressLint("MissingPermission")
+    public static boolean isBluetoothToggle(Context mContext){
+        final BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+        if(bluetoothManager == null || bluetoothManager.getAdapter() == null){
+            return false;
+        }
+        return bluetoothManager.getAdapter().isEnabled();
+    }
+
+
+    //Check BlueTooth is Enalbe or not.
+    public static boolean isBlueToothEnable(Context mContext){
+        boolean isBluetooth = isBluetoothToggle(mContext);
+        if (isSupportedBluetooth(mContext)) {
+            if (!isBluetooth ) {
+                return isBluetooth;
+            }
+        } else {
+            Toast.makeText(mContext, R.string.bt_not_support_str, Toast.LENGTH_SHORT).show();
+        }
+        return isBluetooth;
+    }
+
+    public static boolean isExpiredSession() {
+        DebugLogOutput(TAG, "로그인 후 시간(초) : " + ((System.currentTimeMillis()- getLong(LOGIN_TIME, DEFAULT_LOGIN_TIME))/1000));
+        if (((getLong(LOGIN_TIME, DEFAULT_LOGIN_TIME) / 1000) + (60 * 50)) > (System.currentTimeMillis()/1000)) {
+            return false; //not expired
+        } else {
+            DebugLogOutput(TAG, "Cognito token expired");
+            return true; //expired
+        }
+    }
+
+    public static void getCognitoSession(GetSessionCallback callback) {
+        final AuthenticationHandler authenticationHandler = new AuthenticationHandler() {
+            @Override
+            public void onSuccess(CognitoUserSession cognitoUserSession, CognitoDevice device) {
+                DebugLogOutput(TAG, " -- Auth Success");
+                SharedPrefUtil.putLong(LOGIN_TIME, System.currentTimeMillis());
+                AppHelper.setCurrSession(cognitoUserSession);
+                AppHelper.newDevice(device);
+                if(callback != null) {
+                    callback.successed();
+                }
+            }
+
+            @Override
+            public void getAuthenticationDetails(AuthenticationContinuation authenticationContinuation, String userId) {
+
+            }
+
+            @Override
+            public void getMFACode(MultiFactorAuthenticationContinuation continuation) {
+
+            }
+
+            @Override
+            public void authenticationChallenge(ChallengeContinuation continuation) {
+
+            }
+
+            @Override
+            public void onFailure(Exception e) {
+                DebugLogOutput(TAG, " -- Auth Failed");
+                if(callback != null) {
+                    callback.failed();
+                }
+            }
+        };
+
+        CognitoUser user = AppHelper.getPool().getCurrentUser();
+        final String username = user.getUserId();
+        if(username != null) {
+            AppHelper.setUser(username);
+            user.getSessionInBackground(authenticationHandler);
+        }
+    }
+
+    /**
+     * 백그라운드 대기 시간 초기화 함수
+     */
+    public static void resetBackgroundWaitTime() {
+        putLong(BACKGROUND_WAIT_TIME, DEFAULT_BACKGROUND_WAIT_TIME);
+    }
+
+    /**
+     * 백그라운드 진입시 시간설정.
+     */
+    public static void setBackgroundWaitTime() {
+        putLong(BACKGROUND_WAIT_TIME, System.currentTimeMillis());
+    }
+
+    /**
+     * 앱 재시작 여부 확인 함수
+     * @return
+     */
+    public static boolean isRestartAppForBackgroundWaitTime() {
+        boolean result = false;
+        long backgroundWaitTime = getLong(BACKGROUND_WAIT_TIME, DEFAULT_BACKGROUND_WAIT_TIME);
+        if(backgroundWaitTime == 0) {
+            result =  false;
+        } else if((backgroundWaitTime+BACKGROUND_WAIT_EXIT_TIME) < System.currentTimeMillis()){
+            result = true;
+        }
+        resetBackgroundWaitTime();
+        return result;
+    }
+
+    /**
+     * 앱 재시작 함수
+     * @param activity
+     */
+    public static void restartApp(Activity activity) {
+        /*
+        Intent mStartActivity = new Intent(activity, SplashActivity.class);
+        int mPendingIntentId = 123456;
+        PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId,    mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+        AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
+        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
+        System.exit(0);
+        */
+        Intent intent = new Intent(activity, SplashActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+        activity.startActivity(intent);
+        activity.finish();
+    }
+}

+ 126 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/Constants.java

@@ -0,0 +1,126 @@
+package kr.co.icontrols.iotplatform.utils;
+
+public class Constants {
+
+    public static final int RESULT_CHECK_VERSION = 1000;
+    public static final int RESULT_PERMISSION_AGREE = 1001;
+    public static final int RESULT_INIT_APP_CONFIRM = 1002;
+    public static final int RESULT_LOGOUT_SUCCESS = 1003;
+    public static final int RESULT_FORGOT_PASSWORD_CODE = 1004;
+    public static final int RESULT_SIGN_UP = 1005;
+    public static final int RESULT_CONFIRM_SIGN_UP = 1006;
+    public static final int RESULT_NEW_PASSWORD = 1007;
+    public static final int RESULT_CHOOSE_MFA = 1008;
+    public static final int RESULT_MFA = 1009;
+    public static final int RESULT_LAUNCH_USER = 1010;
+    public static final int RESULT_GOOGE_GET_AUTH_CODE = 1011;
+    public static final int RESULT_RETRY_SCAN_DEVICE = 1012;
+    public static final int RESULT_RETRY_BUT_NO_DEVICE =  1013;
+    public static final int RESULT_CONFIRM_SEND_INVITE_MSG = 1014;
+    public static final int RESULT_INPUT_GROUP_NAME = 1015;
+    public static final int RESULT_CHANGE_GROUP_IMG = 1016;
+    public static final int RESULT_CAPTURE_PICTURE = 1017;
+    public static final int RESULT_SELECTED_PICTURE = 1018;
+    public static final int RESULT_CONFIRM_DELETE_MEMBER = 1019;
+    public static final int RESULT_LIGHT_BLINK_SUCCESS = 1020;
+    public static final int RESULT_SWITCH_MAPPER_BUZZER_SUCCESS = 1021;
+    public static final int RESULT_INPUT_NICKNAME = 1022;
+    public static final int RESULT_INPUT_HP = 1023;
+    public static final int RESULT_SIGN_OUT_CONFIRM = 1024;
+    public static final int RESULT_INVITE_CONFIRM = 1025;
+    public static final int RESULT_ADD_HOME_CONFIRM = 1026;
+    public static final int RESULT_INPUT_AP_PASS = 1027;
+    public static final int RESULT_INPUT_DEVICE_NAME = 1028;
+    public static final int RESULT_UPDATE_DATA = 1029;
+    public static final int RESULT_RESEND_CONFIRM = 1030;
+    public static final int RESULT_ENABLE_BT_MAIN = 1031;
+    public static final int RESULT_CHANGE_JAESIL_TIME = 1032;
+    public static final int RESULT_INIT_DEVICE_CONFIRM =  1033;
+    public static final int RESULT_CALL_CENTER = 1034;
+    public static final int RESULT_RETRY_MAPPING_DEVICE = 1035;
+    public static final int RESULT_ADD_GROUP = 1036;
+
+    public static final String COGNITO_NICKNAME = "Nick name";
+    public static final String COGNITO_EMAIL = "Email";
+    public static final String COGNITO_EMAIL_VERIFIED = "Email verified";
+    public static final String COGNITO_PHONENUMBER = "Phone number";
+    public static final String COGNITO_PHONENUMBER_VERIFIED = "Phone number verified";
+    public static final String COGNITO_UID = "Uid";
+    public static final String COGNITO_PROFILE = "Profile";
+    public static final String COGNITO_CONFIRMED = "Confirmed";
+    public static final String COGNITO_PUSH_ACCESS = "Push access";
+    public static final String COGNITO_AUTO_UPDATE = "Auto update";
+    public static final String COGNITO_INVITED = "Invited";
+
+    public static final String COGNITO_FIELD_NICKNAME = "nickname";
+    public static final String COGNITO_FIELD_EMAIL = "email";
+    public static final String COGNITO_FIELD_EMAIL_VERIFIED = "email_verified";
+    public static final String COGNITO_FIELD_PHONENUMBER = "phone_number";
+    public static final String COGNITO_FIELD_PHONENUMBER_VERIFIED = "phone_number_verified";
+    public static final String COGNITO_FIELD_UID = "uid";
+    public static final String COGNITO_FIELD_PROFILE = "profile";
+    public static final String COGNITO_FIELD_CONFIRMED = "confirmed";
+    public static final String COGNITO_FIELD_PUSH_ACCESS = "push_access";
+    public static final String COGNITO_FIELD_AUTO_UPDATE = "auto_update";
+    public static final String COGNITO_FIELD_INVITED = "invited";
+
+    public static final int RSSI_LEVEL_3 = -60;
+    public static final int RSSI_LEVEL_2 = -70;
+    public static final int RSSI_LEVEL_1 = -80;
+    public static final int RSSI_LEVEL_0 = -90;
+
+    public static final String EXTRA_GROUP_LIST = "EXTRA_GROUP_LIST";
+    public static final String EXTRA_DEVICE_LIST = "EXTRA_DEVICE_LIST";
+    public static final String EXTRA_DEVICE_INFO = "EXTRA_DEVICE_INFO";
+    public static final String EXTRA_GROUP_ID = "EXTRA_GROUP_ID";
+    public static final String EXTRA_DEVICE_ID = "EXTRA_DEVICE_ID";
+    public static final String EXTRA_GROUP_NAME = "EXTRA_GROUP_NAME";
+    public static final String EXTRA_DEVICE_NAME = "EXTRA_DEVICE_NAME";
+    public static final String EXTRA_BEFORE_ACTIVITY = "EXTRA_BEFORE_ACTIVITY";
+
+    public static final int DEFUALT_GROUP_INDEX = 0; //기본그룹
+
+    public static final int GROUP_01 = 1;
+    public static final int GROUP_02 = 2;
+    public static final int GROUP_03 = 3;
+    public static final int GROUP_04 = 4;
+    public static final int GROUP_05 = 5;
+    public static final int GROUP_06 = 6;
+    public static final int GROUP_07 = 7;
+    public static final int GROUP_08 = 8;
+    public static final int GROUP_09 = 9;
+
+    public static final String GROUP_IMG_00_STRING = "00";
+    public static final String GROUP_IMG_01_STRING = "01";
+    public static final String GROUP_IMG_02_STRING = "02";
+    public static final String GROUP_IMG_03_STRING = "03";
+    public static final String GROUP_IMG_04_STRING = "04";
+    public static final String GROUP_IMG_05_STRING = "05";
+    public static final String GROUP_IMG_06_STRING = "06";
+    public static final String GROUP_IMG_07_STRING = "07";
+    public static final String GROUP_IMG_08_STRING = "08";
+    public static final String GROUP_IMG_09_STRING = "09";
+
+    public static final String GROUP_BACKGROUND_00_STRING = "00";
+    public static final String GROUP_BACKGROUND_01_STRING = "01";
+    public static final String GROUP_BACKGROUND_02_STRING = "02";
+    public static final String GROUP_BACKGROUND_03_STRING = "03";
+    public static final String GROUP_BACKGROUND_04_STRING = "04";
+    public static final String GROUP_BACKGROUND_05_STRING = "05";
+    public static final String GROUP_BACKGROUND_06_STRING = "06";
+    public static final String GROUP_BACKGROUND_07_STRING = "07";
+    public static final String GROUP_BACKGROUND_08_STRING = "08";
+    public static final String GROUP_BACKGROUND_09_STRING = "09";
+
+    public static final int DEFAULT_RANK = 999;
+
+    //Device Power status
+    public static final int NOTING = -1;
+    public static final int NOT_CONNECTED = -1;
+    public static final int POWER_ON = 1;
+    public static final int POWER_OFF = 0;
+
+    public static final int GROUP_LIST_COUNT_LINE = 4;
+
+    public static final long MQTT_PUBLISH_DELAY_TIME = 70;
+}

+ 109 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/CustomDialog.java

@@ -0,0 +1,109 @@
+package kr.co.icontrols.iotplatform.utils;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import kr.co.icontrols.iotplatform.MainActivity;
+import kr.co.icontrols.iotplatform.R;
+
+public class CustomDialog {
+
+    private Context activity;
+
+    public final static int MORE_SMART_LIGHT = 0;
+
+    public CustomDialog(Activity activity) {
+        this.activity = activity;
+    }
+
+    // 호출할 다이얼로그 함수를 정의한다.
+    public void callFunction(int mode) {
+
+        // 커스텀 다이얼로그를 정의하기위해 Dialog클래스를 생성한다.
+        final Dialog dlg = new Dialog(activity);
+
+        // 액티비티의 타이틀바를 숨긴다.
+        dlg.requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+        // 커스텀 다이얼로그의 레이아웃을 설정한다.
+        dlg.setContentView(R.layout.custom_dialog_more);
+
+        // 커스텀 다이얼로그를 노출한다.
+        dlg.show();
+
+        // 커스텀 다이얼로그의 각 위젯들을 정의한다.
+        final LinearLayout layoutAdd = dlg.findViewById(R.id.layoutAdd);
+        final LinearLayout layoutDelete = dlg.findViewById(R.id.layoutDelete);
+        final LinearLayout layoutRank = dlg.findViewById(R.id.layoutRank);
+
+        final TextView txtTitle = dlg.findViewById(R.id.txtTitle);
+        final TextView txtAdd = dlg.findViewById(R.id.txtAdd);
+        final TextView txtRank = dlg.findViewById(R.id.txtRank);
+        final TextView txtDelete = dlg.findViewById(R.id.txtDelete);
+
+        final ImageView imgAdd = dlg.findViewById(R.id.imgAdd);
+        final ImageView imgRank = dlg.findViewById(R.id.imgRank);
+        final ImageView imgDelete = dlg.findViewById(R.id.imgDelete);
+
+        final ImageButton btnCloseMore = dlg.findViewById(R.id.btnCloseMore);
+
+        btnCloseMore.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // 커스텀 다이얼로그를 종료한다.
+                dlg.dismiss();
+            }
+        });
+
+        switch (mode) {
+            case MORE_SMART_LIGHT:
+                txtTitle.setText("스마트 조명 홈관리");
+                txtAdd.setText("홈에 조명 추가");
+                txtRank.setText("스마트 조명 순서");
+                txtDelete.setText("스마트 조명 삭제");
+
+                imgAdd.setImageResource(R.drawable.ico_home_set_add);
+                imgRank.setImageResource(R.drawable.ico_home_set_order);
+                imgDelete.setImageResource(R.drawable.ico_home_set_del);
+                break;
+        }
+
+        /*
+        okButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                // '확인' 버튼 클릭시 메인 액티비티에서 설정한 main_label에
+                // 커스텀 다이얼로그에서 입력한 메시지를 대입한다.
+                main_label.setText(message.getText().toString());
+                Toast.makeText(context, "\"" +  message.getText().toString() + "\" 을 입력하였습니다.", Toast.LENGTH_SHORT).show();
+
+                // 커스텀 다이얼로그를 종료한다.
+                dlg.dismiss();
+            }
+        });
+        cancelButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Toast.makeText(context, "취소 했습니다.", Toast.LENGTH_SHORT).show();
+
+                // 커스텀 다이얼로그를 종료한다.
+                dlg.dismiss();
+            }
+        });
+         */
+    }
+}

+ 91 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/CustomProgressDialog.java

@@ -0,0 +1,91 @@
+package kr.co.icontrols.iotplatform.utils;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.view.View;
+import android.view.Window;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import kr.co.icontrols.iotplatform.R;
+
+/**
+ * Progress bar를 Custom화 함(화면 표시 방법 변경)
+ */
+public class CustomProgressDialog extends Dialog {
+
+    private TextView mTxtTitle;
+    private TextView mTxtSubTitle;
+
+    public CustomProgressDialog(Context context) {
+        super(context);
+        initComponent(null,null);
+    }
+
+    public CustomProgressDialog(Context context, String title) {
+        super(context);
+        initComponent(title,null);
+    }
+
+    public CustomProgressDialog(Context context, String title, String subTitle) {
+        super(context);
+        initComponent(title,subTitle);
+    }
+
+    private void initComponent(String t, String s){
+        requestWindowFeature(Window.FEATURE_NO_TITLE); // 지저분한(?) 다이얼 로그 제목을 날림
+        getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        setContentView(R.layout.custom_dialog); // 다이얼로그에 박을 레이아웃
+
+        RelativeLayout layoutMain = (RelativeLayout)findViewById(R.id.layoutMain);
+        ProgressBar progressBar = (ProgressBar)findViewById(R.id.progressBar);
+
+        mTxtTitle = (TextView) findViewById(R.id.txtTitle);
+        mTxtTitle.setVisibility(View.GONE);
+        if(t!=null){
+            mTxtTitle.setText(t);
+            mTxtTitle.setVisibility(View.VISIBLE);
+        }
+
+        mTxtSubTitle = (TextView)findViewById(R.id.txtSubTitle);
+        mTxtSubTitle.setVisibility(View.GONE);
+        if(s!=null){
+            mTxtSubTitle.setText(s);
+            mTxtSubTitle.setVisibility(View.VISIBLE);
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+    }
+
+    public void showTitle(){
+        if(mTxtTitle.length() > 0) {
+            mTxtTitle.setVisibility(View.VISIBLE);
+        }
+    }
+
+    public void showTitle(String str){
+        if(str != null) {
+            mTxtTitle.setText(str);
+            mTxtTitle.setVisibility(View.VISIBLE);
+        }
+    }
+
+    public void showSubTitle(){
+        if(mTxtSubTitle.length() > 0) {
+            mTxtSubTitle.setVisibility(View.VISIBLE);
+        }
+    }
+
+    public void showSubTitle(String str){
+        if(str != null) {
+            mTxtSubTitle.setText(str);
+            mTxtSubTitle.setVisibility(View.VISIBLE);
+        }
+    }
+}

+ 40 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/DownloadImageTask.java

@@ -0,0 +1,40 @@
+package kr.co.icontrols.iotplatform.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.widget.ImageView;
+
+import java.io.InputStream;
+
+import kr.co.icontrols.iotplatform.IotPlatformApplication;
+import kr.co.icontrols.iotplatform.R;
+
+public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
+        ImageView bmImage;
+
+    public DownloadImageTask(ImageView bmImage) {
+            this.bmImage = bmImage;
+            }
+
+    protected Bitmap doInBackground(String... urls) {
+        String urldisplay = urls[0];
+        Bitmap mIcon11 = null;
+
+        try {
+            InputStream in = new java.net.URL(urldisplay).openStream();
+            mIcon11 = BitmapFactory.decodeStream(in);
+        } catch (Exception e) {
+            Log.e("Error", e.getMessage());
+            e.printStackTrace();
+            mIcon11 = BitmapFactory.decodeResource(IotPlatformApplication.getInstance().getResources(), R.drawable.profile_thum_176);
+        }
+
+        return mIcon11;
+    }
+
+    protected void onPostExecute(Bitmap result) {
+        bmImage.setImageBitmap(CommonUtils.getCircleBitmap(result));
+    }
+}

+ 6 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/GetSessionCallback.java

@@ -0,0 +1,6 @@
+package kr.co.icontrols.iotplatform.utils;
+
+public interface GetSessionCallback {
+    void successed();
+    void failed();
+}

+ 907 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/HttpApi.java

@@ -0,0 +1,907 @@
+package kr.co.icontrols.iotplatform.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Base64;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import kr.co.icontrols.iotplatform.DataClass.DeviceGroupInfo;
+import kr.co.icontrols.iotplatform.IotPlatformApplication;
+import kr.co.icontrols.iotplatform.R;
+import kr.co.icontrols.iotplatform.cognito.AppHelper;
+import okhttp3.Callback;
+import okhttp3.Headers;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+
+import static com.amazonaws.util.EncodingSchemeEnum.BASE64;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.ErrorLogOutput;
+import static kr.co.icontrols.iotplatform.utils.CommonUtils.WarnLogOutput;
+import static kr.co.icontrols.iotplatform.utils.Constants.DEFUALT_GROUP_INDEX;
+
+public class HttpApi {
+    private static final String TAG = "HttpApi";
+
+    public static final int HTTP_ACCEPTED = 202;
+    public static final int HTTP_BAD_GATEWAY = 502;
+    public static final int HTTP_BAD_METHOD = 405;
+    public static final int HTTP_BAD_REQUEST = 400;
+    public static final int HTTP_CLIENT_TIMEOUT = 408;
+    public static final int HTTP_CONFLICT = 409;
+    public static final int HTTP_CREATED = 201;
+    public static final int HTTP_ENTITY_TOO_LARGE = 413;
+    public static final int HTTP_FORBIDDEN = 403;
+    public static final int HTTP_GATEWAY_TIMEOUT = 504;
+    public static final int HTTP_GONE = 410;
+    public static final int HTTP_INTERNAL_ERROR = 500;
+    public static final int HTTP_LENGTH_REQUIRED = 411;
+    public static final int HTTP_MOVED_PERM = 301;
+    public static final int HTTP_MOVED_TEMP = 302;
+    public static final int HTTP_MULT_CHOICE = 300;
+    public static final int HTTP_NOT_ACCEPTABLE = 406;
+    public static final int HTTP_NOT_AUTHORITATIVE = 203;
+    public static final int HTTP_NOT_FOUND = 404;
+    public static final int HTTP_NOT_IMPLEMENTED = 501;
+    public static final int HTTP_NOT_MODIFIED = 304;
+    public static final int HTTP_NO_CONTENT = 204;
+    public static final int HTTP_OK = 200;
+    public static final int HTTP_PARTIAL = 206;
+    public static final int HTTP_PAYMENT_REQUIRED = 402;
+    public static final int HTTP_PRECON_FAILED = 412;
+    public static final int HTTP_PROXY_AUTH = 407;
+    public static final int HTTP_REQ_TOO_LONG = 414;
+    public static final int HTTP_RESET = 205;
+    public static final int HTTP_SEE_OTHER = 303;
+    /** @deprecated */
+    @Deprecated
+    public static final int HTTP_SERVER_ERROR = 500;
+    public static final int HTTP_UNAUTHORIZED = 401;
+    public static final int HTTP_UNAVAILABLE = 503;
+    public static final int HTTP_UNSUPPORTED_TYPE = 415;
+    public static final int HTTP_USE_PROXY = 305;
+    public static final int HTTP_VERSION = 505;
+
+    private static int TIMEOUT_CONNECT = 30;
+    private static int TIMEOUT_WRITE = 30;
+    private static int TIMEOUT_READ = 30;
+
+    private static String URL_END_POINT = "https://1j6edvovka.execute-api.ap-northeast-2.amazonaws.com/Prod/"; //실사용 DB 주소.
+    private static String URL_KAKAO_GET_ADDRESS = "https://dapi.kakao.com/v2/local/search/address.json";
+
+    private static final int TYPE_GET = 0;
+    private static final int TYPE_PUT = 1;
+    private static final int TYPE_POST = 2;
+    private static final int TYPE_PATCH = 3;
+    private static final int TYPE_DELETE = 4;
+
+    /**
+     * OKHTTP를 통해 통신
+     * @param type (GET, POST, PATCH 방식)
+     * @param hasHeader (헤더 포함 유무)
+     * @param url : API 주소
+     * @param callback : 전송후 Callback 함수
+     * @throws IOException
+     */
+    private static void send(final int type, final boolean hasHeader, final String url, final String json, final Callback callback) throws IOException {
+        Handler mHandler = new Handler(Looper.getMainLooper());
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                String typeString = type==TYPE_GET?"GET":type==TYPE_POST?"POST":type==TYPE_PATCH?"PATCH":type==TYPE_PUT?"PUT":type==TYPE_DELETE?"DELETE":"UNKNOWN";
+                ErrorLogOutput(TAG, "HTTP API START("+typeString+")\n url : " + url + "\nbody : "+ json);
+
+                final MediaType JSON
+                        = MediaType.parse("application/json; charset=utf-8");
+
+                try {
+                    final TrustManager[] trustAllCerts = new TrustManager[]{
+                            new X509TrustManager() {
+                                @Override
+                                public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                                        throws CertificateException {
+                                }
+
+                                @Override
+                                public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                                        throws CertificateException {
+                                }
+
+                                @Override
+                                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                                    return new java.security.cert.X509Certificate[]{};
+                                }
+                            }
+                    };
+
+                    // Install the all-trusting trust manager
+                    final SSLContext sslContext = SSLContext.getInstance("SSL");
+                    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+                    // Create an ssl socket factory with our all-trusting manager
+                    final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+
+                    OkHttpClient client = new OkHttpClient.Builder()
+                            .connectTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS)
+                            .writeTimeout(TIMEOUT_WRITE, TimeUnit.SECONDS)
+                            .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
+                            .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
+                            .hostnameVerifier(new HostnameVerifier() {
+                                @Override
+                                public boolean verify(String hostname, SSLSession session) {
+                                    return true;
+                                }
+                            })
+                            .build();
+
+                    RequestBody body = RequestBody.create(JSON, json);
+                    Request.Builder builder = new Request.Builder();
+
+                    builder.url(url);
+
+                    builder.addHeader("Accept", "application/json")
+                            .addHeader("Content-Type", "application/json")
+                            .addHeader("x-api-key", "C45ls6kqaU2yvjNltlmuC2qyZmEHQMRB1ixNJ6B1");
+
+                    if(hasHeader) {
+                        builder.addHeader("Authorization", "Bearer "+ AppHelper.getCurrSession().getIdToken().getJWTToken());
+                    }
+
+
+                    switch (type) {
+                        case TYPE_PUT:
+                            builder.put(body);
+                            break;
+                        case TYPE_POST:
+                            builder.post(body);
+                            break;
+                        case TYPE_PATCH:
+                            builder.patch(body);
+                            break;
+                        case TYPE_DELETE:
+                            builder.delete(body);
+                            break;
+                        case TYPE_GET:
+                            builder.get();
+                            break;
+                        default:
+                            break;
+                    }
+
+                    Request request = builder.build();
+                    client.newCall(request).enqueue(callback);
+                } catch (Exception e) {
+                    ErrorLogOutput(TAG,"Exception while configuring IgnoreSslCertificate " + e);
+                }
+            }
+        }, 0);
+    }
+
+    /**
+     * 사용자 - 01.회원 정보 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getUserInfo (Callback c) throws IOException {
+        String url = URL_END_POINT + "user";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 사용자 - 02.회원 정보 수정
+     * @param profile 프로필 이미지 uri
+     * @param pushAccess 푸시메시지 설정 여부
+     * @param autoUpdate 연결된 디바이스들의 자동 업데이트 설정여부
+     * @param c
+     * @throws IOException
+     */
+    public static void changeUserInfo(String profile, String nickName, String phoneNumber, boolean pushAccess, boolean autoUpdate, String pushToken, Callback c) throws IOException {
+        String url = URL_END_POINT + "user";
+        send(TYPE_PATCH, true, url,changeUserInfoBowlingJson(profile, nickName, phoneNumber, pushAccess, autoUpdate, pushToken),c);
+    }
+
+    private static String changeUserInfoBowlingJson(String profile, String nickName, String phoneNumber, boolean pushAccess, boolean autoUpdate, String pushToken) {
+        return "{"
+                + (profile.isEmpty()?"":"\"profile\":\""+profile+"\",")
+                + (nickName.isEmpty()?"":"\"nickName\":\""+nickName+"\",")
+                + (phoneNumber.isEmpty()?"":"\"phoneNumber\":\""+phoneNumber+"\",")
+                + (pushToken.isEmpty()?"":"\"pushToken\":\""+pushToken+"\",")
+                + "\"pushAccess\":"+pushAccess+","
+                + "\"autoUpdate\":"+autoUpdate+"}";
+    }
+
+
+    public static void changeUserInfo(String profile, String nickName, String phoneNumber, String pushToken, Callback c) throws IOException {
+        String url = URL_END_POINT + "user";
+        send(TYPE_PATCH, true, url,changeUserInfoBowlingJson(profile, nickName, phoneNumber, pushToken),c);
+    }
+
+    private static String changeUserInfoBowlingJson(String profile, String nickName, String phoneNumber, String pushToken) {
+        String jsonString = "{";
+        if(!profile.isEmpty()) {
+            jsonString += (jsonString.length()>1?",":"")+"\"profile\":\""+profile+"\"";
+        }
+        if(!nickName.isEmpty()) {
+            jsonString += (jsonString.length()>1?",":"")+"\"nickName\":\""+nickName+"\"";
+        }
+        if(!phoneNumber.isEmpty()) {
+            jsonString += (jsonString.length()>1?",":"")+"\"phoneNumber\":\""+phoneNumber+"\"";
+        }
+        if(!pushToken.isEmpty()) {
+            jsonString += (jsonString.length()>1?",":"")+"\"pushToken\":\""+pushToken+"\"";
+        }
+        jsonString += "}";
+        return jsonString;
+    }
+
+
+    /**
+     * 사용자 - 03.회원 정보 검증
+     * @param email 이메일
+     * @param nickname 별명
+     * @param phone_number 휴대폰 번호
+     * @param c
+     * @throws IOException
+     */
+    public static void isValidateUserInfo (String email, String nickname, String phone_number, Callback c) throws IOException {
+        String url = URL_END_POINT + "user/validate";
+        send(TYPE_POST, false, url,isValidateUserInfoBowlingJson(email, nickname, phone_number),c);
+    }
+
+    private static String isValidateUserInfoBowlingJson(String email, String nickname, String phone_number) {
+        return "{\"email\":\""+email+"\","
+                + "\"nickname\":\""+nickname+"\","
+                + "\"phoneNumber\":\""+phone_number+"\"}";
+    }
+
+
+    /**
+     * 사용자 - 04.회원 비밀번호 수정
+     * @param uid 사용자 고유번호
+     * @param password 암호화된 비밀번호
+     * @param c
+     * @throws IOException
+     */
+    public static void changeUserPassword (String uid, String password, Callback c) throws IOException {
+        String url = URL_END_POINT + "user/changePassword";
+        send(TYPE_PATCH, true, url,changeUserPasswordBowlingJson(uid, password),c);
+    }
+
+    private static String changeUserPasswordBowlingJson(String uid, String password) {
+        return "{\"uid\":\""+uid+"\","
+                + "\"password\":\""+password+"\"}";
+    }
+
+    /**
+     * 사용자 - 05.인증 요청
+     * @param isEmail 인증방식이 이메일인 경우
+     * @param isUserConfirm 인증요청을 진행하는 시점이 User Confirm인 경우
+     * @param value 인증메일을 전송할 메일 주소 또는 인증코드를 전송할 핸드폰 번호
+     * @param c
+     * @throws IOException
+     */
+    public static void requestConfirmUser (boolean isEmail, boolean isUserConfirm, String value, Callback c) throws IOException {
+        String url = URL_END_POINT + "user/reqConfirm";
+        send(TYPE_POST, true, url,requestConfirmUserBowlingJson(isEmail, isUserConfirm, value),c);
+    }
+
+    private static String requestConfirmUserBowlingJson(boolean isEmail, boolean isUserConfirm, String value) {
+        String s_type = isEmail?"email":"“phone"; //인증진행방식 [“email”, “phone”]
+        String c_case = isUserConfirm?"user_confirm":"change_password"; //인증요청을 진행하는 시점 [“user_confirm”, “change_password”]
+        return "{\"sType\":\""+s_type+"\","
+                + (isEmail?"\"email\":\""+value+"\",":"\"phoneNumber\":\""+value+"\",")
+                + "\"cCase\":\""+c_case+"\"}";
+    }
+
+
+    /**
+     * 사용자 - 06.회원탈퇴
+     * @param uid 사용자 ID (Email)
+     * @param password 비밀번호
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteUserInfo (String uid, String password, Callback c) throws IOException {
+        String url = URL_END_POINT + "user";
+        send(TYPE_DELETE, true, url,""/*deleteUserInfoBowlingJson(uid, password)*/,c); //TODO- 문서와 JSON 실행문 상의함.
+    }
+    /*
+    private static String deleteUserInfoBowlingJson(String uid, String password) {
+        return "{\"uid\":\""+uid+"\","
+                + "\"password\":\""+password+"\"}";
+    }
+    */
+
+    /**
+     * 사용자 - 07.사용자인증
+     * @param uid 사용자 ID(회원 정보 조회 uid)
+     * @param isEmail 인증방식이 이메일인 경우
+     * @param isUserConfirm 인증요청을 진행하는 시점이 User Confirm인 경우
+     * @param value 인증메일을 전송할 메일 주소 또는 인증코드를 전송할 핸드폰 번호
+     * @param c
+     * @throws IOException
+     */
+    public static void certificationUserInfo (String uid, boolean isEmail, boolean isUserConfirm, String value, Callback c) throws IOException {
+        String url = URL_END_POINT + "activate"+"/" + uid;
+        send(TYPE_POST, true, url,certificationUserInfoBowlingJson(isEmail, isUserConfirm, value),c);
+    }
+
+    private static String certificationUserInfoBowlingJson(boolean isEmail, boolean isUserConfirm, String value) {
+        String s_type = isEmail?"email":"“phone"; //인증진행방식 [“email”, “phone”]
+        String c_case = isUserConfirm?"user_confirm":"change_password"; //인증요청을 진행하는 시점 [“user_confirm”, “change_password”]
+        return "{\"s_type\":\""+s_type+"\","
+                + (isEmail?"\"email\":\""+value+"\",":"\"phoneNumber\":\""+value+"\",")
+                + "\"c_case\":\""+c_case+"\"}";
+    }
+
+
+    /**
+     * 멤버 - 01.(마스터)게스트 멤버추가 - 멤버초대
+     * @param isEmail 인증방식이 이메일인 경우
+     * @param value 인증메일을 전송할 메일 주소 또는 인증코드를 전송할 핸드폰 번호
+     * @param c
+     * @throws IOException
+     */
+    public static void inviteGuestForMaster (boolean isEmail, String value, Callback c) throws IOException {
+        String url = URL_END_POINT + "member/guest";
+        send(TYPE_POST, true, url,inviteGuestBowlingJson(isEmail, value),c);
+    }
+
+    private static String inviteGuestBowlingJson(boolean isEmail, String value) {
+        return "{\"sType\":\""+(isEmail?"email":"“phone")+"\","
+                + (isEmail?"\"email\":\""+value+"\"":"\"phoneNumber\":\""+value+"\"")+"}";
+    }
+
+
+    /**
+     * 멤버 - 02.(마스터) 게스트 초대이력 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getHistoryForInviteGuest (Callback c) throws IOException {
+        String url = URL_END_POINT + "member/guest/inviteHist";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 멤버 - 03.(마스터) 게스트 초대이력 삭제
+     * @param hist_id 초대이력 고유아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteHistoryForInviteGuest (String hist_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "member/guest/inviteHist"+"/" + hist_id;;
+        send(TYPE_DELETE, true, url,"",c);
+    }
+
+    /**
+     * 멤버 - 04.(마스터) 게스트 멤버삭제
+     * @param guest_id 게스트유저의 고유 id
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteGuest (String guest_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "member/guest"+"/" + guest_id;;
+        send(TYPE_DELETE, true, url,"",c);
+    }
+
+    /**
+     * 멤버 - 05.(마스터) 게스트 멤버 목록 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getGuestList (Callback c) throws IOException {
+        String url = URL_END_POINT + "member/guests";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 멤버 -06.(마스터) 마스터에서 탈퇴 - 마스터<->게스트 관계 해제
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteMasterInfo (Callback c) throws IOException {
+        String url = URL_END_POINT + "member/guests";
+        send(TYPE_DELETE, true, url,"",c);
+    }
+
+
+    /**
+     * 멤버 - 07.(게스트) 초대이력 목록 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getHistoryForInvite (Callback c) throws IOException {
+        String url = URL_END_POINT + "member/inviteHist";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 멤버 - 08. (게스트) 초대 승락 / 거절
+     * @param hist_id 초대 이력의 고유 아이디
+     * @param isAccept 초대 응답 ( 승낙인경우 ‘yes’, 거절인경우 ‘no’ )
+     * @param c
+     * @throws IOException
+     */
+    public static void acceptToInvite (String hist_id, boolean isAccept, Callback c) throws IOException {
+        String url = URL_END_POINT + "/member/inviteHist/"+hist_id+"/invited/"+(isAccept?"yes":"no");
+        send(TYPE_POST, true, url,"",c);
+    }
+
+    /**
+     * 멤버 - 09. (게스트) 마스터 정보 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getMasterInfo (Callback c) throws IOException {
+        String url = URL_END_POINT + "member/master";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 멤버 - 10. (게스트) 멤버탈퇴 - 마스터<->게스트 관계 해제
+     * @param master_id 마스터의 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteGuestInfo (String master_id,Callback c) throws IOException {
+        String url = URL_END_POINT + "member/master"+"/"+master_id;
+        send(TYPE_DELETE, true, url,"",c);
+    }
+
+
+    /**
+     * 디바이스 그룹 - 01. 디바이스 그룹 등록
+     * @param friendlyName 디바이스 그룹
+     * @param icon App에 표시할 그룹별 아이콘 정보(아이콘 정보는 App과 미리 정의한 icon의 정보를 전달해야합니다.)
+     * @param background App에 표시할 그룹별 배경화면 정보(배경화면 정보를 App과 미리 정의한 정보를 전달해야합니다.)
+     * @param c
+     * @throws IOException
+     */
+    public static void registerDeviceGroup (String friendlyName, String icon, String background, Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups";
+        send(TYPE_POST, true, url,registerDeviceGroupBowlingJson(friendlyName, icon, background),c);
+    }
+
+    private static String registerDeviceGroupBowlingJson(String friendlyName, String icon, String background) {
+        return "{\"friendlyName\":\""+friendlyName+"\","
+                + "\"icon\":\""+icon+"\","
+                + "\"background\":\""+background+"\"}";
+    }
+
+    /**
+     * 디바이스 그룹 - 02. 디바이스 그룹 조회
+     * @param dgroup_id 디바이스 그룹의 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void getDeviceGroup (String dgroup_id,Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups"+"/"+dgroup_id;
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 디바이스 그룹 - 03. 디바이스 그룹 수정
+     * @param dgroup_id 디바이스 그룹의 고유 아이디
+     * @param friendlyName 디바이스 그룹 명(기본그룹은 디바이스 그룹명을 수정할 수 없습니다.)
+     * @param icon App에 표시할 그룹별 아이콘 정보(아이콘 정보는 App과 미리 정의한 icon의 정보를 전달해야합니다.)
+     * @param background App에 표시할 그룹별 배경화면 정보(배경화면 정보를 App과 미리 정의한 정보를 전달해야합니다.)
+     * @param c
+     * @throws IOException
+     */
+    public static void modifyDeviceGroup (String dgroup_id, String friendlyName, String icon, String background, Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups"+"/"+dgroup_id;
+        send(TYPE_PATCH, true, url,modifyDeviceGroupBowlingJson(friendlyName, icon, background),c);
+    }
+
+    private static String modifyDeviceGroupBowlingJson(String friendlyName, String icon, String background) {
+        String jsonString = "{";
+        if(!friendlyName.isEmpty()) {
+            jsonString += (jsonString.length()>1?",":"")+"\"friendlyName\":\""+friendlyName+"\"";
+        }
+        if(!icon.isEmpty()) {
+            jsonString += (jsonString.length()>1?",":"")+"\"icon\":\""+icon+"\"";
+        }
+        if(!background.isEmpty()) {
+            jsonString += (jsonString.length()>1?",":"")+"\"background\":\""+background+"\"";
+        }
+        jsonString += "}";
+
+        return jsonString;
+    }
+
+    /**
+     * 디바이스 그룹 - 04. 디바이스 그룹 삭제
+     * @param dgroup_id 디바이스 그룹의 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteDeviceGroup (String dgroup_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups"+"/"+dgroup_id;
+        send(TYPE_DELETE, true, url,"",c);
+    }
+
+
+    /**
+     * 디바이스 그룹 - 05. 디바이스 그룹 목록 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getDeviceGroupList (Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /***
+     * 디바이스 그룹 - 06. 디바이스 그룹 목록 순서변경
+     * @param list
+     * @param c
+     * @throws IOException
+     */
+    public static void changeDeviceGroupListRank (final List<DeviceGroupInfo> list, Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups";
+        send(TYPE_PATCH, true, url,changeDeviceGroupListRank(list),c);
+    }
+
+    private static String changeDeviceGroupListRank(final List<DeviceGroupInfo> list) {
+        String jsonListString = "";
+        for(int i = 0 ; i < list.size() ; i++) {
+            if(list.get(i).index != DEFUALT_GROUP_INDEX) {
+                if (jsonListString.length() != 0) {
+                    jsonListString += ",";
+                }
+                jsonListString += "{\"dgroupId\":\"" + list.get(i).dgroupId + "\"" +
+                        ",\"index\":" + list.get(i).index + "}";
+            }
+        }
+        return "{\"dgroups\":["+jsonListString+"]}";
+    }
+
+    /**
+     * 디바이스 그룹 - 07. 디바이스 그룹 별 디바이스 목록
+     * @param dgroup_id
+     * @param c
+     * @throws IOException
+     */
+    public static void getDeviceListInDeviceGroup (String dgroup_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups"+"/"+dgroup_id + "/devices";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 디바이스 그룹 - 08. 디바이스 그룹에 디바이스 연결
+     * @param dgroup_id 디바이스와 연결하고자 하는 디바이스 그룹의 고유 아이디
+     * @param device_id 디바이스 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void connectDeviceInDeviceGroup (String dgroup_id, String device_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups"+"/"+dgroup_id + "/devices/"+device_id;
+        send(TYPE_POST, true, url,"",c);
+    }
+
+
+    /**
+     * 디바이스 그룹 - 09. 디바이스 그룹에 디바이스 삭제
+     * @param dgroup_id 디바이스와 연결하고자 하는 디바이스 그룹의 고유 아이디
+     * @param device_id 디바이스 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteDeviceInDeviceGroup (String dgroup_id, String device_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "dgroups"+"/"+dgroup_id + "/devices/"+device_id;
+        send(TYPE_DELETE, true, url,"",c);
+    }
+
+    /**
+     * 디바이스 - 01. 디바이스 매핑 완료
+     * @param device_id 디바이스 그룹의 고유 아이디
+     * @param friendlyName 디바이스 그룹 명(기본그룹은 디바이스 그룹명을 수정할 수 없습니다.)
+     * @param icon App에 표시할 그룹별 아이콘 정보(아이콘 정보는 App과 미리 정의한 icon의 정보를 전달해야합니다.)
+     * @param background App에 표시할 그룹별 배경화면 정보(배경화면 정보를 App과 미리 정의한 정보를 전달해야합니다.)
+     * @param c
+     * @throws IOException
+     */
+    public static void mappingDevice (String device_id, String friendlyName, String icon, String background, Callback c) throws IOException {
+        String url = URL_END_POINT + "device"+"/"+device_id;
+        send(TYPE_POST, true, url,mappingDeviceBowlingJson(friendlyName, icon, background),c); //TODO- 문서와 JSON 실행문 상의함.
+    }
+
+    public static void mappingDevice (String device_id, /*String friendlyName, String icon, String background,*/ Callback c) throws IOException {
+        String url = URL_END_POINT + "device"+"/"+device_id;
+        send(TYPE_POST, true, url,""/*mappingDeviceBowlingJson(friendlyName, icon, background)*/,c); //TODO- 문서와 JSON 실행문 상의함.
+    }
+
+    private static String mappingDeviceBowlingJson(String friendlyName, String icon, String background) {
+        return "{\"friendlyName\":\""+friendlyName+"\","
+                + "\"icon\":\""+icon+"\","
+                + "\"background\":\""+background+"\"}";
+    }
+
+    /**
+     * 디바이스 - 02. 디바이스 상세정보 조회
+     * @param device_id 디바이스 그룹의 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void getDeviceInfo (String device_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "device"+"/"+device_id;
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 디바이스 - 03. 디바이스 상세정보 수정
+     * @param device_id 디바이스 그룹의 고유 아이디
+     * @param friendlyName App에 표시할 디바이스 이름 (본인 소유의 디바이스일 경우)
+     * @param c
+     * @throws IOException
+     */
+    public static void modifyDeviceInfo (String device_id, String friendlyName, Callback c) throws IOException {
+        String url = URL_END_POINT + "device"+"/"+device_id;
+        send(TYPE_PATCH, true, url,modifyDeviceInfoBowlingJson(friendlyName),c);
+    }
+
+    private static String modifyDeviceInfoBowlingJson(String friendlyName) {
+        return "{\"friendlyName\":\""+friendlyName+"\"}";
+    }
+
+    /**
+     * 디바이스 - 04. 디바이스 삭제(디바이스 초기화)
+     * @param device_id 디바이스 그룹의 고유 아이디
+     * friendlyName App에 표시할 디바이스 이름 (본인 소유의 디바이스일 경우)
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteDevice (String device_id, /*String friendlyName,*/ Callback c) throws IOException {
+        String url = URL_END_POINT + "device"+"/"+device_id;
+        send(TYPE_DELETE, true, url,""/*deleteDeviceBowlingJson(friendlyName)*/,c);
+    }
+
+    private static String deleteDeviceBowlingJson(String friendlyName) {
+        return "{\"friendlyName\":\""+friendlyName+"\"}";
+    }
+
+
+    /**
+     * 디바이스 - 05. 디바이스 최신 펌웨어 정보 조회
+     * @param device_id 디바이스 그룹의 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void getFirmwareInfoAtDevice (String device_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "device"+"/"+device_id + "/firmware";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 디바이스 - 06. 디바이스 최신 펌웨어 업데이트
+     * @param device_id 디바이스 그룹의 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void updateFirmwareInfoAtDevice (String device_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "device"+"/"+device_id + "/firmware";
+        send(TYPE_PUT, true, url,"",c);
+    }
+
+    /**
+     * 디바이스 - 07. 디바이스 전체 목록 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getAllDevice (Callback c) throws IOException {
+        String url = URL_END_POINT + "devices";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+
+    /**
+     * HOME - 01.위치 정보 등록
+     * @param latitude 위도
+     * @param longitude 경도
+     * @param city 도시 (ex: 경기도, 서울특별시)
+     * @param district 시-구 (ex: 고양시덕양구, 용인시수지구)
+     * @param town 동(ex: 성사1동, 풍덕천1동)
+     * @param c
+     * @throws IOException
+     */
+    public static void registerLocationInfo (String latitude, String longitude, String city, String district,String town, Callback c) throws IOException {
+        String url = URL_END_POINT + "home/location";
+        send(TYPE_POST, true, url,registerLocationInfoBowlingJson(latitude, longitude, city, district, town),c);
+    }
+
+    private static String registerLocationInfoBowlingJson(String latitude, String longitude, String city, String district,String town) {
+        return "{\"latitude\":\""+latitude+"\","
+                + "\"longitude\":\""+longitude+"\","
+                + "\"address\":{"
+                + "\"city\":\""+city+"\","
+                + "\"district\":\""+district+"\","
+                + "\"town\":\""+town+"\""
+                +"}}";
+    }
+
+    /**
+     * HOME - 02.위치 정보 조회
+     * @param c
+     * @throws IOException
+     */
+    public static void getLocationInfo (Callback c) throws IOException {
+        String url = URL_END_POINT + "home/location";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * HOME - 03.위치 정보 수정
+     * @param location_id 위치정보의 고유 아이디
+     * @param latitude 위도
+     * @param longitude 경도
+     * @param city 도시 (ex: 경기도, 서울특별시)
+     * @param district 시-구 (ex: 고양시덕양구, 용인시수지구)
+     * @param town 동(ex: 성사1동, 풍덕천1동)
+     * @param c
+     * @throws IOException
+     */
+    public static void modifyLocationInfo (String location_id, String latitude, String longitude, String city, String district,String town, Callback c) throws IOException {
+        String url = URL_END_POINT + "home/location/"+location_id;
+        send(TYPE_PATCH, true, url,modifyLocationInfoBowlingJson(latitude, longitude, city, district, town),c);
+    }
+
+    private static String modifyLocationInfoBowlingJson(String latitude, String longitude, String city, String district,String town) {
+        return "{\"latitude\":\""+latitude+"\","
+                + "\"longitude\":\""+longitude+"\","
+                + "\"address\":{"
+                + "\"city\":\""+city+"\","
+                + "\"district\":\""+district+"\","
+                + "\"town\":\""+town+"\""
+                +"}}";
+    }
+
+    /**
+     * HOME - 04.위치 정보 삭제
+     * @param location_id 위치정보의 고유 아이디
+     * @param c
+     * @throws IOException
+     */
+    public static void deleteLocationInfo (String location_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "home/location/"+location_id;
+        send(TYPE_DELETE, true, url,"",c);
+    }
+
+    /**
+     * HOME - 05.날씨 정보 조회
+     * @param location_id 위치정보의 고유 아이디
+     * @param location_id
+     * @param c
+     * @throws IOException
+     */
+    public static void getWeatherInfo (String location_id, Callback c) throws IOException {
+        String url = URL_END_POINT + "/home/location/" + location_id +"/weather";
+        send(TYPE_GET, true, url,"",c);
+    }
+
+    /**
+     * 기타 - 01. 데이터 암호화
+     * @param plainText 암호화 하고자 하는 평문 데이터
+     * @param c
+     * @throws IOException
+     */
+    public static void encryptData(String plainText, Callback c) throws IOException {
+        String url = URL_END_POINT + "encrypt";
+        send(TYPE_POST, true, url,encryptDataBowlingJson(plainText),c);
+    }
+
+    private static String encryptDataBowlingJson(String plainText) {
+        return "{\"plainText\":\""+plainText+"\"}";
+    }
+
+
+    /**
+     * 카카오 로컬 REST API - (검색어로 위치 정보 획득 API)
+     * @param serach - 검색할 문구
+     * @param c
+     * @throws IOException
+     */
+    public static void getLocationInfo(String serach, Callback c) throws IOException {
+        String url = URL_KAKAO_GET_ADDRESS + "?page=1&size=10&query=" + serach;
+
+        ErrorLogOutput(TAG, "KAKAO HTTP API START(getLocationInfo)\n url : " + url);
+
+        final MediaType JSON
+                = MediaType.parse("application/json; charset=utf-8");
+
+        try {
+            final TrustManager[] trustAllCerts = new TrustManager[]{
+                    new X509TrustManager() {
+                        @Override
+                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                                throws CertificateException {
+                        }
+
+                        @Override
+                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
+                                throws CertificateException {
+                        }
+
+                        @Override
+                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                            return new java.security.cert.X509Certificate[]{};
+                        }
+                    }
+            };
+
+            // Install the all-trusting trust manager
+            final SSLContext sslContext = SSLContext.getInstance("SSL");
+            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+            // Create an ssl socket factory with our all-trusting manager
+            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+
+            OkHttpClient client = new OkHttpClient.Builder()
+                    .connectTimeout(TIMEOUT_CONNECT, TimeUnit.SECONDS)
+                    .writeTimeout(TIMEOUT_WRITE, TimeUnit.SECONDS)
+                    .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
+                    .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
+                    .hostnameVerifier(new HostnameVerifier() {
+                        @Override
+                        public boolean verify(String hostname, SSLSession session) {
+                            return true;
+                        }
+                    })
+                    .build();
+
+            RequestBody body = RequestBody.create(JSON, "");
+            Request.Builder builder = new Request.Builder();
+            builder.url(url);
+            builder.addHeader("Authorization", "KakaoAK " + IotPlatformApplication.getInstance().getApplicationContext().getString(R.string.kakao_rest_api_key));
+            builder.get();
+            Request request = builder.build();
+
+            client.newCall(request).enqueue(c);
+        } catch (Exception e) {
+            ErrorLogOutput(TAG,"Exception while configuring IgnoreSslCertificate " + e);
+        }
+    }
+
+
+
+    public static void appendLog(String text)
+    {
+        File logFile = new File("sdcard/log.file");
+        if (!logFile.exists())
+        {
+            try
+            {
+                logFile.createNewFile();
+            }
+            catch (IOException e)
+            {
+                e.printStackTrace();
+            }
+        }
+        try
+        {
+            //BufferedWriter for performance, true to set append to file flag
+            BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
+            buf.append(text);
+            buf.newLine();
+            buf.close();
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+        }
+    }
+}

+ 120 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/SharedPrefUtil.java

@@ -0,0 +1,120 @@
+package kr.co.icontrols.iotplatform.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class SharedPrefUtil {
+    private static SharedPreferences mPreferences;
+    private final static String PREF_NAME = "kr.co.icontrols.iotplatform";
+
+
+    public final static String USER_ID = "user_id";
+    public final static String USER_PASS = "user_pass";
+    public final static String AUTO_LOGIN = "auto_login";
+    public final static String NOTI_ALL = "noti_all";
+    public final static String NOTI_ADD_DEVICE = "noti_add_device";
+    public final static String NOTI_DELETE_DEVICE = "noti_delete_member";
+    public final static String NOTI_ADD_MEMBER = "noti_add_member";
+    public final static String NOTI_REMOVE_MEMBER = "noti_remove_member";
+    public final static String SHOW_SMART_GUIDE = "shwo_smart_guide"; //최초진입 가이드 표시
+    public final static String PUSH_TOKEN = "push_token";
+    public final static String LOGIN_TIME = "login_time";
+    public final static String BACKGROUND_WAIT_TIME = "background_wait_time"; //앱 백그라운드 대기시간
+
+    public final static String DEFAULT_USER_ID = "";
+    public final static String DEFAULT_USER_PASS = "";
+    public final static boolean DEFAULT_AUTO_LOGIN = false;
+    public final static boolean DEFAULT_NOTI_ALL = false;
+    public final static boolean DEFAULT_NOTI_ADD_DEVICE = false;
+    public final static boolean DEFAULT_NOTI_DELETE_DEVICE = false;
+    public final static boolean DEFAULT_NOTI_ADD_MEMBER = false;
+    public final static boolean DEFAULT_NOTI_REMOVE_MEMBER = false;
+    public final static boolean DEFAULT_SHOW_SMART_GUIDE = true;
+    public final static String DEFAULT_PUSH_TOKEN = "";
+    public final static long DEFAULT_LOGIN_TIME = 0;
+    public final static long DEFAULT_BACKGROUND_WAIT_TIME = 0;
+
+    public static void init(Context context) {
+        if (mPreferences == null) {
+            mPreferences = context.getSharedPreferences(PREF_NAME, Activity.MODE_PRIVATE);
+        }
+    }
+
+    public static void reset() {
+        if (mPreferences == null) return;
+        putString(USER_ID,DEFAULT_USER_ID);
+        putString(USER_PASS,DEFAULT_USER_PASS);
+        putBoolean(AUTO_LOGIN, DEFAULT_AUTO_LOGIN);
+        putBoolean(NOTI_ALL, DEFAULT_NOTI_ALL);
+        putBoolean(NOTI_ADD_DEVICE, DEFAULT_NOTI_ADD_DEVICE);
+        putBoolean(NOTI_DELETE_DEVICE, DEFAULT_NOTI_DELETE_DEVICE);
+        putBoolean(NOTI_ADD_MEMBER, DEFAULT_NOTI_ADD_MEMBER);
+        putBoolean(NOTI_REMOVE_MEMBER, DEFAULT_NOTI_REMOVE_MEMBER);
+        putBoolean(SHOW_SMART_GUIDE, DEFAULT_SHOW_SMART_GUIDE);
+        putString(PUSH_TOKEN,DEFAULT_PUSH_TOKEN);
+        putLong(LOGIN_TIME, DEFAULT_LOGIN_TIME);
+        putLong(BACKGROUND_WAIT_TIME, DEFAULT_BACKGROUND_WAIT_TIME);
+    }
+
+    public static boolean getBoolean(String key, boolean defaultValue) {
+        if (mPreferences == null) return defaultValue;
+        return mPreferences.getBoolean(key, defaultValue);
+    }
+
+    public static void putBoolean(String key, boolean Value) {
+        if (mPreferences == null) return;
+        SharedPreferences.Editor mEditer = mPreferences.edit();
+        mEditer.putBoolean(key, Value);
+        mEditer.commit();
+    }
+
+    public static String getString(String key, String defaultValue) {
+        if (mPreferences == null) return defaultValue;
+        return mPreferences.getString(key, defaultValue);
+    }
+
+    public static void putString(String key, String value) {
+        if (mPreferences == null) return;
+
+        SharedPreferences.Editor mEditer = mPreferences.edit();
+        mEditer.putString(key, value);
+        mEditer.commit();
+    }
+
+    public static int getInt(String key, int defaultValue) {
+        if (mPreferences == null) return defaultValue;
+        return mPreferences.getInt(key, defaultValue);
+    }
+
+    public static void putInt(String key, int value) {
+        if (mPreferences == null) return;
+        SharedPreferences.Editor mEditer = mPreferences.edit();
+        mEditer.putInt(key, value);
+        mEditer.commit();
+    }
+
+    public static long getLong(String key, long defaultValue) {
+        if (mPreferences == null) return defaultValue;
+        return mPreferences.getLong(key, defaultValue);
+    }
+
+    public static void putLong(String key, long value) {
+        if (mPreferences == null) return;
+        SharedPreferences.Editor mEditer = mPreferences.edit();
+        mEditer.putLong(key, value);
+        mEditer.commit();
+    }
+
+    public static float getFloat(String key, float defaultValue) {
+        if (mPreferences == null) return defaultValue;
+        return mPreferences.getFloat(key, defaultValue);
+    }
+
+    public static void putFloat(String key, float value) {
+        if (mPreferences == null) return;
+        SharedPreferences.Editor mEditer = mPreferences.edit();
+        mEditer.putFloat(key, value);
+        mEditer.commit();
+    }
+}

+ 21 - 0
app/src/main/java/kr/co/icontrols/iotplatform/utils/hdcUUID.java

@@ -0,0 +1,21 @@
+package kr.co.icontrols.iotplatform.utils;
+
+public class hdcUUID {
+    // light service uuid
+    public static final java.util.UUID LIGHT_GATT_SERVICE_UUID                         = java.util.UUID.fromString("C6F2D9E3-49E7-4125-9014-BFC6D669FF00");
+    // light write uuid
+    public static final java.util.UUID LIGHT_GATT_WRITE_CHARACTERISTIC_UUID            = java.util.UUID.fromString("C6F2D9E3-49E7-4125-9014-BFC6D669FF02");
+    // light notification uuid
+    public static final java.util.UUID LIGHT_GATT_NOTIFICATION_CHARACTERISTIC_UUID     = java.util.UUID.fromString("C6F2D9E3-49E7-4125-9014-BFC6D669FF01");
+    // light descriptor uuid
+    public static final java.util.UUID LIGHT_GATT_DESCRIPTORS_UUID                     = java.util.UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
+
+    // switch service uuid
+    public static final java.util.UUID SWITCH_GATT_SERVICE_UUID                        = java.util.UUID.fromString("0003cdd0-0000-1000-8000-00805f9b34fb");
+    // switch write uuid
+    public static final java.util.UUID SWITCH_GATT_WRITE_CHARACTERISTIC_UUID           = java.util.UUID.fromString("0003cdd2-0000-1000-8000-00805f9b34fb");
+    // switch notification uuid
+    public static final java.util.UUID SWITCH_NOTIFICATION_CHARACTERISTIC_UUID         = java.util.UUID.fromString("0003cdd1-0000-1000-8000-00805f9b34fb");
+    // switch descriptor uuid
+    public static final java.util.UUID SWITCH_GATT_DESCRIPTORS_UUID                    = java.util.UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff