Activities, Services, and BroadcastReceivers.. whoa. Advice? - Android Software Development

I'm working on an application that will use a Service Controller (Activity) to set personal settings, and then run a Service in the background constantly to take action on your preferences, based on system changes.
I'm trying to wrap my head around getting said changes. It sounds like I need a BroadcastReceiver class--but it also appears that I could simply add the <intent-filter> element to my Manifest for my Service (or even my Activity) and get the same results. Is that true? Can I simply create a BroadcastReceiver object within the Service to obtain broadcasted Intents?
I understand that BroadcastReceivers are essentially dormant until an Intent is broadcast, but it seems like unnecessary overhead as compared to having my already-running Service analyzing broadcasts for applicable data.
Additionally, if BroadcastReceivers are the ideal (or only) way of handling broadcast Intents, is transporting data to my Service as another (Explicit) Intent the only way to do so? It appears that I'd have to use the "ExtraData" fields, and that seems.. worthless? I want to transport my own structure with flags for the values I care about, rather than creating a string (or other specific layout) to interpret values from the "ExtraData" field.
Thoughts on BroadcastReceivers? Insight from the more advanced developers would be appreciated.
Thanks for your time.

Related

kSOAP2 and custom string types, or alt. engine?

Hi All,
I'm trying to access a SOAP service (I know, why can't people provide REST?!) from my android app.
I found ksoap2 and it looks nice and simple, I've imported the lib into my project (eclipse fwiw) and it all appears to be functioning normally.
However, the SOAP service I'm attempting to use is extremely picky.
It expects string data ("SAE" or "SHY" for example) named "crs" in a "type" of "CRSType". I can not figure out how I can get kSoap2 to do this. If I send the data as type String, I get a soap error back from the server of "no crs specified".
I have tried setting the request property to a java type of CRSType implementing CharSequence and a toString() method that returns the data. Wireshark shows me it's still reported to the service as "d:string".
I tried creating a custom data type with KvmSerializable and it nested it, and called the data "d:anyType" containing a normal "d:string"...
I'm starting to think I should craft the xml myself, but it's frustrating me and I figured I'd see if anyone here had any ideas.
ETA: Feel free to suggest other libs that might be suitable and as easy-to-use as kSoap2.
BTW, it's an opensource app, probably won't reach market but is on googlecode under droidtransport if anyone wants it (no webservice code committed as so far it's not working!)
TIA
--
Martyn

Communication between Service & Activity

Hello
I want to create an Android App. In the last days i read a lot of Android API Documentations, Tutorials and "how do do's". Now I'm really confused, because on one hand, it's nice to have so many possibility, but on the other it confuses me, whats the best way to do it. So I played a little with Activity life-cycle.
Now I'm going to start to build my first 'real' App.
This App should download initial data from a Webservice, process these data with a database, download more data related to results from a database, save these data again to database, and make it accessible in the Acitvity.
So please correct me if I'm wrong from this point on:
I assumend that a Android Service is the best way, to process these Data in a background-worker-thread.
So far so good. So I read about the Android Handler which seems to be a pipeline, so Threads can work on the Handler message queue. I can Access the Service from within my MainActivity over the ServiceConnection.onServiceConnetcted where I recieve a simple Binder which gives me access to my Service. From there i can put Messages in the Message queue from the HandlerThread. But how I can tell my Acitivity from within the Service, that it has finished Processing and send the Data to it?
I read that I don't have to use AIDL since the Service is running in the same Process. But How can I do it then? I tried to call onBind() in the Service, hoping OnServiceConnected will be triggered in the Acitvity which initially Binds these Services, but it doesn't seem to work. I also tried to "hack" the Funktionality, by spending my Service a singleton member "MyMainActivity" with corresponing static setMainActivity(MyMainActivity activity), calling it in the ServiceConnector onServiceConnected, with the same result: Runtime Error
I even don't think I understand the functionality behind these HandlerThread.
will it loop infinitly waiting for HandlerMessages while draining the Battery, or is it on a wait() status since it recieves a message?
I read the Android API about Services but for me, it seems that they only describe to Access the Service from the Activity, and not the other way round.
If I call a method of my Service within my Activity over the ServiceBinder, which has a return value, but was started in another Thread in my Service, how will my Activity know that it processes finish? 'busy waiting' on a boolean member of my Service doesn't seem to be the way to go. If I do that, like I saw in a tutorial, I don't see a reason not to do the work in the Activity Thread itself.
I read also about an AsyncTask, but this doesn't seem to be practicable for me, because I have to do different work related to JSON Objects I get.
Please you expirienced Android guys: show me the way.
You probably don't need a full-fledged service if you just need a background thread. Services can run independent of an activity and reconnect, etc. If you don't intend for the thread to be stand-alone, then just use a class that implements Runnable. Since you seem to have a handle (no pun intended) on handlers, this won't be too hard. Just post a message to your handler from your thread to let the activity know what's going on.

[GUIDE] Implement RemoteController in your app

Hello, fellow XDA-ers.
Today I want to tell you about new RemoteController class introduced in Android 4.4.
What does this class do?
The RemoteController class is used to control media playback, display and update media metadata and playback status, published by applications using the RemoteControlClient class.
Click to expand...
Click to collapse
However, the documentation is rather empty. Sure, there are methods etc., but it's not really helpful if you have zero knowledge about this class and you want to implement it right away.
So, here I am to help you.
Sit down and get your IDE and a cup of coffee/tea ready.
WARNING: This guide is oriented at experienced Android developers. So, I'll cover the main points, but don't expect me to go into details of something which is not directly related to RemoteController.
1. Creating a service to control media playback.
To avoid illegal access to metadata and media playback, user have to activate a specific NotificationListenerService in Security settings.
As a developer, you have to implement one.
Requirements for this service:
1. Has to extend NotificationListenerService[/B
2. Has to implement RemoteController.OnClientUpdateListener.
You can look at my implementation on GitHub.
Let's now talk about details.
It's better to leave onNotificationPosted and onNotificationRemoved empty if you don't plan to actually process notifications in your app; otherwise you know what to do.
Now we need to register this service in AndroidManifest.xml.
Add the following to the manifest(replacing <service-package> with actual package where your service lies, and <service-name> with your Service class name):
Code:
<service
android:name="<service-package>.<service-name>"
android:label="@string/service_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
Please note: do not override onBind() method, as it will break the functionality of the service.
"service_name" is a name for your service which will be shown in Security->Notification access.
2. Handling client update events.
Now on to implementation of RemoteController.OnClientUpdateListener. .
You may process everything inside this service, or (which I consider a better alternative, as it gives more flexibility, and you can re-use your service for different apps) re-call methods of external callback to process the client update events.
Here, however, we will only talk about the methods and which parameters are passed to them.
The official description is good enough and I recommend reading it before processing further.
Code:
[B]onClientChange(boolean clearing)[/B]
Pretty self-explanatory. "true" will be passed if metadata has to be cleared as there is no valid RemoteControlClient, "false" otherwise.
Code:
[B]onClientMetadataUpdate(RemoteController.MetadataEditor metadataEditor)[/B]
[I]metadataEditor[/I] is a container which has all the available metadata.
How to access it? Very simple.
For text data, you use RemoteController.MetadataEditor#getString(int key, String defaultValue);
"R.string.unknown" is a reference to String resource with name "unknown", which will be used to replace missing metadata.
To get artist name as a String, use:
[B]metadataEditor.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, getString(R.string.unknown))[/B]
To get title of the song as a String, use:
[B]metadataEditor.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getString(R.string.unknown))[/B]
To get the duration of the song as a long, use:
[B]metadataEditor.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, 1)[/B]
1 is the default duration of the song to be used in case the duration is unknown.
To get the artwork as a Bitmap, use:
[B]metadataEditor.getBitmap(RemoteController.MetadataEditor.BITMAP_KEY_ARTWORK, null)[/B]
"null" is the default value for the artwork. You may use some placeholder image, however.
And here is one pitfall.
Naturally, you would expect artist name to be saved with the key MediaMetadataRetriever.METADATA_KEY_ARTIST.
However, some players, like PowerAmp, save it with key MediaMetadataRetriever.METADATA_KEY_ALBUMARTIS.
So, to avoid unnecessary checks, you may use the following(returns String):
[B]mArtistText.setText(editor.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, editor.getString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, getString(R.string.unknown))));[/B]
What does it do - it tries to get the artist name by the key METADATA_KEY_ARTIST, and if there is no such String with this key, it will fall back to default value, which, in turn, will try to get the artist name by the key METADATA_KEY_ALBUMARTIST, and if it fails again, it falls back to "unknown" String resource.
So, you may fetch the metadata using these methods and then process it as you like.
Code:
[B]onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, long currentPosMs, float speed)[/B]
Called when the state of the player has changed.
Right now this method is not called, probably due to bug.
[I]state[/I] - playstate of player. Read the [URL="http://developer.android.com/reference/android/media/RemoteControlClient.html"]RemoteControllerClient class description[/URL] to get the list of available playstates.
For example, RemoteControlClient.PLAYSTATE_PLAYING means that music is currently playing.
[I]stateChangeTimeMs[/I] - the system time at which the change happened.
[I]currentPosMs[/I] - current playback position in milliseconds.
[I]speed[/I] - a speed at which playback occurs. 1.0f is normal playback, 2.0f is 2x-speeded playback, 0.5f is 0.5x-speeded playback etc.
Code:
[B]onClientPlaybackStateUpdate (int state)
[I]state[/I] - playstate of player. Read the [URL="http://developer.android.com/reference/android/media/RemoteControlClient.html"]RemoteControllerClient class description[/URL] to get the list of available playstates.[/B]
Code:
[B]onClientTransportControlUpdate (int transportControlFlags)[/B]
[I]transportControlFlags[/I] - player capabilities in form of bitmask.
This is one interesting method. It reports the capabilities of current player in form of bitmask.
Let's say, for example, you want to know if current player supports "fast forward" media key.
Here is how to do it:
[B]if(transportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD != 0) doSomethingIfSupport(); else doSomethingIfDoesNotSupport(); [/B]
All of the flags are listed in [URL="http://developer.android.com/reference/android/media/RemoteControlClient.html"]RemoteControlClient class description.[/URL]
3. Creating RemoteController object.
The preparations are finished.
Now we need to construct RemoteController object.
The constructor of RemoteController takes two arguments. First is Context, and second is RemoteController.OnClientUpdateListener.
You should know how to fetch Context already.
Now let's talk about the second parameter. You have to pass YOUR SERVICE implementing RemoteController.OnClientUpdateListener and extending NotificationListenerService. This is a must, otherwise you won't be able to register your RemoteController to the system.
So, in your service, use something like this:
Code:
public class RemoteControlService extends NotificationListenerService implements RemoteController.OnClientUpdateListener {
private RemoteController mRemoteController;
private Context mContext;
...
@Override
public void onCreate() {
mContext = getApplicationContext();
mRemoteController = new RemoteController(mContext, this);
}
...
Now to activate our RemoteController we have to register it using AudioManager.
Please note that AudioManager#registerRemoteController returns "true" in case the registration was successful, and "false" otherwise.
When can it return "false"? I know only two cases:
1. You have not activated your NotificationListenerService in Security -> Notification Access.
2. Your RemoteController.OnClientUpdateListener implementation is not a class extending NotificationListenerService.
Code:
if(!((AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE)).registerRemoteController(mRemoteController)) {
//handle registration failure
} else {
mRemoteController.setArtworkConfiguration(BITMAP_WIDTH, BITMAP_HEIGHT);
setSynchronizationMode(mRemoteController, RemoteController.POSITION_SYNCHRONIZATION_CHECK);
}
Of course, we will have to deactivate RemoteController at some point with this code.
Code:
((AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE)).unregisterRemoteController(mRemoteController);
By default you will NOT receive artwork updates.
To receive artwork, call setArtworkConfiguration (int, int). First argument is width of the artwork, and second is the height of the artwork.
Please note that this method can fail, so check if it returns true or false.
To stop receiving artwork, call clearArtworkConfiguration().
4. Controlling media playback.
We can send media key events to RemoteControlClient.
Also, we can change position of playback for players which support it(currently only Google Play Music supports it).
You can send key events using this helper method:
Code:
private boolean sendKeyEvent(int keyCode) {
//send "down" and "up" keyevents.
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
boolean first = mRemoteController.sendMediaKeyEvent(keyEvent);
keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
boolean second = mRemoteController.sendMediaKeyEvent(keyEvent);
return first && second; //if both clicks were delivered successfully
}
"keyCode" is the code of the pressed media button. For example, sending KeyEvent.KEYCODE_MEDIA_NEXT will cause the player to change track to next. Note that we send both "down" event and "up" method - without that it will get stuck after first command.
To seek to some position in current song, use RemoteController#seekTo(long). The parameter is the position in the song in milliseconds.
Please note that it will have no effect if the player does not support remote position control.
5. Getting current position.
RIght now the onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, long currentPosMs, float speed) method is broken, as it's not called when the position is updated. So, you have to manually fetch current position. To do this, use the RemoteController#getEstimatedMediaPosition() method - it returns current position in milliseconds(or other values, like 0, if player does not support position update).
To update it periodically, you may use Handler and Runnable. Look at the implementation on GitHub as the reference.
Hi. Good tutorial! How can i use RemoteController without MediaPlayer class? I'm using custom engine for playback.
XDA is usually pretty shi tty so I never come on here, and hence I had to reset my password to say thank you! This was really useful.
Including this code in NotificationListener Service on 4.3
Thank you for taking the time to describe the implementation in such detail. My only problem now is including implements RemoteController.OnClientUpdateListener in my NotificationListener class for an app that supports 4.0+. As soon as this service is started up by a 4.3 device the app crashes (reason is obvious, 4.3 doesn't support remote controller). The only solution I've found is to create 2 seperate notification listener classes, one for 4.3 and one for 4.4 (which has the RemoteController code in it). This also creates 2 entries in Notification Access list in the security settings.
Any ideas on how to make a hybrid service for 4.3/4.4 that implements the necessary RemoteController code?
corrytrevor said:
Thank you for taking the time to describe the implementation in such detail. My only problem now is including implements RemoteController.OnClientUpdateListener in my NotificationListener class for an app that supports 4.0+. As soon as this service is started up by a 4.3 device the app crashes (reason is obvious, 4.3 doesn't support remote controller). The only solution I've found is to create 2 seperate notification listener classes, one for 4.3 and one for 4.4 (which has the RemoteController code in it). This also creates 2 entries in Notification Access list in the security settings.
Any ideas on how to make a hybrid service for 4.3/4.4 that implements the necessary RemoteController code?
Click to expand...
Click to collapse
I have the exact same problem. Been searching for hours now, but I just couldn't come up with a solution for that issue.
Does anybody know how to solve this? Any kind of hint would be highly appreciated!
corrytrevor said:
Thank you for taking the time to describe the implementation in such detail. My only problem now is including implements RemoteController.OnClientUpdateListener in my NotificationListener class for an app that supports 4.0+. As soon as this service is started up by a 4.3 device the app crashes (reason is obvious, 4.3 doesn't support remote controller). The only solution I've found is to create 2 seperate notification listener classes, one for 4.3 and one for 4.4 (which has the RemoteController code in it). This also creates 2 entries in Notification Access list in the security settings.
Any ideas on how to make a hybrid service for 4.3/4.4 that implements the necessary RemoteController code?
Click to expand...
Click to collapse
I found a solution to this problem (or rather WisdomWolf did). http://stackoverflow.com/questions/...motecontroller-onclientupdatelistener-crashes
Problem solved
Finding music to play
Hi Thanks for the fantastic tutorial! Is there away to find a receiver if there is no music playing. I have noticed the line
Code:
I/RemoteController﹕ No-op when sending key click, no receiver right now
is logged. Thanks
Thanks for the great tutorial, very helpful for my current project. RemoteController is also used in KitKat built-in Keyguard KeyguardTransportControlView to replace RDC in older version.
ATI-ERP expeditious Business Solutions
Great article on building Remotecontroller in an Android application.
For More Android Apps visit ati-erp.com
ATI-ERP
i can't bind the service, i debug and seemd android start the service well , however trying bind the service onStart seems does not work in 4.4.4 someone has this issue? i tried reboot and other options
Nevermind i forgot to put the intent filter on manifest (shame on me)
Sorry to drag up an old thread. Does anyone know how to get the package of the client that is currently connected to / updating the RemoteController? I can't find it anywhere...

[Q] Hack for rooted S5 to keep GPS on regardless of toggle?

Is there such a mod? Need to locate some of my business phones regardless of what the user sets the location setting to.
hockeyfreak said:
Is there such a mod? Need to locate some of my business phones regardless of what the user sets the location setting to.
Click to expand...
Click to collapse
I can't believe that you tried searching for an answer before posting as this has been asked and answered more than once in the forums before.
GPS is a heavy draw on battery and this is an niche request. Given that it is for business use, perhaps you should consider paying someone to make you a custom app.
Alternately, go look in the Play store to see if there is an existing app that does what you want.
Or you could use the xposed framework or tasker to hook onto the method that sets the GPS in the settings.
Or set a BroadcastReceiver service to listen for any change to the GPS status and renable it if changes. e.g.
Code:
String provider = Settings.Secure.getString(getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if (!provider.contains("gps")) {
// if gps is disabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings","com.andr oid.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
/*
* Toast.makeText(getApplicationContext(), "GPS On Success",
* Toast.LENGTH_SHORT).show();
*/
Code untested by me
Credit to ken.okech.94
.

Writing Global Overlay Help

So the end goal in this project is to view an RTMP stream and interact with the server at the same time.
This is a little hard, because obviously VLC or Mx Player or whatever I'm viewing the RTMP stream with doesn't know how to communicate with the custom server. So I'm trying to make an overlay that will sit on top of the player which has buttons, the buttons can then communicate with the server which will alter the stream and you'll see the result streamed to the player. This is sort of like an interactive HUD if you will.
Problem is that it's difficult to make an overlay which will take actions if they're clicked, but can also pass those actions to the background app (in this case the player) if it opts not to process them. Right now my app creates a service and the service catches the input and displays whatever. This WORKS, but the problem is no matter what I return from "onTouchEvent(MotionEvent)" in the view of the service (true or false) the home screen is frozen. It catches the press but it won't pass it on, even if the function returns "false" - to say it's not handled.
I'm not sure if I'm not passing the touch event on correctly, or is this not even possible? I read somewhere that it's not possible for app A to provide input to app B, but I'm not sure if that is correct (frankly I don't believe it). Basically I want to handle some presses in the overlay and allow some others to go to the active app beneath it.
Does anybody have any input for making this work as expected? I'm creating a view to pass to the window manager with the following flags:
Code:
LayoutParams params = new LayoutParams(LayoutParams.TYPE_SYSTEM_ALERT,
LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
I've tried playing with the flags but to no avail, I cannot get the touches to go through, even if all the onTouchEvent(MotionEvent) function does is return false.
Anybody have any ideas?
I'd be open to alternative implementation ideas too, the point is I need to basically have two communication channels open, one is a player (ie. VLC) for viewing the RTMP stream, which likely would not change, the other would be the command stream, which will control said server, which I am open to change. I considered trying to use the accelerometer in the service instead of an overlay, but I think that would create a bad proof of concept UI (because there's some latency in the video stream), any alternative ways to communicate with the server would be up for discussion! Maybe I can plug an IO device into the USB port or something, like a keyboard? Extra buttons could be of use. It's only a POC, so some clunkyness IS okay.
EDIT:
As you can see above, I used an alert type instead of an overlay type, I guess this is because overlays won't accept focus after a certain Android version number. I'm using 4.0.4 ICS.

Categories

Resources