When user kill app,my service is running in the background.
It has a button which I can return to the map activity.However,when the user return to the app through the notification button after destroying the app,the map created but the information I am transfering from my service to my map activity is null.
When the user isn't killing the app and return through the notification button,the data exist.
This is my code:
//Map Activity
//OnPause = transfer the data to service intent(Working fine)
BackgroundLocation backgroundLocation = new BackgroundLocation();
mServiceIntent = new Intent(this, backgroundLocation.getClass());
if (!isMyServiceRunning(backgroundLocation.getClass())) {
mServiceIntent.putExtra("AddressBackgound",mAddress);
mServiceIntent.putExtra("AddressLatBackgound",destinationLat);
mServiceIntent.putExtra("AddressLngBackgound",destinationLng);
startService(mServiceIntent);
}
// OnMapReady = Getting the data from service intent(return null for all data)
if (myLocation != null) {
BackgroundLocation backgroundLocation = new BackgroundLocation();
mServiceIntent = new Intent(this, backgroundLocation.getClass());
Bundle extras = getIntent().getExtras();
if (isMyServiceRunning(backgroundLocation.getClass()) && extras != null) {
String mAddress2 = extras.getString("AddressBackgound22");
Double destinationLat2 = extras.getDouble("AddressLatBackgound22");
Double destinationLng2 = extras.getDouble("AddressLngBackgound22");
Log.e("onResume", "onResume stats");
Log.e("Address", "" + mAddress2);
Log.e("Lat", String.valueOf(destinationLat2));
Log.e("Lng", String.valueOf(destinationLng2));
Log.e("OnMapReady","Service is running....");
}
else{
Log.e("OnMapReady","Service is not running");
}
}
Background Location(Service Intent) = Getting information from MapsActivity and returnthe information to MapsActivity aswell.
//Service Intent
// OnStartCommand
Bundle extras = intent.getExtras();
if (extras != null) {
//getting the data to the service is working fine even when the app killed the service still working with the data.
mAddress = extras.getString("AddressBackgound");
destinationLat = extras.getDouble("AddressLatBackgound");
destinationLng = extras.getDouble("AddressLngBackgound");
//This is what I am trying to send to MapsActivity:
extras.putString("AddressBackgound22",mAddress);
extras.putDouble("AddressLatBackgound22",destinationLat);
extras.putDouble("AddressLngBackgound22",destinationLng);
Log.e("onStartCommand", "onStartCommand started");
Log.e("Address","" + mAddress);
Log.e("Lat", "" + destinationLat);
Log.e("Lng", "" + destinationLng);
}
Thank you for your time.
There is a many way,I say some of that:
1-Use store data in service (such as SharedPrefrences,DB and ...) and retrive in activity
2-Use eventbus or broadcast receivers
3-Use callback patern for callback data from service to activiy
So thanks to #Mohammadreza I used sharedPref on the service intent and its worked.
This is what I did:
public static final String MY_PREFS_NAME = "MyData";
//Save SharedPref(BackgroundActivity):
SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
editor.putString("AddressBackgound",mAddress);
editor.putString("AddressLatBackgound", String.valueOf(destinationLat));
editor.putString("AddressLngBackgound", String.valueOf(destinationLng));
editor.apply();
//Read SharedPref(MapsActivity):
SharedPreferences prefs = getSharedPreferences(BackgroundLocation.MY_PREFS_NAME, MODE_PRIVATE);
BackgroundLocation backgroundLocation = new BackgroundLocation();
if (isMyServiceRunning(backgroundLocation.getClass()) && prefs != null ) {
String mAddress = prefs.getString("AddressBackgound","mAddress");
Double UserStartLocationLat = Double.parseDouble(String.valueOf(Double.valueOf(prefs.getString("UserStartLocationLat", "UserStartLocationLatNull"))));
Double UserStartLocationLng = Double.parseDouble(String.valueOf(Double.valueOf(prefs.getString("UserStartLocationLng", "UserStartLocationLngNull"))));
}
isMyServiceRunning:
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("Service status", "Running");
return true;
}
}
Log.i ("Service status", "Not running");
return false;
}
you can use eventbus or broadcast receivers to send data from service to Activity/Fragment
Good day everyone.
I would like to make an application which replies to received SMS automatically.
For example.
Jon Doe sends me - "Hi", Application gets the message body, checks it with my database where I have a potential response:
ID | Text | Potential Answer
01 | Hi | Hello how are you?
and Application sends the Potential response.
So far what I have achieved -
App receives the Message, checks it with the database ( using Like '%') and gets the correct "Potential Answer" Column and passes it as message text body, but to send it I am using a button.
My Reciever is a sperate file class
public class MyReceiver extends BroadcastReceiver {
public static String textSmsbody="";
private static final String TAG=MyReceiver.class.getSimpleName();
public static final String pdu_type="pdus";
#TargetApi(Build.VERSION_CODES.M)
#Override
public void onReceive(Context context, Intent intent) {
// Get the SMS message.
Bundle bundle = intent.getExtras();
SmsMessage[] msgs;
String strMessage = "";
String format = bundle.getString("format");
// Retrieve the SMS message received.
Object[] pdus = (Object[]) bundle.get(pdu_type);
if (pdus != null) {
// Check the Android version.
boolean isVersionM =
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
// Fill the msgs array.
msgs = new SmsMessage[pdus.length];
for (int i = 0; i < msgs.length; i++) {
// Check Android version and use appropriate createFromPdu.
if (isVersionM) {
// If Android version M or newer:
msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
} else {
// If Android version L or older:
msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
// Build the message to show.
String a=msgs[i].getMessageBody();
textSmsbody=msgs[i].getMessageBody();
if (a.contains("?")) {
strMessage=msgs[i].getOriginatingAddress();
// strMessage += " :" + msgs[i].getMessageBody() + "\n";
}
else {
strMessage=a;
// strMessage += "SMS from" + msgs[i].getOriginatingAddress();
// strMessage += "ELSE:" + msgs[i].getMessageBody() + "\n";
}
// Log and display the SMS message.
Log.d(TAG, "onReceive: " + strMessage);
Toast.makeText(context, strMessage, Toast.LENGTH_LONG).show();
}
}
}
}
Sending method is in my MainActivity.
public void smsSendMessage(View view) {
databaseSearch();
// Set the destination phone number to the string in editText.
String destinationAddress = "2020";
// Find the sms_message view.
// Get the text of the SMS message.
String smsMessage = sendingText;
// Set the service center address if needed, otherwise null.
String scAddress = null;
// Set pending intents to broadcast
// when message sent and when delivered, or set to null.
PendingIntent sentIntent = null, deliveryIntent = null;
// Use SmsManager.
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage
(destinationAddress, scAddress, smsMessage,
sentIntent, deliveryIntent);
}
In layout I have a button which calls smsSendMessage () ;
My question is how I can make it automatically without button.
When the phone receives a message, the app shall check it with the database and send it by itself.
Please tell me if you need to see my Manifest file, or databasehelper.
Using JobService should be a suitable option in your case.
Create a JobService class like that
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class ExampleJobService extends JobService {
#Override
public boolean onStartJob(JobParameters params) {
//send a message
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
return true;
}
}
Also Declare In your Manifest
<service
android:name=".ExampleJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
Now in your Receiver, you can start it like that
ComponentName componentName = new ComponentName(context, ExampleJobService.class);
PersistableBundle bundle = new PersistableBundle();
bundle.putLong("lat", lat);
bundle.putLong("lon", lon);
JobInfo jobInfo = new JobInfo.Builder(0, componentName)
.setExtras(bundle)
.build();
For more details about JobServices https://www.vogella.com/tutorials/AndroidTaskScheduling/article.html
I have a RecyclerView for chat app with firebase realtime database and local SQLite database. I am saving message into local database (SQLite) then calling adapter.notifyDataSetChanged().
If the message is already in the database (message unique id) then SQLite will return 0 on database insertOrThrow method. I am checking the availability like this.
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
} else {
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
adapter.notifyDataSetChanged();
}
However, even the else statement is being called, my RecyclerView screen is not updated, but in the background, if I type or receive any message it saves into the local database but doesn't show on screen.
Chat RecyclerView works in following cases
When I clear app from recent app
Stop Fetching data from the local database
First Launch of Chat screen
I am facing this issue when I come directly to chat screen from notification.
That's How I load chat fragment normally.
getFragmentManager().beginTransaction().add(R.id.Navigation_Drawer, chatFragment).commit();
Loading Fragment from Notification using Asynctask
((Navigation_Drawer)context).getFragmentManager().beginTransaction().replace(R.id.Navigation_Drawer, chatFragment).commit();
That's how i launches ChatFragment from notification.
public class FireBase_Messaging_Service extends FirebaseMessagingService {
public static final String TAG="###FireBase MSG###";
public static final int NOTIFICATION=5;
String UserName;
String ID;
String Msg;
Map<String,String> data;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG,"From "+remoteMessage.getFrom());
if (remoteMessage.getData().size()>0){
data = remoteMessage.getData();
Log.d(TAG,"Message Data "+remoteMessage.getData());
data = remoteMessage.getData();
UserName = data.get("name");
ID = data.get("ID");
Msg = data.get("Message");
showNotification(Msg,ID,UserName);
}
if (remoteMessage.getNotification()!=null){
Log.d(TAG,"Message Notification Body "+remoteMessage.getNotification().getBody());
// Toast.makeText(this, "Notification "+remoteMessage.getNotification().getBody(), Toast.LENGTH_LONG).show();
}
}
private void showNotification(String Message,String ID,String UserName) {
Log.d(TAG,"Show Notification "+Message+" "+ID);
Intent intent=new Intent(this, Navigation_Drawer.class);
intent.putExtra("Type","Text");
//intent.putExtra("Type",MsgType);
intent.putExtra("ID",ID);
intent.putExtra("uname",UserName);
intent.putExtra("Message",Msg);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent=PendingIntent.getActivity(this,NOTIFICATION,intent,PendingIntent.FLAG_UPDATE_CURRENT);
int color = getResources().getColor(R.color.black);
String ChannelID = "Message";
notificationChannel(ChannelID,"Chat");
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(),ChannelID)
.setSmallIcon(R.drawable.default_x)
.setColor(color)
.setContentTitle(UserName)
.setContentText(Message)
.setChannelId(ChannelID)
.setTicker("My App")
.setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND | Notification.FLAG_SHOW_LIGHTS)
.setLights(0xff00ff00, 1000, 500) // To change Light Colors
.setStyle(new NotificationCompat.BigTextStyle().bigText(Message))//For Expandable View
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
managerCompat.notify(NOTIFICATION,builder.build());
}
#Override
public void onDeletedMessages() {
super.onDeletedMessages();
}
private void notificationChannel (String ChannelID, String channelName) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(ChannelID,channelName, NotificationManager.IMPORTANCE_DEFAULT);
channel.setLightColor(Color.GREEN);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}
}
}
I also noticed by logging that after adding data to list message.add(chat_wrapper) it first showing an increase in size but when while loop is over, it shows the last size of the ArrayList.
Here's the ChatFragment class.
public class Chat_Screen_Fragment extends Fragment implements View.OnClickListener, ChildEventListener{
public static final String TAG = "###CHAT SCREEN###";
List<Chat_Wrapper> message = new ArrayList<>();
Chat_Adapter adapter;
RecyclerView recyclerView;
LinearLayoutManager layoutManager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.chat_screen_main_fragment,container,false);
setRetainInstance(true);
// GET INTENT VALUES FROM USER PROFILE CLASS
UserName_Intent = getArguments().getString("Get_Name");
UserImage_Intent = getArguments().getString("Get_Image");
UserPhone_Intent = getArguments().getString("Get_Phone");
UserID_Intent = getArguments().getString("Get_ID");
FirebaseToken_Intent = getArguments().getString("Get_Token"); //Firebase Token of other person
Room_Name_Intent = getArguments().getString("Get_Other"); // Room Name of chat
UserLastSeen_Intent=getArguments().getString("LastSeen");
//Sender_FCMToken = Session.getFirebaseID();
// RECYCLER VIEW
recyclerView = v.findViewById(R.id.Chat_Screen_Message_List);
layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setStackFromEnd(true);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(layoutManager);
databaseReference = FirebaseDatabase.getInstance().getReference().child(Room_Name_Intent);
databaseReference.addChildEventListener(this);
adapter = new Chat_Adapter(getActivity(), message);
recyclerView.setAdapter(adapter);
// FETCH OLD MESSAGE FROM DATABASE
chatDatabase();
return v;
}
// FIREBASE REAL TIME DATABASE WHICH FETCH ALL MESSAGES (SYNC) FROM ONLINE DATABASE
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
private synchronized void append_chat_conversation(DataSnapshot dataSnapshot) {
iterator = dataSnapshot.getChildren().iterator();
while (iterator.hasNext()) {
// GETTING DATA FROM FIREBASE DATABASE
Chat_Msg = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FROM = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FROM_ID = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_TO = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_TimeStamp = (String) ((DataSnapshot) iterator.next()).getValue();
Chat_Type= (String) ((DataSnapshot) iterator.next()).getValue();
Random_ID=(String) ((DataSnapshot) iterator.next()).getValue();
Chat_FCM_FROM= (String) ((DataSnapshot) iterator.next()).getValue();
Chat_FCM_TO= (String) ((DataSnapshot) iterator.next()).getValue();
Log.d(TAG, "Chat Items " + Chat_Msg + " " + Random_ID);
Chat_Database tempChatDatabase = new Chat_Database(getActivity());
boolean hasValue = tempChatDatabase.CheckValueExist(Random_ID);
Log.d(TAG,"DATABASE ALREADY HAS VALUE OF TIMESTAMP= "+hasValue);
if (!hasValue) {
Log.d(TAG,"DATABASE DON'T HAVE SAME ENTRY FOR TIME STAMP. ENTERED INTO HAS VALUE");
Log.d(TAG,"Chat Message "+Chat_Msg);
if (Chat_Type.equals("Typed_Message")) {
Log.d(TAG, "VIEW TYPE IS Message " + Chat_Msg);
long id = chat_database.Insert_Chat(Session.getUserID(),Room_Name_Intent, UserID_Intent, "Text", Chat_Msg, Chat_FROM, Chat_TO, Chat_TimeStamp, Chat_FCM_FROM, Chat_FCM_TO, Session.getPhoneNO(), UserPhone_Intent,Random_ID,UserImage_Intent,UserLastSeen_Intent,Chat_FROM_ID);
//Adding Chat Data Into Database
Log.d(TAG,"Database Entry ID "+id);
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
continue;
} else {
Log.d(TAG,"Database Don't Has Value Of This Random Id ");
Log.d(TAG,"Message Size "+message.size());
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
Log.d(TAG,"Message Size "+message.size());
adapter.notifyDataSetChanged();
Log.d(TAG,"Adapter Notified Data Set "+adapter.getItemCount());
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
}
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
chat_database.isDatabaseClose();
}
private void chatDatabase(){
//Database Init and Filling Adapter
Log.d(TAG,"Chat Database Function");
chat_database=new Chat_Database(getActivity());
chatCursor=chat_database.getUserChat(UserID_Intent);
boolean checkDB_Exist=functions.DatabaseExist(getActivity(),"CHAT_DATABASE.DB");
boolean chatItemsCounts=chatCursor.getCount()>0;
chatCursor.moveToFirst();
Log.d(TAG,"Value At Chat Database "+ checkDB_Exist+" "+chatItemsCounts);
if (checkDB_Exist && chatCursor.getCount()>0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)){
Log.d(TAG,"Database Exist Chat Database");
message.clear();
chatCursor.moveToFirst();
do {
database_rowID=chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID=chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName =chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType=chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message=chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp=chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom=chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto=chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath=chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom=chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo=chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG,"Value Of Database Message String = "+database_Message);
Log.d(TAG,"Row ID of Database "+database_rowID);
// Check Message Type
Log.d(TAG,"Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom,null,null,database_rowID);
message.add(text);
}
while(chatCursor.moveToNext());
Room_Name_Intent = database_RoomName;
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
adapter.notifyDataSetChanged();
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size()-1);
}
});
Log.d(TAG,"Value Of Database Close or Not "+value);
}
}
}
I think you need to implement the LoaderCallbacks here and to listen to the changes done in the database to update the RecyclerView accordingly. I have created a repository in Github here which shows the database read, write and update and making the changes in the database to have the proper effect in corresponding RecyclerView with registering a content observer to the database.
The whole idea is to have an observer attached to your database table which will notify you of any change in the table. When a change is detected the database read query will be generated automatically and on reloading the data again from your table will trigger the RecyclerView update inside onLoadFinished function.
I have a demo implementation in my Github project, which shows a list of users to be shown in a RecyclerView and updating the RecyclerView automatically without calling notifyDataSetChanged. Please check the adapter which controls the data to be shown in the RecyclerView again. This is the simplest implementation that you might have in your case avoiding any complexity.
Hope that helps.
Update
After going through the code, I have come up with some fixes that you should do. It is pretty difficult to understand why it is not working as you expected it to be because the code given is not very well formatted and complete. However, I can suggest you some key fixes here which might help.
In the append_chat_conversation function, you do not have to call notifyDataSetChanged each time you find a new chat message and add that to the message. I would like to suggest that, do not add the chat messages in your message list. You might get rid of the following if-else block. Just updating the chat database and inserting new chat messages into it is fine. So I would suggest you remove the following section.
if (id == 0) {
Log.d(TAG,"Database Already Has Value Of This Random Id ");
adapter.notifyDataSetChanged();
continue;
} else {
Log.d(TAG,"Database Don't Has Value Of This Random Id ");
Log.d(TAG,"Message Size "+message.size());
Chat_Wrapper chat_wrapper = new Chat_Wrapper(Chat_Msg, null, null, null, null, null, null, Chat_TimeStamp, UserPhone_Intent, UserImage_Intent, Chat_FROM, null,null,id);
message.add(chat_wrapper);
Log.d(TAG,"Message Size "+message.size());
adapter.notifyDataSetChanged();
Log.d(TAG,"Adapter Notified Data Set "+adapter.getItemCount());
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(adapter.getItemCount());
}
});
}
Call chatDatabase function before this line to load messages from the database.
// FETCH MESSAGE FROM DATABASE
chatDatabase();
Log.d(TAG, "MESSAGE ARRAY SIZE " + message.size());
chat_database.isDatabaseClose();
Now fix your chatDatabase function. You are creating a new LinearLayoutManager and assigning it to your RecyclerView each time you are calling the chatDatabase function. I would suggest removing it as well.
Modify your chatDatabase function like the following.
private void chatDatabase() {
//Database Init and Filling Adapter
Log.d(TAG, "Chat Database Function");
chat_database = new Chat_Database(getActivity());
chatCursor = chat_database.getUserChat(UserID_Intent);
boolean checkDB_Exist = functions.DatabaseExist(getActivity(), "CHAT_DATABASE.DB");
boolean chatItemsCounts = chatCursor.getCount() > 0;
chatCursor.moveToFirst();
Log.d(TAG, "Value At Chat Database " + checkDB_Exist + " " + chatItemsCounts);
if (checkDB_Exist && chatCursor.getCount() > 0 && chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID")).equals(UserID_Intent)) {
Log.d(TAG, "Database Exist Chat Database");
message.clear();
chatCursor.moveToFirst();
do {
database_rowID = chatCursor.getInt(chatCursor.getColumnIndex("ID"));
database_userID = chatCursor.getString(chatCursor.getColumnIndex("USER_ID"));
database_RoomName = chatCursor.getString(chatCursor.getColumnIndex("ROOM_NAME"));
database_ReceiverID = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_USER_ID"));
database_MessageType = chatCursor.getString(chatCursor.getColumnIndex("MESSAGE_TYPE"));
database_Message = chatCursor.getString(chatCursor.getColumnIndex("USER_MESSAGE"));
database_MsgFrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_NAME"));
database_MsgTo = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_NAME"));
database_TimeStamp = chatCursor.getString(chatCursor.getColumnIndex("TIME_STAMP"));
database_FCMfrom = chatCursor.getString(chatCursor.getColumnIndex("SENDER_TOKEN"));
database_FCMto = chatCursor.getString(chatCursor.getColumnIndex("RECEIVER_TOKEN"));
database_LocalPath = chatCursor.getString(chatCursor.getColumnIndex("DOWNLOADED_AT"));
database_PhoneFrom = chatCursor.getString(chatCursor.getColumnIndex("MY_PHONE"));
database_PhoneTo = chatCursor.getString(chatCursor.getColumnIndex("OTHER_PHONE"));
Log.d(TAG, "Value Of Database Message String = " + database_Message);
Log.d(TAG, "Row ID of Database " + database_rowID);
// Check Message Type
Log.d(TAG, "Message Type Is Text");
Chat_Wrapper text = new Chat_Wrapper(database_Message, null, null, null, null, null, null, database_TimeStamp, database_PhoneTo, UserImage_Intent, database_MsgFrom, null, null, database_rowID);
message.add(text);
} while (chatCursor.moveToNext());
Room_Name_Intent = database_RoomName;
adapter.setChatMessages(message); // Add a public function in your adapter to set the chat messages
adapter.notifyDataSetChanged();
chatCursor.close();
boolean value = chat_database.isDatabaseClose();
recyclerView.post(new Runnable() {
#Override
public void run() {
Log.d(TAG, "Moving to Bottom");
recyclerView.smoothScrollToPosition(message.size() - 1);
}
});
Log.d(TAG, "Value Of Database Close or Not " + value);
}
}
The setChatMessages function in your adapter will look something like this.
public void setChatMessages(ArrayList<Message> messageList) {
this.message = messageList;
}
I solved this issue by digging bit into android lifecycle. As i mentioned above i am facing this issue only when i enter ChatFragment from notification. I was only activity state issue. Code was working. What i was doing i always recreate activity on notification tap ( In Firebase Code ).
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
After digging lots of code and changing multiple flags in Intent. Only android:launchMode="singleTask" solves my issue. Which only create activity if it's not exist. Now its not calling activity onDestroy method when tapping on notification. It's start activity from onStart which avoid activity to recreate.
I add this flag in AndroidManifest's NavigationDrawer activity tag.
<activity
android:name=".Navigation_Drawer"
android:launchMode="singleTask"
android:theme="#style/AppTheme"/>
These kind of issue is really headache for developers but i don't know that with this single line of code i can solve this issue. Thanks everyone for help.
I have problem with starting a new activity in android. I have looked through many other questions here, but I didn't find an answer. Here's the problem:
Four classes:
1. WelcomeActivity;
2. MainActivity;
3. PopUpActivity;
4. Client;
At the begining starts WelcomeActivity where you you type all needed credentials to connect to the server, after you clicked the button, string is sent to server. Server send validation string if everything is OK. If OK is received, then MainActivity is started. Users types different things in MainActivity, the presses another button, which send another string to the server. Server processes it (string) and send back a response, also a string. And here's the problem. When server send that last string to client I want to start PopUpActivity, where will be displayed this aprticualr string in TextView.
My code:
Client part (last else if):
public void run() throws Exception {
Socket client = new Socket(ip, port);
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true);
out.println(welcomeActivity.getCredentials());
while (true) {
final String line = in.readLine();
if (line.equals("#GO#")) {
System.out.println("#GO#");
mainActivityIntent = new Intent(welcomeActivity,
MainActivity.class);
welcomeActivity.startActivity(mainActivityIntent);
} else if (line.equals("#CLOSE#")) {
client.close();
break;
} else if (line.startsWith("#RESULTS")) {
Intent i = new Intent(MainActivity.getContext(), PopUpActivity.class);
i.putExtra(line, line);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.getContext().startActivity(i);
}
}
}
WelcomeActivity:
public void onClick(View v) {
ip = ipText.getText().toString();
port = Integer.parseInt(portText.getText().toString());
login = loginText.getText().toString();
password = passwordText.getText().toString();
credentials = login + "#" + password + "#" + brand + "#" + device + "#"
+ hardware + "#" + manufacturer + "#" + product;
client = new Client(ip, port, this);
new Handler().start();
}
private class Handler extends Thread {
public void run() {
try {
client.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
PopUpActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pop_up);
closeButton = (Button) findViewById(R.id.closeButton);
testOutcome = (TextView) findViewById(R.id.textArea);
closeButton.setOnClickListener(this);
//
Bundle extras = getIntent().getExtras();
if (extras == null) {
return;
}
String value = extras.getString(Intent.EXTRA_TEXT);
if (value != null) {
testOutcome.setText(value);
}
}
PopUpActivity is started, but text is not displayed.
Before that I tried to use Context in MainActivity:
final static Context context;
....
public void onCreate() {
context = getBaseContext();
// or context = getApplicationContext();
....
}
...
public static Context getContext() {
return context;
}
And from clint tried to call:
MainActivity.getContext().getTextView().setText(line);
At the begining I tied to call a AlertDialog, but it also was bad, NullPointerException
The problem is with your intent calling, so you may write
i.putExtra("line",line); in your MainActivity
and you can retrieve it by
Intent intent = getIntent(); intent.getStringExtra("line"); in your PopUp Activity.
In my Android app, I use the Mobile Backend Starter from Google. I'd like to get a notification when the CloudEntities on the server get updated, and this notification should contain some data from the updated Entity. It works when the app is running in the background, but when I close the app (by swiping it away in the multitasking view), I can't make such a notification because I haven't got access to the CloudBackendAsync in the GCMIntentService.
I already saw this question:
Mobile Backend handle continuous queries in background
But it doesn't have a solution for the problem of accessing the cloud data in the GCMIntentService.
EDIT: My current code in GCMIntentService.java
protected void onHandleIntent(Intent intent) {
//... (Check if the GCM Message is about an update of the Mobile Backend)
// dispatch message
if (GCM_TYPEID_QUERY.equals(typeId)) {
// Here, a broadcast is sent to the Main Activity of the app, which then downloads
// the new content and shows a notification in the CloudCallbackHandler. That
// only works when the Activity is running.
// So I would like to get access to the CloudBackendAsync instance from
// the app here to download data in the background and show a notification.
Intent messageIntent = new Intent(BROADCAST_ON_MESSAGE);
messageIntent.putExtras(intent);
messageIntent.putExtra("token", tokens[2]);
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
}
//...
}
The Android client does not receive the message content through the push notification event from the backend (only the subId token is sent from the demo backend which is enough to notify the client that some new message has been received for the given topic and refresh it).
So as I understand, it is not possible to directly get the entity data within the client GCMIntentService.onHandleIntent() method unless we change the backend code. I have made the following changes in the backend class ProspectiveSearchServlet so that it includes as well the message content within the push notification:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// Return if push notification is not enabled
if (!backendConfigManager.isPushEnabled()) {
log.info("ProspectiveSearchServlet: couldn't send push notification because it is disabled.");
return;
}
// dispatch GCM messages to each subscribers
String[] subIds = req.getParameterValues("id");
// Each subId has this format "<regId>:query:<clientSubId>"
for (String subId : subIds) {
String regId = SubscriptionUtility.extractRegId(subId);
if (isSubscriptionActive(regId)) {
Entity matchedEntity = ProspectiveSearchServiceFactory.getProspectiveSearchService().getDocument(req);
if(matchedEntity != null) {
log.info(String.format("ProspectiveSearchServlet: matchedEntity.toString: " + matchedEntity.toString()));
} else {
log.info(String.format("ProspectiveSearchServlet: matchedEntity is null."));
}
//Add the matchedEntity object.
sendPushNotification(regId, subId, matchedEntity);
} else {
SubscriptionUtility.clearSubscriptionAndDeviceEntity(Arrays.asList(regId));
}
}
}
private void sendPushNotification(String regId, String subId, Entity matchedEntity) throws IOException {
SubscriptionUtility.MobileType type = SubscriptionUtility.getMobileType(subId);
if (SubscriptionUtility.MobileType.ANDROID == type) {
sendGcmAlert(subId, regId, matchedEntity);
} else if (SubscriptionUtility.MobileType.IOS == type) {
sendIosAlert(subId, new String[] {regId}, matchedEntity);
}
}
private void sendGcmAlert(String subId, String regId, Entity matchedEntity)
throws IOException {
String gcmKey = backendConfigManager.getGcmKey();
boolean isGcmKeySet = !(gcmKey == null || gcmKey.trim().length() == 0);
// Only attempt to send GCM if GcmKey is available
if (isGcmKeySet) {
Sender sender = new Sender(gcmKey);
if(matchedEntity != null) {
Message message = new Message.Builder().addData(SubscriptionUtility.GCM_KEY_SUBID, subId)
//extra data.<key> elements can be added here
.addData("data.message", (String) matchedEntity.getProperty("message"))
.addData("data.updatedBy", (String) matchedEntity.getProperty("_updatedBy"))
.addData("data.owner", (String) matchedEntity.getProperty("_owner"))
.addData("data.kindName", (String) matchedEntity.getProperty("_kindName"))
.build();
Result r = sender.send(message, regId, GCM_SEND_RETRIES);
if (r.getMessageId() != null) {
log.info("ProspectiveSearchServlet: GCM sent: subId: " + subId);
} else {
log.warning("ProspectiveSearchServlet: GCM error for subId: " + subId +
", senderId: " + gcmKey + ", error: " + r.getErrorCodeName());
ArrayList<String> deviceIds = new ArrayList<String>();
deviceIds.add(regId);
SubscriptionUtility.clearSubscriptionAndDeviceEntity(deviceIds);
}
}
} else {
// Otherwise, just write a log entry
log.info(String.format("ProspectiveSearchServlet: GCM is not sent: GcmKey: %s ",
isGcmKeySet));
}
}
Now on the client side you can make the following changes in the GCMIntentService to display a proper push notification (with the message body and the user name):
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) { // has effect of unparcelling Bundle
/*
* Filter messages based on message type. Since it is likely that GCM will be
* extended in the future with new message types, just ignore any message types you're
* not interested in, or that you don't recognize.
*/
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
Log.i(Consts.TAG, "onHandleIntent: message error");
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) {
Log.i(Consts.TAG, "onHandleIntent: message deleted");
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
String subId = intent.getStringExtra(GCM_KEY_SUBID);
Log.i(Consts.TAG, "onHandleIntent: subId: " + subId);
String[] tokens = subId.split(":");
String typeId = tokens[1];
// dispatch message
if (GCM_TYPEID_QUERY.equals(typeId)) {
Intent messageIntent = new Intent(BROADCAST_ON_MESSAGE);
messageIntent.putExtras(intent);
messageIntent.putExtra("token", tokens[2]);
boolean isReceived = LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
//Check if the broadcast has been handled, if not show the notification.
if (!isReceived) {
Log.i(Consts.TAG, "A message has been recieved but no broadcast was handled.");
generateNotification(this, intent, tokens[2]);
} else {
Log.i(Consts.TAG, "A message has been recieved, broadcasted and handled.");
}
}
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GCMBroadcastReceiver.completeWakefulIntent(intent);
}
public static void generateNotification(Context context, Intent intent, String message) {
//Event keys
HashMap data = new HashMap();
for (String key : intent.getExtras().keySet()) {
Log.d(Consts.TAG, "Message key: " + key + " value: " + intent.getExtras().getString(key));
String eventKey = key.startsWith("data.") ? key.substring(5) : key;
data.put(eventKey, intent.getExtras().getString(key));
}
CharSequence contentTitle = (CharSequence) data.get("updatedBy");
if (contentTitle == null) contentTitle = "New Message";
CharSequence contentText = (CharSequence) data.get("message");
if (contentText == null) contentText = "";
CharSequence userId = (CharSequence) data.get("updatedBy");
Bitmap iconBitmap = getUserIcon(context, userId.toString());
if (iconBitmap == null) iconBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
// Creates an Intent for the Activity
Intent resultIntent = new Intent(context, GuestbookActivity.class);
// The stack builder object will contain an artificial back stack for the started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(IntroductionActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder mBuilder = new Notification.Builder(context);
mBuilder.setContentIntent(resultPendingIntent);
Notification notification = mBuilder
.setContentTitle(contentTitle)
.setContentText(contentText)
.setSmallIcon(R.drawable.notification_icon)
.setLargeIcon(iconBitmap)
.setTicker(contentTitle + ": " + contentText)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.build();
///Get the notification ID, /it allows to update the notification later on.
int notifyID = 1;
String contentID = (String) data.get("id");
if(contentID != null) {
notifyID = Integer.parseInt(contentID);
}
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(notifyID, notification);
}