Raspberry pi

9편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (App편)

[혜안] 2017. 5. 6. 19:36
728x90

[Raspberry pi] - 1편 스마트 콘센트? Smart Plug!

[Raspberry pi] - 2편 스마트 콘센트? Smart Plug!

[Raspberry pi] - 3편 스마트 콘센트? Smart Plug!

[Raspberry pi] - 4편 스마트 콘센트? Smart Plug! + 앱위젯

[Raspberry pi] - 5편 스마트 콘센트? Smart Plug! + 터치스위치

[Raspberry pi] - 6편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (개요편)

[Raspberry pi] - 7편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (Firebase 등록)

[Raspberry pi] - 8편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (서버편)

[Raspberry pi] - 9편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (App편)



이제 Push를 받을 안드로이드 App을 구현할 차례이다.

일전에 언급한대로 구버전의 GCM API 기반으로 작성하였다.


App 구성은 이렇다.

1. App Widget : 스마트폰 화면에서 바로 Plug를 켜고 끄고 할 Widget

2. Basic Activity: Plug 켜고 끄기, GCM 등록하기 등의 단위 테스트 및 상태표시 용도

3. Service: Push가 발생되었을 때에 호출되는 Listener Service


1번 App Widget은 이미 구현되어 있고, Push 이벤트를 받아서 갱신시켜주는 코드만 추가하면 된다.

App Widget 구현은 4편을 참고

[Raspberry pi] - 4편 스마트 콘센트? Smart Plug! + 앱위젯


3번 Service는 Push를 수신하고, 이를 App Widget에 넘겨주는 기능만 한다.


자 시작.

우선 firebase에서 다운로드 받은 google-services.json 파일을 프로젝트/app 밑에 배치시킨다.

google-services.json은 Push 서비스에 필요한 api key와 프로젝트 번호 등이 저장된 파일이다.


다운로드 경로를 모르겠으면 7편 firebase 등록 편을 다시 보면 된다.

[Raspberry pi] - 7편 스마트 콘센트? Smart Plug! + Node.js Push 서버 (Firebase 등록)


App Widget 수정

Widget이 스마트폰 바탕화면에 처음 추가될 때에 업데이트 이벤트가 발생하고, 이 때에 Registration ID를 서버에 전송하고자 한다.

Registration ID는 최초 한번 전송하면 되고, 서버는 이 정보를 기반으로 GCM에 Push를 요청한다.

한 번 받은 Registration ID는 SharedPreferences에 저장하여, 반복 전송을 방지한다.

@Override

public void onReceive(Context context, Intent intent){


................중략................


if(intent.getAction().equals(ACTION_UPDATE) || intent.getAction().equals(ACTION_STATE)) {

     int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, getClass()));

    for(int id:appWidgetIds) {
     Log.d(TAG, "onReceive.appWidgetId : " + String.valueOf(id));
     manager.updateAppWidget(id, views);
    }

     regid = getRegistrationId(context);

    if (regid.isEmpty()) {
    registerInBackground(context);
    Log.d(TAG, "Issue gcm regid : " + regid);
    }

}

................중략................ }


private String getRegistrationId(Context context) {


SharedPreferences prefs = context.getSharedPreferences(Plug.class.getSimpleName(), Context.MODE_PRIVATE);

return prefs.getString(PROPERTY_REG_ID, "");

}


private void storeRegistrationId(Context context, String regid) {


SharedPreferences prefs = context.getSharedPreferences(Plug.class.getSimpleName(), Context.MODE_PRIVATE);

SharedPreferences.Editor editor = prefs.edit();

editor.putString(PROPERTY_REG_ID, regid);


editor.apply();

}


private void registerInBackground(final Context context) {


new AsyncTask<Void, Void, String>() {


@Override

protected String doInBackground(Void... params) {


String msg = "";

try {

GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);

regid = gcm.register(defaultSenderId);

String user = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);


sendRegistrationIdToBackend(user);

storeRegistrationId(context, regid);

} catch (IOException e) {

e.printStackTrace();

}

return msg;

}


@Override

protected void onPostExecute(String msg) {

Log.d(TAG, msg);

}

}.execute(null, null, null);

}


private void sendRegistrationIdToBackend(String user) {


RequestPI requestPi = null;


String url = strRegClient + "?user=" + user + "&regid=" + regid;

Log.d(TAG, url);

requestPi = new RequestPI(url);

requestPi.start();

}


GcmListenerService 추가

서버로 부터 Push가 수신되었을 때에 호출되는 Listener Service 이다.

단순히 Push 메시지를 받아서 App Widget 에 던져주기만 하면 된다.

public class PushListnerService extends GcmListenerService {
private final static String ACTION_STATE = "com.viewise.home.plugwidget.GETPUSH";

public PushListnerService() {
}

@Override
public void onMessageReceived(String s, Bundle bundle) {

String state = bundle.getString("state");
Log.d("GCM", state);

Intent intent = new Intent(this, Plug.class);
intent.setAction(ACTION_STATE);
intent.putExtra("state", state);
sendBroadcast(intent);

super.onMessageReceived(s, bundle);
}
}


App 수준 gradle

.........................중략.......................... dependencies { compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.google.android.gms:play-services-gcm:9.0.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.0'
testCompile 'junit:junit:4.12'
}


Project 수준 gradle

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.google.gms:google-services:3.0.0'
} } .........................후략..........................


AndroidManifest.xml

.........................중략..........................
<service
android:name=".PushListnerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service> .........................중략..........................


처음 구현할때에는 고민이 많아서 복잡하게 코딩한 듯 했는데 정리해보니 생각보다 단촐하다.

이미 한번 올린 시험영상이지만 한 번 더...

아래의 빨간색 상자에 집중해서 보면 된다.

Plug를 켜면 스마트폰 위젯도 켜지고, 끄면 같이 꺼진다. 


총 9편으로 스마트 콘센트(Smart Plug) 기능은 이제 완성된 듯 하다.


728x90