diff --git a/.gitignore b/.gitignore index 9f00b09..e70e33a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,13 @@ local.properties bin/ target gen/ +node/node_modules +sf-gcm-demo/build +sf-gcm-demo/google-services.json +sf-gcm-demo/gradle +sf-gcm-demo/app/build +sf-gcm-demo/app/gradle +sf-gcm-demo/app/google-services.json + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba3a209 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 SungKwang Song + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README b/README deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md new file mode 100644 index 0000000..c67af5e --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +## 블로그 글 + +이 예제는 [saltfactory's blog](http://blog.saltfactory.net)의 [최신 Android Studio, GCM(Google Cloud Messaging), Node.js를 이용하여 Android 푸시 서비스 구현하기](http://blog.saltfactory.net/android/implement-push-service-via-gcm.html) 글의 예제 소스 코드입니다. + +이 소스코드는 **Android** 개발을 할 때 [GCM(Google Cloud Messaging)](https://developers.google.com/cloud-messaging/)을 이용하여 푸시 서비스를 **Android Studio**로 개발하는 내용을 포함하고 있습니다. + +## 디렉토리 구조 + +* **sf-gcm-demo/** : Android Studio로 개발한 GCM Demo 앱 소스코드가 포함되어 있습니다. +* **node/** : Node.js로 만든 GCM을 이용하여 Android 디바이스로 메세지를 보내는 소스코드가 포함되어 있습니다. +* **bash/** : GCM 서비스의 Google Connection Server를 이용하여 Android 디바이스로 메세지를 보내는 소스코드가 포함되어 있습니다. + + +## 연구원 소개 + +* 작성자 : [송성광](http://about.me/saltfactory) 개발 연구원 +* 블로그 : http://blog.saltfactory.net +* 이메일 : [saltfactory@gmail.com](mailto:saltfactory@gmail.com) +* 트위터 : [@saltfactory](https://twitter.com/saltfactory) +* 페이스북 : https://facebook.com/salthub +* 연구소 : [하이브레인넷](http://www.hibrain.net) 부설연구소 +* 연구실 : [창원대학교 데이터베이스 연구실](http://dblab.changwon.ac.kr) + + +## 기부하기 + +> 기부금은 연구활동과 블로그 운영에 사용됩니다. + +기부방법은 [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=NR99D2BERKK8Y&lc=KR&item_name=donate%2esaltfactory%2enet&item_number=net%2esaltfactory%2edonate¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)을 이용하는 방법이 있습니다. + +[![paypal button](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=NR99D2BERKK8Y&lc=KR&item_name=donate%2esaltfactory%2enet&item_number=net%2esaltfactory%2edonate¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) + + +## 라이센스 + +The MIT License (MIT) + +Copyright (c) 2014 SungKwang Song + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bash/gcm_sender.sh b/bash/gcm_sender.sh new file mode 100644 index 0000000..b949a81 --- /dev/null +++ b/bash/gcm_sender.sh @@ -0,0 +1,6 @@ +server_api_key='server api key를 입력합니다.' +token='Instance ID의 token을 입력합니다.' +curl --header "Authorization: key=$server_api_key" \ +--header Content-Type:"application/json" \ +https://gcm-http.googleapis.com/gcm/send \ +-d "{\"data\":{\"title\":\"saltfactory GCM demo\",\"message\":\"Google Cloud Messaging 테스트\"},\"to\":\"$token\"} diff --git a/node/gcm_provider.js b/node/gcm_provider.js new file mode 100644 index 0000000..027f527 --- /dev/null +++ b/node/gcm_provider.js @@ -0,0 +1,26 @@ +var gcm = require('node-gcm'); +var fs = require('fs'); + +var message = new gcm.Message({ + collapseKey: 'demo', + delayWhileIdle: true, + timeToLive: 3, + data: { + title: 'saltfactory GCM demo', + message: 'Google Cloud Messaging 테스트', + custom_key1: 'custom data1', + custom_key2: 'custom data2' + } +}); + + +var server_api_key = 'Server API Key를 입력합니다.'; +var sender = new gcm.Sender(server_api_key); +var registrationIds = []; + +var token = 'Instance ID 의 token을 입력합니다.'; +registrationIds.push(token); + +sender.send(message, registrationIds, 4, function (err, result) { + console.log(result); +}); diff --git a/node/package.json b/node/package.json new file mode 100644 index 0000000..b1b5e34 --- /dev/null +++ b/node/package.json @@ -0,0 +1,8 @@ +{ + "name": "gcm-demo", + "version": "0.0.0", + "description": "", + "dependencies": { + "node-gcm":"latest" + } +} diff --git a/sf-gcm-demo/.gitignore b/sf-gcm-demo/.gitignore new file mode 100644 index 0000000..9c4de58 --- /dev/null +++ b/sf-gcm-demo/.gitignore @@ -0,0 +1,7 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/sf-gcm-demo/app/.gitignore b/sf-gcm-demo/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/sf-gcm-demo/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/sf-gcm-demo/app/build.gradle b/sf-gcm-demo/app/build.gradle new file mode 100644 index 0000000..8883cf8 --- /dev/null +++ b/sf-gcm-demo/app/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'com.android.application' +apply plugin: 'com.google.gms.google-services' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "net.saltfactory.demo.gcm" + minSdkVersion 14 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.google.android.gms:play-services-gcm:7.5.+' + compile 'com.android.support:appcompat-v7:22.1.1' + +} diff --git a/sf-gcm-demo/app/proguard-rules.pro b/sf-gcm-demo/app/proguard-rules.pro new file mode 100644 index 0000000..36cb415 --- /dev/null +++ b/sf-gcm-demo/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Projects/Libraries/adt-bundle-mac-x86_64/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/sf-gcm-demo/app/src/androidTest/java/net/saltfactory/demo/gcm/ApplicationTest.java b/sf-gcm-demo/app/src/androidTest/java/net/saltfactory/demo/gcm/ApplicationTest.java new file mode 100644 index 0000000..32ea580 --- /dev/null +++ b/sf-gcm-demo/app/src/androidTest/java/net/saltfactory/demo/gcm/ApplicationTest.java @@ -0,0 +1,13 @@ +package net.saltfactory.demo.gcm; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/sf-gcm-demo/app/src/main/AndroidManifest.xml b/sf-gcm-demo/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b77e809 --- /dev/null +++ b/sf-gcm-demo/app/src/main/AndroidManifest.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MainActivity.java b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MainActivity.java new file mode 100644 index 0000000..40412df --- /dev/null +++ b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MainActivity.java @@ -0,0 +1,144 @@ +package net.saltfactory.demo.gcm; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesUtil; + +public class MainActivity extends AppCompatActivity { + + private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; + private static final String TAG = "MainActivity"; + + private Button mRegistrationButton; + private ProgressBar mRegistrationProgressBar; + private BroadcastReceiver mRegistrationBroadcastReceiver; + private TextView mInformationTextView; + + /** + * Instance ID를 이용하여 디바이스 토큰을 가져오는 RegistrationIntentService를 실행한다. + */ + public void getInstanceIdToken() { + if (checkPlayServices()) { + // Start IntentService to register this application with GCM. + Intent intent = new Intent(this, RegistrationIntentService.class); + startService(intent); + } + } + + /** + * LocalBroadcast 리시버를 정의한다. 토큰을 획득하기 위한 READY, GENERATING, COMPLETE 액션에 따라 UI에 변화를 준다. + */ + public void registBroadcastReceiver(){ + mRegistrationBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + + if(action.equals(QuickstartPreferences.REGISTRATION_READY)){ + // 액션이 READY일 경우 + mRegistrationProgressBar.setVisibility(ProgressBar.GONE); + mInformationTextView.setVisibility(View.GONE); + } else if(action.equals(QuickstartPreferences.REGISTRATION_GENERATING)){ + // 액션이 GENERATING일 경우 + mRegistrationProgressBar.setVisibility(ProgressBar.VISIBLE); + mInformationTextView.setVisibility(View.VISIBLE); + mInformationTextView.setText(getString(R.string.registering_message_generating)); + } else if(action.equals(QuickstartPreferences.REGISTRATION_COMPLETE)){ + // 액션이 COMPLETE일 경우 + mRegistrationProgressBar.setVisibility(ProgressBar.GONE); + mRegistrationButton.setText(getString(R.string.registering_message_complete)); + mRegistrationButton.setEnabled(false); + String token = intent.getStringExtra("token"); + mInformationTextView.setText(token); + } + + } + }; + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_main); + + registBroadcastReceiver(); + + // 토큰을 보여줄 TextView를 정의 + mInformationTextView = (TextView) findViewById(R.id.informationTextView); + mInformationTextView.setVisibility(View.GONE); + // 토큰을 가져오는 동안 인디케이터를 보여줄 ProgressBar를 정의 + mRegistrationProgressBar = (ProgressBar) findViewById(R.id.registrationProgressBar); + mRegistrationProgressBar.setVisibility(ProgressBar.GONE); + // 토큰을 가져오는 Button을 정의 + mRegistrationButton = (Button) findViewById(R.id.registrationButton); + mRegistrationButton.setOnClickListener(new View.OnClickListener() { + /** + * 버튼을 클릭하면 토큰을 가져오는 getInstanceIdToken() 메소드를 실행한다. + * @param view + */ + @Override + public void onClick(View view) { + getInstanceIdToken(); + } + }); + + } + + /** + * 앱이 실행되어 화면에 나타날때 LocalBoardcastManager에 액션을 정의하여 등록한다. + */ + @Override + protected void onResume() { + super.onResume(); + LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, + new IntentFilter(QuickstartPreferences.REGISTRATION_READY)); + LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, + new IntentFilter(QuickstartPreferences.REGISTRATION_GENERATING)); + LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver, + new IntentFilter(QuickstartPreferences.REGISTRATION_COMPLETE)); + + } + + /** + * 앱이 화면에서 사라지면 등록된 LocalBoardcast를 모두 삭제한다. + */ + @Override + protected void onPause() { + LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver); + super.onPause(); + } + + + /** + * Google Play Service를 사용할 수 있는 환경이지를 체크한다. + */ + private boolean checkPlayServices() { + int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); + if (resultCode != ConnectionResult.SUCCESS) { + if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { + GooglePlayServicesUtil.getErrorDialog(resultCode, this, + PLAY_SERVICES_RESOLUTION_REQUEST).show(); + } else { + Log.i(TAG, "This device is not supported."); + finish(); + } + return false; + } + return true; + } +} diff --git a/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MyGcmListenerService.java b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MyGcmListenerService.java new file mode 100644 index 0000000..d1e4141 --- /dev/null +++ b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MyGcmListenerService.java @@ -0,0 +1,66 @@ +package net.saltfactory.demo.gcm; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import com.google.android.gms.gcm.GcmListenerService; + +/** + * Created by saltfactory on 6/8/15. + */ +public class MyGcmListenerService extends GcmListenerService { + + private static final String TAG = "MyGcmListenerService"; + + /** + * + * @param from SenderID 값을 받아온다. + * @param data Set형태로 GCM으로 받은 데이터 payload이다. + */ + @Override + public void onMessageReceived(String from, Bundle data) { + String title = data.getString("title"); + String message = data.getString("message"); + + Log.d(TAG, "From: " + from); + Log.d(TAG, "Title: " + title); + Log.d(TAG, "Message: " + message); + + // GCM으로 받은 메세지를 디바이스에 알려주는 sendNotification()을 호출한다. + sendNotification(title, message); + } + + + /** + * 실제 디바에스에 GCM으로부터 받은 메세지를 알려주는 함수이다. 디바이스 Notification Center에 나타난다. + * @param title + * @param message + */ + private void sendNotification(String title, String message) { + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, + PendingIntent.FLAG_ONE_SHOT); + + Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.ic_stat_ic_notification) + .setContentTitle(title) + .setContentText(message) + .setAutoCancel(true) + .setSound(defaultSoundUri) + .setContentIntent(pendingIntent); + + NotificationManager notificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); + } +} diff --git a/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MyInstanceIDListenerService.java b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MyInstanceIDListenerService.java new file mode 100644 index 0000000..8cde9ec --- /dev/null +++ b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/MyInstanceIDListenerService.java @@ -0,0 +1,19 @@ +package net.saltfactory.demo.gcm; + +import android.content.Intent; + +import com.google.android.gms.iid.InstanceIDListenerService; + +/** + * Created by saltfactory on 6/8/15. + */ +public class MyInstanceIDListenerService extends InstanceIDListenerService { + + private static final String TAG = "MyInstanceIDLS"; + + @Override + public void onTokenRefresh() { + Intent intent = new Intent(this, RegistrationIntentService.class); + startService(intent); + } +} diff --git a/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/QuickstartPreferences.java b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/QuickstartPreferences.java new file mode 100644 index 0000000..92a9cf5 --- /dev/null +++ b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/QuickstartPreferences.java @@ -0,0 +1,12 @@ +package net.saltfactory.demo.gcm; + +/** + * Created by saltfactory on 6/8/15. + */ +public class QuickstartPreferences { + + public static final String REGISTRATION_READY = "registrationReady"; + public static final String REGISTRATION_GENERATING = "registrationGenerating"; + public static final String REGISTRATION_COMPLETE = "registrationComplete"; + +} diff --git a/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/RegistrationIntentService.java b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/RegistrationIntentService.java new file mode 100644 index 0000000..77fbaa5 --- /dev/null +++ b/sf-gcm-demo/app/src/main/java/net/saltfactory/demo/gcm/RegistrationIntentService.java @@ -0,0 +1,61 @@ +package net.saltfactory.demo.gcm; + +import android.annotation.SuppressLint; +import android.app.IntentService; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import com.google.android.gms.gcm.GoogleCloudMessaging; +import com.google.android.gms.iid.InstanceID; + +import java.io.IOException; + +/** + * Created by saltfactory on 6/8/15. + */ +public class RegistrationIntentService extends IntentService { + + private static final String TAG = "RegistrationIntentService"; + + public RegistrationIntentService() { + super(TAG); + } + + /** + * GCM을 위한 Instance ID의 토큰을 생성하여 가져온다. + * @param intent + */ + @SuppressLint("LongLogTag") + @Override + protected void onHandleIntent(Intent intent) { + + // GCM Instance ID의 토큰을 가져오는 작업이 시작되면 LocalBoardcast로 GENERATING 액션을 알려 ProgressBar가 동작하도록 한다. + LocalBroadcastManager.getInstance(this) + .sendBroadcast(new Intent(QuickstartPreferences.REGISTRATION_GENERATING)); + + // GCM을 위한 Instance ID를 가져온다. + InstanceID instanceID = InstanceID.getInstance(this); + String token = null; + try { + synchronized (TAG) { + // GCM 앱을 등록하고 획득한 설정파일인 google-services.json을 기반으로 SenderID를 자동으로 가져온다. + String default_senderId = getString(R.string.gcm_defaultSenderId); + // GCM 기본 scope는 "GCM"이다. + String scope = GoogleCloudMessaging.INSTANCE_ID_SCOPE; + // Instance ID에 해당하는 토큰을 생성하여 가져온다. + token = instanceID.getToken(default_senderId, scope, null); + + Log.i(TAG, "GCM Registration Token: " + token); + } + } catch (IOException e) { + e.printStackTrace(); + } + + // GCM Instance ID에 해당하는 토큰을 획득하면 LocalBoardcast에 COMPLETE 액션을 알린다. + // 이때 토큰을 함께 넘겨주어서 UI에 토큰 정보를 활용할 수 있도록 했다. + Intent registrationComplete = new Intent(QuickstartPreferences.REGISTRATION_COMPLETE); + registrationComplete.putExtra("token", token); + LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete); + } +} diff --git a/sf-gcm-demo/app/src/main/res/drawable-hdpi-v11/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-hdpi-v11/ic_stat_ic_notification.png new file mode 100644 index 0000000..db2441b Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-hdpi-v11/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/drawable-hdpi/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-hdpi/ic_stat_ic_notification.png new file mode 100644 index 0000000..3cbd048 Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-hdpi/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/drawable-mdpi-v11/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-mdpi-v11/ic_stat_ic_notification.png new file mode 100644 index 0000000..660e2a2 Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-mdpi-v11/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/drawable-mdpi/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-mdpi/ic_stat_ic_notification.png new file mode 100644 index 0000000..105b2d7 Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-mdpi/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/drawable-xhdpi-v11/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-xhdpi-v11/ic_stat_ic_notification.png new file mode 100644 index 0000000..d071e8f Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-xhdpi-v11/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/drawable-xhdpi/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-xhdpi/ic_stat_ic_notification.png new file mode 100644 index 0000000..f749b60 Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-xhdpi/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/drawable-xxhdpi-v11/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-xxhdpi-v11/ic_stat_ic_notification.png new file mode 100644 index 0000000..79dd369 Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-xxhdpi-v11/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/drawable-xxhdpi/ic_stat_ic_notification.png b/sf-gcm-demo/app/src/main/res/drawable-xxhdpi/ic_stat_ic_notification.png new file mode 100644 index 0000000..c1985da Binary files /dev/null and b/sf-gcm-demo/app/src/main/res/drawable-xxhdpi/ic_stat_ic_notification.png differ diff --git a/sf-gcm-demo/app/src/main/res/layout/activity_main.xml b/sf-gcm-demo/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..d4d6266 --- /dev/null +++ b/sf-gcm-demo/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,24 @@ + + +