Android Data Backup: dynamic file list with BackupAgentHelper - android

I want to backup my internal files. These are created by My App: random number of files and random names. Like data1.xml, data7,xml, data13.xml, ....
So I do not have any fixed file list.
When MyBackupAgentHelper::onCreate is running before the onBackup(), I can easily provide the filenames by querying the files getApplicationContext().fileList();
public class MyBackupAgentHelper extends BackupAgentHelper
{
#Override
public void onCreate()
{
String[] files = getApplicationContext().fileList();
FileBackupHelper helper = new FileBackupHelper(this, files );
addHelper(FILES_BACKUP_KEY, helper);
}
...
However, if the onRestore is ready to run after an uninstall/re-install, I cannot provide the filenames in the onCreate as this time the getApplicationContext().fileList() returns empty list - obviously.
So nothing is restored :(
Is there any way to restore all files which were backuped without specifying the filenames? Just saying, "do it all".
If not, how could I use the Data Backup in this scenario?
Thanks

I just ran into the same problem. It's frustrating because FileBackupHelper does almost exactly what we want it to do.
If you look at the code for FileBackupHelper's restoreEntity function here
https://android.googlesource.com/platform/frameworks/base.git/+/android-4.2.2_r1/core/java/android/app/backup/FileBackupHelper.java
public void restoreEntity(BackupDataInputStream data) {
if (DEBUG) Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size());
String key = data.getKey();
if (isKeyInList(key, mFiles)) {
File f = new File(mFilesDir, key);
writeFile(f, data);
}
}
...you can see that the only reason the files aren't being written is because they're not in the list that you passed to the FileBackupHelper constructor.
My first solution was to override isKeyInList to always return true. And that actually worked, but then it struck me as odd because isKeyInList has default protection and my FileBackupHelper subclass is not in the same package. It turns out this is some sort dalvik vm bug that allows this so I wouldn't want to rely on it (see Android method with default (package) visibility overriding (shouldn't work, but does - why?) )
But then I realized I could just hold on to the array of files that I passed to the FileBackupHelper constructor and then change the first element to always be the name of the file that wanted to be created. That way it would always be found in the list.
class MyFileBackupHelper extends FileBackupHelper
{
String[] mMyFiles;
MyFileBackupHelper(Context context, String... files)
{
super(context,files);
mMyFiles = files;
}
/* boolean isKeyInList(String key, String[] list)
{
return true;
} */
public void restoreEntity(BackupDataInputStream data)
{
mMyFiles[0] = data.getKey();
super.restoreEntity(data);
}
}
Of course this also relies on FileBackupHelper keeping the same implementation where it doesn't make a copy of the Files list. I'm not exactly sure why they went to so much trouble to prevent restoring arbitrary files, and maybe they'll try to thwart this solution later. But for now, I'm calling it good!
Oh yeah, one extra detail to making my solution work is that you need to make sure there's always one file in the list when you're restoring. That way there will always be an array element 0 to replace. This is what I did in my BackupAgent
public class MyBackupAgent extends BackupAgentHelper
{
public void AddFileHelper(String files[])
{
FileBackupHelper aHelper = new MyFileBackupHelper(this,files);
addHelper("userfiles", aHelper);
}
#Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException
{
String[] anArray = GetAllUserFiles(); // I'm not including this function for brevity
AddFileHelper(anArray);
super.onBackup(oldState, data, newState);
}
#Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException
{
AddFileHelper(new String[] { "filename" } );
super.onRestore(data, appVersionCode, newState);
}
}
So you see that I don't rely on onCreate(). Instead I put the correct files in the list in onBackup and I just put one filename in the list in onRestore. Then MyFileBackupHelper replaces array element 0 in that list every time before calling the parent restoreEntity. Hopefully google will let this solution continue to work in future version of their libraries since it seems like a nice feature to have!

EDIT: You cannot backup folders - you need to individually list files in the file helper to backup those.

