app needs notifications of all inserts, deletes of contacts - android

(working code extract added below)
My app needs to be notified of all inserts and deletes (and maybe updates, but less important) of contacts. This means when the app is started it will need a list of changes. While it is running it should be notified immediately (is it even possible to make changes to contacts outside the app while it is running?).
Should I be using a ContentObserver? Do I need a Service? Is there a way at app startup to get a list of changes that occurred since the last time the app ran?
Thanks.
ContentObserver does indeed work. However, for contacts, it does much less than I hoped for. You only get a notification that something has changed (in fact, you may get several notifications). You wont know what changed. Better than no notification though, I guess.
When you receive the notificaton, you'll have to run queries to find out if any of the contacts you are interested in have changed. If you need to check all of them, I think you'll be better off using a SyncAdapter.
Here's the code I ended up using. First a ContentObserver subclass; this receives notifications from whatever provider you register with (see next block of code):
class MainContentObserver extends ContentObserver
{
public MainContentObserver (Handler handler)
{
super (handler);
}
#Override
public void onChange (boolean selfChange)
{
Message msg = handler.obtainMessage();
msg.what = CONTACTS_CHANGED; // const int declared elsewhere
msg.obj = null;
handler.sendMessage (msg);
}
}
Here's the sceond block - this is the onCreate from your activity (or it could be in onResume). There are two important parts. One, I implement and instantiate a handler. This will receive "messages" from the observer, which runs in a separate thread, and relay them to my activity. The second piece is the creation of the observer, which happens through the register call.
#Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// receive notices from our background threads.
handler = new Handler()
{
#Override
public void handleMessage (Message msg)
{
if (msg.what == CONTACTS_CHANGED) // const int declared elsewhere
System.out.println ("handler: contacts changed");
else
throw new IllegalArgumentException ("unrecognized handler message source: " + msg.what);
}
};
// register content observer for contact changes
contactsObserver = new MainContentObserver (handler);
getContentResolver().registerContentObserver (ContactsContract.AUTHORITY_URI, true,
contactsObserver);
... other initialization ...
}
Finally, one more block of code - you need to unregister the observer or (I've read) you'll have a memory leak. (If you regsiter in onResume, be sure to unregister in onPause.)
#Override
public void onDestroy ()
{
super.onDestroy();
getContentResolver().unregisterContentObserver (contactsObserver);
}

I know there is no broadcast for what you want to do. ContentObserver is what you have to go with. Also check:
Native contact change notification

I think ContentObserver is better option, you can refer following ContentOberver
dealing with contacts.

i think you will have to look into the Broadcast Receiver for your question..

Related

Handling services and sending data across activities

I'm trying to start a service from one activity to make an api call and obtain its result in another activity. I use a BroadcastReceiver to receive the data but how do I make sure that the activity is created and the receiver is attached before sending the broadcast from the service. Is there something wrong with the way that I'm designing this?
Thanks in advance.
Edit: To simply this, I'm starting a 3 second animation when the app is called. I do not want to waste that time and so I'm trying to get data from network and then display it on the activity called after the animation ends. I assumed IntentService would be the way to go but if its not please suggest me how to go about this.
You can use Sticky Events from EventBus library. Basically it will cache your data in memory before broadcasting events. The data can be delivered to subscribers. Thus, you don’t need any special logic to consider already available data.
First you need to declare a class to hold data which you get from network.
public class MyDataEvent {
String token;
// Write more properties here
}
In the service, after getting the data from network, post event which contains data to subscribers.
MyDataEvent data = new MyDataEvent();
data.token = "123456789abcxyz";
EventBus.getDefault().postSticky(data);
In activity which you want to receive the data
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// UI updates must run on MainThread
#Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onMyDataEvent(MyDataEvent data) {
// Process the data here
Log.i("TAG", data.token);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}

BroadcastReceiver - Can't get button to change background

