I'm trying to extend the functionality of the Wired Mic Remote Button through a Service and Receiver that sits on top of the Google Play Music App and communicates with it through broadcasts. I'm on ICS with a Google Nexus S.
The issue I have is my code works great once the MusicPlaybackService is running, which is not always the case. How do I start this service without showing any UI (without the user knowing). My code has the following definitions:
public static final String SERVICECMD = "com.android.music.musicservicecommand";
public static final String CMDNAME = "command";
public static final String CMDTOGGLEPAUSE = "togglepause";
public static final String TOGGLEPAUSE_ACTION = "com.android.music.musicservicecommand.togglepause";
I have tried the following options:
1) Based off this SO question. I'm not sure whether about SERVICECMD, as the question states I should use "com.android.music.musicservicecommand" though for ICS I believe the package names have changed.
Intent i = new Intent(SERVICECMD);
i.putExtra(CMDNAME, CMDTOGGLEPAUSE);
sendBroadcast(i);
I have also tried replacing SERVICECMD with TOGGLEPAUSE_ACTION.
2) From this android project issue.
Intent musicIntent = new Intent();
musicIntent.setClassName("com.google.android.music", "com.google.android.music.MusicPlaybackService");
musicIntent.setAction(TOGGLEPAUSE_ACTION);
musicIntent.putExtra(CMDNAME, CMDTOGGLEPAUSE); // or "next" or "previous"
sendBroadcast(musicIntent);
3) This code crashes with the error below. I'm not sure how to go further with this option.
Caused by: android.content.ActivityNotFoundException: Unable to find explicit activity class {com.google.android.music/com.google.android.music.MusicPlaybackService}; have you declared this activity in your AndroidManifest.xml?
Intent intent = new Intent();
intent.putExtra(CMDNAME, CMDTOGGLEPAUSE);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName("com.google.android.music", "com.google.android.music.MusicPlaybackService");
startActivity(intent);
4) I've noticed that when the headset is removed from my phone, the MusicPlaybackService is started because it is responding to the Intent.ACTION_HEADSET_PLUG broadcast that is picked up by WiredAccessoryObserver. I tried duplicating that broadcast in my code using the values shown in LogCat.
Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra("state", 0);
intent.putExtra("name", "h2w");
intent.putExtra("microphone", 1);
sendBroadcast(intent);
I'm really looking for a method that works all the time that just starts up the Service. I'm open to anything. I still have to try binding to the service that uses IMediaPlaybackService.aidl which is supposed to break randomly, and is not ideal. Thanks in advance for helping out.
EDIT
I tried binding to the service using the code below which threw an error:
Caused by: java.lang.SecurityException: Not allowed to bind to service Intent { cmp=com.google.android.music/.MusicPlaybackService }
In my OnCreate function
if (mPlaybackService == null) {
Intent i = new Intent();
i.setClassName("com.google.android.music","com.google.android.music.MusicPlaybackService" );
bindService(i, connection, Context.BIND_AUTO_CREATE);
}
In my class
IMediaPlaybackService mPlaybackService = null;
ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
mPlaybackService = IMediaPlaybackService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) {
mPlaybackService = null;
}
};
I don't think this is possible. My workaround is to let the apps own broadcastreceiver wake up the service when the button is pressed. I just request audiofocus for my app and wait til it gets lost.
Related
Recently I have uploaded my android apk on the app store and its been told that the next upload to Google play store will get rejected and we need to check and resolve it. Below is the screenshot of the message:
They are referring to package name also. Below is the code:
#Override
public void onDestroy() {
cleanup();
super.onDestroy();
Intent intent = new Intent("com.test.dummyapp");
sendBroadcast(intent);
}
Please assist me how to resolve this.
Below is the code where the component is triggered:
IntentFilter restartFilter = new IntentFilter("com.test.dummyapp");
registerReceiver(restartBroadcastReciver, restartFilter);
private BroadcastReceiver restartBroadcastReciver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
doBindService();
}
};
When you do this, you are broadcasting an "implicit Intent". This is dangerous because any app can register to get this (potential leak of information) and any app can also broadcast this Intent (triggering your app).
Intent intent = new Intent("com.test.dummyapp");
sendBroadcast(intent);
To fix this you can use LocalBroadcastManager (it is deprecated, but still works). Using a local broadcast ensures that other apps cannot see your broadcast Intent and other apps cannot trigger your app this way.
See https://developer.android.com/reference/androidx/localbroadcastmanager/content/LocalBroadcastManager
As an alternative, you should be able to make the Intent explicit by setting the package name:
Intent intent = new Intent("com.test.dummyapp");
intent.setPackage("my.package.name");
sendBroadcast(intent);
It seems really weird to send a Broadcast in onDestroy. I can't possibly see a use for that, and I can see a lot of problems due to onDestroy being called unexpectedly (rotation, screen size change, etc).
But if you have to do it, use new Intent(getPackageName()). What they're looking for is a hardcoded package name like that. The problem is that if you run 'com.facebook.whateveritscalled' and a piece of malware is installed that named itself that, you would be sending the intent to it. Which if you have extras in the intent could be leaking information to it.
Thanks for the information.
I made some changes to the posted code. Let me know if this works fine.
#Override
public void onDestroy() {
cleanup();
super.onDestroy();
openApp((Context) context,"com.test.dummyapp");
}
public static boolean openApp(Context context, String packageName) {
PackageManager manager = context.getPackageManager();
try {
Intent i = manager.getLaunchIntentForPackage(packageName);
if (i == null) {
return false;
}
i.addCategory(Intent.CATEGORY_LAUNCHER);
context.sendBroadcast(i);
return true;
} catch (ActivityNotFoundException e) {
return false;
}
}
Scenario :
When two alarms are set at the same time, then only of them should be shown.
My Problem:
I have a broadcastreceiver which forwards an intent to AlarmGoOffActivity when its time. However, I need to check if AlarmGoOffActivity is already running before forwarding an intent. From one of the answers I saw in SO. I tried this approach.Earlier, both alarms fired. Now, neither alarm fires now.
Code
MyBroadCastReceiver.java
if (AlarmGoOffActivity.running) {
BLog("AlarmActivity running");
} else {
BLog("AlarmActivity not running ");
//set this variable true so that new broadcasts are not entertained
AlarmGoOffActivity.running = true;
Intent intent1 = new Intent(context, AlarmGoOffActivity.class);
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtra(DBHelper.COLUMN_ID, id);
String ext = extras.getString(DBHelper.TASK_TITLE);
if (ext != null) {
intent1.putExtra(DBHelper.TASK_TITLE, ext);
}
BLog("Starting alarm fire");
context.startActivity(intent1);
}
AlarmGoOffActivity.java
public class AlarmGoOffActivity extends ActionBarActivity {
public static boolean running = false;
.....
#Override
public void onDestroy() {
super.onDestroy();
//changing the lock to false here. So a new one can start from
//broadcastreceiver
running = false;
}
}
I was expecting the first one to fire. Now, neither one fires. Why is this not working? Is it because of the static variable (some concept I missed?) or is it because of the time gap of a 10 or less milliseconds between the broadcasts. Thanks for your help.
The receiver should handle the intent and then exit quickly. It should not try to determine if the Activity is visible or active or whatever.
Instead, you should have the Activity handle re-notifications. In particular, you will probably find onNewIntent useful for processing new intents sent from the receiver. In your activity you can set flags to determine how to handle re-notifications versus the state the Activity is currently in.
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;
}
I built a custom lock screen app that uses a broadcast receiver and service to listen for when the user turns on or off the screen and from there launch my activity. The activity is supposed to completely replace the lock screen. In order to do this my app is supposed to disable the android stock lock so that my app can function as the new lock screen.
Instead what happens is once the application is first installed the the service first started the application appears to be working. and when the user first turns off the screen of their phone when they turn it back on they are presented with my app running on top and is able to unlock their phone with my app. But then once inside the android OS if the user presses the home button the next time they turn off the screen and turn it back on instead of being brought back to my application they are brought to the stock unlock screen with my application open underneath it, when it should be on top.
Here is my code:
My Service:
public class MyService extends Service {
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.d("MyService","Service STARTED");
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
final BroadcastReceiver mReceiver = new ScreenReceiver();
registerReceiver(mReceiver, filter);
}
}
My broadcast receiver:
public class ScreenReceiver extends BroadcastReceiver {
public static ArrayList<String> runningApplications = new ArrayList<String>();
private Context ctext;
public static boolean screenIsLocked;
public static KeyguardManager keyguardManager;
public static KeyguardLock lock;
#Override
public void onReceive(final Context context, final Intent intent) {
ctext = context;
keyguardManager = (KeyguardManager)ctext.getSystemService(Activity.KEYGUARD_SERVICE);
lock = keyguardManager.newKeyguardLock(Context.KEYGUARD_SERVICE);
lock.disableKeyguard();
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
screenIsLocked = true;
Log.d("ScreenReceiver", "False");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
screenIsLocked = false;
Log.d("ScreenReceiver", "True");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
}
}
My activity that is started is basically empty with just one unlock button that calls finish(); when pressed.
The behavior of keyguard-related logic can vary from device to device. That's because lockscreens are often custom-made by device manufacturers (i.e. not stock), some of them respect the keyguard logic you use, some don't.
Also, afaik the newer way to control keyguard is to use window flags:
// inside activity
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
This will not solve the problem though, devices still have their say about this.
E.g. from my experience, Galaxy Nexus will show your activity's window above keyguard but will not dismiss it (you'd think Google-branded device should respect the flag, eh), so if you hit the back button in your activity - you'll get standard lockscreen --- while HTC One X seems to handle the dismiss part properly: your activity window will cause standard lockscreen to get dismissed as expected.
I found no way to force all devices to behave properly. Android API is not meant to enable you to create custom lock screens (at least not currently). Take a look at the ones in the store - they all have the exact same problem of not being stable enough.
As Dianne Hackborn says in this Google Groups answer, anything you can do in this regard is a hack so expect it to break from time to time.
I tried to compile your code and got the same error you were talking about. I tried to modify it to make it to work and finally got the problem!!!
public class ScreenReceiver extends BroadcastReceiver {
public static ArrayList<String> runningApplications = new ArrayList<String>();
private Context ctext;
public static boolean screenIsLocked;
public static KeyguardManager keyguardManager;
public static KeyguardLock lock;
#Override
public void onReceive(final Context context, final Intent intent) {
ctext = context;
keyguardManager = (KeyguardManager)ctext.getSystemService(Activity.KEYGUARD_SERVICE);
lock = keyguardManager.newKeyguardLock(Context.KEYGUARD_SERVICE);
lock.disableKeyguard();
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
screenIsLocked = true;
Log.d("ScreenReceiver", "False");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
screenIsLocked = false;
Log.d("ScreenReceiver", "True");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
}
}
With this change to the broadcast receiver class I was able to overcome the problem
Try it and tell me if there is any problem.
EDIT:I think the problem might lie in the finish() method....Android dumps apps when it requires memory...I think finish() might be helping android in trashing the app(and this might be the reason why your problem occurs randomly)
EDIT: I found the solution, see below
My first post on StackOverFlow. However I have been reading about this problem for a while without a solution that works.
What I would like to do is register the following Intent: android.nfc.action.TAG_DISCOVERED
I am doing the following in my Code:
IntentFilter filter = new IntentFilter();
filter.addAction("android.nfc.action.TAG_DISCOVERED");
filter.addCategory("android.intent.category.DEFAULT");
Log.d(TAG, "Created the new filter");
reciever = new NFCBroadcastReciever(this);
Log.d(TAG, "Created the new Broadcast Reciever");
this.registerReceiver(reciever, filter);
Log.d(TAG, "Registered new reciever");
The BroadCastReciever is defined as follows:
public class NFCBroadcastReciever extends BroadcastReceiver {
private Screen screen;
public static String TAG = "NFCBroadcastReciever";
NFCBroadcastReciever(Screen _screen){
screen = _screen;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "Action recieved: "+action);
if(action != null && NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
paymentScreen.onNewIntent(intent);
}
}
}
However I get an exception that the intent being fired from a tag read has no corresponding Activity. I would like to be able to only start listening for NFC events at a certain point in my application.
Thanks in advance for your help.
I found the solution to the problem actually, the key to getting NFC events to occur only on a specific activity while it is active and not when other activities are running. The sample in the Android SDK explains it: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/nfc/ForegroundDispatch.html
I found the solution to the problem actually, the key to getting NFC events to occur only on a specific activity while it is active and not when other activities are running. The sample in the Android SDK explains it: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/nfc/ForegroundDispatch.html
Is your intention to start an activity when the broadcast is received? It doesn't seem to me that paymentScreen.onNewIntent(intent); is going to accomplish that. Instead, you will likely need to build an intent that you can use with startActivity() and you'll likely want to include the relevant data from your broadcast receiver's intent into your activity intent in the form of extras.