Make a service close an external activity (created through implicit intent) - android

What I want to do is quite simple: my application offers the user to watch a maximum of 10 min of video, then it stops the video and gets back to my application (previous activity). The video is shown in an external player with that code:
Intent intentVideo = new Intent();
intentVideo.setAction(Intent.ACTION_VIEW);
intentVideo.setData(Uri.parse(url)));
startActivity(intentVideo);
Then a background Service check periodically if time is elapsed or not.
How can my service kill the video activity (where I can't add code or listeners, or whatever, because it is provided by an external app) and make my app go back to its previous activity when time is elapsed?
Thanks

One way to solve this problem is to define a BroadcastReceiver inside the Activity. When the Service needs to notify the Activity that the time is up, send a broadcast and receive it in the BroadcastReceiver. Then, inside the onReceive() call finish() on the Activity to kill it. Hope this helps.

Okay here's my final code if it can help, thanks to Egor.
Note: Two solution are possible to force stopping the player activity:
using startActivityForResult(intent, rq) / finishActivity(rq)
using FLAG_ACTIVITY_CLEAR_TOP
Be careful using finishActivity(), some external apps won't close because of their behavior. For me it worked well when I open videos using VLC player, but not working when I open videos with Dailymotion app.
ActivityThatLaunchesPlayer.java
public class ActivityThatLaunchesPlayer extends Activity
{
private BroadcastReceiver brdreceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
System.out.println("broadcast signal received");
//either
finishActivity(57); //57 is my arbitrary requestcode
//or either :
Intent intentback = new Intent(getApplicationContext(), ActivityThatLaunchesPlayer.class);
intentback.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intentback);
}
};
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//set the brdcstreceiver to listen to the slot
getApplicationContext().registerReceiver(brdreceiver, new IntentFilter("com.example.portail10.timeElapsed"));
//here we launch the player (android opens a new appropriate activity)
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setData(Uri.parse(uri));
startActivityForResult(intent, 57); //again arbitrary rqstcode
//here we start the service that watch the time elapsed watching the video
intentServ = new Intent(this, TimeWatcher.class);
startService(intentServ);
}
}
TimeWatcher.java
public class TimeWatcher extends Service
{
//... some code is missing, but the main idea is here
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
timer.scheduleAtFixedRate(new TimerTask()
{
public void run()
{
//send the broadcast when time's up
Intent intentbrd = new Intent();
intentbrd.setAction("com.example.portail10.timeElapsed");
sendBroadcast(intentbrd);
System.out.println("Brdcast sent");
stopSelf();
}
}, 0, 600000); //in ms = 10min
return START_NOT_STICKY;
}

Related

IntentService - Wakelock release issue

