diff --git a/app/build.gradle b/app/build.gradle index 725fb1c8..3be43f7e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' -apply plugin: 'android-apt' +apply plugin: 'com.google.gms.google-services' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 29 + buildToolsVersion "29.0.3" defaultConfig { applicationId "android.example.com.squawker" - minSdkVersion 16 - targetSdkVersion 25 + minSdkVersion 19 + targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -21,26 +21,20 @@ android { } 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.1.0' - testCompile 'junit:junit:4.12' + implementation fileTree(dir: 'libs', include: ['*.jar']) - // RecyclerView - compile 'com.android.support:recyclerview-v7:25.1.0' + implementation 'androidx.appcompat:appcompat:1.1.0' - // Schematic dependencies for ContentProvider - apt 'net.simonvt.schematic:schematic-compiler:0.6.3' - compile 'net.simonvt.schematic:schematic:0.6.3' + testImplementation 'junit:junit:4.13.2' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + annotationProcessor 'net.simonvt.schematic:schematic-compiler:0.6.3' + implementation 'net.simonvt.schematic:schematic:0.6.3' + implementation "androidx.preference:preference:1.1.1" + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - // Preferences Dependencies - compile 'com.android.support:preference-v7:25.1.0' + implementation platform('com.google.firebase:firebase-bom:29.0.3') + implementation 'com.google.firebase:firebase-analytics' - // Firebase dependency - compile 'com.google.firebase:firebase-messaging:10.0.1' + implementation 'com.google.firebase:firebase-messaging:23.0.0' } -// 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/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6ad5d39c..9307dacc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,6 +25,19 @@ android:authorities="android.example.com.squawker.provider.provider" android:exported="false" /> + + + + + + + + + + + @@ -33,13 +46,12 @@ 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 afe130d3..0d239bdf 100644 --- a/app/src/main/java/android/example/com/squawker/MainActivity.java +++ b/app/src/main/java/android/example/com/squawker/MainActivity.java @@ -22,20 +22,20 @@ import android.example.com.squawker.provider.SquawkContract; import android.example.com.squawker.provider.SquawkProvider; import android.os.Bundle; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.preference.PreferenceManager; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.CursorLoader; +import androidx.loader.content.Loader; +import androidx.preference.PreferenceManager; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; - -import com.google.firebase.iid.FirebaseInstanceId; +import android.widget.Toast; +import com.google.firebase.messaging.FirebaseMessaging; public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { @@ -86,13 +86,23 @@ protected void onCreate(Bundle savedInstanceState) { mRecyclerView.setAdapter(mAdapter); // Start the loader - getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); - - // 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); - + LoaderManager.getInstance(this).initLoader(LOADER_ID_MESSAGES, null, this); + + FirebaseMessaging.getInstance().getToken() + .addOnCompleteListener(task -> { + if (!task.isSuccessful()) { + Log.w(MainActivity.LOG_TAG, "Fetching FCM registration token failed", task.getException()); + return; + } + + // Get new FCM registration token + String token = task.getResult(); + + // Log and toast + String msg = getString(R.string.message_token_format, token); + Log.d(MainActivity.LOG_TAG, msg); + }); + FirebaseMessaging.getInstance().setAutoInitEnabled(true); } @Override diff --git a/app/src/main/java/android/example/com/squawker/SquawkAdapter.java b/app/src/main/java/android/example/com/squawker/SquawkAdapter.java index 89eb58dd..6a7f111f 100644 --- a/app/src/main/java/android/example/com/squawker/SquawkAdapter.java +++ b/app/src/main/java/android/example/com/squawker/SquawkAdapter.java @@ -18,7 +18,7 @@ import android.database.Cursor; import android.example.com.squawker.provider.SquawkContract; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; 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 index 1d2f90f9..75fe80af 100644 --- a/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseInstanceIdService.java +++ b/app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseInstanceIdService.java @@ -1,57 +1,37 @@ /* -* 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. -*/ + * 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; +import com.google.firebase.messaging.FirebaseMessagingService; -/** - * Listens for changes in the InstanceID - */ -public class SquawkFirebaseInstanceIdService extends FirebaseInstanceIdService { +public class SquawkFirebaseInstanceIdService extends FirebaseMessagingService{ 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); + public void onNewToken(String token) { + Log.d(LOG_TAG, "Refreshed token: " + token); // 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); + // FCM registration token to your app server. + sendRegistrationToServer(token); } - - /** - * 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. 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 9c868dbe..f8cbf2c2 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 @@ -1,18 +1,19 @@ /* -* 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. -*/ + * 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; @@ -27,9 +28,10 @@ import android.media.RingtoneManager; import android.net.Uri; import android.os.AsyncTask; -import android.support.v4.app.NotificationCompat; import android.util.Log; +import androidx.core.app.NotificationCompat; + import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; @@ -57,22 +59,6 @@ public class SquawkFirebaseMessageService extends FirebaseMessagingService { */ @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()); @@ -99,9 +85,8 @@ private void insertSquawk(final Map data) { // Database operations should not be done on the main thread AsyncTask insertSquawkTask = new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { + 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()); @@ -151,4 +136,4 @@ private void sendNotification(Map data) { notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); } -} \ No newline at end of file +} diff --git a/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceActivity.java b/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceActivity.java index 72769779..ecc4eabe 100644 --- a/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceActivity.java +++ b/app/src/main/java/android/example/com/squawker/following/FollowingPreferenceActivity.java @@ -17,9 +17,9 @@ import android.example.com.squawker.R; import android.os.Bundle; -import android.support.v4.app.NavUtils; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.NavUtils; import android.view.MenuItem; /** 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 563ceceb..cb1141e4 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,22 @@ */ 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.PreferenceFragmentCompat; +import android.util.Log; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.SwitchPreferenceCompat; + +import com.google.firebase.messaging.FirebaseMessaging; /** * Shows the list of instructors you can follow */ -// TODO (1) Implement onSharedPreferenceChangeListener -public class FollowingPreferenceFragment extends PreferenceFragmentCompat { +public class FollowingPreferenceFragment extends PreferenceFragmentCompat + implements SharedPreferences.OnSharedPreferenceChangeListener { private final static String LOG_TAG = FollowingPreferenceFragment.class.getSimpleName(); @@ -33,16 +39,50 @@ 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. + /** + * 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 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 - // 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. + // 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 (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); + } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4f8500c1..805c739a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -16,7 +16,7 @@ android:layout_height="match_parent" tools:context="android.example.com.squawker.MainActivity"> - \ No newline at end of file diff --git a/build.gradle b/build.gradle index 5ae0e993..a290e7d7 100644 --- a/build.gradle +++ b/build.gradle @@ -2,11 +2,12 @@ buildscript { repositories { - jcenter() + google() + mavenCentral() } 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:7.0.4' + classpath 'com.google.gms:google-services:4.3.10' // Google Services plugin classpath 'com.google.gms:google-services:3.0.0' @@ -18,7 +19,8 @@ buildscript { allprojects { repositories { - jcenter() + google() + mavenCentral() } } diff --git a/gradle.properties b/gradle.properties index aac7c9b4..29b531a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m - +android.useAndroidX=true # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 04e285f3..f3bd5913 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ 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-7.0.2-all.zip