I have an app where a user can create/login an account, when the user logs in, I pass their valid email as an intent to the main/landing activity .
I also have another activity for the user profile, from which I pass the intent from the landing activity (the user email).
With the user email I created queries to get all the user projects (it's a PM tool kind of thing) - in my landing activity i have a fragment also where I use these queries based on the user email.
In my user profile activity i also created queries to get the users details (name, email etc) to show in their profile where they can change it etc.
========
The issue is, initially when I log in with valid details and I'm brought to the landing activity, I get the users projects which is great, I can also navigate to the users profile activity and I get the users details which is what I want.
Then when I move back to the landing activity my intent (users emaill) which was passed from the Login activity is no longer valid, so I do not get any results from my DB queries and when I move back to the profile activity the intent is null so i can't get the current user anymore.
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.example.ppmtoolmobile.model.User.getFirstName()' on a null object reference
I wanted some advice on how to handle this to avoid getting NPE when moving back and forth.
I removed the variables for components to make it more readable, but I have initialized them all etc..
Landing Activity / ProjectActivity.java
public class ProjectActivity extends AppCompatActivity implements View.OnClickListener, MyRecyclerAdapter.OnProjectClickListener {
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_project);
// My dao implementation with DB queries
daoHelper = new DaoHelper(this);
// getting current username through intent from LoginActivity.class
authenticatedUser = getIntent().getStringExtra("authenticatedUser");
Toast.makeText(this, "project activity: " + authenticatedUser, Toast.LENGTH_SHORT).show();
// current user id
userId = daoHelper.getCurrentUserId(authenticatedUser);
// Getting users first name and amount of projects (This will be displayed in the heading of the main screen)
userFirstName = daoHelper.getCurrentUserFirstName(authenticatedUser);
projectCount = daoHelper.getProjectCount(userId);
welcomeUserTextView1.setText("Welcome " + userFirstName + ", " + userId);
displayUserProjectCountTextView.setText("You currently have " + projectCount + " projects");
loadFragment(new ProjectFragment());
// Perform item selected listener
bottomNavView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch(item.getItemId())
{
case R.id.nav_profile:
Intent goToProfileActivityIntent = new Intent(ProjectActivity.this, ProfileActivity.class);
goToProfileActivityIntent.putExtra("authenticatedUser", authenticatedUser);
startActivity(goToProfileActivityIntent);
overridePendingTransition(0,0);
return true;
case R.id.nav_home:
return true;
case R.id.nav_settings:
startActivity(new Intent(getApplicationContext(), SettingsActivity.class));
overridePendingTransition(0,0);
return true;
}
return false;
}
});
}
}
ProfileActivity.java
public class ProfileActivity extends AppCompatActivity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
// getting current username through intent from ProjectActivity.class
authenticatedUser = getIntent().getStringExtra("authenticatedUser");
Toast.makeText(this, "profile activity: " + authenticatedUser, Toast.LENGTH_SHORT).show();
daoHelper = new DaoHelper(this);
loadUserDetails();
// Perform item selected listener
bottomNavView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch(item.getItemId())
{
case R.id.nav_home:
startActivity(new Intent(getApplicationContext(), ProjectActivity.class));
overridePendingTransition(0,0);
return true;
case R.id.nav_profile:
return true;
case R.id.nav_settings:
startActivity(new Intent(getApplicationContext(), SettingsActivity.class));
overridePendingTransition(0,0);
return true;
}
return false;
}
});
}
private void loadUserDetails() {
// I get NPE here when moving from ProjectActivity for the second time
User user = daoHelper.getUserDetails(authenticatedUser);
profileFirstNameEditText.setText(user.getFirstName());
profileLastNameEditText.setText(user.getLastName());
profileEmailAddressEditText.setText(user.getEmailAddress());
}
}
DaoHelper.java methods
// get user details
public User getUserDetails(String theEmailAddress) {
SQLiteDatabase db = this.getReadableDatabase();
User user = null;
Cursor cursor = db.query(USER_TABLE,// Selecting Table
new String[]{COLUMN_USER_ID, COLUMN_USER_FIRST_NAME, COLUMN_USER_LAST_NAME, COLUMN_USER_EMAIL_ADDRESS, COLUMN_USER_PASSWORD},//Selecting columns want to query
COLUMN_USER_EMAIL_ADDRESS + " = ?",
new String[]{String.valueOf(theEmailAddress)},//Where clause
null, null, null);
System.out.println("cursor count: " + cursor.getCount());
if(cursor.moveToNext()) {
long userId = cursor.getLong(0);
String firstName = cursor.getString(1);
String lastName = cursor.getString(2);
String emailAddress = cursor.getString(3);
String password = cursor.getString(4);
user = new User(userId, firstName, lastName, emailAddress, password);
}
return user;
}
// get project count of user
public int getProjectCount(long userId) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM " + PROJECT_TABLE + " WHERE " + COLUMN_USER_PROJECT_FK + " = ?", new String[]{String.valueOf(userId)})
return cursor.getCount();
}
// get all of users projects
#RequiresApi(api = Build.VERSION_CODES.O)
public List<Project> getUserProjects(long userId) {
SQLiteDatabase db = this.getReadableDatabase();
List<Project> projectList = new ArrayList<>();
Cursor cursor = db.rawQuery("SELECT * FROM " + PROJECT_TABLE + " WHERE " + COLUMN_USER_PROJECT_FK + " = ?", new String[]{String.valueOf(userId)});
while(cursor.moveToNext()) {
long id = cursor.getLong(0);
String title = cursor.getString(1);
String description = cursor.getString(2);
String dateCreated = cursor.getString(3);
String dateDue = cursor.getString(4);
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime dateCreatedFormatted = LocalDateTime.parse(dateCreated, formatter);
LocalDateTime dateDueFormatted = LocalDateTime.parse(dateDue, formatter);
String priority = cursor.getString(5);
String checklist = cursor.getString(6);
int theUserId = cursor.getInt(7);
Project project = new Project(id, title, description, dateCreatedFormatted, dateDueFormatted, priority, checklist, theUserId);
projectList.add(project);
}
return projectList;
}
The problem here is, your are starting another ProjectActivity instance in your ProfileActivity's onNavigationItemSelected listener of bottomNavView, which has no arguments (startActivity(new Intent(getApplicationContext(), ProjectActivity.class));)
That's why in your second instance of ProjectActivity, it has no value for parameter authenticatedUser and returning empty string.
You can fix this by modifying code of bottomNavView's onNavigationItemSelected listener in your ProfileActivity class.
Replace your switch case logic for id R.id.nav_home like below in ProfileActivity class
case R.id.nav_home:
finish();
overridePendingTransition(0,0);
return true;
Or, if you want to keep multiple instance of same activity (ProjectActivity and ProfileActivity), then you can add parameter to Intent instance in ProfileActivity's bottomNavView's itemSelectedListener.
In that case, your code would become something like below
case R.id.nav_home:
Intent goToProjectActivity = new Intent(ProfileActivity.this, ProjectActivity.class);
goToProjectActivity.putExtra("authenticatedUser", authenticatedUser);
startActivity(goToProjectActivity);
overridePendingTransition(0,0);
return true;
so I'm having some trouble with SQLLite/Android studio, I have an activity that lets me update a user's password through an SQLLite function but it doesn't seem to be working, when I press the update password button, nothing happens, and the database doesn't update.
SQLLITE Update:
public int updateShopper(Shopper shopper)
{
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COL_SPPASS, shopper.getShopperPS());
String[] l = {shopper.getShopperID()+""};
return db.update(TABLE_NAME, values, COL_SPID + " =? ",l);
}
Get all shoppers :
public ArrayList<Shopper> getAllShoppers()
{
ArrayList splist = new ArrayList();
String selectQuery = "SELECT * FROM " + TABLE_NAME;
SQLiteDatabase db = this.getReadableDatabase();
Cursor c = db.rawQuery(selectQuery, null);
if(c.moveToFirst())
{
do {
Shopper shopper = new Shopper();
shopper.setShopperUN(c.getString((c.getColumnIndex(COL_SPNAME))));
shopper.setShopperPS(c.getString(c.getColumnIndex(COL_SPPASS)));
splist.add(shopper);
} while(c.moveToNext());
}
return splist;
}
}
Activity Button :
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String username = ed1.getText().toString();
String password = ed2.getText().toString();
SQLShop sqlsp2 = new SQLShop(getApplicationContext());
ArrayList splist=sqlsp2.getAllShoppers();
for(int i=0;i<splist.size();i++)
{
Shopper sp=(Shopper) splist.get(i);
if(username.equalsIgnoreCase(sp.getShopperUN()))
{
Shopper shopper = new Shopper();
shopper.setShopperUN(username);
shopper.setShopperPS(password);
shopper.setShopperID(shopper.getShopperID());
sqlsp2.updateShopper(shopper);
break;
}
}
Intent i = new Intent(getApplicationContext(), MainActivity.class);
startActivity(i);
}
});
In your getAllShoppers() you're not setting the shopper id and it remains as 0. Trying to update a row with id 0 matches no rows, so no rows are updated.
After adding reading the shopper id from the database, fix this part too:
shopper.setShopperID(shopper.getShopperID());
Here you're assigning the id of a new Shopper back to itself so it stays as 0 default value. Change shopper.getShopperID() to sp.getShopperID().
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.
This question already has answers here:
Android: Make phone call from service
(2 answers)
Closed 6 months ago.
i'm trying to make a phone call, when a specific notification arrive,
i use Notification Service Listener to read incoming notificaion,
public void onNotificationPosted(StatusBarNotification sbn) {
// if(if this is my notificaion..){
String name = sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE));
List<String> numbers = getPhoneNumbers(name);
Log.d(TAG, "i have all this numbers - " + numbers.toString());
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + numbers.get(1)));
startActivity(intent);
}
the "getPhoneNumbers" method is this one
public List<String> getPhoneNumbers(String name) {
List<String> numbers = new ArrayList<String>();
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null,
"DISPLAY_NAME = '" + name + "'", null, null);
if (cursor.moveToFirst()) {
String contactId =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
// Get all phone numbers.
Cursor phones = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
while (phones.moveToNext()) {
String number = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
numbers.add(number);
}
phones.close();
}
cursor.close();
return numbers;
}
all work fine, ( i used break points to cheak everything...)
the "if this is my notification" work perfect, i get the name from the sbn extras, the 'numbers' arraylist include all the contact numbers after the "getPhoneNumbers" method used, but when i start the intent nathing happend..
what is my problem? :/
Let's clarify:
Problem
Your onNotificationPosted method does not launch a phone call when calling startActivity(intent);.
Why
NotificationListenerService is not an activity.
Solution
Make your MainActivity call the startActivity(intent);.
How
Define an attribute activity in NotificationListenerService and define a constructor that accepts an activity:
NotificationListenerService.java
// define attribute activity:
MainActivity activity;
public NotificationListenerService(MainActivity activity) {
this.activity = activity;
}
MainActivity.java
// create a NotificationListenerService sending itself as reference
NotificationListenerService nls = new NotificationListenerService(this);
Then inside the onNotificationPosted you will see the attribute, so you can:
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+ numbers.get(1)));
activity.startActivity(intent);
I am trying to find out whether a incoming call from favourites contacts in Android.
So far, my code is:
public class PhoneCallReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
TelephonyManager telephony = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
if (state == TelephonyManager.CALL_STATE_RINGING) {
if (ContactHelper.fromFavourites(context, incomingNumber)) {
//do stuff
}
}
};
And my ContactHelper is like this:
public static boolean fromFavourites(Context context, String phoneNumber) {
final String[] projection = new String[] {ContactsContract.PhoneLookup._ID};
Uri lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); //use this to look up a phone number
Cursor cursor = context.getContentResolver().query(lookupUri, projection, "starred=?", new String[] { "1" }, null);
if (cursor != null && cursor.getCount() != 0) {
System.out.println("OUTPUT: "+cursor0.getCount());
return true;
} else return false;
}
I have tried this solution but it only gives me all favourites contacts. I am trying to use PhoneLookup because from the Android doc, it says
Columns from the Contacts table are also available through a join.
So I think I can query a join between PhoneLookUp and Contacts table but seems like the Content Providers can't do a join. I intend to write a raw SQLite script for this but I don't know how to join the PhoneLookUp and Contacts table, can't find their foreign key :( Thanks for all the helps
public static boolean fromFavourites(Context context, String phoneNumber) {
final String[] projection = new String[] {ContactsContract.PhoneLookup.STARRED};
Uri lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); //use this to look up a phone number
Cursor cursor = context.getContentResolver().query(lookupUri, projection,
ContactsContract.PhoneLookup.NUMBER + "=?",
new String[] { phoneNumber}, null);
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
if (cursor.getInt(cursor.getColumnIndex(ContactsContract.PhoneLookup.STARRED)) == 1) {
System.out.println("OUTPUT: " + cursor.getInt(0) );
return true;
}
cursor.moveToNext();
}
}
return false;
}
your first link is okay to get all favorite contacts. now to determine whether the incoming number is from favorite you have to detect a incoming call. So use a PhoneStateListener to detect the inocming call. When there is a incoming call detection then simply check for the favorite
To detect an incoming call
public class CustomPhoneStateListener extends PhoneStateListener {
public void onCallStateChange(int state, String number){
switch(state){
case TelephonyManager.CALL_STATE_RINGING:
//call from number. check whether it is favorite or not
break;
}
}
also use following permission
< uses-permission android:name="android.permission.READ_PHONE_STATE" />