I have an Activity and a Service.
In my Activity, a button interacts with the Service to start/stop GPS logging.
My Service has 3 state indicators: One for being connected to Google Play Services, one for actively logging GPS, and one for processing what was logged.
When connected to Google Play Services the Service flow is this:
Ready -> Logging -> Processing -> Ready
The Service will broadcast these states as follows:
private void UpdateStatusBroadcast() {
//Save status variables to intent
Intent intent = new Intent(this.getString(R.string.BroadcastStatusIntent));
intent.putExtra(getString(R.string.BroadcastIsConnected), mIsConnected);
intent.putExtra(getString(R.string.BroadcastIsTripActive), mIsTripActive);
intent.putExtra(getString(R.string.BroadcastIsProcessing), mIsProcessing);
//Send the broadcast
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
My Activity receives the states as follows:
private class StatusReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
mIsConnected = intent.getBooleanExtra(getString(R.string.BroadcastIsConnected), false);
mIsTripActive = intent.getBooleanExtra(getString(R.string.BroadcastIsTripActive), false);
mIsProcessing = intent.getBooleanExtra(getString(R.string.BroadcastIsProcessing), false);
HandleConnectionStatus();
HandleTripStatus();
}
}
Then comes my problem. In HandleTripStatus(), posted below, i change the text and background of a button to reflect what the Service is currently doing. This works fine for the first and the third case. I never see the second background drawn however, in spite of receiving the correct boolean values.
private void HandleTripStatus() {
Button tripButton = (Button) findViewById(R.id.TripButton);
Button liveMapButton = (Button) findViewById(R.id.LiveMapButton);
if (mIsTripActive) {
tripButton.setText(R.string.TripButtonTitleStop);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_stop_shape));
liveMapButton.setEnabled(true);
} else if (mIsProcessing) {
tripButton.setText(R.string.TripButtonTitleStopping);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_stopping_shape));
liveMapButton.setEnabled(false);
} else {
tripButton.setText(R.string.TripButtonTitleStart);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_start_shape));
liveMapButton.setEnabled(false);
}
}
To debug the issue i verified the following:
Text and background resource is correctly defined (i.e. trying to use
it instead of the first and third case works)
The if-else conditions runs when expected (i.e. the "else if" condition actually runs when I expect it to. Verified by breakpoint.)
No other if-else condition is used in the process. (i.e, only the correct condition is run.)
Some other code that could possibly be relevant:
This is how the Activity requests that the GPS logging should stop (Leading to the processing step before finishing)
private void EndTrip() {
//Create message to TripService with intent to run case for END_TRIP
Message message = Message.obtain(null, TripService.END_TRIP, 0, 0);
//Send the Message to the Service
try {
mMessenger.send(message);
Toast.makeText(mContext, R.string.TripStopToast, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
Log.e("Debug", "Failed to contact TripService");
}
}
This is the structure of what happens in the Service after receiving the message from the Activity.
private void EndTrip() {
//Stop retrieving location updates
//Broadcast the updated status and begin processing the trip
mIsTripActive = false;
mIsProcessing = true;
UpdateStatusBroadcast();
//Processing the collected data
//Finish up
mIsProcessing = false;
UpdateStatusBroadcast();
stopForeground(true);
}
I am all out of ideas. What can the cause be? Why does the button background not change in the else-if?
After too many hours of trial and error, I found the cause to be thread-related.
What I learned:
My service doing its work (Processing) would hang up the UI thread until done
This was quite simply because the service was running on the UI thread
Android does not automatically run services in a thread seperate from the rest of your application.
It is possible to run your service on a different thread. To do this, add the following to your AndroidManifest, inside your service:
android:process=":WhateverNameYouLikeForYourThread"
Note that this of course broke the broadcasts i relied on. This was however easy to fix; The consequence is that I can no longer use LocalBroadcastManager
By example - Instead of
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
i now use
sendBroadcast(intent);
instead. This does however mean that the broadcasts are less private.

Android NFC scan on method

I'm creating an app that will also use NFC to read specific tags. I have a parent activity class where I would like to put function like:
public String scanTagId(){...}
I'm able to get actually tagID from tag the problem is it only occurs onNewIntent so actually first it will finish scanTagId() function and then invoke onNewIntent actually. So the question is how to write scanTagId() to actually wait for reading Tag ID from NFC tag and return string with this tag ID.
Maybe I need to approach this somehow differently or it's not possible - but right now I have a headache from trying to solve this :)
Normally NFC is handled via onNewIntentas you already said. It works like this: everytime a new NFC-Tag (that matches your filters) is detected by the phone the TAG gets connected and your app gets a reference to the TAG via the onNewIntent. Therefore you don't need an extra function scanTagId() since the device is already scaning for TAGs all the time.
If you want to disable that your app gets notified you can do it like this:
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
If you want to (re-)enable the notification about new TAG via onNewIntent you can use this code:
if (mNfcAdapter != null) {
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
If you still have to have a function like you mentioned, I'd store the TAG-Id in an instance-variable and implement the function like this:
protected String mLastTagId = "";
protected synchronized void setLastTagId(String id) {
mLastTagId = id;
}
public synchronized String scanTagId(){
return mLastTagId;
}
Please notice, that this function will not wait for a TAG to be connected, but will just return the ID of the last TAG that was found in the past.
Update
Another approach would be to synchronize onNewIntent and your scanTagId-function with wait() and notifyAll(). I assume you want to invoke your function from main-thread. In combination with synchronization this is a very dangerous thing to do and therefore I strictly advice against it. It will put your main-thread to sleep which is going to cause android to notify the user ("App is not responding") or even quit your app. At least your app won't be responding to UI-events.
You could avoid these effects if you'd use an AsyncTask. But I'd really advice to rethink your design and prepare it for async-events.
What are you trying to achieve in first place?
Update2
If you want to quit your Activity after a specific time has elapsed without a TAG being found, you can use a Timer:
protected String tag = null;
protected Timer timer = new Timer();
And in your onCreate-method activate the timer:
timer.schedule(new TimerTask() {
public void run() {
AlertDialog.Builder b = new AlertDialog.Builder(MyActivity.this);
b.setTitle("Timeout").setMessage("Nothing found");
b.setPositiveButton("OK", new OnClickListener() {
MyActivity.finish();
});
b.setCancelable(false);
b.create().show();
}, 10000);
This code hasn't been tested.

synchronous messages from service to UI layer

is the order of a broadcast intent guaranteed? that is, if i do,
sendBroadcast(intent1);
sendBroadcast(intent2);
are the receivers guaranteed to get intent1 before intent2? i suspect the answer to this is no, but in that case, i'm not quite sure how to solve my problem.
i'm trying to create a "busy" indicator for my app that shows busy when the device is talking on the network, and then goes away when the network communication is done. all network communication happens in an intent service.
my attempt at this was to send a BUSY_START intent when i begin network communication in the service, and a BUSY_STOP when network communication ends. this seems to mostly work, but i'm finding occasionally that i get the stop and start messages out of order.
is there a better way to solve this problem?
i'm thinking of adding an ID to each busy intent, so they can be paired. that way if i receive a start for which i've already received a stop, i can ignore it. or, perhaps more simply, add an integer sequence number into each broadcast. if i ever receive a broadcast for which the sequence of the current intent is less than the sequence of the last received intent, ignore it.
Have you considered using a Handler object to communicate from the background thread in the IntentService? The advantage of a Handler over the BroadcastReciver approach is that the Handler uses a message queue to sequence the Message objects.
(I'm assuming your Service is in the same process as the app's main thread).
At least one viable alternative to intents is to execute messaging through the application class, i.e.,
create a listener interface
Manager a collection of listener objects in the application / provide methods to add / remove listener
Interested entities call the application methods to add / remove themselves as listeners
Add "notify" methods in the application, that call the appropriate listener interface method on each of the registered listeners
Services call the application's notification methods to
For example,
public class MyApplication extends Application {
public interface MyListener {
void onEvent();
}
private Set<MyListener> listeners = new HashSet<Listener>();
public void addListener(MyListener l) {
listeners.add(l);
}
public void removeListener(MyListener l) {
listeners.remove(l);
}
public void sendEvent() {
for (MyListener l: listeners) { l.onEvent(); }
}
}
Now, from your activity (or fragment),
public class MyActivity extends Activity implements MyListener {
...
...
...
#Override
public void onEvent() {
// do something
}
#Override
protected void onResume() {
super.onResume();
((MyApplication)getApplication()).addListener(this);
}
#Override
protected void onPause() {
super.onPause();
((MyApplication)getApplication()).removeListener(this);
}
}
And in your service,
((MyApplication)getApplication()).sendEvent();
This provides synchronous messaging without using intents or static variables.

update UI from and event

I have a service which sends continously values to an activity through some custom event listeners.
Here everything works fine. Certain values are displayed in my activity as expected, but some others make the application to crash. This is because some of the incoming data is calculated inside a normal thread (that I cannot have access for changing it), and I know I have to use a handler here, but as far as I tried the app still crashing.
more graphically I would like to do the following
onValuesChanged(float val) {
myTextView.setText( Float.toString(val) )
}
where val is calculated in a normal thread, but of course it makes crash the app when doing the setText.
Any suggestions?
Use AsyncTask instead of Thread and in the onPostExecute() you can update the UI.
or use Activity.runOnUiThread(new Runnable() {
void run() {
// do something interesting.
}
});
hey u can send a custom broadcast from your service like this
Intent mintent = new Intent();
mintent.setAction("com.action");
mintent.putExtra("name", "value");
sendBroadcast(mintent);
and register a receiver in your activity which will get the value from incoming intent and then call the handler like this to update the UI ..plese parse the int to string at receiving
myTextView.post(new Runnable() {
public void run() {
myTextView.setText( Float.toString(val) )
}
});
Every time you send a broadcast to your activity and it will update the ui ..
However the above mentioned way is also right but if you have to stay with service then go for this way else above......

Categories

Resources