I am creating an app that "Catches" all the notifications during a specified time period and then displays them all at one time. However, I am running into an issue with my NotificationListenerService Java class.
I currently am able to "Catch" the nofifications as they come through and stop them from displaying. I am also able to preserve the notification information in ArrayLists (as you can see in the onNotificationPosted method). However, when I try to use one of the ArrayList getters to pull the information into another class, the ArrayList is completly empty. Any thoughts as to why this is and why I can't pull this informaiton in another Java class?
NotificationListenerService Class
public class NotificationListenerServiceUsage extends NotificationListenerService {
private static final String TAG = "NotificationListenerSer";
ArrayList<Integer> idMap = new ArrayList<>();
ArrayList<Notification> notificationMap = new ArrayList<>();
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return super.onBind(intent);
}
#Override
public void onNotificationPosted(StatusBarNotification sbn){
Integer notificationInt = sbn.getId();
Notification notificationContent = sbn.getNotification();
idMap.add(notificationInt);
notificationMap.add(notificationContent);
cancelAllNotifications();
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn){
}
public ArrayList<Integer> getIdMap() {
return idMap;
}
public ArrayList<Notification> getNotificationMap() {
return notificationMap;
}
}
Implementation Class
public class Batch_Notifications extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
public void getHeldNotifications(View view){
NotificationListenerServiceUsage noteListenerService = new NotificationListenerServiceUsage();
ArrayList<Integer> idMap = noteListenerService.getIdMap();
ArrayList<Notification> notificationMap = noteListenerService.getNotificationMap();
Log.d(TAG, "getHeldNotifications: " + idMap + notificationMap);
}
}
You can not persist data in runtime memory. Your NotificationListenerService will not always be running it will destroy and then again Instantiated and now all your properties will reinitialized.
The best way to this type of task is preserve data in a persistent Storage i.e Database. And when you try to send batch notification you get from the database. You can Use Sqlite database with Android-Room for easy implantation.
Have a look at https://developer.android.com/training/data-storage.
Related
public void PrintRecordToResultTA() {
int i = 0;
Log.d("data","\nCodec: " + avlRecordCollection.getCodecID());
Log.d("data","\nRecord Count: " + avlRecordCollection.getRecordCount());
I have used log to see if my program woks, but now I need to display this data on UI thread in MainActivity, this method was used to display data in java program, I was thinking should I recreate this class as Activity to reach data from another Activity to Main?
EDIT:
I have created ArrayList of AVL Records
public List<AVLRecord> avlRecords = new ArrayList<>();
public AVLRecordCollection CreateCollection() { // private
return new AVLRecordCollection(codec, recordC, avlRecords);
}
And method to Create Records, which get all data I need to display... And I use avlRecord.add(AVLRecord) to pass all data.
public void CreateRecord() {
AVLRecord AVLRecord;
RecordHeader recordHeader = GetRecord_Data();
RecordGPS_Element recordGPS_element = GetRecord_GPS();
RecordIO_Element recordIOElement = GetRecord_IO();
AVLRecord = new AVLRecord(recordHeader, recordGPS_element, recordIOElement);
avlRecords.add(AVLRecord);
}
Can someone give me an example how can I display data in MainActivity
If you are calling this method from MainActivity then you can use return to send back data to Activity class or can call a method to display with your data arrayList in Activity class.
To use return change your method return type from void to arraList of your data type.
public ArrayList<DataType> PrintRecordToResultTA() {
ArrayList<DataType> avlRecordCollectionArray = new ArrayList<DataType>;
// add data into avlRecordCollectionArray arraylist
return avlRecordCollectionArray;
}
And in Activity class change method calling,
ArrayList<DataType> avlRecordCollectionArray = ClassName.PrintRecordToResultTA();
Then you will have arraylist of data in Activity class. Display data.
I am struggling in choosing the right way to pass data from broadcastReceiver to ViewModel and from there I pass data to my Repository and update LiveData. I use FCM push notifications and have local broadCastReceiver which uses ActivityLifecycle.
I found that it is bad practice to access ViewModel from BroadcastReceiver, but not sure why?
If I manage lifecycle of broadcastReceiver it should not cause any problems... So what is the best way to pass received data from FCM to my Repository's MediatorLiveData? I use MediatorLiveData, because I add different LiveData sources(API request and FCM).
Would be grateful for advice and correct way of implementing broadCastReceiver.
I have thought about accessing Repository from BroadCastReceiver, like this:
RepositoryMain.getSingletonInstance().setResponse(state);
You need to define single source of truth (SSoT). In your case it Repository (if Repository encapsulate db persistence storage, SSoT it is db). Now you need to implement data flow from receiver to view through SSoT (Repository) like in example below:
Receiver implementation
public class FcmReceiver extends BroadcastReceiver {
private final MutableLiveData<MyData> mData = new MutableLiveData<>();
public LiveData<MyData> getData() {
return mData;
}
#Override
public void onReceive(Context context, Intent intent) {
// entry point of data
mData.setValue(new MyData());
}
}
Repository
public class Repository {
private static final Repository INSTANCE = new Repository();
private final MediatorLiveData<MyData> mData = new MediatorLiveData<>();
private Repository() {}
public static Repository instance() {
return INSTANCE;
}
public LiveData<MyData> getData() {
return mData;
}
public void addDataSource(LiveData<MyData> data) {
mData.addSource(data, mData::setValue);
}
public void removeDataSource(LiveData<MyData> data) {
mData.removeSource(data);
}
}
View model
public class MyViewModel extends ViewModel {
public LiveData<MyData> getData() {
// for simplicity return data directly to view
return Repository.instance().getData();
}
}
Activity
There is binding of receiver data and Repository
public class MainActivity extends AppCompatActivity {
private FcmReceiver mReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReceiver = new FcmReceiver();
}
#Override
protected void onStart() {
super.onStart();
// add data source
Repository.instance().addDataSource(mReceiver.getData());
registerReceiver(mReceiver, IntentFilter.create("action", "type"));
}
#Override
protected void onStop() {
super.onStop();
// don't forget to remove receiver data source
Repository.instance().removeDataSource(mReceiver.getData());
unregisterReceiver(mReceiver);
}
}
I think you don't need to access the BroadCastReceiver within the viewmodel. Alternatively, use the BroadCastReceiver to move data between activities, if you want to do any logic to the data, send it to the viewModel related to that activity.
In Simple words:
Suppose we have the following components:
ActivityOne observes ViewModelOne
ActivityTwo observes ViewModelTwo
BroadCastReceiver [Send actions from ActivityOne]. ActivityTwo Listens to those actions
once ActivityOne receives data from the viewModelOne, it sends the data via the BroadCastReceiver.
ActivityTwo has registered for the BroadCastReceiver, thus it receives those actions, if it dose need to do any logic to the data, it can send it to the ViewModelTwo.
I am working on an Android project in which I am trying to integrate PUSH service offered by Cometd framework.
Now, whenever a new message arrives for a Conversation, I would like to inform ChatMessagesActivity which contains the list of messages between the two users.
Now, when the other user sends a message to the Android app, I would like to update the view of the user. I tried doing that by calling notifyDataSetHasChanged() on the adapter, but because I was calling it outside of View thread, I am getting an error.
The method is static, because new messages are received in Conversation class, while the messages are going-on in ChatMessagesActivity class. For communication between both classes, I have created 2 static methods which act like a bi-directional bridge for sending & receiving messages.
I hope I was clear, if there are any doubts, kindly let me know.
ChatMessagesActivity.java :
public class ChatMessagesActivity extends ApplicationDrawerLoader {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat_messages);
chatList = (ListView)findViewById(R.id.chatList);
new getPrivateChatsForUser(this).execute();
}
// IN the method below, I receive information from another activity.
public static void recieveUpdatedMessage(String channelName, Map<String, Object> input){
Boolean found = Arrays.asList(channelName.split(" ")).contains("chat");
if(found){
int processedChannelName = Integer.valueOf(channelName.replace("/chat/",""));
if(processedChannelName == groupAccountId){
// Here i tried calling adapter.NotifyDataSetchanged();.. Didn't fly.
}
}
}
// Async method retrieving messages.
public class getPrivateChatsForUser extends AsyncTask<Void, Void, ResponseEntity<RestReplies[]>> {
ChatMessagesActivity chatMessagesActivity = null;
getPrivateChatsForUser(ChatMessagesActivity chatMessagesActivity) {
this.chatMessagesActivity = chatMessagesActivity;
}
#Override
protected ResponseEntity<RestReplies[]> doInBackground(Void... params) {
// network connection related stuff, not relevant to problem
}
#Override
protected void onPostExecute(ResponseEntity<RestReplies[]> responseEntity) {
super.onPostExecute(responseEntity);
RestReplies[] restRepliesArray = responseEntity.getBody();
Collections.addAll(restRepliesList, restRepliesArray);
ArrayList<HashMap<String, String>> chatMessagesHashMapList = new ArrayList<HashMap<String, String>>();
for (RestReplies restReplies : restRepliesList) {
HashMap<String, String> chatMap = new HashMap<>();
chatMap.put(chatText, restReplies.getReplyText());
chatMap.put(firstName, restReplies.getReplyingPersonName());
chatMap.put(chatImage, restReplies.getSenderImage());
chatMap.put(privateChannel,"/service/person/"+String.valueOf(conversationId));
chatMessagesHashMapList.add(chatMap);
}
chatList = (ListView) findViewById(R.id.chatList);
chatMessagesAdapter = new ChatMessagesAdapter(chatMessagesActivity, chatMessagesHashMapList);
chatList.setAdapter(chatMessagesAdapter);
chatList.scrollTo(0, chatList.getHeight());
}
}
So, how should I instruct that the data-set has been changed.. And how does the adapter knows where and how to get the data-set which has changed. Can anyone help me with this problem. Thanks a lot... :-)
Use broadcast receiver at your adapter and fire a local broadcast with android LocalBroadcast in your push service
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equals("MYREFRESH"))
{
notifiyDataSetChanged();
}
}
};
In your constructor in adapter register this reciever
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("MYREFRESH");
LocalBroadcastManager.getInstance(context).registerReceiver(broadReceiver, intentFilter);
In your push if you get a push notification trigger this
Intent intent = new Intent();
intent.putExtra(...) //send any data to your adapter
Intent.setAction("myaction");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
The way to deal with this is using a broadcast or bus pattern. You can use some good bus libraries such as http://square.github.io/otto/ or a LocalBroadcast
This past answer from me shows how to use the LocalBroadcast system: Refreshing fragments in FragmentActivity after sync service runs
So I've debugged my program and have found that the part of my program is updating, whilst another isn't.
I have a method:
public void storeApplication(String name, String item){
Application app = new Application(name, item);
peopleAttending.add(app);
}
The debugger reports that an object is contained in the LinkedList (peopleAttending).
In another method:
public void populateListView() {
int noOfPeopleAttending = peopleAttending.size();
String noPeopleAttending = String.valueOf(noOfPeopleAttending);
Toast.makeText(GuestsAttending.this, noPeopleAttending, Toast.LENGTH_SHORT).show();
}
This method can be called after the previous one and states that there isn't an object within the LinkedList.
I've checked the object references just to make sure that they are pointing at the same reference and they are.
Any help would be greatly appreciated.
EDIT: Entire Class:
public class GuestsAttending extends Activity {
private LinkedList<Application> peopleAttending = new LinkedList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_guests_attending);
populateListView();
}
public void storeApplication(String name, String item){
Application app = new Application(name, item);
peopleAttending.add(app);
}
public void populateListView() {
// GuestsAdapter adapter = new GuestsAdapter(this, peopleAttending);
// ListView listView = (ListView) findViewById(R.id.listView);
// listView.setAdapter(adapter);
peopleAttending.size();
int noOfPeopleAttending = peopleAttending.size();
String noPeopleAttending = String.valueOf(noOfPeopleAttending);
Toast.makeText(GuestsAttending.this, noPeopleAttending, Toast.LENGTH_SHORT).show();
}
Second Edit:
Java Booking Screen Method:
public void saveBookingInfo(View view) {
GuestsAttending sendApplication = new GuestsAttending();
EditText applicantNameText = (EditText) findViewById(R.id.applicantNameTextField);
EditText itemToBurnText = (EditText) findViewById(R.id.itemToBurnTextField);
String appName = applicantNameText.getText().toString();
String appItemToBurn = itemToBurnText.getText().toString();
if (appItemToBurn.isEmpty() || appName.isEmpty()) {
Toast.makeText(BookingScreen.this, "Please fill in all fields.", Toast.LENGTH_SHORT).show();
}
else {
sendApplication.storeApplication(appName, appItemToBurn);
}
}
GuestsAttending Java Class: -- See Above.
Useful hint: It's really popular to set type of List as a List<> interface from java.util package instead of LinkedList<> itself.
Anyway, i am pretty sure that storeApplication method is not automatically triggered before onCreate method ran by Activity framework. Maybe your debugger is stopoing on it in different order (because of using threads or smth), but you should to log some invoke. Try to find it out.
I've found out what the problem is:
When I submit the booking information, it runs all the necessary methods. However, when the "storeApplication()" method has finished executing, the ArrayList 'empties' all the objects out.
I only noticed this when I used breakpoint and tried running the method twice, on the second time I entered booking details, the ArrayList stated it was empty.
I'm going to see if I can try and store the ArrayList in a more secure place.
I am trying to initialize a class that calls another class that uses AsyncTask. I am using GetDataFromDB gDataFromDB = new GetDataFromDB() but that does not initialize the class, it just gives me access to any static methods in the class. So what do I do to get the onCreate method to run? I have tried using intent but keep getting an error because this is a static class
public class FacadeDataFromDB extends Activity {
static ArrayList<HashMap<String, String>> visitorsList;
private static FacadeDataFromDB dataFromDB;
static boolean accessDB = false;
private FacadeDataFromDB() {
}
public static void initInstance() {
}
public static FacadeDataFromDB getInstance() {
if (dataFromDB == null) {
// Create the instance
dataFromDB = new FacadeDataFromDB();
}
return dataFromDB;
}
public static void setData() {
if (!accessDB) {
GetDataFromDB gDataFromDB = new GetDataFromDB();
accessDB = true;
}
// visitorsList = gDataFromDB.returnInfoFromDB();
}
public static ArrayList<HashMap<String, String>> getVisitorForDay() {
// TODO Auto-generated method stub
setData();
return visitorsList;
}
}
GetDataFromDB is the other class that I am calling. The current class is a static class and uses a singleton because I only want one initialization of the class the gets data from the db. If you have more questions or want me to post code let me know. Thanks
It seems to me that your two classes FacadeDataFromDB GetDataFromDB should not inherit Activity
Activities are made for GUI and user-interaction (I don't see any in your example) and their life-cycle is managed by the framework : you never create them manually with new.
See the android tutorial : https://developer.android.com/guide/components/activities.html and Activity javadoc : https://developer.android.com/reference/android/app/Activity.html.
I'm not sure that you completely understand the Android runtime. You should start Activities using Intent objects, not by creating them with the new keyword as you are. To ensure that your onCreate() method is called within your Activity, you could launch an explicit Intent from some other Activity/Context: Intent intent = new Intent(currentContext, FacadeDataFromDB.class);.
Also, when it comes to Activities, you shouldn't use private constructors. See this post for reasons why.