Monitor Android system settings values - android

I want to watch a system setting and get notified when its value changes. The Cursor class has a setNotificationUri method which sounded nice, but it doesn't work and coding it also feels strange... Thats what I did:
// Create a content resolver and add a listener
ContentResolver resolver = getContentResolver();
resolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS | ContentResolver.SYNC_OBSERVER_TYPE_PENDING | ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, new MyObserver());
// I somehow need to get an instance of Cursor to use setNotificationUri in the next step...
Cursor cursor2 = resolver.query(Settings.System.CONTENT_URI, null, null, null, null);
// For testing purposes monitor all system settings
cursor2.setNotificationUri(resolver, Settings.System.CONTENT_URI);
The listener:
public class MyObserver implements SyncStatusObserver {
public void onStatusChanged(int which) {
Log.d("TEST", "status changed, which = " + which);
}
}
Well, obviously the listener gets never called, I can't find an entry with the specified TEST tag in logcat ): (For testing I manually changed the brightness setting from manual to automatic in the android settings menu). Any hint what I am doing wrong? Any other, better way to monitor Android system settings?
Thanks for any hint!

Here's some example code:
ContentResolver contentResolver = getContentResolver();
Uri setting = Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION);
// Make a listener
ContentObserver observer = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
};
// Start listening
contentResolver.registerContentObserver(setting, false, observer);
// Stop listening
contentResolver.unregisterContentObserver(observer);
Check out the documentation for any of these methods for more details.

here is how it can be done, works great: How to implement a ContentObserver for call logs. note than some settings are first written / reallly changed when the user presses the back key in the system preference screen where he changed something!

Related

Get Notification about new/changed/deleted contacts in Android when App is not running

it there a way to get noticed if a new or changed contact is made in Android? I want to get notified when the app starts, if there are any changes. Using a ContentObserver seems to me, that the app must run it in a activity. Or do i have to load all contacts every time from my DB and i am only able to recognize contact changed while my app runs and has an implemented ContentObserver?
i am only able to recognize contact changed while my app runs and has an implemented ContentObserver?
Correct, at least through Android 6.0.
The N Developer Preview has an enhanced JobScheduler that implements a ContentObserver for you, invoking your JobService when a change is detected. Unless there are problems, we can expect that enhanced JobScheduler to ship in the next release of Android, and you can opt into using it on newer Android devices.
Ok, what i did now is: Using a background service and build up an ContentObservice in the onCreate() function. Finally declaring it in the manifest. It will of course not work if the App is totally closed but if it is in background. Thats enough for me. It detects changes to the contacts. Are there any disadvantages in using this approach?
This is the service:
public class ContactsChangeService extends IntentService {
/**
* An IntentService must always have a constructor that calls the super constructor. The
* string supplied to the super constructor is used to give a name to the IntentService's
* background thread.
*/
public ContactsChangeService() {
super("ContactsChangeReceiver");
}
#Override
public void onCreate() {
super.onCreate();
//if created make an Observer
ContactsChangeObserver contentObserver = new ContactsChangeObserver();
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contentObserver);
Log.d(Constants.TAG, "[" + Constants.CONTACTS_OBSERVER_SERVICE + "] " + "started");
}
#Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
//...
// Do work here, based on the contents of dataString
//...
}
}
This is the Observer:
public class ContactsChangeObserver extends ContentObserver{
public ContactsChangeObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(Constants.TAG, "[" + Constants.CONTACTS_OBSERVER_SERVICE + "] " + "Change in Contacts detected");
}
}
And this is the manifest entry:
<service
android:name=".service.ContactsChangeService"
android:exported="true">
</service>

NullPointerException in android.view.accessibility.CaptioningManager$1.onChange