I have an alarm application.
Flow looks like this :
WakefulBroadcastReceiver(Acquires wakelock) --->> Intent service -->> startActivity
public class AlarmService extends IntentService {
protected void onHandleIntent(Intent intent) {
Intent activityIntent = new Intent(this, TriggeredActivity.class);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activityIntent);
Basically WakefulBroadcaseReceiver starts an intent service using startWakefulService(). Inside intent service's onHandleIntent(), only work I am doing is further starting a new activity using startActivity(). That new activity is where I am using mediaPlayer in a loop, which sounds the alarm. That activity has a dismiss button, which waits for user click to stop the media player & activity finishes.
Now the problem I am facing is that after calling startactivity() inside intent service, I can not wait for TriggeredActivity to finish(no equivalent to startActivityForResult in Service) and then complete wakeful intent. Related link
startActivity(activityIntent);
WakefulBCastReceiver.completeWakefulIntent(intent); /* can't do here */
So I am not explicitly releasing wakelock here.
My question is will the wakelock be released automatically(link-to-death), when the process that is holding it is killed.
If yes, then in my particular scenario, I need not call WakefulBCastReceiver.completeWakefulIntent().
Yes, you need to use completeWakefulIntent.
You need to put your TriggeredActivity intent into EXTRAs.
#Override
public void onReceive(Context context, Intent intent) {
Intent intentService = new Intent(context, NotificationsIntentService.class);
// Inserting data inside the Intent
intentService.putExtra(NotificationsIntentService.EXTRA_NOTIF, new Intent(context, TriggeredActivity.class));
startWakefulService(context, intentService);
}
NotificationsIntentService.class
public class NotificationsIntentService extends IntentService {
private static final String TAG = "DebugNotifIntent";
public static final String EXTRA_NOTIF = "extra_notif";
public NotificationsIntentService(){
super(NotificationsIntentService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent: ");
Intent extraIntent = intent.getParcelableExtra(EXTRA_NOTIF);
extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(extraIntent);
NotificationWakefulBroadcastReceiver.completeWakefulIntent(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}
I have managed to find a solution for my problem. I am now using a Messenger for message based cross process communication between intent service & triggered activity.
I am passing a handler - alarmServiceHandler, from intent service to activity through a messenger.
Handler alarmServiceHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
if(msg.arg1 == 1) {
completedTriggeredActivity = true;
}
}
};
Inside onHandleIntent(), I am passing handler through Messenger object in intent's extra data.
Messenger alarmServiceMessenger = new Messenger(alarmServiceHandler);
Intent activityIntent = new Intent(this, TriggeredActivity.class);
activityIntent.putExtra("AlarmServiceMessenger", alarmServiceMessenger);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activityIntent);
while(!completedTriggeredActivity){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
WakefulBCastReceiver.completeWakefulIntent(intent);
In TriggeredActivity, I am retrieving messenger in Dismiss button's OnClickListener, just before calling finish() on the activity. And sending back a message to AlarmService with arg = 1, implying end of processing in triggered activity.
buttonDismiss.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Messenger alarmServiceMessenger = getIntent().getParcelableExtra("AlarmServiceMessenger");
Message alarmServiceMessage = Message.obtain();
alarmServiceMessage.arg1 = 1;
try {
alarmServiceMessenger.send(alarmServiceMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
finish();
}
After starting triggered activity, I am putting AlarmService in sleep mode till boolean variable completedTriggeredActivity has not been set to true in handleMessage(). Once true, it means triggered activity has finished & now I can proceed with releasing wake lock.
I would be glad to receive comments about my approach & any suggestions towards a better solution to my problem, than the one I have deviced.

Service not working?

I am building a game where I want the user to go through many activities in 20 seconds. Once the 20 seconds is over, I want to send the user to the GameOver screen. To run the timer in the background, I used a service. The issue is, the service doesn't seem to be running?
The weird thing is, that even the toast isn't showing. Here is the place where I call the service:
Here is the manifest:
Please let me know as to why the service or the timer aren't running. Thank you so much for all of your help, I really appreciate it! If you need any more code, just let me know and I will show you. Thanks!
:-)
{Rich}
Services can't interact with UI, which is what Toast does. If you want to do that, try using runOnUIThread along with getApplicationContext or the fancy way with binding/callbacks. Also, take a look at AlarmManager, might be a simpler solution instead of running a service.
BroadcastReciever should be a solution to get and to show the toasts. Just send the message from service and catch it in activity. Then use it wherever you want.
//Service class
final static String ACTION = "ACTION";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
intent = new Intent();
intent.setAction(ACTION);
intent.putExtra("StartToast", "Started!");
sendBroadcast(intent);
return START_STICKY;
}
//Activity class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(MainActivity.this, ServiceClass.class);
myReceiver = new MyReceiver();
intentFilter = new IntentFilter();
intentFilter.addAction(ServiceClass.ACTION);
registerReceiver(myReceiver, intentFilter);
}
private class MyReceiver extends BroadcastReceiver {
public String startToast;
#Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
startToast = arg1.getStringExtra("StartedToast");
Toast.makeText(MainActivity.this, startToast, Toast.LENGTH_SHORT).show();
}
Once you register this receiver, you get the message and create a toast automatically when you send data with intent.putExtra(....); .

