├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── notification.xml
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── grumpyshoe
│ │ │ │ └── modules
│ │ │ │ └── pushmanager
│ │ │ │ └── sample
│ │ │ │ ├── Utils.kt
│ │ │ │ ├── NotificationActivity.kt
│ │ │ │ ├── MyService.kt
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── grumpyshoe
│ │ │ └── modules
│ │ │ └── pushmanager
│ │ │ └── sample
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── grumpyshoe
│ │ └── modules
│ │ └── pushmanager
│ │ └── sample
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── pushmanager
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── fcm.xml
│ │ │ ├── drawable-hdpi
│ │ │ │ └── baseline_notifications_black.png
│ │ │ └── drawable
│ │ │ │ └── ic_baseline_notifications_24px.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── grumpyshoe
│ │ │ │ └── module
│ │ │ │ └── pushmanager
│ │ │ │ ├── models
│ │ │ │ ├── NotificationLight.kt
│ │ │ │ ├── RemoteMessageData.kt
│ │ │ │ └── NotificationData.kt
│ │ │ │ ├── PushManager.kt
│ │ │ │ └── impl
│ │ │ │ ├── PushManagerImpl.kt
│ │ │ │ └── PushmanagerMessagingService.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── grumpyshoe
│ │ │ └── pushmanager
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── grumpyshoe
│ │ └── pushmanager
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── CHANGELOG.md
├── gradle.properties
├── LICENSE
├── gradlew.bat
├── gradlew
└── README.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':pushmanager'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
NotificationLight contains all information for handling smartphone-led on incoming message
5 | * 6 | * @since 1.0.0 7 | * @version 1.0.0 8 | * @author grumpyshoe 9 | * 10 | */ 11 | data class NotificationLight(val argb: Int, val onMS: Int, val offMS: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/grumpyshoe/modules/pushmanager/sample/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.grumpyshoe.modules.pushmanager.sample 2 | 3 | import android.content.Context 4 | import android.widget.TextView 5 | import android.widget.Toast 6 | 7 | fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) = Toast.makeText(this, message, duration).show() 8 | fun TextView.onUI(action: (TextView) -> Unit) { 9 | this.post { action(this) } 10 | } -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 |PushManager - interface for easy access to FCM
10 | * 11 | * @version 1.3.0 12 | * @since 1.0.0 13 | * @author grumpyshoe 14 | * 15 | */ 16 | interface PushManager { 17 | 18 | 19 | /** 20 | * register device for cloudmessaging 21 | * 22 | */ 23 | suspend fun register(context: Context, onTokenReceived: (String) -> Unit, onFailure: (Exception?) -> Unit) 24 | 25 | 26 | /** 27 | * delete this Firebase app installation from the Firebase backend 28 | * 29 | */ 30 | suspend fun unregister(context: Context) 31 | 32 | 33 | /** 34 | * subscribe to topic 35 | * 36 | */ 37 | fun subscribeToTopic(topic: String, onSuccess: (() -> Unit)? = null, onFailure: ((Exception?) -> Unit)? = null) 38 | 39 | 40 | /** 41 | * unsubscribe from topic 42 | * 43 | */ 44 | fun unsubscribeFromTopic(topic: String, onSuccess: (() -> Unit)? = null, onFailure: ((Exception?) -> Unit)? = null) 45 | 46 | } -------------------------------------------------------------------------------- /pushmanager/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'maven' 5 | 6 | android { 7 | compileSdkVersion compile_sdk_version 8 | 9 | defaultConfig { 10 | minSdkVersion min_sdk_version 11 | targetSdkVersion target_sdk_version 12 | buildToolsVersion build_tools_version 13 | versionCode version_code 14 | versionName version_name 15 | 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | } 28 | 29 | dependencies { 30 | implementation fileTree(include: ['*.jar'], dir: 'libs') 31 | implementation libs.kotlin 32 | implementation libs.appcompat 33 | api libs.firebase_core 34 | api libs.firebase_iid 35 | api libs.firebase_messaging 36 | 37 | testImplementation libs.junit 38 | 39 | androidTestImplementation libs.test_runner 40 | androidTestImplementation libs.espresso_core 41 | } 42 | 43 | repositories { 44 | mavenCentral() 45 | google() 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 |RemoteMessageData is a model for holding all notification information that's available from incomming message
7 | * 8 | * @since 1.0.0 9 | * @version 1.0.0 10 | * @author grumpyshoe 11 | * 12 | */ 13 | data class RemoteMessageData( 14 | var tag: String? = null, 15 | var bodyLocalizationKey: String? = null, 16 | var bodyLocalizationArgs: ArrayNotificationData is a wrapper for creating a custom notification based on a incomming remote message.
11 | * 12 | * @since 1.0.0 13 | * @version 1.0.0 14 | * @author grumpyshoe 15 | * 16 | */ 17 | data class NotificationData(val context: Context, 18 | val title: String, 19 | val message: String, 20 | val soundUri: Uri? = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), 21 | val smallIcon: Int = R.drawable.ic_baseline_notifications_24px, 22 | val autoCancel: Boolean = true, 23 | val channelId: String = context.getString(R.string.default_notification_channel_id), 24 | var color: Int? = null, 25 | val colorize: Boolean = false, 26 | val contentInfo: String? = null, 27 | val category: String? = null, 28 | val light: NotificationLight? = null, 29 | val timeoutAfter: Long? = null, 30 | val subtext: String? = null, 31 | val publicVersion: NotificationData? = null, 32 | val onGoing: Boolean? = null, 33 | val ticker: String? = null, 34 | val useChronometer: Boolean? = null, 35 | val notificationId: Int? = null, 36 | val pendingIntent: PendingIntent? = null, 37 | val notificationType: NotificationType = NotificationType.STACK) 38 | 39 | enum class NotificationType { SINGLE, STACK } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 |PushManagerImpl is based on PushManager and contains all connection logic for handling FCM
13 | * 14 | * @version 1.3.0 15 | * @since 1.0.0 16 | * @author grumpyshoe 17 | * 18 | */ 19 | object PushManagerImpl : PushManager { 20 | 21 | private var onTokenReceived: ((String) -> Unit)? = null 22 | 23 | 24 | /** 25 | * init push 26 | * 27 | */ 28 | private fun initPushmanager(context: Context) { 29 | FirebaseApp.initializeApp(context) 30 | } 31 | 32 | 33 | /** 34 | * register 35 | * 36 | */ 37 | override suspend fun register(context: Context, onTokenReceived: (String) -> Unit, onFailure: (Exception?) -> Unit) { 38 | 39 | this.onTokenReceived = onTokenReceived 40 | 41 | // init firebase 42 | initPushmanager(context) 43 | 44 | FirebaseInstallations.getInstance().id 45 | .addOnCompleteListener(OnCompleteListener { task -> 46 | if (!task.isSuccessful) { 47 | onFailure(task.exception) 48 | return@OnCompleteListener 49 | } 50 | 51 | // Get new Instance ID token 52 | val token = task.result 53 | 54 | // Log and toast 55 | token?.let { 56 | this.onTokenReceived?.let { it(token) } 57 | } 58 | }) 59 | } 60 | 61 | 62 | /** 63 | * unregister 64 | * 65 | */ 66 | override suspend fun unregister(context: Context) { 67 | 68 | // init firebase 69 | initPushmanager(context) 70 | 71 | FirebaseInstallations.getInstance().delete() 72 | } 73 | 74 | 75 | /** 76 | * subscribe to topic 77 | * 78 | */ 79 | override fun subscribeToTopic(topic: String, onSuccess: (() -> Unit)?, onFailure: ((Exception?) -> Unit)?) { 80 | 81 | FirebaseMessaging.getInstance().subscribeToTopic(topic) 82 | .addOnCompleteListener(OnCompleteListener { task -> 83 | if (!task.isSuccessful) { 84 | onFailure?.invoke(task.exception) 85 | return@OnCompleteListener 86 | 87 | } 88 | 89 | onSuccess?.invoke() 90 | 91 | }) 92 | } 93 | 94 | 95 | /** 96 | * unsubscribe from topic 97 | * 98 | */ 99 | override fun unsubscribeFromTopic(topic: String, onSuccess: (() -> Unit)?, onFailure: ((Exception?) -> Unit)?) { 100 | FirebaseMessaging.getInstance().unsubscribeFromTopic(topic) 101 | .addOnCompleteListener(OnCompleteListener { task -> 102 | if (!task.isSuccessful) { 103 | onFailure?.invoke(task.exception) 104 | return@OnCompleteListener 105 | } 106 | 107 | onSuccess?.invoke() 108 | 109 | }) 110 | } 111 | 112 | 113 | /** 114 | * send registration to server 115 | * 116 | */ 117 | fun sendRegistrationToServer(token: String) { 118 | this.onTokenReceived?.let { it(token) } 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Pushmanager 3 | 4 |  5 |  6 | 7 | `Pushmanager` is a small wrapper for [FCM (Firebase Cloud Messageing)](http://https://firebase.google.com/docs/cloud-messaging/ "FCM (Firebase Cloud Messageing)") and your app needs just a few methods to interact with it. 8 | 9 | ## Installation 10 | 11 | Add google-services as dependency to your project `build.gradle` 12 | ```gradle 13 | buildscript { 14 | ... 15 | dependencies { 16 | ... 17 | classpath "com.google.gms:google-services:4.3.5" 18 | ... 19 | } 20 | } 21 | ``` 22 | 23 | and add `jitpack`to your repositories 24 | ```gradle 25 | allprojects { 26 | repositories { 27 | ... 28 | maven { url "https://jitpack.io" } 29 | } 30 | ... 31 | } 32 | ``` 33 | 34 | Add this dependency to your app _build.gradle_ and apply the plugin at the bottom: 35 | ```gradle 36 | implementation 'com.github.grumpyshoe:android-module-pushmanager:1.3.1' 37 | ``` 38 | ```gradle 39 | ... 40 | apply plugin: 'com.google.gms.google-services' 41 | ``` 42 | 43 | ## Usage 44 | 45 | Get instance of PushManager: 46 | ```kotlin 47 | val pushmanager: PushManager = PushManagerImpl 48 | ``` 49 | 50 | Put your `google-services.json` to the app-root folder. 51 | 52 | 53 | Create a class extending `PushmanagerMessagingService` and implement `handleNotificationPayload`. Here you can decide if a notification should be silent handled or shown to the user according to it's reason. 54 | ```kotlin 55 | class MyService : PushmanagerMessagingService() { 56 | 57 | override fun handleNotificationPayload(context:Context, remoteMessageData: RemoteMessageData): NotificationData? { 58 | 59 | Log.d("PushManager", "handlePayload - ${remoteMessageData.title} - ${remoteMessageData.body}" ) 60 | 61 | // create pending intent (example) 62 | val notificationIntent = Intent(context, SomeActivity::class.java) 63 | notificationIntent.putExtra("info", "Some information for pending intent") 64 | notificationIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP 65 | val contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT) 66 | 67 | // create notification (example) 68 | return NotificationData( 69 | context = context, 70 | title = remoteMessageData.title ?: "Default Title", 71 | message = remoteMessageData.body ?: "Default Message", 72 | channelId = "channel_global_notifications", // needed SDK >= Android O 73 | autoCancel = true, 74 | pendingIntent = contentIntent) 75 | } 76 | } 77 | ``` 78 | Add your implementation to you `Manifest.xml` 79 | ``` 80 |PushmanagerMessagingService is the service implementation fro handling incoming fcm-messages.
16 | * 17 | * @version 1.2.0 18 | * @since 1.0.0 19 | * @author grumpyshoe 20 | * 21 | */ 22 | abstract class PushmanagerMessagingService : FirebaseMessagingService() { 23 | 24 | val pushManager = PushManagerImpl 25 | 26 | /** 27 | * Called when message is received. 28 | * 29 | * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. 30 | */ 31 | // [START receive_message] 32 | override fun onMessageReceived(remoteMessage: RemoteMessage) { 33 | // [START_EXCLUDE] 34 | // There are two types of messages data messages and notification messages. Data messages are handled 35 | // here in onMessageReceived whether the app is in the foreground or background. Data messages are the type 36 | // traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app 37 | // is in the foreground. When the app is in the background an automatically generated notification is displayed. 38 | // When the user taps on the notification they are returned to the app. Messages containing both notification 39 | // and data payloads are treated as notification messages. The Firebase console always sends notification 40 | // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options 41 | // [END_EXCLUDE] 42 | 43 | // Not getting messages here? See why this may be: https://goo.gl/39bRNJ 44 | Log.d(TAG, "Message reveived") 45 | 46 | val remoteMessageData = RemoteMessageData() 47 | 48 | remoteMessageData.messageType = remoteMessage.messageType 49 | remoteMessageData.collapseKey = remoteMessage.collapseKey 50 | remoteMessageData.from = remoteMessage.from 51 | remoteMessageData.messageId = remoteMessage.messageId 52 | remoteMessageData.originalPriority = remoteMessage.originalPriority 53 | remoteMessageData.priority = remoteMessage.priority 54 | remoteMessageData.sentTime = remoteMessage.sentTime 55 | remoteMessageData.to = remoteMessage.to 56 | 57 | // Check if message contains a data payload. 58 | if (remoteMessage.data.isNotEmpty()) { 59 | 60 | remoteMessageData.data = remoteMessage.data 61 | 62 | } 63 | 64 | // Check if message contains a notification payload. 65 | var notification = remoteMessage.notification 66 | if (notification != null) { 67 | 68 | remoteMessageData.tag = notification.tag 69 | remoteMessageData.bodyLocalizationKey = notification.bodyLocalizationKey 70 | remoteMessageData.bodyLocalizationArgs = notification.bodyLocalizationArgs 71 | remoteMessageData.title = notification.title 72 | remoteMessageData.body = notification.body 73 | remoteMessageData.clickAction = notification.clickAction 74 | remoteMessageData.color = notification.color 75 | remoteMessageData.icon = notification.icon 76 | remoteMessageData.link = notification.link 77 | remoteMessageData.sound = notification.sound 78 | remoteMessageData.titleLocalizationKey = notification.titleLocalizationKey 79 | remoteMessageData.titleLocalizationArgs = notification.titleLocalizationArgs 80 | } 81 | 82 | 83 | // show notification to user if handleNotificationPayload returns a value 84 | handleNotificationPayload(applicationContext, remoteMessageData)?.let { 85 | sendNotification(it) 86 | } 87 | 88 | // Also if you intend on generating your own notifications as a result of a received FCM 89 | // message, here is where that should be initiated. See sendNotification method below. 90 | } 91 | // [END receive_message] 92 | 93 | 94 | /** 95 | * custom implementation of payload handling 96 | * 97 | */ 98 | abstract fun handleNotificationPayload(context:Context, remoteMessageData : RemoteMessageData) : NotificationData? 99 | 100 | 101 | // [START on_new_token] 102 | /** 103 | * Called if InstanceID token is updated. This may occur if the security of 104 | * the previous token had been compromised. Note that this is called when the InstanceID token 105 | * is initially generated so this is where you would retrieve the token. 106 | */ 107 | override fun onNewToken(p0: String) { 108 | Log.d(TAG, "Refreshed token: " + p0!!) 109 | 110 | // If you want to send messages to this application instance or 111 | // manage this apps subscriptions on the server side, send the 112 | // Instance ID token to your app server. 113 | sendRegistrationToServer(p0) 114 | } 115 | // [END on_new_token] 116 | 117 | 118 | /** 119 | * Persist token to third-party servers. 120 | * 121 | * Modify this method to associate the user's FCM InstanceID token with any server-side account 122 | * maintained by your application. 123 | * 124 | * @param token The new token. 125 | */ 126 | private fun sendRegistrationToServer(token: String?) { 127 | token?.let { 128 | pushManager.sendRegistrationToServer(token) 129 | } 130 | } 131 | 132 | /** 133 | * Create and show a simple notification containing the received FCM message. 134 | * 135 | * @param messageBody FCM message body received. 136 | */ 137 | private fun sendNotification(notificationData: NotificationData) { 138 | 139 | // generate notification from notification data 140 | val notification = generateNotification(notificationData) 141 | 142 | // show notification 143 | val notificationManager = notificationData.context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 144 | 145 | val notificationId = when { 146 | notificationData.notificationId != null -> notificationData.notificationId 147 | notificationData.notificationType == NotificationType.STACK -> System.currentTimeMillis().toInt() 148 | else -> 0 149 | } 150 | 151 | notificationManager.notify(notificationId, notification) 152 | } 153 | 154 | 155 | /** 156 | * generate notification item 157 | * 158 | */ 159 | private fun generateNotification(notificationData: NotificationData, addPublicVersion: Boolean = true): Notification { 160 | // set required data 161 | val notificationBuilder = NotificationCompat.Builder(notificationData.context, notificationData.channelId) 162 | .setSmallIcon(notificationData.smallIcon) 163 | .setContentTitle(notificationData.title) 164 | .setContentText(notificationData.message) 165 | .setAutoCancel(notificationData.autoCancel) 166 | .setSound(notificationData.soundUri) 167 | 168 | 169 | // set optional data 170 | notificationData.color?.let { notificationBuilder.color = it } 171 | notificationData.colorize?.let { notificationBuilder.setColorized(it) } 172 | notificationData.contentInfo?.let { notificationBuilder.setContentInfo(it) } 173 | notificationData.category?.let { notificationBuilder.setCategory(it) } 174 | notificationData.light?.let { notificationBuilder.setLights(it.argb, it.onMS, it.offMS) } 175 | notificationData.timeoutAfter?.let { notificationBuilder.setTimeoutAfter(it) } 176 | notificationData.subtext?.let { notificationBuilder.setSubText(it) } 177 | if (addPublicVersion) { 178 | notificationData.publicVersion?.let { 179 | notificationBuilder.setPublicVersion(generateNotification(notificationData.publicVersion, false)) 180 | } 181 | } 182 | notificationData.onGoing?.let { notificationBuilder.setOngoing(it) } 183 | notificationData.ticker?.let { notificationBuilder.setTicker(it) } 184 | notificationData.useChronometer?.let { notificationBuilder.setUsesChronometer(it) } 185 | notificationData.pendingIntent?.let { notificationBuilder.setContentIntent(it) } 186 | 187 | return notificationBuilder.build() 188 | } 189 | 190 | 191 | companion object { 192 | 193 | private val TAG = "Pushmanager" 194 | 195 | } 196 | 197 | 198 | } --------------------------------------------------------------------------------