I've seen strange crash reports from my app.
android.view.accessibility.CaptioningManager$1.onChange (CaptioningManager.java:226)
android.database.ContentObserver.onChange (ContentObserver.java:145)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:703)
http://crashes.to/s/db9e325f0f5
It looks like that there is a problem when accessibility functions are enabled. But how I can detect on what UI element or screen that error appears?
I tried to enable accessibility on my own device and navigate through all application screens, but don't receive an exeption.
EDIT
Can this error be caused by using Span in TextView?
// welcome text
TextView welcome = (TextView) view.findViewById(R.id.home_user_name);
welcome.setText(Html.fromHtml(getString(R.string.home_welcome_text, accountManager.getActiveUser())));
// change...
welcome.append(" ");
SpannableString str = SpannableString.valueOf(getString(R.string.home_user_change));
str.setSpan(new URLSpan(getString(R.string.home_user_change)) {
#Override
public void onClick(View view) {
mGuiHandler.sendEmptyMessage(MESSAGE_CHANGE_USER);
}
}, 0, str.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
welcome.append(str);
welcome.setMovementMethod(LinkMovementMethod.getInstance());
First, this isn't part of the accessibility service APIs. It is part of the View's implementation of accessibility. See the google code project. CaptioningManager is in the core/java/android/view/accessibility package. So, this crash is happening regardless of whether accessibility is on or not, or the very least, independent of what accessibility service may be on.
In Captioning Manager on line 235 (the version on Google Code is out of date, but pretty close.). The onChange function is like this:
#Override
public void onChange(boolean selfChange, Uri uri) {
final String uriPath = uri.getPath();
final String name = uriPath.substring(uriPath.lastIndexOf('/') + 1);
if (Secure.ACCESSIBILITY_CAPTIONING_ENABLED.equals(name)) {
notifyEnabledChanged();
} else if (Secure.ACCESSIBILITY_CAPTIONING_LOCALE.equals(name)) {
notifyLocaleChanged();
} else if (Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE.equals(name)) {
notifyFontScaleChanged();
} else {
// We only need a single callback when multiple style properties
// change in rapid succession.
mHandler.removeCallbacks(mStyleChangedRunnable);
mHandler.post(mStyleChangedRunnable);
}
}
This is being called by the ContentObserver class, from this point:
/**
* Dispatches a change notification to the observer. Includes the changed
* content Uri when available and also the user whose content changed.
*
* #param selfChange True if this is a self-change notification.
* #param uri The Uri of the changed content, or null if unknown.
* #param userId The user whose content changed. Can be either a specific
* user or {#link UserHandle#USER_ALL}.
*
* #hide
*/
public void onChange(boolean selfChange, Uri uri, int userId) {
onChange(selfChange, uri);
}
Notice in the documentation for the ContentObserver class explicitly states that the uri can be null, but the CaptioningManager immediately calls getPath without checking fi the value is null. This is why it is crashing. The uri passed to onChange is null.
Now, this is where it gets a little fuzzy right. The rest is private, not available on Google Code. SO, we can only guess as to what zygote is doing. Although, it likely wouldn't be helpful, even if we could see it.
Now, what can we glean from this. In the documentation for the CaptioningManager we see the following explanation for its purpose:
Contains methods for accessing and monitoring preferred video
captioning state and visual properties.
So, based on all of this, check any URIs or other properties of any video and perhaps other media elements in your application...

Instabug, is it possible to deactivate the shake for feedback, if there is no internet access?

I have a networkStateReceiver, that checks if I have internet or not.
If I do, I reinitiate instabug, if not, I want to deactivate. How can I do that?
I tried just setting it as null, but it doesn't work.
if(haveConnectedMobile || haveConnectedWifi){
//TODO will need to make a queue, and go through all that queue
PSLocationCenter.getInstance().initInstabug();
}else{
PSLocationCenter.getInstance().instabug = null;
}
This is my init:
public void initInstabug() {
String[] feedbackArray = getResources().getStringArray(R.array.feedback);
String randomStr = feedbackArray[new Random().nextInt(feedbackArray.length)];
Instabug.DEBUG = true;
instabug = Instabug.initialize(this)
.setAnnotationActivityClass(InstabugAnnotationActivity.class)
.setShowIntroDialog(true, PSTimelineActivity.class)
.enableEmailField(true, false)
.setEnableOverflowMenuItem(true)
.setDebugEnabled(true)
.setCommentRequired(true)
.setPostFeedbackMessage(randomStr)
.setPostBugReportMessage(randomStr) //TODO will be the post report message, random from array
.setCommentFieldHint("Please describe what went wrong")
.setPreSendingRunnable(new Runnable() {
#Override
public void run() {
String[] files = new String[2];
files[0] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log.txt";
files[1] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log2.txt";
Compress compress = new Compress(files, Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
compress.zip(new CrudStateCallback() {
#Override
public void onResponse(String string) {
Log.i("", "ended making the archive");
}
});
}
})
.attachFileAtLocation(Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
}
You can use this code to disable Instabug automatic invocation:
Instabug.getInstance().setInvocationEvent(IBGInvocationEvent.IBGInvocationEventNone)
This way it won't be invoked automatically. This will only affect the next Activity though (not the current one). You may force to stop and restart all listeners by calling onPause and onResume on the current Activity. (We may address that soon though, so that such changes are applied on the currently running Activity).
Don't forget to also enable the shake invocation event when internet access is restored.
Please keep in mind that Instabug SDK already caches all reports and will re-attempt to send them on next app launch until they're uploaded successfully.
Just wanted to post the updated answer.
The newer SDK has changed the name and now you can disable it by the following code:
Instabug.changeInvocationEvent(InstabugInvocationEvent.NONE)
Notice, if you want to disable it for entire application, just call this method in your Application class

Listener for enable/disable mobile data (not connected or disconnnected)

I test some actions (see below).
ConnectivityManager.CONNECTIVITY_ACTION
WifiManager.NETWORK_STATE_CHANGED_ACTION
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE (it is not actually action)
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE (it is not actually action)
But they listen only state (connected or disconnected).
When wifi disconnected, It can listen (enable mobile data -> connnected -> broadcast - > listener)
When wifi copnnnected, It cannot listen (enable mobile data -> connetivity does not changed!)
I need wheather mobile data settings is enable or not
Can I listen mobile data enabled or disabled event?
While there's no broadcast by the system for this, we can actually use a ContentObserver to get notified of when the user toggles the Mobile Data setting.
e.g:
ContentObserver mObserver = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange, Uri uri) {
// Retrieve mobile data value here and perform necessary actions
}
};
...
Uri mobileDataSettingUri = Settings.Secure.getUriFor("mobile_data");
getApplicationContext()
.getContentResolver()
.registerContentObserver(mobileDataSettingUri, true,
observer);
Don't forget to unregister the observer appropriately! E.g.
getContentResolver().unregisterContentObserver(mObserver);
So, after digging into it a bit, it doesn't seem like any broadcasts are sent out when that value is changed. Even the mobile network settings fragment in the Android Settings app doesn't listen for changes; it only checks in onCreate() and onResume(). So it seems you can't listen for changes, but you can get the current state. Unfortunately, it's a private API so you'll have to use reflection:
public static boolean isMobileDataEnabled(Context ctx) {
try {
Class<?> clazz = ConnectivityManager.class;
Method isEnabled = clazz.getDeclaredMethod("getMobileDataEnabled", null);
isEnabled.setAccessible(true);
ConnectivityManager mgr = (ConnectivityManager)
ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
return (Boolean) isEnabled.invoke(mgr, null);
} catch (Exception ex) {
// Handle the possible case where this method could not be invoked
return false;
}
}

Is it possible to replace by my own ContentProvider Android's default one?

I'm considering possibility to replace Android's default SMS ContentProvider with my own one.
I'm talking about those which is called after:
context.getContentResolver().query(Uri.parse("content://sms/"),....);
I would dare to ask: is it possible?
No, That is internally used by the SMS messaging application AND the telephony layer of Android.
Replacing any in-built content providers is guaranteed to break Android - that is a given!
But what you can do is create your own content provider and craft your application to use your own instead.
If you're talking about monitoring the sms content provider, what you can do is use a ContentObserver to watch on the sms content provider and forward the changes made to the sms content provider to your own.
Here's an example of such scenario, everytime a change is made, the onChange gets fired, it is within there, that relaying to your own custom content provider will suffice.
private class MySMSContentObserver extends ContentObserver{
public MySMSContentObserver() {
super();
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// This is where the change notifications gets received!
}
}
// For example
MySMSContentObserver contentSMSObserver = new MySMSContentObserver();
//
context.getContentResolver().registerContentObserver (
"content://sms",
true,
contentSMSObserver);
Also, do not forget to unregister the content observer when the application is finished, i.e.:
context.getContentResolver().unregisterContentObserver(contentSMSObserver);

Categories

Resources