adds firebase connection and notifications

- notification handler pushes background and
  foreground notifications to the phone directly
- firebase connection pushes remote notifications
  in background and foreground
- firebase unique token currently logged only, should
  be send directly to server via rest
This commit is contained in:
Raphael Maenle 2021-12-30 13:08:52 +01:00
parent 51bf245f00
commit 9ea64fb460
28 changed files with 179 additions and 142 deletions

View File

@ -38,6 +38,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:16.1.4'
implementation 'com.android.volley:volley:1.2.0'
implementation 'com.google.firebase:firebase-messaging-ktx:21.0.1'
def camerax_version = "1.0.2"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
@ -53,4 +54,6 @@ dependencies {
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
}
apply plugin: 'com.google.gms.google-services' // Google Play services Gradle plugin

View File

@ -1,9 +1,9 @@
package com.example.bump
package com.maenle.bump
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.example.bump.util.LocalData
import com.example.bump.util.MessageProcessor
import com.maenle.bump.util.LocalData
import com.maenle.bump.util.MessageProcessor
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith

View File

@ -1,10 +1,10 @@
package com.example.bump
package com.maenle.bump
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.bump.util.Message
import com.example.bump.util.MessageProcessor
import com.example.bump.util.RestSingleton
import com.maenle.bump.util.Message
import com.maenle.bump.util.MessageProcessor
import com.maenle.bump.util.RestSingleton
import org.json.JSONArray
import org.json.JSONObject

View File

@ -7,8 +7,9 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.example.bump.ui.MainApplication"
android:name=".ui.MainApplication"
android:allowBackup="true"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
@ -16,8 +17,29 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BumpForAndroid">
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_launcher_foreground"/>
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/black"/> <!-- [END fcm_default_icon] -->
<!-- [START fcm_default_channel] -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/bump_notification_channel_id" />
<!-- [END fcm_default_channel] -->
<activity
android:name="com.example.bump.ui.MainActivity"
android:name=".ui.MainActivity"
tools:node="merge"
android:exported="true"
android:label="@string/app_name"

View File

@ -1,28 +0,0 @@
package com.example.bump.receiver
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.example.bump.util.sendNotification
import com.maenle.bump.R
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(
"Hello",
context
)
}
}

View File