Android App problems bringing right main activity to front

In my project I am trying to bring my application to front after app is finished (User pressed back button). But it only gives me the last opened app to front. So if I am using a different app in the mean time it will open this app and not my app. Maybe some one can help me - I tried a lot of different combinations with Flags, but it didn't work. Additionally I didn't found a solution to get my app back if the display is locked - Maybe someone has a good solution for that or can give me advice.
Thanks
public class IncomingBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
MyActivity myActivity = (MyActivity) context;
myActivity.doWork();
}
public class MyActivity extends Activity {
private boolean appInBackground = false;
...
#Override
public void onStop() {
appInBackground = true;
super.onStop();
}
#Override
public void finish() {
if (exitPressed) {
super.finish();
} else {
appInBackground = true;
moveTaskToBack(false);
}
}
public void doWork() {
// bring app to foreground
if (appInBackground) {
Intent intent1 = new Intent(getBaseContext(), MyActivity.class);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent1);
}
....
}
}
Android Manifest launchMode: android:launchMode="singleTask"
UPDATE:
I used my app to indicate incoming SIP calls, I had a look at the demo application for WalkieTalkieActivity (https://android.googlesource.com/platform/development/+/master/samples/SipDemo)
// main Activity
#Override
public void onCreate(Bundle savedInstanceState) {
...
callReceiver = new IncomingBroadcastReceiver ();
this.registerReceiver(callReceiver, new IntentFilter(
"com.example.INCOMING"));
...
// me = SipProfile
Intent i = new Intent();
i.setAction("com.example.INCOMING");
PendingIntent pi = PendingIntent.getBroadcast(getBaseContext(), 0,
i, Intent.FILL_IN_DATA);
manager.open(me, pi, null);
}
EXAMPLE:
My app is in foreground and I press back button to take it to back. Then I open another app for example hangouts, than I press the home button to go back to main screen. If the broadcast receiver gets an incoming call "INCOMING" than my app should appear, but it opens hangouts.
My app is in foreground and I press back button
When you press back button, the current activity instance is lost.hence the value of appInBackground set in
#Override
public void onStop() {
appInBackground = true;
super.onStop();
}
is lost too.
so next time when you receive the broadcast and arrive at public void doWork(), appInBackground is null.
Now I have found a solution: I should take the pendingIntent in my doWork() method and send it. Now my app is taking to front, even if it wasn't the last opened app. Thanks for the hints, now I have to work on bringing my app to front if screenlock is enabled. Any ideas?
public void doWork() {
// bring app to foreground
if (appInBackground) {
Intent intent1 = new Intent();
intent1.setClass(getBaseContext(),MyActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getBaseContext(), 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
pendingIntent.send();
}
...
}

android services and activity lifecycle?

I am having problem with my android IntentService. When I first open the application, the service gets started by intent from the profile activity and data is fetched from this service. If I switch to other activity and then back service is still running and that is ok.
However if you press back, so that activity is finished and put in the background, the service is still working as the application is in background but If I get it back to foreground service stops. I do not know why. Bellow is my code, please help.
I have read activity life cycle couple of times and still do not get it why this is happening.
What is weird is that Service receive data one more time before it stops when MainActivity is brought back to running state. Service is not crashing.
Service
public class SomeService extends IntentService
{
public static final String extra = "someData";
public SomeService()
{
super(SomeService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent)
{
Log.e("SomeService", "starting service");
while (true)
{
SomeData data = Api.getNewSocketData();
//Broadcast data when received to update the view
Intent broadcastData = new Intent();
broadcastData.setAction(dataBroadcastReceiver.ACTION_DATA_RECEIVED);
broadcastData.addCategory(Intent.CATEGORY_DEFAULT);
broadcastData.putExtra(extra, " ");
sendBroadcast(broadcastData);
Log.e("SomeService", "received from socket");
}
}
}
Receiver
public class dataBroadcastReceiver extends BroadcastReceiver
{
public final static String ACTION_DATA_RECEIVED = "net.bitstamp.intent.action.ACTION_SOMEDATA_RECEIVED";
#Override
public void onReceive(Context context, Intent intent)
{
Log.e("receiver", "data received");
}
}
Main Activity
#Override
public void onPause()
{
super.onPause();
unregisterReceiver(dataBroadcastReceiver);
}
#Override
public void onResume()
{
super.onResume();
IntentFilter intentFilter = new IntentFilter(dataBroadcastReceiver.ACTION_DATA_RECEIVED);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
dataBroadcastReceiver = new dataBroadcastReceiver();
registerReceiver(dataBroadcastReceiver, intentFilter);
Intent someService = new Intent(this, SomeService.class);
startService(someService);
}
I really need help on this. Thanks
You don't want to the up the IntentService in an infinite loop. It will block all other incoming requests. From the documentation:
All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.
Your Service is likely still happily running along, it just isn't processing your new request because your old one is still being handled in the infinite loop.

Android-Broadcast Receiver

I am new to android. I what to know the difference between Intent and BroadcastReceiver. I am more confused with BroadcastReceiver than Intent.
Please help me out. Simple code will be helpful.
Ok, I will explain it with an example.
Let's suppose I want to create an app to check subway status from it's webpage. I also want a system notification if the subway is not working ok.
I will have:
An Activity to show results.
A Service to check if the subway is working and show a notification if it's not working.
A Broadcast Receiver called Alarm Receiver to call the service every 15 minutes.
Let me show you some code:
/* AlarmReceiver.java */
public class AlarmReceiver extends BroadcastReceiver {
public static final String ACTION_REFRESH_SUBWAY_ALARM =
"com.x.ACTION_REFRESH_SUBWAY_ALARM";
#Override
public void onReceive(Context context, Intent intent) {
Intent startIntent = new Intent(context, StatusService.class);
context.startService(startIntent);
}
}
Explanation:
As you can see you can set an alarm. and when the alarm is received we use an intent to start the service. Basically the intent is a msg which can have actions, an serialized stuff.
public class StatusService extends Service {
#Override
public void onCreate() {
super.onCreate();
mAlarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intentToFire = new Intent(AlarmReceiver.ACTION_REFRESH_ALARM);
mAlarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
}
#Override
public void onStart(Intent intent, int arg1) {
super.onStart(intent, arg1);
Log.d(TAG, "SERVICE STARTED");
setAlarm();
Log.d(TAG, "Performing update!");
new SubwayAsyncTask().execute();
stopSelf();
}
private void setAlarm() {
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
mAlarms.setInexactRepeating(alarmType, SystemClock.elapsedRealtime() + timeToRefresh(),
AlarmManager.INTERVAL_HALF_DAY, mAlarmIntent);
}
}
Explanation:
The service starts and:
Set the alarm for the next call. (Check the intent it's used. Just a msg)
Calls an AsyncTask which takes care of updating an notifying the Activity
It doesn't make sense to paste the AsyncTask but when it finished it calls:
private void sendSubwayUpdates(LinkedList<Subway> subways) {
Intent intent = new Intent(NEW_SUBWAYS_STATUS);
intent.putExtra("subways", subways);
sendBroadcast(intent);
}
This creates a new Intent with a certain NEW_SUBWAYS_STATUS action, put inside the intent the subways and sendBroadcast. If someone is interested in getting that info, it will have a receiver.
I hope I made myself clear.
PS: Some days ago someone explained broadcast and intents in a very cool way.
Someone wants to share his beer, so he sends a broadcast
with an intent having action:"FREE_BEER" and with an extra: "A glass of beer".
The API states:
A BroadcastReceiver is a base class for code that will receive intents sent by sendBroadcast().
An intent is an abstract description of an operation to be performed.
So, a BroadcastReceiver is just an Activity that responds to Intents. You can send your own broadcasts or even the Android Device can send these system wide broadcasts including things like the battery is low, or the device just booted-up.

Categories

Resources