From 86ec91c6e1ef972b7a12d4a10ca64b66b6d94723 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Thu, 30 Nov 2017 21:55:23 +0530 Subject: [PATCH 01/10] TFCM.01-Solution-AddGradleDependencies --- app/build.gradle | 23 +++++++++++++---------- build.gradle | 11 +++++++---- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7d9b9a76..65c40e4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,12 @@ apply plugin: 'com.android.application' -apply plugin: 'android-apt' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 27 + buildToolsVersion "27.0.1" defaultConfig { applicationId "android.example.com.squawker" minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -25,22 +24,26 @@ dependencies { 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.1.0' + compile 'com.android.support:appcompat-v7:27.0.1' testCompile 'junit:junit:4.12' // RecyclerView - compile 'com.android.support:recyclerview-v7:25.1.0' + compile 'com.android.support:recyclerview-v7:27.0.1' // Schematic dependencies for ContentProvider - apt 'net.simonvt.schematic:schematic-compiler:0.6.3' + annotationProcessor 'net.simonvt.schematic:schematic-compiler:0.6.3' compile 'net.simonvt.schematic:schematic:0.6.3' // Preferences Dependencies - compile 'com.android.support:preference-v7:25.1.0' + compile 'com.android.support:preference-v7:27.0.1' - // TODO (3) Add the Firebase libraries and apply the GPS plugin in this file. Make sure to save the + // TODO COMPLETED (3) Add the Firebase libraries and apply the GPS plugin in this file. Make sure to save the // google.services.json file in the app folder. After syncing the changes, you should be able // to send a notification to your app using the Firebase console. The app must be in the // background! - + compile 'com.google.firebase:firebase-messaging:11.6.2' } + +// Apply the Google Services plugin. Make sure to add the google-services.json file in the app +// folder. You download it from the Firebase console +apply plugin: 'com.google.gms.google-services' diff --git a/build.gradle b/build.gradle index fddab040..626b32ea 100644 --- a/build.gradle +++ b/build.gradle @@ -5,13 +5,13 @@ buildscript { jcenter() } dependencies { - classpath 'com.neenbedankt.gradle.plugins:android-apt:1.2' - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:3.0.1' - // TODO (1) Make a new Firebase project in the Firebase Console and follow the instructions. + // TODO COMPLETED (1) Make a new Firebase project in the Firebase Console and follow the instructions. // The console is here: https://console.firebase.google.com/ - // TODO (2) Add Google Services plugin here + // TODO COMPLETED (2) Add Google Services plugin here + classpath 'com.google.gms:google-services:3.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -22,6 +22,9 @@ buildscript { allprojects { repositories { jcenter() + maven { + url "https://maven.google.com" // Google's Maven repository + } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 04e285f3..98377102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Dec 28 10:00:20 PST 2015 +#Thu Nov 30 20:19:40 IST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip From 8b85ed9a63dd272934794e34628949efe507df4a Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Thu, 30 Nov 2017 22:04:40 +0530 Subject: [PATCH 02/10] TFCM.02-Exercise-TestGettingExtraData --- app/build.gradle | 1 - .../main/java/android/example/com/squawker/MainActivity.java | 3 +++ build.gradle | 5 ----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 65c40e4a..9b01c07c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,7 +37,6 @@ dependencies { // Preferences Dependencies compile 'com.android.support:preference-v7:27.0.1' - // TODO COMPLETED (3) Add the Firebase libraries and apply the GPS plugin in this file. Make sure to save the // google.services.json file in the app folder. After syncing the changes, you should be able // to send a notification to your app using the Firebase console. The app must be in the // background! diff --git a/app/src/main/java/android/example/com/squawker/MainActivity.java b/app/src/main/java/android/example/com/squawker/MainActivity.java index fa53a850..5abffe75 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -86,6 +86,9 @@ protected void onCreate(Bundle savedInstanceState) { // Start the loader getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); + // TODO (1) Get the test data here from the extras bundle that came with this intent. + // To confirm that the data was passed in, make sure to show the data in a log statement. + } @Override diff --git a/build.gradle b/build.gradle index 626b32ea..1c04c2d1 100644 --- a/build.gradle +++ b/build.gradle @@ -6,11 +6,6 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' - - // TODO COMPLETED (1) Make a new Firebase project in the Firebase Console and follow the instructions. - // The console is here: https://console.firebase.google.com/ - - // TODO COMPLETED (2) Add Google Services plugin here classpath 'com.google.gms:google-services:3.1.1' // NOTE: Do not place your application dependencies here; they belong From abc1d58e031d743296272b7be5c5244574e61dca Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Thu, 30 Nov 2017 22:06:21 +0530 Subject: [PATCH 03/10] TFCM.02-Solution-TestGettingExtraData --- .../android/example/com/squawker/MainActivity.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/android/example/com/squawker/MainActivity.java b/app/src/main/java/android/example/com/squawker/MainActivity.java index 5abffe75..58f9a6db 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -61,6 +61,7 @@ public class MainActivity extends AppCompatActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Log.v(LOG_TAG, "-> onCreate"); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView) findViewById(R.id.squawks_recycler_view); @@ -86,8 +87,15 @@ protected void onCreate(Bundle savedInstanceState) { // Start the loader getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); - // TODO (1) Get the test data here from the extras bundle that came with this intent. - // To confirm that the data was passed in, make sure to show the data in a log statement. + // TODO COMPLETED (1) Get the test data here from the extras bundle that came with this intent. + // Gets the extra data from the intent that started the activity. For *notification* + // messages, this will contain key value pairs stored in the *data* section of the message. + Bundle extras = getIntent().getExtras(); + // Checks if the extras exist and if the key "test" from our FCM message is in the intent + if (extras != null && extras.containsKey("test")) { + // If the key is there, print out the value of "test" + Log.d(LOG_TAG, "-> Contains: " + extras.getString("test")); + } } From 01eee1e2a53bed2b54cd42583eae4f160485a448 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Thu, 30 Nov 2017 22:08:01 +0530 Subject: [PATCH 04/10] TFCM.03-Exercise-GetInstanceIdToken --- app/src/main/AndroidManifest.xml | 3 +++ .../java/android/example/com/squawker/MainActivity.java | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 39412b52..29ce1f00 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,6 +33,9 @@ android:value=".MainActivity" /> + + \ No newline at end of file diff --git a/app/src/main/java/android/example/com/squawker/MainActivity.java b/app/src/main/java/android/example/com/squawker/MainActivity.java index 58f9a6db..69426ae1 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -87,7 +87,6 @@ protected void onCreate(Bundle savedInstanceState) { // Start the loader getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); - // TODO COMPLETED (1) Get the test data here from the extras bundle that came with this intent. // Gets the extra data from the intent that started the activity. For *notification* // messages, this will contain key value pairs stored in the *data* section of the message. Bundle extras = getIntent().getExtras(); @@ -97,6 +96,13 @@ protected void onCreate(Bundle savedInstanceState) { Log.d(LOG_TAG, "-> Contains: " + extras.getString("test")); } + // TODO (1) Make a new package for your FCM service classes called "fcm" + // TODO (2) Create a new Service class that extends FirebaseInstanceIdService. + // You'll need to implement the onTokenRefresh method. Simply have it print out + // the new token. + // TODO (3) Here, in MainActivity, get a token using FirebaseInstanceId.getInstance().getToken() + // TODO (4) Get the message from that token and print it in a log statement + } @Override From 78a676bcc8f3d56edb9ad97a4de7f8d1dc4bf261 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Thu, 30 Nov 2017 22:14:39 +0530 Subject: [PATCH 05/10] TFCM.03-Solution-GetInstanceIdToken --- app/src/main/AndroidManifest.xml | 8 ++- .../example/com/squawker/MainActivity.java | 15 +++-- .../fcm/SquawkFirebaseInstanceIdService.java | 59 +++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseInstanceIdService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 29ce1f00..d5349778 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,8 +33,14 @@ android:value=".MainActivity" /> - + + + + + + diff --git a/app/src/main/java/android/example/com/squawker/MainActivity.java b/app/src/main/java/android/example/com/squawker/MainActivity.java index 69426ae1..d463edf0 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -35,6 +35,8 @@ import android.view.MenuInflater; import android.view.MenuItem; +import com.google.firebase.iid.FirebaseInstanceId; + public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { @@ -96,12 +98,17 @@ protected void onCreate(Bundle savedInstanceState) { Log.d(LOG_TAG, "-> Contains: " + extras.getString("test")); } - // TODO (1) Make a new package for your FCM service classes called "fcm" - // TODO (2) Create a new Service class that extends FirebaseInstanceIdService. + // TODO COMPLETED (1) Make a new package for your FCM service classes called "fcm" + // TODO COMPLETED (2) Create a new Service class that extends FirebaseInstanceIdService. // You'll need to implement the onTokenRefresh method. Simply have it print out // the new token. - // TODO (3) Here, in MainActivity, get a token using FirebaseInstanceId.getInstance().getToken() - // TODO (4) Get the message from that token and print it in a log statement + // TODO COMPLETED (3) Here, in MainActivity, get a token using FirebaseInstanceId.getInstance().getToken() + // TODO COMPLETED (4) Get the message from that token and print it in a log statement + + // Get token from the ID Service you created and show it in a log + String token = FirebaseInstanceId.getInstance().getToken(); + String msg = getString(R.string.message_token_format, token); + Log.d(LOG_TAG, "-> " + msg); } diff --git a/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseInstanceIdService.java b/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseInstanceIdService.java new file mode 100644 index 00000000..fd088a21 --- /dev/null +++ b/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseInstanceIdService.java @@ -0,0 +1,59 @@ +/* +* Copyright (C) 2017 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package android.example.com.squawker.fcm; + +import android.util.Log; + +import com.google.firebase.iid.FirebaseInstanceId; +import com.google.firebase.iid.FirebaseInstanceIdService; + +/** + * Listens for changes in the InstanceID + */ +public class SquawkFirebaseInstanceIdService extends FirebaseInstanceIdService { + + private static String LOG_TAG = SquawkFirebaseInstanceIdService.class.getSimpleName(); + + /** + * Called if InstanceID token is updated. This may occur if the security of + * the previous token had been compromised. Note that this is called when the InstanceID token + * is initially generated so this is where you would retrieve the token. + */ + @Override + public void onTokenRefresh() { + // Get updated InstanceID token. + String refreshedToken = FirebaseInstanceId.getInstance().getToken(); + Log.d(LOG_TAG, "Refreshed token: " + refreshedToken); + + // If you want to send messages to this application instance or + // manage this apps subscriptions on the server side, send the + // Instance ID token to your app server. + sendRegistrationToServer(refreshedToken); + } + + /** + * Persist token to third-party servers. + *

+ * Modify this method to associate the user's FCM InstanceID token with any server-side account + * maintained by your application. + * + * @param token The new token. + */ + private void sendRegistrationToServer(String token) { + // This method is blank, but if you were to build a server that stores users token + // information, this is where you'd send the token to the server. + } +} \ No newline at end of file From 5c7a5f68bdc89dbafbdc26d6a61ddc4740c539e4 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Fri, 1 Dec 2017 00:00:32 +0530 Subject: [PATCH 06/10] TFCM.04-Exercise-AddFirebaseMessagingService --- app/src/main/AndroidManifest.xml | 5 ++-- .../example/com/squawker/MainActivity.java | 29 ++++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d5349778..33aecbe0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,8 +33,6 @@ android:value=".MainActivity" /> - @@ -42,6 +40,9 @@ + + \ No newline at end of file diff --git a/app/src/main/java/android/example/com/squawker/MainActivity.java b/app/src/main/java/android/example/com/squawker/MainActivity.java index d463edf0..6e53a07e 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -89,6 +89,28 @@ protected void onCreate(Bundle savedInstanceState) { // Start the loader getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); + // TODO (1) Make a new Service in the fcm package that extends from FirebaseMessagingService. + // TODO (2) As part of the new Service - Override onMessageReceived. This method will + // be triggered whenever a squawk is received. You can get the data from the squawk + // message using getData(). When you send a test message, this data will include the + // following key/value pairs: + // test: true + // author: Ex. "TestAccount" + // authorKey: Ex. "key_test" + // message: Ex. "Hello world" + // date: Ex. 1484358455343 + // TODO (3) As part of the new Service - If there is message data, get the data using + // the keys and do two things with it : + // 1. Display a notification with the first 30 character of the message + // 2. Use the content provider to insert a new message into the local database + // Hint: You shouldn't be doing content provider operations on the main thread. + // If you don't know how to make notifications or interact with a content provider + // look at the notes in the classroom for help. + + + // TODO (5) You can delete the code below for getting the extras from a notification message, + // since this was for testing purposes and not part of Squawker. + // Gets the extra data from the intent that started the activity. For *notification* // messages, this will contain key value pairs stored in the *data* section of the message. Bundle extras = getIntent().getExtras(); @@ -98,13 +120,6 @@ protected void onCreate(Bundle savedInstanceState) { Log.d(LOG_TAG, "-> Contains: " + extras.getString("test")); } - // TODO COMPLETED (1) Make a new package for your FCM service classes called "fcm" - // TODO COMPLETED (2) Create a new Service class that extends FirebaseInstanceIdService. - // You'll need to implement the onTokenRefresh method. Simply have it print out - // the new token. - // TODO COMPLETED (3) Here, in MainActivity, get a token using FirebaseInstanceId.getInstance().getToken() - // TODO COMPLETED (4) Get the message from that token and print it in a log statement - // Get token from the ID Service you created and show it in a log String token = FirebaseInstanceId.getInstance().getToken(); String msg = getString(R.string.message_token_format, token); From 37f09e23a1a34daaf5afa2665a9e06e2bcdc923d Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Fri, 1 Dec 2017 01:23:52 +0530 Subject: [PATCH 07/10] TFCM.04-Solution-AddFirebaseMessagingService --- app/src/main/AndroidManifest.xml | 8 +- .../example/com/squawker/MainActivity.java | 30 +-- .../fcm/SquawkFirebaseMessageService.java | 182 ++++++++++++++++++ 3 files changed, 190 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 33aecbe0..24868f09 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -40,8 +40,14 @@ - + + + + + + diff --git a/app/src/main/java/android/example/com/squawker/MainActivity.java b/app/src/main/java/android/example/com/squawker/MainActivity.java index 6e53a07e..d5f5e80a 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -89,37 +89,9 @@ protected void onCreate(Bundle savedInstanceState) { // Start the loader getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); - // TODO (1) Make a new Service in the fcm package that extends from FirebaseMessagingService. - // TODO (2) As part of the new Service - Override onMessageReceived. This method will - // be triggered whenever a squawk is received. You can get the data from the squawk - // message using getData(). When you send a test message, this data will include the - // following key/value pairs: - // test: true - // author: Ex. "TestAccount" - // authorKey: Ex. "key_test" - // message: Ex. "Hello world" - // date: Ex. 1484358455343 - // TODO (3) As part of the new Service - If there is message data, get the data using - // the keys and do two things with it : - // 1. Display a notification with the first 30 character of the message - // 2. Use the content provider to insert a new message into the local database - // Hint: You shouldn't be doing content provider operations on the main thread. - // If you don't know how to make notifications or interact with a content provider - // look at the notes in the classroom for help. - - - // TODO (5) You can delete the code below for getting the extras from a notification message, + // TODO COMPLETED (5) You can delete the code below for getting the extras from a notification message, // since this was for testing purposes and not part of Squawker. - // Gets the extra data from the intent that started the activity. For *notification* - // messages, this will contain key value pairs stored in the *data* section of the message. - Bundle extras = getIntent().getExtras(); - // Checks if the extras exist and if the key "test" from our FCM message is in the intent - if (extras != null && extras.containsKey("test")) { - // If the key is there, print out the value of "test" - Log.d(LOG_TAG, "-> Contains: " + extras.getString("test")); - } - // Get token from the ID Service you created and show it in a log String token = FirebaseInstanceId.getInstance().getToken(); String msg = getString(R.string.message_token_format, token); diff --git a/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java b/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java new file mode 100644 index 00000000..e30ac2c4 --- /dev/null +++ b/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java @@ -0,0 +1,182 @@ +/* +* Copyright (C) 2017 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package android.example.com.squawker.fcm; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.example.com.squawker.MainActivity; +import android.example.com.squawker.R; +import android.example.com.squawker.provider.SquawkContract; +import android.example.com.squawker.provider.SquawkProvider; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.AsyncTask; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +import java.util.Map; + +/** + * Listens for squawk FCM messages both in the background and the foreground and responds + * appropriately + * depending on type of message + */ +public class SquawkFirebaseMessageService extends FirebaseMessagingService { + + private static final String JSON_KEY_AUTHOR = SquawkContract.COLUMN_AUTHOR; + private static final String JSON_KEY_AUTHOR_KEY = SquawkContract.COLUMN_AUTHOR_KEY; + private static final String JSON_KEY_MESSAGE = SquawkContract.COLUMN_MESSAGE; + private static final String JSON_KEY_DATE = SquawkContract.COLUMN_DATE; + + private static final int NOTIFICATION_MAX_CHARACTERS = 30; + private static String LOG_TAG = SquawkFirebaseMessageService.class.getSimpleName(); + + // TODO COMPLETED (1) Make a new Service in the fcm package that extends from FirebaseMessagingService. + // TODO COMPLETED (2) As part of the new Service - Override onMessageReceived. This method will + // be triggered whenever a squawk is received. You can get the data from the squawk + // message using getData(). When you send a test message, this data will include the + // following key/value pairs: + // test: true + // author: Ex. "TestAccount" + // authorKey: Ex. "key_test" + // message: Ex. "Hello world" + // date: Ex. 1484358455343 + // TODO COMPLETED (3) As part of the new Service - If there is message data, get the data using + // the keys and do two things with it : + // 1. Display a notification with the first 30 character of the message + // 2. Use the content provider to insert a new message into the local database + // Hint: You shouldn't be doing content provider operations on the main thread. + // If you don't know how to make notifications or interact with a content provider + // look at the notes in the classroom for help. + + /** + * Called when message is received. + * + * @param remoteMessage Object representing the message received from Firebase Cloud Messaging + */ + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + // There are two types of messages data messages and notification messages. Data messages + // are handled + // here in onMessageReceived whether the app is in the foreground or background. Data + // messages are the type + // traditionally used with FCM. Notification messages are only received here in + // onMessageReceived when the app + // is in the foreground. When the app is in the background an automatically generated + // notification is displayed. + // When the user taps on the notification they are returned to the app. Messages + // containing both notification + // and data payloads are treated as notification messages. The Firebase console always + // sends notification + // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options\ + + // The Squawk server always sends just *data* messages, meaning that onMessageReceived when + // the app is both in the foreground AND the background + + Log.d(LOG_TAG, "-> From: " + remoteMessage.getFrom()); + + // Check if message contains a data payload. + + Map data = remoteMessage.getData(); + + if (data.size() > 0) { + Log.d(LOG_TAG, "-> Message data payload: " + data); + + // Send a notification that you got a new message + sendNotification(data); + insertSquawk(data); + + } + } + + /** + * Inserts a single squawk into the database; + * + * @param data Map which has the message data in it + */ + private void insertSquawk(final Map data) { + + // Database operations should not be done on the main thread + AsyncTask insertSquawkTask = new InsertSquawkTask(this, data); + insertSquawkTask.execute(); + } + + private static class InsertSquawkTask extends AsyncTask { + + private Context context; + private Map data; + + public InsertSquawkTask(Context context, Map data) { + this.context = context; + this.data = data; + } + + @Override + protected Void doInBackground(Void... voids) { + ContentValues newMessage = new ContentValues(); + newMessage.put(SquawkContract.COLUMN_AUTHOR, data.get(JSON_KEY_AUTHOR)); + newMessage.put(SquawkContract.COLUMN_MESSAGE, data.get(JSON_KEY_MESSAGE).trim()); + newMessage.put(SquawkContract.COLUMN_DATE, data.get(JSON_KEY_DATE)); + newMessage.put(SquawkContract.COLUMN_AUTHOR_KEY, data.get(JSON_KEY_AUTHOR_KEY)); + context.getContentResolver().insert(SquawkProvider.SquawkMessages.CONTENT_URI, newMessage); + context = null; + return null; + } + } + + + /** + * Create and show a simple notification containing the received FCM message + * + * @param data Map which has the message data in it + */ + private void sendNotification(Map data) { + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + // Create the pending intent to launch the activity + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, + PendingIntent.FLAG_ONE_SHOT); + + String author = data.get(JSON_KEY_AUTHOR); + String message = data.get(JSON_KEY_MESSAGE); + + // If the message is longer than the max number of characters we want in our + // notification, truncate it and add the unicode character for ellipsis + if (message.length() > NOTIFICATION_MAX_CHARACTERS) { + message = message.substring(0, NOTIFICATION_MAX_CHARACTERS) + "\u2026"; + } + + Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.ic_duck) + .setContentTitle(String.format(getString(R.string.notification_message), author)) + .setContentText(message) + .setAutoCancel(true) + .setSound(defaultSoundUri) + .setContentIntent(pendingIntent); + + NotificationManager notificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); + } +} \ No newline at end of file From a244724b3421f0ca4147bddc50c9947fa97a19f9 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Fri, 1 Dec 2017 02:10:44 +0530 Subject: [PATCH 08/10] TFCM.05-Exercise-ImplementInstructorTopicFollowing --- app/src/main/AndroidManifest.xml | 2 -- .../example/com/squawker/MainActivity.java | 3 --- .../fcm/SquawkFirebaseMessageService.java | 18 ------------------ .../following/FollowingPreferenceFragment.java | 13 +++++++++++++ 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 24868f09..021d37a0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -40,8 +40,6 @@ - diff --git a/app/src/main/java/android/example/com/squawker/MainActivity.java b/app/src/main/java/android/example/com/squawker/MainActivity.java index d5f5e80a..04c9dec4 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -89,9 +89,6 @@ protected void onCreate(Bundle savedInstanceState) { // Start the loader getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); - // TODO COMPLETED (5) You can delete the code below for getting the extras from a notification message, - // since this was for testing purposes and not part of Squawker. - // Get token from the ID Service you created and show it in a log String token = FirebaseInstanceId.getInstance().getToken(); String msg = getString(R.string.message_token_format, token); diff --git a/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java b/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java index e30ac2c4..bc117fc5 100644 --- a/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java +++ b/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java @@ -50,24 +50,6 @@ public class SquawkFirebaseMessageService extends FirebaseMessagingService { private static final int NOTIFICATION_MAX_CHARACTERS = 30; private static String LOG_TAG = SquawkFirebaseMessageService.class.getSimpleName(); - // TODO COMPLETED (1) Make a new Service in the fcm package that extends from FirebaseMessagingService. - // TODO COMPLETED (2) As part of the new Service - Override onMessageReceived. This method will - // be triggered whenever a squawk is received. You can get the data from the squawk - // message using getData(). When you send a test message, this data will include the - // following key/value pairs: - // test: true - // author: Ex. "TestAccount" - // authorKey: Ex. "key_test" - // message: Ex. "Hello world" - // date: Ex. 1484358455343 - // TODO COMPLETED (3) As part of the new Service - If there is message data, get the data using - // the keys and do two things with it : - // 1. Display a notification with the first 30 character of the message - // 2. Use the content provider to insert a new message into the local database - // Hint: You shouldn't be doing content provider operations on the main thread. - // If you don't know how to make notifications or interact with a content provider - // look at the notes in the classroom for help. - /** * Called when message is received. * diff --git a/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java b/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java index 682f000c..b9e4cd46 100644 --- a/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java +++ b/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java @@ -23,6 +23,7 @@ /** * Shows the list of instructors you can follow */ +// TODO (1) Implement onSharedPreferenceChangeListener public class FollowingPreferenceFragment extends PreferenceFragmentCompat { private final static String LOG_TAG = FollowingPreferenceFragment.class.getSimpleName(); @@ -32,4 +33,16 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { // Add visualizer preferences, defined in the XML file in res->xml->preferences_squawker addPreferencesFromResource(R.xml.following_squawker); } + + // TODO (2) When a SharedPreference changes, check which preference it is and subscribe or + // un-subscribe to the correct topics. + + // Ex. FirebaseMessaging.getInstance().subscribeToTopic("key_lyla"); + // subscribes to Lyla's squawks. + + // HINT: Checkout res->xml->following_squawker.xml. Note how the keys for each of the + // preferences matches the topic to subscribe to for each instructor. + + // TODO (3) Make sure to register and unregister this as a Shared Preference Change listener, in + // onCreate and onDestroy. } From 08d860151a0e8fb219ddba696b427e6267959cd2 Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Fri, 1 Dec 2017 02:23:12 +0530 Subject: [PATCH 09/10] TFCM.05-Solution-ImplementInstructorTopicFollowing --- .../FollowingPreferenceFragment.java | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java b/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java index b9e4cd46..a5cc48b1 100644 --- a/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java +++ b/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java @@ -15,16 +15,23 @@ */ package android.example.com.squawker.following; +import android.content.SharedPreferences; import android.example.com.squawker.R; import android.os.Bundle; +import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; +import android.support.v7.preference.SwitchPreferenceCompat; +import android.util.Log; + +import com.google.firebase.messaging.FirebaseMessaging; /** * Shows the list of instructors you can follow */ -// TODO (1) Implement onSharedPreferenceChangeListener -public class FollowingPreferenceFragment extends PreferenceFragmentCompat { +// TODO COMPLETED (1) Implement onSharedPreferenceChangeListener +public class FollowingPreferenceFragment extends PreferenceFragmentCompat implements + SharedPreferences.OnSharedPreferenceChangeListener { private final static String LOG_TAG = FollowingPreferenceFragment.class.getSimpleName(); @@ -34,7 +41,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.following_squawker); } - // TODO (2) When a SharedPreference changes, check which preference it is and subscribe or + // TODO COMPLETED (2) When a SharedPreference changes, check which preference it is and subscribe or // un-subscribe to the correct topics. // Ex. FirebaseMessaging.getInstance().subscribeToTopic("key_lyla"); @@ -43,6 +50,52 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { // HINT: Checkout res->xml->following_squawker.xml. Note how the keys for each of the // preferences matches the topic to subscribe to for each instructor. - // TODO (3) Make sure to register and unregister this as a Shared Preference Change listener, in + /** + * Triggered when shared preferences changes. This will be triggered when a person is followed + * or un-followed + * + * @param sharedPreferences SharedPreferences file + * @param key The key of the preference which was changed + */ + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + + Preference preference = findPreference(key); + if (preference != null && preference instanceof SwitchPreferenceCompat) { + // Get the current state of the switch preference + boolean isOn = sharedPreferences.getBoolean(key, false); + if (isOn) { + // The preference key matches the following key for the associated instructor in + // FCM. For example, the key for Lyla is key_lyla (as seen in + // following_squawker.xml). The topic for Lyla's messages is /topics/key_lyla + + // Subscribe + FirebaseMessaging.getInstance().subscribeToTopic(key); + Log.d(LOG_TAG, "-> Subscribing to " + key); + } else { + // Un-subscribe + FirebaseMessaging.getInstance().unsubscribeFromTopic(key); + Log.d(LOG_TAG, "-> Un-subscribing to " + key); + } + } + } + + // TODO COMPLETED (3) Make sure to register and unregister this as a Shared Preference Change listener, in // onCreate and onDestroy. + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Add the shared preference change listener + getPreferenceScreen().getSharedPreferences() + .registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onDestroy() { + super.onDestroy(); + // Remove the shared preference change listener + getPreferenceScreen().getSharedPreferences() + .unregisterOnSharedPreferenceChangeListener(this); + } } From c1b4f13c4849ff743a511ed9e89473a2fd4bfc2e Mon Sep 17 00:00:00 2001 From: Hrishikesh Kadam Date: Fri, 1 Dec 2017 02:29:23 +0530 Subject: [PATCH 10/10] Update README.md --- README.md | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 028c614a..d925595d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,99 @@ # Squawker Code -This is a exercise repository for the Squawker example app which is part of Udacity's Advanced Android course. The Squawker example app uses Firebase Cloud Messenger to receive Twitter-like messages, sent from [this server](https://squawkerfcmserver.udacity.com/), in real time. You can learn more about how to use this repository [here](https://classroom.udacity.com/courses/ud857/lessons/8b2a9d63-0ff5-48ff-90d3-a9855b701dae/concepts/41b82e3c-2797-46e5-8a66-684098ca8cbb). \ No newline at end of file +This is a exercise repository for the Squawker example app which is part of Udacity's Advanced Android course. The Squawker example app uses Firebase Cloud Messenger to receive Twitter-like messages, sent from [this server](https://squawkerfcmserver.udacity.com/), in real time. + +This is the toy app for the FCM lesson of the [Advanced Android App Development course on Udacity](https://www.udacity.com/course/advanced-android-app-development--ud855). + +## How to use this repo while taking the course + +Each code repository in this class has a chain of commits that looks like this: + +![listofcommits](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58befe2e_listofcommits/listofcommits.png) + +These commits show every step you'll take to create the app. They include **Exercise** commits and **Solution** commits. + +Exercise commits contain instructions for completing the exercise, while solution commits show the completed exercise. You can tell what a commit is by looking at its commit message. + +For example, **TFCM.01-Exercise-AddGradleDependencies** is the first code step in the Firebase Cloud Messaging (FCM) lesson. This is the exercise commit, and the exercise is called Add Gradle Dependencies. + +Each commit also has a **branch** associated with it of the same name as the commit message, seen below: + +![branches](https://d17h27t6h515a5.cloudfront.net/topher/2017/April/590390fe_branches-ud855/branches-ud855.png +) +Access all branches from this tab + +![listofbranches](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58befe76_listofbranches/listofbranches.png +) + + +![branchesdropdown](https://d17h27t6h515a5.cloudfront.net/topher/2017/April/590391a3_branches-dropdown-ud855/branches-dropdown-ud855.png +) + + +The branches are also accessible from the drop-down in the "Code" tab + + +## Working with the Course Code + +Here are the basic steps for working with and completing exercises in the repo. This information is linked whenever you start a new exercise project, so don't feel you need to memorize all of this! In fact, skim it now, make sure that you know generally how to do the different tasks, and then come back when you start your first exercise. + +The basic steps are: + +1. Clone the repo +2. Checkout the exercise branch +3. Find and complete the TODOs +4. Optionally commit your code changes +5. Compare with the solution + + +**Step 1: Clone the repo** + +As you go through the course, you'll be instructed to clone the different exercise repositories, so you don't need to set these up now. You can clone a repository from github in a folder of your choice with the command: + +```bash +git clone https://github.com/udacity/REPOSITORY_NAME.git +``` + +**Step 2: Checkout the exercise branch** + +As you do different exercises in the code, you'll be told which exercise you're on, as seen below: +![exerciseexample](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf0087_exerciseexample/exerciseexample.png +) + +To complete an exercise, you'll want to check out the branch associated with that exercise. For the exercise above, the command to check out that branch would be: + +```bash +git checkout TFCM.01-Exercise-AddGradleDependencies +``` + +**Step 3: Find and complete the TODOs** + +This branch should always have **Exercise** in the title. Once you've checked out the branch, you'll have the code in the exact state you need. You'll even have TODOs, which are special comments that tell you all the steps you need to complete the exercise. You can easily navigate to all the TODOs using Android Studio's TODO tool. To open the TODO tool, click the button at the bottom of the screen that says TODO. This will display a list of all comments with TODO in the project. + +We've numbered the TODO steps so you can do them in order: +![todos](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf00e7_todos/todos.png +) + +**Step 4: Optionally commit your code changes** + +After You've completed the TODOs, you can optionally commit your changes. This will allow you to see the code you wrote whenever you return to the branch. The following git code will add and save **all** your changes. + +```bash +git add . +git commit -m "Your commit message" +``` + +**Step 5: Compare with the solution** + +Most exercises will have a list of steps for you to check off in the classroom. Once you've checked these off, you'll see a pop up window with a link to the solution code. Note the **Diff** link: + +![solutionwindow](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf00f9_solutionwindow/solutionwindow.png +) + +The **Diff** link will take you to a Github diff as seen below: +![diff](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf0108_diffsceenshot/diffsceenshot.png +) + +All of the code that was added in the solution is in green, and the removed code (which will usually be the TODO comments) is in red. +## Report Issues +Notice any issues with a repository? Please file a github issue in the repository. \ No newline at end of file