I realize this question is quite old, but I was running into a similar problem (wanting to back up an arbitrary set of files from a folder), and my solution was to take all of the files, and put them into a zip file, and the have the FileBackupHelper backup the zip file. For onRestore, after the .zip file gets restored, I extract the files back. This may not be the best solution, but it seems to work for me.

Related

How to Clear the RealmResults<> of a particular Query while Filtering through the Realm in Android?

I am applying filters on realm using RealmResults<>.
I begin to do like this -
RealmResults<data> filteredRealmResults;
List<data> tranfilteredlist;
private OrderedRealmCollectionChangeListener<RealmResults<data>> filteredTransChangeListener =
new OrderedRealmCollectionChangeListener<RealmResults<data>>() {
#Override
public void onChange(RealmResults<data> results, OrderedCollectionChangeSet changeSet) {
Log.d("realm", "filteredRealmResults.size():" + filteredRealmResults.size());
tranfilteredlist = results;
initFilterAdapter();
}
};
Now I want to delete the filteredRealmResults. I did like this -
void deleteFilteredRealmResults() {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Delete all matches
filteredRealmResults.deleteAllFromRealm();
}
});
}
After doing this my data in the realm got deleted. So I just try to delete the tranfilteredlist but it throws an exception that it does not support .clear();
I want to clear if from the memory whatever is holder the query data. Correct me if I am wrong or doesn't understand or just worrying too much.
I read This class holds all the matches of a RealmQuery for a given Realm. The objects are not copied from the Realm to the RealmResults list, but are just referenced from the RealmResult instead. This saves memory and increases speed.
I want to clear if from the memory whatever is holder the query data.
Correct me if I am wrong or doesn't understand or just worrying too
much.
Once you invoke filteredRealmResults.deleteAllFromRealm, it will clear the internal resultant elements object(which holds the elements) and as you know, resultant objects are reference so data will be deleted from realm database too. Hence, there is no need to call clear on the RealmResults object.
You can verify this by calling filteredRealmResults.size() after deletion, it will return 0.
I just try to delete the tranfilteredlist but it throws an exception
that it does not support .clear();
It is the expected behaviour as clear has been deprecated so don't use it.
Why deprecated?
deleteAllFromRealm automatically clears the list so no need to call it again explicitly.
Calling clear on RealmResults object will result in deletion of data from database, can cause unexpected behaviour if the user is not aware so API is being modified to avoid unexpected behaviours.

Managing Android ORMLite Concurrency and Overwriting

