I have an app that has a feature to launch an app, Pandora station, or shortcut. That all works fine. Later I want to stop the app I started. This works for most things except Pandora and Spotify don't always close. Sometimes they do but not always. It seems to be related to the current UI state. For instance, it works fine when I have Pandora showing or the home screen showing. When Home Dock or Car Mode is active it does not work. You can see all my source code here: http://code.google.com/p/a2dpvolume/
service.java is the file that has this functionality.
Here is the part of that code that tries to stop the music from playing and then stop the app.
if (bt2.hasIntent()) {
// if music is playing, pause it
if (am2.isMusicActive()) {
// first pause the music so it removes the notify icon
Intent i = new Intent("com.android.music.musicservicecommand");
i.putExtra("command", "pause");
sendBroadcast(i);
// for more stubborn players, try this too...
Intent downIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
KeyEvent downEvent2 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP);
downIntent2.putExtra(Intent.EXTRA_KEY_EVENT, downEvent2);
sendOrderedBroadcast(downIntent2, null);
}
// if we opened a package for this device, try to close it now
if (bt2.getPname().length() > 3 && bt2.isAppkill()) {
// also open the home screen to make music app revert to
// background
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
// now we can kill the app is asked to
final String kpackage = bt2.getPname();
CountDownTimer killTimer = new CountDownTimer(6000, 3000) {
#Override
public void onFinish() {
try {
stopApp(kpackage);
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, "Error " + e.getMessage());
}
}
#Override
public void onTick(long arg0) {
if (am2.isMusicActive()) {
// for more stubborn players, try this too...
Intent downIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
KeyEvent downEvent2 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP);
downIntent2.putExtra(Intent.EXTRA_KEY_EVENT, downEvent2);
sendOrderedBroadcast(downIntent2, null);
}
try {
stopApp(kpackage);
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, "Error " + e.getMessage());
}
}
};
killTimer.start();
}
}
Here is the function stopApp().
protected void stopApp(String packageName) {
Intent mIntent = getPackageManager().getLaunchIntentForPackage(
packageName);
if (mIntent != null) {
try {
ActivityManager act1 = (ActivityManager) this
.getSystemService(ACTIVITY_SERVICE);
// act1.restartPackage(packageName);
act1.killBackgroundProcesses(packageName);
List<ActivityManager.RunningAppProcessInfo> processes;
processes = act1.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo info : processes) {
for (int i = 0; i < info.pkgList.length; i++) {
if (info.pkgList[i].contains(packageName)) {
android.os.Process.killProcess(info.pid);
}
}
}
} catch (ActivityNotFoundException err) {
err.printStackTrace();
Toast t = Toast.makeText(getApplicationContext(),
R.string.app_not_found, Toast.LENGTH_SHORT);
if (notify)
t.show();
}
}
}
Has someone else run into this problem? How can I reliably stop the launched app? I need to first get it to pause and put it in the background. That is the problem I am having. It works for most situations but not all. Some cases Pandora and Spotify will not respond to the key event being sent and they just keep playing. This keeps the notify icon active and makes the app a foreground activity so I can't stop it.
I finally figured out that Pandora does pause music when it sees a headset disconnect. So, I just had to send that disconnect intent so Pandora would pause. Once paused, it was able to be pushed to background and killed.
//Try telling the system the headset just disconnected to stop other players
Intent j = new Intent("android.intent.action.HEADSET_PLUG");
j.putExtra("state", 0);
sendBroadcast(j);
For anyone else trying this; The android.intent.action.HEADSET_PLUG intent is no longer allowed to be broadcast unless you are running as the system.
As the "HEADSET_PLUG" intent is now only supported if called by a system, I found app specific intents to be the way to go:
Intent pauseSpotify = new Intent("com.spotify.mobile.android.ui.widget.PLAY");
pauseSpotify.setPackage("com.spotify.music");
sendBroadcast(pauseSpotify);
Essentially, what this does, is it calls "PLAY" from the spotify app.
I got the idea from an article and applied it to normal android.
Related
I can open skype activity but I dont know how to open my application after "skype calling" finish.
public static void skype(String number, Context ctx) {
try {
Intent sky = new Intent("android.intent.action.VIEW");
sky.setData(Uri.parse("skype:" + number));
ctx.startActivity(sky);
} catch (ActivityNotFoundException e) {
Log.e("SKYPE CALL", "Skype failed", e);
}
}
Close to skype after calling is enough for me first step. I searched phone broadcastrecevier but It doesnt detect anyting.
I have the following situation: One main application is the launcher application (A), always the application is running, and this application call another a child application (B) fig 1 . The problem occurs when the application (A) launch an intent service and the application(B) use this intent (fig 2).
This is the code used to launch the intent service:
Service
intentCodeRead.putExtra(BARCODE_TEXT, readStr);
intentCodeRead.putExtra(BARCODE_DATE, System.currentTimeMillis());
intentCodeRead.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendBroadcast(intentCodeRead);
Any suggestion of flags to prevent this behaviour?
EDIT 1
In the application (A)( The launcher application) I have an intentService that made a broadcast to another applications. The application (B) is an application that listen to this broadcast. When (A) make a broadcast the application (B) listen then. The problem is that (A) bring to front of (B) when (A) do this broadcast. How can I made a broadcast without change the order of applications presentation?
EDIT 2
application A
BarcodeScannerService.java
intentCodeRead.putExtra(Constants.BARCODE_TEXT, readStr);
intentCodeRead.putExtra(Constants.BARCODE_DATE, System.currentTimeMillis());
intentCodeRead.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
sendBroadcast(intentCodeRead);
initservice.java
Intent intentService = new Intent(context, BarcodeScannerService.class);
context.startService(intentService);
application B
receive.java
BroadcastReceiver actionBarcodeScannerAndPresenceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
/* Receiver for the barcode scan and presence sensor events */
if (intent.getAction().equals(Constants.ACTION_PRESENCE)) {
try
{
Log.e("Sensor Presenca","Detectada");
}
catch (Exception e) {
Toast.makeText(getApplicationContext(), "Ocorreu um erro no sensor de presença", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
} else if (intent.getAction().equals(Constants.ACTION_ON_BARCODE)) {
try {
String scannedText = intent.getStringExtra(Constants.BARCODE_TEXT);
if(scannedText.equals("0123456789"))
{
barcodeResult.setText(scannedText);
barcodeResult.setTextColor(getResources().getColor(R.color.green));
//Thread.sleep(5000);
ReturnResult("BARCODE", 0);
}else{
barcodeResult.setText(scannedText);
barcodeResult.setTextColor(getResources().getColor(R.color.red));
//Thread.sleep(5000);
ReturnResult("BARCODE", 1);
}
}
catch (Exception e){
Toast.makeText(getApplicationContext(), "Ocorreu um erro ao ler o código de barras!", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
This code works well.The only problem is, always when (A) send the broadcast message the application (A) bring to front of (B). What is the flags or methods to prevent this?
My workaround for this problem was use ActivityManager to bring to front the application that the broadcast activity send to background. The following code was used and was adapted from Android, how to bring a Task to the foreground?
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RunningTaskInfo> recentTasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (int i = 0; i < recentTasks.size(); i++)
{
if (!recentTasks.get(i).baseActivity.toShortString().contains("your_class_to_be_avoid")) {
activityManager.moveTaskToFront(recentTasks.get(i).id, ActivityManager.MOVE_TASK_WITH_HOME);
}
}
In this code I'm send to foreground every application that is different to my main application.
Background Info: I need to detect whenever a user presses the play/pause button found on most headsets (KEYCODE_MEDIA_PLAY_PAUSE).
I have it all mostly working using MediaSessions, but when another app starts playing audio, I stop getting callbacks.
It seems like this is because the app that's playing audio created its own MediaSession and Android sends KeyEvents only to the newest MediaSession. To prevent this I create an OnActiveSessionsChangedListener and create a new MediaSession every time it fires.
This does work, but every time I create a new MediaSession, the listener fires again, so I find myself stuck in an inf loop.
My Question: does anyone know how I can do any of the following??:
Prevent other apps from stealing my media button focus
Detect when I've lost media button focus to another app, so I can create a new MediaSession only then, rather then whenever the active
sessions change
Check if I currently already have media button focus so I needlessly create a new MediaSession
What didn't work:
BroadcastReceiver on
AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION didn't work
because apps have to manually trigger that Broadcast, and many apps,
like NPR One do not
AudioManager.OnAudioFocusChangeListener didn't work because it requires I have
audio focus
BroadcastReceiver with max priority on android.intent.action.MEDIA_BUTTON & calling abortBroadcast(), but when other apps were playing audio, my receiver wasn't triggered. Also, other apps can set max priority as well.
My Code:
mMediaSessionManager.addOnActiveSessionsChangedListener(controllers -> {
boolean updateButtonReceiver = false;
// recreate MediaSession if another app handles media buttons
for (MediaController mediaController : controllers) {
if (!TextUtils.equals(getPackageName(), mediaController.getPackageName())) {
if ((mediaController.getFlags() & (MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)) != 0L) {
updateButtonReceiver = true;
}
}
}
if (updateButtonReceiver) {
// using a handler with a delay of about 2 seconds because this listener fires very often.
mAudioFocusHandler.removeCallbacksAndMessages(null);
mAudioFocusHandler.sendEmptyMessageDelayed(0, AUDIO_FOCUS_DELAY_MS);
}
}, ClickAppNotificationListener.getComponentName(this));
Here is the handler that gets triggered:
private final Handler mAudioFocusHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (mShouldBeEnabled) {
updateButtonReceiverEnabled(true);
}
}
};
And finally here is the method that the Handler triggers:
private void updateButtonReceiverEnabled(boolean shouldBeEnabled) {
// clear old session (not sure if this is necessary)
if (mMediaSession != null) {
mMediaSession.setActive(false);
mMediaSession.setFlags(0);
mMediaSession.setCallback(null);
mMediaSession.release();
mMediaSession = null;
}
mMediaSession = new MediaSessionCompat(this, MEDIA_SESSION_TAG);
mMediaSession.setCallback(mMediaButtonCallback);
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setPlaybackToLocal(AudioManager.STREAM_MUSIC);
mMediaSession.setActive(true);
mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
.setState(PlaybackStateCompat.STATE_CONNECTING, 0, 0f)
.build());
if (shouldBeEnabled != mShouldBeEnabled) {
getPackageManager().setComponentEnabledSetting(mMediaButtonComponent,
shouldBeEnabled
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
mShouldBeEnabled = shouldBeEnabled;
}
Thanks!
if you just want to capture MediaButton you can register a BroadcastReceiver to get Media Button action all the time .
MediaButtonIntentReceiver class :
public class MediaButtonIntentReceiver extends BroadcastReceiver {
public MediaButtonIntentReceiver() {
super();
}
#Override
public void onReceive(Context context, Intent intent) {
String intentAction = intent.getAction();
if (!Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
return;
}
KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (event == null) {
return;
}
int action = event.getAction();
if (action == KeyEvent.ACTION_DOWN) {
// do something
Toast.makeText(context, "BUTTON PRESSED!", Toast.LENGTH_SHORT).show();
}
abortBroadcast();
}
}
add this to manifest.xml:
<receiver android:name=".MediaButtonIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
and register your BroadcastReceiver like this ( in main activity)
IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
MediaButtonIntentReceiver r = new MediaButtonIntentReceiver();
filter.setPriority(1000);
registerReceiver(r, filter);
also look at :
How to capture key events from bluetooth headset with android
How do I intercept button presses on the headset in Android?
The controllers you get in OnActiveSessionsChangedListener is ordered by priority. You only have to create a new MediaSession if you see that your MediaSessionis not the first one in the list.
Note that you might still run into an infinite loop if there is another app contending the media key events using the same approach.
I have a service that turn on my app sometimes, the main activity prompt Dialog message to user sometimes,
after the user answer YES\NO I call to finish() to close the app.
my problem is when the message is shown, user answers it and app was call to finish() and when you look in the recent history you played before (in samsung for example you press long on home button) you will see my app along with other apps user started.
when you push it to open it again the Dialog show again..
How to show the activity when launched from recent app without showing the Dialog
public void dialog_1(){
myDialogViewN = new MyDialogViewNegativeTime(MainActivity.this);
// Setting vibrator
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
long[] pattern= {100, 1000};
vibrator.vibrate( pattern,0 );
// Setting 2 Dialog Listeners
myDialogViewN.setOnDialogListener(new DialogListener() {
#Override
public void onNegativeClick()
{
// Stopping Vibraror
if (vibrator.hasVibrator()){
vibrator.cancel();
vibrator = null;
}
initialize_DialogToUser(); /// ??
SendDataToService(3); //doesn't want reminder
myDialogViewN.dismissDialog();
waitForDialogAnswer=false;
finish();
}
#Override
public void onPositiveClick()
{
// Stopping Vibrator
if (vibrator.hasVibrator()){
vibrator.cancel();
vibrator = null;
}
initialize_DialogToUser();
SendDataToService(1); //remind!
myDialogViewN.dismissDialog();
squre.setImageResource(R.drawable.triangle_red2);
waitForDialogAnswer=false;
finish();
} });
myDialogViewN.show();
}
#Override
public void onDestroy()
{
super.onDestroy();
// close/stop running application on background
int id= android.os.Process.myPid();
android.os.Process.killProcess(id);
}
finally i find solution,
the problem was when you open the app from history - the last intent that comes from service to your activity stay there and not goes, in contrary for openning the app by click it's own icon. (different openning ways).
my solution:
send 2 intent's from service to activity,
the first - with what you really need.
the second - after you receive your answer in the service. in the second you will not put any data! it is a mere intent that comes to change the "stuck" intent in history-app open way.
maby it is stupid, but it's the only solution i found :)
I have a receiver that listens for headset MEDIA_PAUSE_PLAY and for AUDIO_BECOMING_NOISY they work fine if only one is called. But some Ford Sync systems will send a play/pause command when turning off the car. So this then has 2 receivers active at the same time and it causes a force close because I am stopping the media player in either situation. I have tried using a boolean but from what I have read the on receive gets killed after each event so the boolean value never gets used. So how can I ignore the audio becoming noisy if the media play pause is received at the same time? Thanks in advance.
Here is my code:
package com.joebutt.mouseworldradio;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.view.KeyEvent;
public class RemoteControlReceiver extends BroadcastReceiver
{
//I created stopCounter to try and keep this from running more than 1 time
int stopCounter = 0;
//I created mediaAction to try and keep both receivers from activating
boolean mediaAction = false;
#Override
public void onReceive(Context context, Intent intent)
{
//boolean mediaAction = false;
//int stopCounter = 0;
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()))
{
mediaAction = true;
//stopCounter = 1;
if (stopCounter < 1)
{
//mediaAction = true; force closes here to
KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE == event.getKeyCode())
{
stopCounter = 1;
//mediaAction only works here if you hit the stop button 1 time, then it will work the next time you shut the car off
mediaAction = true;
//stop and release the media player
if (Play.mp.isPlaying())
{
Play playService = new Play();
playService.stopPlaying();
//stop the play service
Intent stopPlayingService = new Intent(context, Play.class);
context.stopService(stopPlayingService);
//switch back to the main screen
Intent showMain = new Intent(context, MouseWorldRadioActivity.class);
showMain.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(showMain);
}
}
}
}
else if (!mediaAction)
{
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction()))
{
if (Play.mp.isPlaying())
{
//stop and release the mediaplayer
Play playService = new Play();
playService.stopPlaying();
//}
//stop the play service
Intent stopPlayingService = new Intent(context, Play.class);
context.stopService(stopPlayingService);
//switch back to the main screen
Intent showMain = new Intent(context, MouseWorldRadioActivity.class);
showMain.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(showMain);
}
}
}
}
}
Here is my method to stop the playback:
public void stopPlaying()
{
if (mp.isPlaying())
{
//stop playback and release everything
mp.setOnBufferingUpdateListener(null);
mp.setOnErrorListener(null);
mp.stop();
mp.release();
mp = null;
}
It should be okay to have two receivers active at the same time. If the issue is that you are trying to stop the media player when it is already stopped try this in your receiver:
if (mp.isPlaying()) {
mp.stop();
}
That way you only stop the media player if it is playing. If that's not the case, can you post code so we can see exactly what you're trying.
To solve the problem I checked to see if the media player was null for the audio becoming noisy listener. This prevented it from trying to stop a media player that no longer existed. Now it works great with my sync system.