├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── render.experimental.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── robin │ │ └── notification │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── robin │ │ │ └── notification │ │ │ ├── MainActivity.kt │ │ │ └── MyFirebaseMessagingService.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_notify.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── robin │ └── notification │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/render.experimental.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notification 2 | 3 | Notification app demos how to send push notifications between mobile devices without using any external server. 4 | 5 |     6 | 7 | 8 | Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably deliver messages at no cost. 9 | It allows you to send push notification directly from Firebase console or with an app server or some trusted server where the 10 | logic runs. 11 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | apply plugin: 'com.google.gms.google-services' 7 | 8 | android { 9 | compileSdkVersion 28 10 | defaultConfig { 11 | applicationId "com.example.robin.notification" 12 | minSdkVersion 19 13 | targetSdkVersion 28 14 | versionCode 1 15 | versionName "1.0" 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | dataBinding{ 26 | enabled true 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 33 | implementation 'androidx.appcompat:appcompat:1.0.2' 34 | implementation 'androidx.core:core-ktx:1.0.2' 35 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 36 | implementation 'com.google.firebase:firebase-messaging:19.0.1' 37 | testImplementation 'junit:junit:4.12' 38 | androidTestImplementation 'androidx.test:runner:1.2.0' 39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 40 | implementation 'com.android.volley:volley:1.1.1' 41 | } 42 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/robin/notification/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.robin.notification 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.example.robin.notification", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/robin/notification/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.robin.notification 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.util.Log 6 | import android.widget.Toast 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.databinding.DataBindingUtil 9 | import com.android.volley.RequestQueue 10 | import com.android.volley.Response 11 | import com.android.volley.toolbox.JsonObjectRequest 12 | import com.android.volley.toolbox.Volley 13 | import com.example.robin.notification.databinding.ActivityMainBinding 14 | import com.google.firebase.messaging.FirebaseMessaging 15 | import kotlinx.android.synthetic.main.activity_main.* 16 | import org.json.JSONException 17 | import org.json.JSONObject 18 | 19 | class MainActivity : AppCompatActivity() { 20 | 21 | private val FCM_API = "https://fcm.googleapis.com/fcm/send" 22 | private val serverKey = 23 | "key=" + "Enter_your_server_key" 24 | private val contentType = "application/json" 25 | private val requestQueue: RequestQueue by lazy { 26 | Volley.newRequestQueue(this.applicationContext) 27 | } 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | val binding = DataBindingUtil.setContentView(this, R.layout.activity_main) 32 | 33 | FirebaseMessaging.getInstance().subscribeToTopic("/topics/Enter_topic") 34 | 35 | binding.submit.setOnClickListener { 36 | if (!TextUtils.isEmpty(binding.msg.text)) { 37 | val topic = "/topics/Enter_topic" //topic has to match what the receiver subscribed to 38 | 39 | val notification = JSONObject() 40 | val notifcationBody = JSONObject() 41 | 42 | try { 43 | notifcationBody.put("title", "Firebase Notification") 44 | notifcationBody.put("message", binding.msg.text) 45 | notification.put("to", topic) 46 | notification.put("data", notifcationBody) 47 | Log.e("TAG", "try") 48 | } catch (e: JSONException) { 49 | Log.e("TAG", "onCreate: " + e.message) 50 | } 51 | 52 | sendNotification(notification) 53 | } 54 | } 55 | } 56 | 57 | 58 | private fun sendNotification(notification: JSONObject) { 59 | Log.e("TAG", "sendNotification") 60 | val jsonObjectRequest = object : JsonObjectRequest(FCM_API, notification, 61 | Response.Listener { response -> 62 | Log.i("TAG", "onResponse: $response") 63 | msg.setText("") 64 | }, 65 | Response.ErrorListener { 66 | Toast.makeText(this@MainActivity, "Request error", Toast.LENGTH_LONG).show() 67 | Log.i("TAG", "onErrorResponse: Didn't work") 68 | }) { 69 | 70 | override fun getHeaders(): Map { 71 | val params = HashMap() 72 | params["Authorization"] = serverKey 73 | params["Content-Type"] = contentType 74 | return params 75 | } 76 | } 77 | requestQueue.add(jsonObjectRequest) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/robin/notification/MyFirebaseMessagingService.kt: -------------------------------------------------------------------------------- 1 | package com.example.robin.notification 2 | 3 | import android.app.NotificationChannel 4 | import android.app.NotificationManager 5 | import android.app.PendingIntent 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.graphics.BitmapFactory 9 | import android.graphics.Color 10 | import android.media.RingtoneManager 11 | import android.os.Build 12 | import androidx.annotation.RequiresApi 13 | import androidx.core.app.NotificationCompat 14 | import androidx.core.content.ContextCompat 15 | import com.google.firebase.messaging.FirebaseMessagingService 16 | import com.google.firebase.messaging.RemoteMessage 17 | import java.util.* 18 | 19 | 20 | class MyFirebaseMessagingService : FirebaseMessagingService() { 21 | 22 | 23 | private val ADMIN_CHANNEL_ID = "admin_channel" 24 | 25 | override fun onMessageReceived(p0: RemoteMessage) { 26 | super.onMessageReceived(p0) 27 | 28 | val intent = Intent(this, MainActivity::class.java) 29 | val notificationManager = 30 | getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 31 | val notificationID = Random().nextInt(3000) 32 | 33 | /* 34 | Apps targeting SDK 26 or above (Android O) must implement notification channels and add its notifications 35 | to at least one of them. 36 | */ 37 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 38 | setupChannels(notificationManager) 39 | } 40 | 41 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 42 | val pendingIntent = PendingIntent.getActivity( 43 | this, 0, intent, 44 | PendingIntent.FLAG_ONE_SHOT 45 | ) 46 | 47 | val largeIcon = BitmapFactory.decodeResource( 48 | resources, 49 | android.R.drawable.ic_delete 50 | ) 51 | 52 | val notificationSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) 53 | val notificationBuilder = NotificationCompat.Builder(this, ADMIN_CHANNEL_ID) 54 | .setSmallIcon(android.R.drawable.ic_delete) 55 | .setLargeIcon(largeIcon) 56 | .setContentTitle(p0.data["title"]) 57 | .setContentText(p0.data["message"]) 58 | .setAutoCancel(true) 59 | .setSound(notificationSoundUri) 60 | .setContentIntent(pendingIntent) 61 | 62 | //Set notification color to match your app color template 63 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 64 | notificationBuilder.color = 65 | ContextCompat.getColor(applicationContext, android.R.color.background_dark) 66 | } 67 | notificationManager.notify(notificationID, notificationBuilder.build()) 68 | } 69 | 70 | 71 | @RequiresApi(api = Build.VERSION_CODES.O) 72 | private fun setupChannels(notificationManager: NotificationManager?) { 73 | val adminChannelName = "New notification" 74 | val adminChannelDescription = "Device to device notification" 75 | 76 | val adminChannel: NotificationChannel 77 | adminChannel = NotificationChannel( 78 | ADMIN_CHANNEL_ID, 79 | adminChannelName, 80 | NotificationManager.IMPORTANCE_HIGH 81 | ) 82 | adminChannel.description = adminChannelDescription 83 | adminChannel.enableLights(true) 84 | adminChannel.lightColor = Color.RED 85 | adminChannel.enableVibration(true) 86 | notificationManager?.createNotificationChannel(adminChannel) 87 | } 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notify.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 17 |