Ok, so I have a main activity called 'Main.java'. This main activity starts an AlarmManager which fires an intent leading to 'AlarmReceiver.java'.
This 'AlarmReceiver.java' then creates a notification which has two buttons on it. One of the buttons is a deletion button, and so when the user clicks on that button, another intent is fired, leading it to 'DelPair.java'.
In DelPair.java, I modify a table in a Database, but then I need the UI of Main.java to reflect this change. I have created two functions in Main.java called updateArrayFromDB() and updateUIFromArray() to do this for me:
updateArrayFromDB() will sync an ArrayList created in Main.java to a
certain table in the DB.
updateUIFromArray() will change the UI of
Main.java to represent the ArrayList that has just been changed.
The problem is that I cannot call these two functions from DelPair.java (they don't exist in that space). I have come across Serializables in trying to find an answer but I don't know enough to know if they apply here or exactly how to implement them across the AlarmManager and the NotificationManager.
How can I access these methods from DelPair.java?
In Main.java:
public void updateArrayFromDB(){
//... The code for this is long and irrelevant
}
public void updateUIFromArray(){
//... The code for this is long and irrelevant
}
private void SendNotification() {
Intent intent = new Intent(this, AlarmReceiver.class);
//...
PendingIntent sender = PendingIntent.getBroadcast(this, 2 , intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, 5000, notif_freq, sender);
}
In AlarmReceiver.java:
Intent delPairI = new Intent(context, DelPair.class);
PendingIntent delPairPI = PendingIntent.getService(context, 0, delPairI, PendingIntent.FLAG_UPDATE_CURRENT);
Notification noti;
noti = new Notification.Builder(context)
//...
.addAction(R.drawable.ic_delete_icon, "Delete the thing", delPairPI)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, noti);
and then in DelPair.java:
public class DelPair extends IntentService {
//...
#Override
protected void onHandleIntent(final Intent intent) {
//...
Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
getApplicationContext().sendBroadcast(it);
handler.post(new Runnable() {
#Override
public void run() {
//... here is where I update the database, which works perfectly
//now need to update the UI and array in Main.java
updateArrayFromDB(); //these lines
updateUIFromArray(); //obviously don't work
}
});
}
}
Why not use broadcasts ? in onHandleIntent just send a broadcast
Intent i = new Intent();
i.setAction(CUSTOM_INTENT);
//put relevant data in intent
getApplicationContext().sendBroadcast(i);
The broadcast receiver:
public class IncomingReceiver extends BroadcastReceiver {
private MainActivity act;
public IncomingReceiver(MainActivity main){
this.act = act;
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(CUSTOM_INTENT)) {
System.out.println("GOT THE INTENT");
// call the method on act
}
}
}
In your activity onResume - register new IncomingReceiver, onPause unregister
private IncomingReceiver receiver;
public void onCreate(Bundle bOs){
//other codes
receiver = new IncomingReceiver(this);
}
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction(CUSTOM_INTENT);
registerReceiver(receiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(receiver);
super.onPause();
}
Since you need to have an updated UI based on database changes, you can call updateArrayFromDB() and updateUIFromArray() in the onResume() method of your activity so the UI gets updated each time the user enters the activity.
Related
I'm developing a countdown app, and currently trying to show a notification when you exit the app while the countdown is running. Correspondingly, I want the notification to disappear when the user returns to the app.
So far I've managed to make it work for a simple notification with static text, do the following: in MainActivity.java, in onStop(), I create an intent and initiate the service with startService(intent). Symmetrically, in onStart() I run stopService(intent) so that when you return to the app the service gets canceled. This works like a charm, the notification appears and disappears when it must.
The next step has been trying to make the notification show a text that varies (it will say "X minutes remaining"). According to the info out there, to update an existing notification you have to create a new one, give it the same ID as the existing one, and call .notify of a NotificationManager. When I do this the notification indeed gets updated correctly (the text changes as expected), BUT: now, returning to the main activity does not cancel the notification. The icon stays up there and doesn't get interrupted.
I've been trying to solve this for hours and hours. I've also tried hacks like sending signals via shared preferences to tell the service to stop, but for some reason, it seems to completely ignore the command stopself() too.
Does anybody have a suggestion of what could be the cause? Any help would be greatly appreciated. Here is the relevant code:
MainActivity.java:
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(getBaseContext(), CounterService.class);
stopService(serviceIntent);
}
protected void onStop() {
super.onStop();
Intent serviceIntent = new Intent(getBaseContext(), CounterService.class);
startService(serviceIntent);
}
CounterService.java:
public class CounterService extends Service {
Notification notification;
NotificationManager notificator;
Intent intentNoti;
CountDownTimer counter;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
intentNoti = new Intent(this, MainActivity.class);
final PendingIntent pending = PendingIntent.getActivity(this, 0, intentNoti, 0);
final Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.common_full_open_on_phone);
//Countdown
counter = new CountDownTimer (30000, 1000) {
public void onTick(long millisUntilFinished) {
String time = String.valueOf(System.currentTimeMillis());
notification = new Notification.Builder(CounterService.this)
.setContentTitle("Name")
.setContentText(time)
.setSmallIcon(R.drawable.icon_start)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pending)
.setOngoing(true).build();
notificator = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificator.notify(1001, notification);
}
public void onFinish() {
}
}.start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
counter.cancel();
}
}
First create a Timer like this
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
timer = new Timer();
timerTask = new TimerTask() {
public void run() {
// Add your code
}
};
timer.schedule(timerTask, 1000, 1000); //
}
Also you need to stop your timer.
So
public void stoptimertask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
Call StartTimer and StopTimer in OnStartCommand() and onDestroy() respectively. Add these lines in onDestroy()
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartservice");
broadcastIntent.setClass(this, Restarter.class);
this.sendBroadcast(broadcastIntent);
it can be handled in multiple ways, you have not stopped your timer
Note:- posting code in Kotlin
1)
override fun onDestroy() {
counter.cancel()
}
in your activity
override fun onResume() {
super.onResume()
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancelAll()
}
I have created a Notification channel In the onCreate method of MapsActivity and called the method startService to start the service.
App stay froze until service execution ends
In logcat Errors caused by is not mentioned.
I have also mentioned using FOREGROUND_SERVICE in ManifestFile
Main Class
public class MapsActivity extends AppCompatActivity {
public static final String CHANNEL_ID = "FenceCalculateChannel";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
createNotificationChannel();
Intent serviceIntent = new Intent(this,BackGroundDistanceCalculate.class);
startService(serviceIntent);
}
public void createNotificationChannel() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(CHANNEL_ID,
"Fence Service Channel",
NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
BackGroundDistanceCalculate Service Class
public class BackGroundDistanceCalculate extends Service {
final public static String TAG = "BackGroundDistance";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MapsActivity.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
int i;
for(i=0;i<1000;i++)
{
Log.d(TAG,"Value of i is : "+ i);
SystemClock.sleep(1000);
}
Notification notification =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Calculating Distance")
.setContentText("Distance Calculation is running")
.setSmallIcon(R.drawable.ic_favorite)
.setContentIntent(pendingIntent)
.build();
//startForeground(1, notification);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
This code should create a Notificationchannel on App start but not working.
What changes may I make to make this App run?
Service runs works on main thread (UI thread) according to official doc, so it blocked UI, that's why your app freezed.
You should use IntentService which already has a worker thread to run your work.
Or you can manually create an AsyncTask or a Thread inside your service to handle your work.
I can see two things that you can fix to make it work.
You are making the BackGroundDistanceCalculate go to sleep for 1000 seconds which makes the Main thread to hang on you as instances of Service class runs on the Main thread. You can either start up a new thread or extend from IntentService (might not work in Pie+ if not using WorkManager)
You can try starting the service using ContextCompat.startForegroundService() to start your service and from within your service you will have to post the notification before you start doing your stuff.
I want to create an app that is constantly checking for location change and put the current location in the firebase (e.g. an app for runners).
Unfortunately the foregroundservice is being stopped or paused every time the device go into sleep mode.
For starters I wanted to create a foreground service that is continuously writing information to the base (that would be a time stamp or a simple string) every second.
After some time it just stops writing to firebase without calling stopself().
The service is working fine on the emulator (even if put to sleep), but stops when tested on a real device – in my case Huawei, Android 8.1.0.
What should I do to force service to run in every state of the device?
My MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "start");
startService(intent);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startForegroundService(intent);
}
else {
Intent intent = new Intent(this, MyService.class);
intent.putExtra("action", "stop");
startService(intent);
}
}
}
MyService:
public class MyService extends Service {
int i =0;
private String CHANNEL_ID = "2345";
#Override
public void onCreate() {
super.onCreate();
}
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(1000, createNotification());
String action = intent.getExtras().getString("action");
switch (action){
case "start":
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
myfunction();
handler.postDelayed(this, 1000);
}
};
break;
case "stop":
stopfunction();
break;
}
handler.postDelayed(runnable, 1000);
return START_NOT_STICKY;
}
private void stopfunction() {
stopSelf();
}
private void myfunction() {
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("locations");
myRef.child("location").setValue(i);
i++;
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
#RequiresApi(Build.VERSION_CODES.O)
private void createChannel(){
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, getString(R.string.infoTxt),
NotificationManager.IMPORTANCE_HIGH);
channel.setShowBadge(false);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(channel);
}
private Notification createNotification(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createChannel();
}
Intent notificationItent = new Intent(this, MainActivity.class);
notificationItent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(this, 0, notificationItent, 0);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setColor(ContextCompat.getColor(this, android.R.color.background_dark))
.setContentIntent(intent)
.setSmallIcon(R.drawable.ic_launcher_background)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOnlyAlertOnce(true)
.setContentTitle("GPS Location")
.build();
}
}
I've tried everything: service, foreground service, broadcast receiver, jobSheduler, WorkerManager – nothing helped. Then I found it’s a new HUAWEI feature called “power-intensive app monitor “. It kills every app that runs in the background for a long time unless user gives special permissions to it.
The path to do this:
Settings -> Security & privacy -> Location services -> recent location requests: YOUR APP NAME -> Battery -> uncheck Power-intensive prompt, App launch: Manage manually: check all three positions: Auto-launch, secondary launch, run in background.
I don’t know is there a way to do this programmatically. I think the best way is to create a sort of help activity and explain the user what to do if application won’t work.
Foreground services generally should be used for task which require user attention such as visual processes.
use Background service instead
This is my first question and I've been trying to find a solution to this for hours but can't get it to work. I'm building an android app that takes an input from the user (number of hours) to fast (not eat). The input is then taken to the service where it does a countdown in the background. Along the way, I'd like the user to access other activities that could you the results from the countdown timer (eg, time_left/total_time = percentage complete). So far, my button that I've created works to make the call for the service. but the service never gets called to update the text view. Thanks
Here is what I have,
public class StartFast extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_fast);
startService(new Intent(this, MyService.class));
Log.i("Started service", "hello started service...");
registerReceiver(br, new IntentFilter("COUNTDOWN_UPDATED"));
}
private BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
intent.getExtras();
long millisUntilFinished = intent.getLongExtra("countdown",0);
String time = Long.toString((millisUntilFinished));
TextView tv = findViewById(R.id.timeView1);
tv.setText(time);
}
};
public void BeginFast(View view){
//Intent intent = new Intent( this, StartFast.class);
// below is how to pass an intent for use in a Service to run in the backgroun
Intent intent =new Intent(this, MyService.class);
startService(intent);
// intent.putExtra() // putExtra longs ...will do after static run succeeds
//intent.putExtra("data", data); //adding the data
Intent intent1 = new Intent(this, Heart.class);
startActivity(intent1);
}
}
and here is the service class,
public class MyService extends Service {
private final static String TAG = "MyService";
public static final String COUNTDOWN_BR = "FastBreak.countdown_br";
Intent bi = new Intent(COUNTDOWN_BR);
CountDownTimer cdt = null;
public void OnCreate(){
super.onCreate();
Log.i(TAG, "starting timer...");
cdt = new CountDownTimer(30000,1000) {
#Override
public void onTick(long millisUntilFinished){
Log.i(TAG, "Countdown seconds remaining: " +millisUntilFinished /1000);
bi.putExtra("countdown", millisUntilFinished);
sendBroadcast(bi);
}
#Override
public void onFinish(){
Log.i(TAG, "Timer finished");
}
};
cdt.start();
}
#Override
public void onDestroy() {
cdt.cancel();
Log.i(TAG, "Timer cancelled");
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
https://github.com/greenrobot/EventBus
Check this out. This library is the best and easiest implementation of broadcasts. You can send any data to any object (StartFast service in your case) from any other object (StartFast activity in your case) and write code to run.
First, you need to start the service and register it in the manifest. After the service is started by an activity, it will keep running in the background.
You can send intents with a service and anybody who has registered a broadcast receiver listening to that intent can hear it.
Let's say FirstActivity started the service and registers receiver listening to intent with the tag BOBBY. The service is the one sending an intent BOBBY to anyone who is interested and has registered for it.
You want to move on to SecondActivity. Before you do that, onPause of FirstActivity you need to unregister that broadcastreceiver.
SecondActivity is interested in the intent with tag BOBBY, so he creates his own broadcast receiver and registers for it.
I hope you can see where this is going.A broadcastreceiver can listen to all sorts of intents that you make up.
Have fun.
I am loading an HTTP request in the background using loopj HTTP CLIENT and when it is done, I want to display a "success" notification (dialog, toast, etc.)
I have the code in a seperate (non-activity) class with a static method that executes the background request. In the end, the response is in a AsyncHttpResponseHandler under a onSuccess method. In this method, I can print out the response, confirm that the request went through, save data to the sd card/ Shared Preferences, but how do I access the UI thread to display a notification?
Thanks in advance.
you can do it using a Handler, or by calling Activity.runOnUiThread(). so you either pass a Handler, or an Activity object to your static method, then in your onSuccess() method, do,
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// i'm on the UI thread!
}
}
);
or,
handler.post(new Runnable() {
#Override
public void run() {
// i'm on the UI thread!
}
}
);
I guess you mean a service as a background process. Service has many built in methods like onCreate, onStartCommand, onDestroy, etc. I suggest using a Notification, because notifications do not require a UI thread to do the job.
Create a method to generate a notification and call it after your HTML read is over.
private static void generateNotification(Context context, String message) {
int icon = R.drawable.ic_stat_gcm;
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context, MainActivity.class);
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
}
You could fire a local broadcast with the message, and show a toast with a receiver.
Do this in the class doing the updates:
Intent intent = new Intent("ACTION_TOAST");
intent.putExtra("message", "Success!");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Then in any activity that might want to know about the update, do this:
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if ("ACTION_TOAST".equals(intent.getAction()) {
Toast.makeText(MyActivity.this, intent.getStringExtra("message"),
Toast.LENGTH_SHORT).show();
}
}
}
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(
receiver, new IntentFilter("ACTION_TOAST"));
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
You still need to pass a context into your static method, but this works even if that context is a Service or some other context that can't show Toasts / create UI.