@ -1,42 +0,0 @@
package com.example.bump.receiver
import android.app.AlarmManager
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.SystemClock
import android.text.format.DateUtils
import androidx.core.app.AlarmManagerCompat
import androidx.core.content.ContextCompat
class SnoozeReceiver: BroadcastReceiver() {
private val REQUEST_CODE = 0
override fun onReceive(context: Context, intent: Intent) {
val triggerTime = SystemClock.elapsedRealtime() + DateUtils.MINUTE_IN_MILLIS
val notifyIntent = Intent(context, AlarmReceiver::class.java)
val notifyPendingIntent = PendingIntent.getBroadcast(
context,
REQUEST_CODE,
notifyIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
AlarmManagerCompat.setExactAndAllowWhileIdle(
alarmManager,
AlarmManager.ELAPSED_REALTIME_WAKEUP,
triggerTime,
notifyPendingIntent
)
val notificationManager = ContextCompat.getSystemService(
context,
NotificationManager::class.java
) as NotificationManager
notificationManager.cancelAll()
}
}

View File

@ -0,0 +1,78 @@
package com.maenle.bump
import android.app.NotificationManager
import android.util.Log
import androidx.core.content.ContextCompat
import com.maenle.bump.util.sendNotification
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class MyFirebaseMessagingService : FirebaseMessagingService() {
/**
* Called when message is received.
*
* @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
// [START receive_message]
override fun onMessageReceived(remoteMessage: RemoteMessage) {
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Log.d(TAG, "From: ${remoteMessage.from}")
// TODO Step 3.5 check messages for data
// Check if message contains a data payload.
remoteMessage.data.let {
Log.d(TAG, "Message data payload: " + remoteMessage.data)
}
// TODO Step 3.6 check messages for notification and call sendNotification
// Check if message contains a notification payload.
remoteMessage.notification?.let {
Log.d(TAG, "Message Notification Body: ${it.body}")
sendNotification(it.body!!)
}
}
// [END receive_message]
//TODO Step 3.2 log registration token
// [START on_new_token]
/**
* 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 fun onNewToken(token: String) {
Log.d(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(token)
}
// [END on_new_token]
/**
* Persist token to third-party servers.
*
* @param token The new token.
*/
private fun sendRegistrationToServer(token: String?) {
// TODO: Implement this method to send token to your app server.
}
/**
* Create and show a simple notification containing the received FCM message.
*
* @param messageBody FCM message body received.
*/
private fun sendNotification(messageBody: String) {
val notificationManager = ContextCompat.getSystemService(applicationContext, NotificationManager::class.java) as NotificationManager
notificationManager.sendNotification(messageBody, applicationContext)
}
companion object {
private const val TAG = "MyFirebaseMsgService"
}
}

View File

@ -1,4 +1,4 @@
package com.example.bump.ui
package com.maenle.bump.ui
import android.Manifest
import android.annotation.SuppressLint
@ -31,8 +31,8 @@ import kotlin.math.min
import android.view.WindowInsets
import android.graphics.Insets
import com.example.bump.util.BumpProcessor
import com.example.bump.util.CameraXViewModel
import com.maenle.bump.util.BumpProcessor
import com.maenle.bump.util.CameraXViewModel
class CameraFragment: Fragment() {

View File

@ -1,17 +1,20 @@
package com.example.bump.ui
package com.maenle.bump.ui
import android.app.NotificationChannel
import android.app.NotificationManager
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.navigation.fragment.findNavController
import com.example.bump.util.BumpProcessor
import com.example.bump.util.RestSingleton
import com.google.firebase.messaging.FirebaseMessaging
import com.maenle.bump.util.BumpProcessor
import com.maenle.bump.util.RestSingleton
import com.maenle.bump.util.sendNotification
import com.maenle.bump.R
import com.maenle.bump.databinding.FragmentFirstBinding
import org.json.JSONArray
@ -30,6 +33,7 @@ class FirstFragment : Fragment() {
private lateinit var rest: RestSingleton
private lateinit var log: JSONArray
private lateinit var bump: BumpProcessor
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@ -40,12 +44,21 @@ class FirstFragment : Fragment() {
bump = BumpProcessor(requireContext())
// TODO: Step 1.7 call create channel
createChannel(
getString(R.string.bump_notification_channel_id),
getString(R.string.bump_notification_channel_name)
)
/*
val notificationManager = ContextCompat.getSystemService(
requireContext(),
NotificationManager::class.java
) as NotificationManager
notificationManager.sendNotification(getString(R.string.notification), requireContext())
*/
subscribeTopic()
return binding.root
}
@ -62,13 +75,11 @@ class FirstFragment : Fragment() {
}
private fun createChannel(channelId: String, channelName: String) {
// TODO: Step 1.6 START create a channel
val notificationChannel = NotificationChannel(
channelId,
channelName,
// TODO: Step 2.4 change importance
NotificationManager.IMPORTANCE_HIGH
)// TODO: Step 2.6 disable badges for this channel
)
.apply {
setShowBadge(false)
}
@ -82,12 +93,24 @@ class FirstFragment : Fragment() {
NotificationManager::class.java
)
notificationManager.createNotificationChannel(notificationChannel)
}
// TODO: Step 1.6 END create a channel
private fun subscribeTopic() {
// [START subscribe_topic]
FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)
.addOnCompleteListener { task ->
var message = getString(R.string.message_subscribed)
if (!task.isSuccessful) {
message = getString(R.string.message_subscribe_failed)
}
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
// [END subscribe_topics]
}
companion object {
fun newInstance() = FirstFragment()
private val TOPIC = "Bump"
}

View File

@ -1,4 +1,4 @@
package com.example.bump.ui
package com.maenle.bump.ui
import android.os.Bundle
import com.google.android.material.snackbar.Snackbar

View File

@ -1,9 +1,9 @@
package com.example.bump.ui
package com.maenle.bump.ui
import android.app.Application
import android.content.Context
import com.example.bump.util.BumpProcessor
import com.example.bump.util.RestSingleton
import com.maenle.bump.util.BumpProcessor
import com.maenle.bump.util.RestSingleton
// Not object class. AndroidManifest.xml error happen.
class MainApplication : Application() {

View File

@ -1,11 +1,10 @@
package com.example.bump.ui
package com.maenle.bump.ui
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.maenle.bump.databinding.FragmentSecondBinding
/**

View File

@ -1,4 +1,4 @@
package com.example.bump.util
package com.maenle.bump.util
import android.content.Context
import android.os.Handler

View File

@ -1,4 +1,4 @@
package com.example.bump.util
package com.maenle.bump.util
import android.app.Application
import android.util.Log

View File

@ -1,4 +1,4 @@
package com.example.bump.util
package com.maenle.bump.util
import android.content.Context
import android.content.Context.MODE_PRIVATE

View File

@ -1,8 +1,7 @@
package com.example.bump.util
package com.maenle.bump.util
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.*
class Message(var sender: String, var data: String, timestamp: String) {
val timestamp = LocalDate.parse(timestamp, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"))

View File

@ -1,4 +1,4 @@
package com.example.bump.util
package com.maenle.bump.util
import java.util.Base64
import javax.crypto.spec.PBEKeySpec
@ -7,7 +7,7 @@ import com.macasaet.fernet.Key
import com.macasaet.fernet.Token
import com.macasaet.fernet.StringValidator
import com.macasaet.fernet.Validator
import com.example.bump.ui.MainActivity
import com.maenle.bump.ui.MainActivity
import java.math.BigInteger
import java.security.SecureRandom
import java.time.Duration

View File

@ -1,20 +1,19 @@
package com.example.bump.util
package com.maenle.bump.util
import android.annotation.SuppressLint
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat
import com.example.bump.receiver.SnoozeReceiver
import com.example.bump.ui.MainActivity
import com.maenle.bump.ui.MainActivity
import com.maenle.bump.R
// Notification ID.
private val NOTIFICATION_ID = 0
private val REQUEST_CODE = 0
private val FLAGS = 0
@SuppressLint("WrongConstant")
fun NotificationManager.sendNotification(messageBody: String, applicationContext: Context) {
// Create the content intent for the notification, which launches
// this activity
@ -33,17 +32,6 @@ fun NotificationManager.sendNotification(messageBody: String, applicationContext
applicationContext.resources,
R.drawable.ic_launcher_foreground
)
val bigPicStyle = NotificationCompat.BigPictureStyle()
.bigPicture(eggImage)
.bigLargeIcon(null)
// TODO: Step 2.2 add snooze action
val snoozeIntent = Intent(applicationContext, SnoozeReceiver::class.java)
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(
applicationContext,
REQUEST_CODE,
snoozeIntent,
FLAGS)
// TODO: Step 1.2 get an instance of NotificationCompat.Builder
// Build the notification
@ -52,8 +40,6 @@ fun NotificationManager.sendNotification(messageBody: String, applicationContext
applicationContext.getString(R.string.bump_notification_channel_id)
)
// TODO: Step 1.8 use the new 'breakfast' notification channel
// TODO: Step 1.3 set title, text and icon to builder
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(applicationContext
@ -65,16 +51,8 @@ fun NotificationManager.sendNotification(messageBody: String, applicationContext
.setAutoCancel(true)
// TODO: Step 2.1 add style to builder
.setStyle(bigPicStyle)
.setLargeIcon(eggImage)
// TODO: Step 2.3 add snooze action
.addAction(
R.drawable.ic_launcher_foreground,
applicationContext.getString(R.string.snooze),
snoozePendingIntent
)
// TODO: Step 2.5 set priority
.setPriority(NotificationCompat.PRIORITY_HIGH)
// TODO: Step 1.4 call notify

View File

@ -1,11 +1,11 @@
package com.example.bump.util
package com.maenle.bump.util
import android.content.Context
import android.util.Log
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.toolbox.*
import com.example.bump.ui.MainActivity
import com.maenle.bump.ui.MainActivity
import org.json.JSONArray
import org.json.JSONObject

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.bump.ui.MainActivity">
tools:context="com.maenle.bump.ui.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.bump.ui.SecondFragment">
tools:context="com.maenle.bump.ui.SecondFragment">
<!--androidx.camera.view.PreviewView
android:id="@+id/viewFinder"

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.bump.ui.FirstFragment">
tools:context="com.maenle.bump.ui.FirstFragment">
<TextView
android:id="@+id/textview_first"

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.bump.ui.SecondFragment">
tools:context="com.maenle.bump.ui.SecondFragment">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"

View File

@ -1,7 +1,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.maenle.bump.com.example.bump.ui.MainActivity">
tools:context="com.maenle.bump.ui.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"

View File

@ -7,7 +7,7 @@
<fragment
android:id="@+id/FirstFragment"
android:name="com.example.bump.ui.FirstFragment"
android:name="com.maenle.bump.ui.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first">
@ -17,7 +17,7 @@
</fragment>
<fragment
android:id="@+id/SecondFragment"
android:name="com.example.bump.ui.SecondFragment"
android:name="com.maenle.bump.ui.SecondFragment"
android:label="@string/second_fragment_label"
tools:layout="@layout/fragment_second">
@ -27,7 +27,7 @@
</fragment>
<fragment
android:id="@+id/CameraFragment"
android:name="com.example.bump.ui.CameraFragment"
android:name="com.maenle.bump.ui.CameraFragment"
android:label="@string/camera_fragment_label"
tools:layout="@layout/fragment_camera">

View File

@ -18,4 +18,7 @@
<string name="bump_notification_channel_id">bump_id</string>
<string name="snooze">Snooze</string>
<string name="bump_notification_channel_name">Bump</string>
<string name="notification">This is a notification</string>
<string name="message_subscribed">Subscribed to Channel</string>
<string name="message_subscribe_failed">Could not subscribe</string>
</resources>

View File

@ -7,6 +7,8 @@ buildscript {
dependencies {
classpath "com.android.tools.build:gradle:7.0.4"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0"
classpath 'com.google.gms:google-services:4.3.2' // Google Services plugin
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -15,4 +17,4 @@ buildscript {
task clean(type: Delete) {
delete rootProject.buildDir
}
}