I'm trying to solve a (hypothetical) concurrency problem in my Android app that uses ORMLite for the database management.
In particular, I have a ContentService class that manages the database, here's some code (simplified to understand the problem):
public ContentManagerImpl(Context context) {
mContext = context;
configure();
// Once configured, get the DatabaseHelper
DatabaseManager.configure(context);
}
private void configure() {
// If needed, unzip a folder with an sqlite file
// representing the database and saves it on device
}
#Override
public void checkForUpdate() {
// Checks if new database version is available
new CheckForUpdateTask(CheckForUpdateTask.CheckForUpdateCallback() {
#Override
public void onCheckForUpdateFinished(boolean updateNeeded) {
if (updateNeeded) {
update();
}
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
#Override
public void update() {
// Downloads the new database zip, unzip the folder and sava an sqlite file
// representing the database
new UpdateTask(mContext, mMetaData, new UpdateTask.UpdateCallback() {
#Override
public void onUpdateFinished() {
DatabaseManager.refresh();
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
This class is created in the onCreate method of my Android Application class.
Since in the app I have only one Activity, the checkForUpdate method is called every time the onResume of that activity is called.
The problem is, sometimes when there's an update needed and the app is resumed (or even when first launched) I get an exception on a database query performed inside the DatabaseManager.configure() and DatabaseManager.refresh() methods.
That's because I try to get the first element of a single-row table but the query returns an empty list. It seems that the database is not ready, or that someone is still writing on it. I've also tried some lock mechanism but without luck.
So, since is the main (UI) thread that overwrites the database file, my questions are:
Can it be a problem to access the database from multiple threads/tasks?
Can I perform database queries on a separated thread if I write the db file on the UI thread?
Thank you all for your support.

Saving data upon closing app and retrieving that data

I know, there are plenty of questions in regards to saving/retrieving data on here. I was doing find looking things up on my own and really thought I could manage to find my answers without having to "ask a question", but I began to wonder something that I haven't seen an answer for on here.
MY SITUATION:
Naturally, I'm making an app. Upon closing the app, I want to save a simple array of numbers (0 or 1) or boolean values as it were. Upon starting the app, I want to search for that array, if it exists, and retrieve it for use within the app.
I began placing my code into the activity in which the array would be used. But, I started wondering if I would have to copy/paste the overridden onStop() function into all of my activities? Or do I do it in the main activity and somehow link the other activities.
Basically, no matter what state/activity the app is currently on when the app is closed, I want to be able to save the array of int/bool and open it back up when the app is started.
Maybe I didn't know how to search for what I wanted, so explaining it felt like the right thing to do.
I don't mind doing more searching, but if someone would point me in the right direction at the very least, I'd be extremely grateful.
EDIT: If there's a better way to do what I want than what I described (i.e. using a different state instead of onStop(), for instance), please feel free to throw out ideas. This is my first time actually having to deal with the activities' lifecycles and I'm a bit confused even after looking through the android development tutorials. I really think they're poorly done in most cases.
When you application needs to save some persistent data you should always do it in onPause() method and rather than onStop(). Because if android OS kills your process then onStop() and onDestroy() methods are never called. Similarly retrieve data in onResume() method.
Looking at the purpose you want to fulfill, SharedPreferences is all you want.
The documentation states:
"SharePreferences provides a general framework that allows you to save
and retrieve persistent key-value pairs of primitive data types. You
can use SharedPreferences to save any primitive data: booleans,
floats, ints, longs, and strings. This data will persist across user
sessions (even if your application is killed)."
Use SharedPreference to store small amount of data or use SQLite to store large amount of data.
See this link
http://developer.android.com/guide/topics/data/data-storage.html
Serialize an object and pass it around which is more dependable than shared preferences (had lots of trouble with consistency with shared preferences):
public class SharedVariables {
public static <S extends Serializable> void writeObject(
final Context context, String key, S serializableObject) {
ObjectOutputStream objectOut = null;
try {
FileOutputStream fileOut = context.getApplicationContext().openFileOutput(key, Activity.MODE_PRIVATE);
objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(serializableObject);
fileOut.getFD().sync();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
} finally {
if (objectOut != null) {
try {
objectOut.close();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
}
}
}
}
Then use a class to use:
public class Timestamps implements Serializable {
private float timestampServer;
public float getTimestampServer() {
return timestampServer;
}
public void setTimestampServer(float timestampServer) {
this.timestampServer = timestampServer;
}
}
Then wherever you want to write to the variable use:
SharedVariables.writeObject(getApplicationContext(), "Timestamps", timestampsData);
Best way to achieve that is:
create a class. Call it MySettings, or whatever suits you
in this class, define the array of ints / booleans you are going to share, as static. Create getter & setter method (property) to access that (also as static methods)
add a static load() method to MySettings that reads from SharedPreferences. When you launch the app (in your first activity or better in a subclass of Application) call MySettings.load(). This load method sets the array
add a static save() method. Public also. Now you can save from anywhere in you app. This save() method reads the array and writes in SharedPreferences
Code sample:
public class MySettings {
private static List<Integer> data;
public static void load() {
data = new ArrayList<Integer>();
// use SharedPreferences to retrieve all your data
}
public static void save() {
// save all contents from data
}
public static List<Integer> getData() {
return data;
}
public static void setData(List<Integer> data) {
MySettings.data = data;
}
}

Save PDF from Webview on Android

I want to save my webview to a PDF file. I know that I can print the WebView with WebView.createPrintDocumentAdapter() and PrintManager.print().
But I need a way to save the PDF, that is generated internally by the PrintDocumentAdapter, directly without any user interactions, because I need the file for further processing inside my app.
Any ideas?
I realise this question is quite old now. But I have just realised how this can be sensibly done.
Essentially as per the question you can use the createPrintDocumentAdapter method mentioned above and pass the result to your own "fake" PrintManager implementation which simply overrides the onWrite method to save the output to your own file. The snippet below shows how to take any PrintDocumentAdapter and send the output from it to a file.
public void print(PrintDocumentAdapter printAdapter, final File path, final String fileName) {
printAdapter.onLayout(null, printAttributes, null, new PrintDocumentAdapter.LayoutResultCallback() {
#Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
printAdapter.onWrite(null, getOutputFile(path, fileName), new CancellationSignal(), new PrintDocumentAdapter.WriteResultCallback() {
#Override
public void onWriteFinished(PageRange[] pages) {
super.onWriteFinished(pages);
}
});
}
}, null);
}
As you can see there's quite a few nulls passed into the adapters methods but I have checked the Chromium source code and these variables are never used so the nulls are ok.
I created a blog post about how to do it here:
http://www.annalytics.co.uk/android/pdf/2017/04/06/Save-PDF-From-An-Android-WebView/
Create a custom WebViewClient (reference) and set it on your WebView.
In this WebViewClient you should override shouldOverrideUrlLoading (WebView view, String url). From here on you can download the PDF manually when it is clicked.

How to use file browser in android

I have an app for uploading files to a server using php!
How can I implement file browser in my app to select which file to be uploaded! ?
Please give me very simple example ! Or simple solution!
if you need to select any type of file there's no simple solution, you'll have to implement a file manager-similar functionality. Here are some tips and samples to get you started:
prerequisites: your have some basic understanding for how Android works; you know how to implement a custom ListView
use ListView to display the files
parametrize your Adapter with an ArrayList<File>
start with the root of external storage:
File root = Environment
.getExternalStorageDirectory();
the following method will list all files and subdirs in a directory:
public static ArrayList<File> getSubfiles(File root) {
File[] mFiles = root.listFiles();
ArrayList<File> files = new ArrayList<File>();
ArrayList<File> dirs = new ArrayList<File>();
ArrayList<File> allData = new ArrayList<File>();
for (int i = 0; i < myFiles.length; i++) {
if (!myFiles[i].isDirectory()) {
files.add(myFiles[i]);
} else {
dirs.add(myFiles[i]);
}
}
Collections.sort(files);
Collections.sort(dirs);
allData.addAll(dirs);
allData.addAll(files);
return allData;
}
using this method you can move "down" the file system by passing any directory to it. To move up, simply use getParentFile() and pass the result to this method.
So you should always pass the result of this method to your Adapter, calling notifyDataSetChanged() afterwards.
That's all, you have a very basic file viewer. Of course you will need to create some layouts and write a bit more of code, the above tips should help you to get started, since as was already mentioned nobody is going to write half of your app for you here on SO. If you have further questions/problems leave a comment and describe your problem.
There are so many solutions for this on google, and here you can't just ask for a full implementation and expect someone to write it here. Here are a couple of good results:
http://www.dreamincode.net/forums/topic/190013-creating-simple-file-chooser/
http://android-er.blogspot.pt/2010/01/implement-simple-file-explorer-in.html
Basically you will have a list dialog that will search for files and directories on your sdcard, then if it is a file (there are methods in File class to check if it is file or directory for example) then choose it and dismiss the dialog, or if it is a directory, change directory and refresh dialog with list of files and directories and so on.
If you only need to select the file path try the Android-Simple-File-Explorer-Library
First show the file explorer:
Intent intent = new Intent(CONTEXT, SimpleFileExplorerActivity);
startActivityForResult(intent, REQUEST_CODE);
And then receive the result:
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
if(data != null){
String selectedAbsolutePath = data.getStringExtra(SimpleFileExplorerActivity.ON_ACTIVITY_RESULT_KEY);
Toast.makeText(CONTEXT, selectedAbsolutePath, Toast.LENGTH_SHORT).show();
}
}

Categories

Resources