If your Android app relies on app servers for data and functionality, instead of android app contacting app server at a specified regular intervals, server can notify events or data changes to android using Firebase cloud messaging (FCM).
This way of app servers notifying client or android apps has advantages compared to client contacting app server at a regular intervals. It reduces the use of android device resources including computing power and consumption of network bandwidth as communication between server and client happens only when there is a change in data or an occurrence of an event at server.
Firebase Cloud Messaging supports two types of messages, notification message and data message. The difference between them is that with data message you can send your own data elements in the message where as with notification message you have to use predefined elements.
As names suggest, notification message type is used to send notifications which will be displayed as notifications in the notification bar. FCM automatically handles notification messages and your app can also process it and customize it. Data message type is used to send data to client. Your app has to process it and take further action. There is restriction of 4kb on the size of message that can be sent to client.
{"message":{"topic":"deals","notification":{"body":"View latest deals from top brands.","title":"Latest Deals"}}}
{"message":{"topic":"deals","data":{"storeNAME":"Nordstorm","deal":"Get upto 50% off on Shoes","dealDesc":"Get upto 50% off on branded shoes.","expiry":"20180110","code":"NORDSH"}}}
{"message":{"topic":"deals","data":{"storeNAME":"Nordstorm","deal":"Get upto 50% off on Shoes","dealDesc":"Get upto 50% off on branded shoes.","expiry":"20180110","code":"NORDSH"},"notification":{"body":"View latest deals from top brands.","title":"Latest Deals"}}}
Note that in the data message, only name and value pairs are allowed under data element, meaning data element can’t have hierarchical data or JSON array.
App server can send a generic message to multiple clients using Firebase topic. Client apps which listen to the topic will get the message sent by the app server. In this post, the example shows how to send messages to a topic from server app and how client apps can register to a topic and handle the messages.
Using FCM, app server can send messages to a specific client or device using firebase generated registration key. To know how to send user or device specific message, please read sending device specific message from app server to FCM server and handling the message on android.
Notification messages are handled automatically when receiving android app is in background and notification is displayed. On taping the notification, app’s launcher activity is started.
If notification needs to be displayed when receiving app is in foreground, the app needs to provide FirebaseMessagingService service implementing its callback onMessageReceived to process notification messages.
To handle the data messages in foreground and background, app needs to provide FirebaseMessagingService service implementing its callback onMessageReceived. Data can be retrieved from RemoteMessage object passed to onMessageReceived method.
If message contains both data and notification and app is in foreground, onMessageReceived callback is called. If message contains both data and notification and app is in background, notification is displayed in notification bar and data is passed as extras in the intent to launcher activity.
To make your android app as client of firebase cloud messaging server to handle and process messages, you need to follow below steps.
plugin: 'com.google.gms.google-services'
classpath 'com.google.gms:google-services:3.1.0'
implementation 'com.google.firebase:firebase-messaging:11.6.2'
<service
android:name="zoftino.com.firebase.fcm.DealsMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
Firebase cloud messaging server delivers to client app the message sent from the app server. App server can send message to FCM server using various protocols. Below example app I created shows how to send messages to FCM server from app server using HTTP v1 protocol.
Authentication and authorization of the app server to use firebase service can be done using short-lived access token. In order for your app server code to get the access token, it needs private key associated with a service account which is either owner or editor.
To get private key in Firebase console, go to settings, then service accounts and click Generate New Private Key. The Json file contain key gets downloaded. You need to store it securely.
To get access token using the private key in your app server code, you need to use Google API Client library. Below are the maven and gradle dependencies which you need to add to your app server project.
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.23.0</version>
</dependency>
implement 'com.google.api-client:google-api-client:1.23.0'
GoogleCredential can be used to get access token using private key downloaded from firebase console.
private static String SCOPE =
"https://www.googleapis.com/auth/firebase.messaging";
GoogleCredential googleCredential = GoogleCredential
.fromStream(new FileInputStream("firebase-admin-key.json"))
.createScoped(Arrays.asList(SCOPE));
googleCredential.refreshToken();
String token = googleCredential.getAccessToken();
return token;
You can use any http library to send message request to FCM server. Http request header should contain access token.
httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken());
httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
I used gson library to create JSON message as shown below.
JsonElement dealsJson = getDealInJsonFormat();
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("topic", "deals");
jsonObj.add("data", dealsJson);
JsonObject msgObj = new JsonObject();
msgObj.add("message", jsonObj);
httpConn.setRequestMethod("POST");
DataOutputStream wr = new DataOutputStream(httpConn.getOutputStream());
wr.writeBytes(msgObj.toString());
wr.flush();
wr.close();
I’ll show the use of Firebase cloud messaging using an example app. The app displays coupons in recycler view retrieved from SQLite database using ROOM. When latest coupons are available, app server sends notification and data messages to FCM server. The android app receives and processes the messages, stores latest coupons data in SQLite database on the device and displays notifications. Since LiveData is used, as data changes in SQLite database, change will be reflected in UI.
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
public class MessagesClientFCMServer {
private static final Logger log = Logger.getLogger(MessagesClientFCMServer.class
.getName());
private static String SCOPE = "https://www.googleapis.com/auth/firebase.messaging";
private static String FCM_DEALS_ENDPOINT
= "https://fcm.googleapis.com/v1/projects/your-project/messages:send";
public static void main(String args[]) {
MessagesClientFCMServer fcmClient = new MessagesClientFCMServer();
fcmClient.sendNotification();
fcmClient.sendData();
}
private void sendNotification(){
String notificationTitle = "Latest Deals";
String notificationBody = "View latest deals from top brands.";
sendMessageToFcm(getFcmMessageJSONNotification(notificationTitle, notificationBody));
}
private void sendData(){
sendMessageToFcm(getFcmMessageJSONData());
}
private void sendDataNotification(){
String notificationTitle = "Latest Deals";
String notificationBody = "View latest deals from top brands.";
sendMessageToFcm(getFcmMessageJSONDataAndNotification(notificationTitle, notificationBody));
}
//Using HttpURLConnection it send http post request containing data to FCM server
private void sendMessageToFcm(String postData) {
try {
HttpURLConnection httpConn = getConnection();
httpConn.setDoOutput(true);
httpConn.setUseCaches(false);
httpConn.setRequestMethod("POST");
DataOutputStream wr = new DataOutputStream(httpConn.getOutputStream());
wr.writeBytes(postData);
wr.flush();
wr.close();
BufferedReader in = new BufferedReader(
new InputStreamReader(httpConn.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
log.info(response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
private static String getAccessToken() throws IOException {
GoogleCredential googleCredential = GoogleCredential
.fromStream(new FileInputStream("firebase-admin-key.json"))
.createScoped(Arrays.asList(SCOPE));
googleCredential.refreshToken();
String token = googleCredential.getAccessToken();
return token;
}
//create HttpURLConnection setting Authorization token
//and Content-Type header
private HttpURLConnection getConnection() throws Exception {
URL url = new URL(FCM_DEALS_ENDPOINT);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken());
httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
return httpURLConnection;
}
private JsonElement getDealInJsonFormat() {
Deal dealList = prepareLatestDealData();
Gson gson = new Gson();
Type type = new TypeToken<Deal>(){}.getType();
JsonElement jsonElement = gson.toJsonTree(dealList, type);
return jsonElement;
}
private String getFcmMessageJSONData() {
JsonElement dealsJson = getDealInJsonFormat();
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("topic", "deals");
jsonObj.add("data", dealsJson);
JsonObject msgObj = new JsonObject();
msgObj.add("message", jsonObj);
log.info("json message "+msgObj.toString());
return msgObj.toString();
}
private String getFcmMessageJSONNotification(String title, String msg) {
JsonObject notifiDetails = new JsonObject();
notifiDetails.addProperty("body", msg);
notifiDetails.addProperty("title", title);
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("topic", "deals");
jsonObj.add("notification", notifiDetails);
JsonObject msgObj = new JsonObject();
msgObj.add("message", jsonObj);
log.info("json message "+msgObj.toString());
return msgObj.toString();
}
private String getFcmMessageJSONDataAndNotification(String title, String msg) {
JsonElement dealsJson = getDealInJsonFormat();
JsonObject notifiDetails = new JsonObject();
notifiDetails.addProperty("body", msg);
notifiDetails.addProperty("title", title);
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("topic", "deals");
jsonObj.add("data", dealsJson);
jsonObj.add("notification", notifiDetails);
JsonObject msgObj = new JsonObject();
msgObj.add("message", jsonObj);
log.info("json message "+msgObj.toString());
return msgObj.toString();
}
private Deal prepareLatestDealData() {
List<Deal> dealList = new ArrayList<Deal>();
Deal deal = new Deal();
deal.setStoreNAME("Bestbuy");
deal.setDeal("Get upto 10% off on Laptops");
deal.setDealDesc("Get upto 10% off on dell, hp and lenovo laptops.");
deal.setExpiry("20180110");
deal.setCode("NORDSH");
dealList.add(deal);
return deal;
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="zoftino.com.firebase.fcm">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ShoppingDealsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".DealsMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:exported="false"
android:name=".DealsJobService">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
</application>
</manifest>
public class DealsMessagingService extends FirebaseMessagingService {
private static final String TAG = "DealsMessagingService";
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "Received message " + remoteMessage.getFrom());
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data " + remoteMessage.getData());
scheduleJob(remoteMessage.getData());
}
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification "
+ remoteMessage.getNotification().getBody());
sendNotification(remoteMessage.getNotification());
}
}
private void scheduleJob(Map<String, String> data) {
Bundle bundle = new Bundle();
for (Map.Entry<String, String> entry : data.entrySet()) {
bundle.putString(entry.getKey(), entry.getValue());
}
FirebaseJobDispatcher dispatcher =
new FirebaseJobDispatcher(new GooglePlayDriver(this));
Job myJob = dispatcher.newJobBuilder()
.setService(DealsJobService.class)
.setTag("deals-job")
.setExtras(bundle)
.build();
dispatcher.schedule(myJob);
}
private void sendNotification(RemoteMessage.Notification notification) {
Intent intent = new Intent(this, ShoppingDealsActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, "fcm-channel")
.setSmallIcon(R.drawable.zoftino)
.setContentTitle(notification.getTitle())
.setContentText(notification.getBody())
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
Since log running task can’t be run in FirebaseMessagingService, firebase job scheduler is used to process FCM data messages.
public class DealsJobService extends JobService {
private static final String TAG = "DealsJobService";
private final Executor executor = Executors.newFixedThreadPool(2);
private DealsDAO dealsDAO = Repository.getDealsDatabase(this).dealsDAO();
@Override
public boolean onStartJob(JobParameters jobParameters) {
Log.d(TAG, "updating ROOM database with latest deals");
addDealsDataToSQLiteDatabase(jobParameters.getExtras());
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
//add data to sqlite database using room
private void addDealsDataToSQLiteDatabase(Bundle bundle) {
final Deal dealObj = getDealObjectFromBundle(bundle);
executor.execute(new Runnable() {
@Override
public void run() {
long rec = dealsDAO.insertDeal(dealObj);
Log.d(TAG, "added record to db "+rec);
}
});
}
private Deal getDealObjectFromBundle(Bundle bundle){
Deal deal = new Deal();
deal.setStoreNAME(bundle.getString("storeNAME"));
deal.setDeal(bundle.getString("deal"));
deal.setDealDesc(bundle.getString("dealDesc"));
deal.setExpiry(bundle.getString("expiry"));
deal.setCode(bundle.getString("code"));
return deal;
}
}
public class ShoppingDealsActivity extends AppCompatActivity {
private static final String TAG = "ShoppingDealsActivity";
private RecyclerView dealsRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.shopping_deals_layout);
Toolbar tb = findViewById(R.id.toolbar);
setSupportActionBar(tb);
tb.setSubtitle("FCM");
//on first run of the app, subscribe to FCM deals topic
//to get latest deals from server as and when they are available
subscribeToFcmDealsTopic();
dealsRecyclerView = findViewById(R.id.deals_lst);
LinearLayoutManager recyclerLayoutManager =
new LinearLayoutManager(this.getApplicationContext());
dealsRecyclerView.setLayoutManager(recyclerLayoutManager);
DividerItemDecoration dividerItemDecoration =
new DividerItemDecoration(dealsRecyclerView.getContext(),
recyclerLayoutManager.getOrientation());
dealsRecyclerView.addItemDecoration(dividerItemDecoration);
Repository.getDealsDatabase(this).dealsDAO().getDeals()
.observe(this, new Observer<List<Deal>>() {
@Override
public void onChanged(@Nullable List<Deal> dealsList) {
if (dealsList == null) {
return;
}
DealsRecyclerViewAdapter recyclerViewAdapter = new
DealsRecyclerViewAdapter(dealsList, ShoppingDealsActivity.this);
dealsRecyclerView.setAdapter(recyclerViewAdapter);
}
});
}
//subscribes to firebase cloud messaging topic one time
private void subscribeToFcmDealsTopic(){
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (!prefs.getBoolean("firstRun", false)) {
FirebaseMessaging.getInstance().subscribeToTopic("deals");
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("firstRun", true);
editor.commit();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="zoftino.com.firebase.fcm.ShoppingDealsActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/deals_head"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:text="Shopping Deals"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
<android.support.v7.widget.RecyclerView
android:id="@+id/deals_lst"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/deals_head"/>
</android.support.constraint.ConstraintLayout>
public class DealsRecyclerViewAdapter extends
RecyclerView.Adapter<DealsRecyclerViewAdapter.ViewHolder> {
private List<Deal> dealsList;
private Context context;
public DealsRecyclerViewAdapter(List<Deal> list, Context ctx) {
dealsList = list;
context = ctx;
}
@Override
public int getItemCount() {
return dealsList.size();
}
@Override
public DealsRecyclerViewAdapter.ViewHolder
onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.deal_item_layout, parent, false);
DealsRecyclerViewAdapter.ViewHolder viewHolder =
new DealsRecyclerViewAdapter.ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(DealsRecyclerViewAdapter.ViewHolder holder, int position) {
final int itemPos = position;
final Deal deal = dealsList.get(position);
holder.store.setText(deal.getStoreNAME());
holder.deal.setText(deal.getDeal());
holder.desc.setText(deal.getDealDesc());
holder.expiry.setText(deal.getExpiry());
holder.code.setText(deal.getCode());
holder.shop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("Deals Recyclerview", "Shop ....");
Toast.makeText(context,
"Visit "+deal.getStoreNAME(),
Toast.LENGTH_SHORT).show();
}
});
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView store;
public TextView deal;
public TextView desc;
public TextView expiry;
public TextView code;
public Button shop;
public ViewHolder(View view) {
super(view);
store = (TextView) view.findViewById(R.id.store_name);
deal = (TextView) view.findViewById(R.id.deal_name);
desc = (TextView) view.findViewById(R.id.deal_description);
expiry = (TextView) view.findViewById(R.id.deal_expiry);
code = (TextView) view.findViewById(R.id.deal_code);
shop = view.findViewById(R.id.shop_b);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
tools:context="zoftino.com.firebase.fcm.ShoppingDealsActivity">
<TextView
android:id="@+id/store_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/deal_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/store_name" />
<TextView
android:id="@+id/deal_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/deal_name" />
<TextView
android:id="@+id/deal_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/deal_expiry"
app:layout_constraintTop_toBottomOf="@+id/deal_description" />
<TextView
android:id="@+id/deal_expiry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:layout_constraintLeft_toRightOf="@+id/deal_code"
app:layout_constraintRight_toLeftOf="@+id/shop_b"
app:layout_constraintTop_toBottomOf="@+id/deal_description" />
<Button android:id="@+id/shop_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.Button.Colored"
android:minHeight="0dp"
android:minWidth="0dp"
android:text="Shop"
app:layout_constraintLeft_toRightOf="@+id/deal_expiry"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/deal_description"/>
</android.support.constraint.ConstraintLayout>
import android.arch.persistence.room.Database;
import android.arch.persistence.room.RoomDatabase;
@Database(entities = {Deal.class}, version = 1)
public abstract class DealsDatabase extends RoomDatabase {
public abstract DealsDAO dealsDAO();
}
import android.arch.lifecycle.LiveData;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;
import java.util.List;
@Dao
public interface DealsDAO {
@Query("SELECT * FROM deal")
public LiveData<List<Deal>> getDeals();
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertDeal(Deal deal);
@Query("DELETE FROM deal WHERE expiry < :expiryIn")
public void deleteAllCoupons(String expiryIn);
}
public class Repository {
private static DealsDatabase dealsDatabase;
private DealsDAO dealsDAO;
private static final Object LOCK = new Object();
public synchronized static DealsDatabase getDealsDatabase(Context context){
if(dealsDatabase == null) {
synchronized (LOCK) {
if (dealsDatabase == null) {
dealsDatabase = Room.databaseBuilder(context,
DealsDatabase.class, "deals db").build();
}
}
}
return dealsDatabase;
}
public LiveData<List<Deal>> getDeals(Context context) {
if (dealsDAO == null) {
dealsDAO = Repository.getDealsDatabase(context).dealsDAO();
}
return dealsDAO.getDeals();
}
}
@Entity
public class Deal {
@PrimaryKey(autoGenerate = true)
private int id;
private String storeNAME;
private String deal;
private String dealDesc;
private String expiry;
private String code;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStoreNAME() {
return storeNAME;
}
public void setStoreNAME(String storeNAME) {
this.storeNAME = storeNAME;
}
public String getDeal() {
return deal;
}
public void setDeal(String deal) {
this.deal = deal;
}
public String getDealDesc() {
return dealDesc;
}
public void setDealDesc(String dealDesc) {
this.dealDesc = dealDesc;
}
public String getExpiry() {
return expiry;
}
public void setExpiry(String expiry) {
this.expiry = expiry;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
dependencies {
. ..
implementation 'com.google.firebase:firebase-messaging:11.6.2'
implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
implementation 'android.arch.lifecycle:extensions:1.0.0'
implementation 'android.arch.lifecycle:runtime:1.0.3'
implementation 'com.google.code.gson:gson:2.8.0'
implementation 'android.arch.persistence.room:runtime:1.0.0'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
}