I'm writing an application which includes a service and mounts an OBB file from service.
I'm referring this documentation: http://developer.android.com/reference/android/os/storage/StorageManager.html
http://developer.android.com/reference/android/os/storage/OnObbStateChangeListener.html
However, onObbStateChange() callback never happens.
If I use the very same code in application's activity, it successfully mounts and I get access to the internals of the OBB file.
Below is the code snippet:
// Extend OnObbStateChangeListener class
public class MyService extends Service {
static StorageManager storageManager;
static class Observer extends OnObbStateChangeListener {
#Override
public void onObbStateChange(String path, int state) {
Log.v(TAG, path + " " + state); // 1
}
}
}
// Mount obb
public void getMountedPath() {
File file = new File("<path to obb file>");
if(!obbFile.exists())
return;
Observer observer = new Observer();
boolean state = storageManager.mountObb(obbFile.getAbsolutePath(), null, obbObserver);
Log.v(TAG, "state: " + state); // 2 It always prints TRUE
}
Any idea why it's not able to mount from my service class?
More observations:
When I execute the app for the first time, I get mount state as TRUE in the second log statement and onObbStateChange() doesn't get invoked.
However, when I run my app second time, I see the first log from onObbStateChange() and it prints state as 24 which means ERROR_ALREADY_MOUNTED.
So it seems that the obb got mounted in the first attempt, however, onObbStateChange() didn't get invoked for some reason.
Any explanations on this behavior?
Related
I have an Android app with a SyncAdapter which works most of the time. It is possible for the user to change what data set they would like to look at, which requires a resync. The user can also change the data set while the app is in the process of syncing which can cause a race condition if the user starts a sync, changes data set, and then the sync completes which leaves them looking at the wrong data.
To fix this, when the user changes data set I would like to cancel any existing sync and then trigger a new sync for the selected data set. I did this like so:
public class MainActivity extends Activity {
... // Some members omitted for clarity
// Called whenever the user changes the data set
private void onDataSetChanged() {
final Account account = getSelectedAccount();
final boolean isSyncActive = ContentResolver.isSyncActive(account, AUTHORITY);
// Cancel sync for the old data set
Log.e(TAG, "onDataSetChanged: " + account.name + " - " + isSyncActive);
if (isSyncActive) {
ContentResolver.cancelSync(account, AUTHORITY);
}
// Request a sync for the new data set
ContentResolver.requestSync(account, AUTHORITY, new Bundle());
}
}
Elsewhere I have a SyncAdapter defined:
public class SyncAdapter extends AbstractThreadedSyncAdapter {
... // Some members omitted for clarity
#Override
public synchronized void onPerformSync(final Account account, final Bundle extras,
final String authority, final ContentProviderClient provider, final SyncResult syncResult) {
Log.e(TAG, "onPerformSync: " + account.name);
... // Sync logic
}
}
The SyncAdapter appears to be set up correctly as it is usually called successfully and the #onPerformSync() log statement appears. Only when ContentResolver.cancelSync() is called immediately before ContentResolver.requestSync(), will it sometimes (not always) fail. In those instances SyncAdapter#onPerformSync() is never called and its log statement is not displayed.
I have tried setting ContentResolver.SYNC_EXTRAS_EXPEDITED and ContentResolver.SYNC_EXTRAS_MANUAL as true on the extras bundle, but that has no effect. I've also verified through my own logging that the account and AUTHORITY are correct. I also see the onPerformSync: ... log when it does sync successfully, so I know I am not mis-using Logcat as many people working with SyncAdapters do.
I know requesting a sync is quite a complicated process, but is there at least some way for me to know why the sync was rejected? Other logs don't seem to indicate why it decided to ignore my request.
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>
Does someone know is there a way I can get notified on file change in /proc/uid_stat/myAppUID folder?
I want to track data usage of my app. The file is there and when I read it manually using BufferedReader, I get the data traffic.
I tried using FileObserver class and also RecursiveFileObserver but I don't get any callback when data usage change. My guess is that it doesn't work on virtual file system. I would like to get notified from linux when these files changes, because looping constantly through files is not a valid option for me.
Here is the code I used:
path = "/proc/uid_stat/"+getApplicationInfo().uid;
observer = new FileObserver(path) {
#Override
public void onEvent(int event, String file) {
Toast.makeText(getApplicationContext(), file + " was changed!", Toast.LENGTH_LONG).show();
}
}
};
observer.startWatching();
I've set up the Android Backup Service in my app using a custom class that extends BackupAgentHelper ... it basically looks like this:
public class MyBackups extends BackupAgentHelper {
#Override
public void onCreate() {
Log.d("MyBackups", "creating backup class");
this.addDefaultHelper();
String defaultSharedPrefsName = this.getPackageName() + "_preferences";
SharedPreferencesBackupHelper defaultPrefsHelper = new SharedPreferencesBackupHelper(this, defaultSharedPrefsName);
this.addHelper("default_prefs", defaultPrefsHelper);
}
#Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException {
Log.d("MyBackups", "backing up " + data);
super.onBackup(oldState, data, newState);
}
#Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException {
Log.d("MyBackups", "restoring");
super.onRestore(data, appVersionCode, newState);
// post-processing code goes here
}
}
I have this registered in the manifest file, and if I delete and reinstall the app, it runs as expected, with all the log messages appearing.
However, if I manually request a restore, like this...
BackupManager backupManager = new BackupManager(getApplicationContext());
int error = backupManager.requestRestore(
new RestoreObserver() {
public void restoreStarting(int numPackages) {
Log.d("MyBackups", "restoreStarting");
}
public void restoreFinished(int error) {
Log.d("MyBackups", "restoreFinished");
}
public void onUpdate(int nowBeingRestored, String currentPackage) {
Log.d("MyBackups", "onUpdate");
}
}
);
Log.d("MyBackups", "requestRestore result: " + error);
...restoreStarting and restoreFinished are called, and the error result is 0, but none of the BackupAgentHelper methods are called -- the "creating backup class" and "restoring" logs don't appear, and my post-processing code doesn't run. It seems as if a manual requestRestore bypasses my custom BackupAgentHelper subclass.
Is there anything else I need to hook up to make a manual restore work the same way as an automatic restore? Have you tried this and is it working for you?
This is an old question, but I was able to do just this today. I hope it helps someone having the same issue.
You need to call BackupManager dataChanged() to send a backup request.
Then, to test it and kick start the backup, you need to run
adb shell bmgr run
This will call OnCreate, OnBackup.
Then after you run backupManager.requestRestore, your OnCreate, OnRestore methods will be called.
Check out the sample here, it does exactly this (when you click the Restore button):
https://android.googlesource.com/platform/development/+/0b3758ea4e53f9bfd0b112eaa4a7dd7b7f4040f5/samples/BackupRestore?autodive=0%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F
See the FileHelperExampleAgent.java agent.
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