Variable not declared until set in preferences - android

I'm working on an app that works with database and offers option to create backup on external storage. App gets the backup directory from preferences, but since it's not set when I run app for the first time, I use a default path instead, simply by getting path to external storage and creating a directory there.
For all this, I use global string value which keeps the backup path. But here's my problem - when I run my app for the first time, I declare the default path in onCreate() method. When I try to write this path to log, it works. But then I have onClick() method, where I listen to click on button, which calls dialog with export/import options. When I try to write the backup path to log from here, the string is empty.
Variable itself shouldn't be the issue, because here's the interesting thing - when I go to preferences and set the backup path from there (which is then saved to string variable just the same way as it was before), I can suddenly get the variable from the onClick method and everything works fine.
Just for the record, I tried setting different path and I've always been choosing same path in preferences as I used manually in the onCreate method.
Any ideas what could be the issue? I can write the whole condition for backup path used in onCreate also into the onClick method and that would probably solve this issue, but I would like to avoid duplicite code and it's also really bugging me.
EDIT:
Here's code of the condition I use in the onCreate method.
location = PreferenceManager.getDefaultSharedPreferences(this);
if(location.getString("backup_location", "").equals("")) {
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)) {
File file = new File(Environment.getExternalStorageDirectory().toString() + "/simpledbmanager_backup");
if(!file.exists()) {
file.mkdirs();
}
this.backup = file.getAbsolutePath().toString();
}
}
else {
this.backup = location.getString("backup_location", "");
}

Related

Checking if file exists in Kotlin without creating the file

Relevant answers checked but they don't exactly address my situation.
At the beginning of my application it is checked if a file exists by the code: someFile.exists(). However I found out that if the user navigates inside the app using menu items and back button the code behaves like someFile is created -although the app fragment to create and write into someFile is not triggered at all. I suppose this is because a File object is created by the system to run the exists() method, and this object causes someFile.exists() to return true in the next call.
How can I solve this?
Is there a bulletproof method to check if a sharedPreferences file exists or not?

Getting directory exists even deleted from gallery

I'm surprised when delete folder from gallery and getting that folder by programatically it's returning isExists() = true.
if(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), directory).exists()) {
return true
}
Note: However it's happening mostly customised devices of android, is there any way to find directory is exists or not?
When you call File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), directory) you are actually creating folder and then you are checking for exist of that file, so you were always get true. Lookout documentation for constructor of File(File parent, String child).
Creates a new instance from a parent abstract
better to use concet string and get file like File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath()+ directory) to check exist or not.

Android Get Application Start Up Path

In C# there are Application.StartUpPath or Application.AppDataPath global methods to get a path where I would store Application Settings.
I tried static String fName = System.getProperty("user.dir") + "SatCli.conf";
"/SatCli.conf" - is the resulted fName
then I call
BufferedOutputStream oustream = new BufferedOutputStream(new
FileOutputStream(fName, false));
and I am getting the common exception
"/SatCli.conf: open failed: EROFS (Read-only file system)"
Well, I've been writing apps under Unix and I understand maybe the file name is refering to internal memory.. in other words to the root part of system file system.
Or maybe not.. maybe it refers to the App Folder?
Anyway, what I would like to get is the correct method to get the right folder to store the settings data.
Also, what is important, I need it before any context is built, when static fields are initializing.
Thank you for any relevant hint and help.
what I would like to get is the correct method to get the right folder to store the settings data.
For arbitrary files, call getFilesDir() on a Context (e.g., Activity). This gives you a File object representing a private directory where your app can read and write.
FWIW, Android's standard way of storing settings that you collect from the user is SharedPreferences, so you can take advantage of PreferenceFragment for the UI.

Calling delete() on a File object, are the effects immediately visible?

If I call File.delete() are the effects on the underlying file system immediately visible? Can I write to the same file name in the same process/thread after without worrying about bad things happening? If not, is there a way to sync the underlying file system with just a File object?
File.delete() return a boolean telling you if the file has been correctly deleted.
So you could write something like :
if(yourFile.delete()) {
//keep doing what you want. You are now sure file has been deleted !
}
Also, before writing a new file, you could check if a file with the same name already exists.
From Oracle documentation :
Returns:
true if and only if the file or directory is successfully deleted; false otherwise
Oracle source
Also there is a SO thread that might help you

How can I share a SharedPreferences file across two different android apps?

I've been struggling with this for a while. Basically, I want to have two applications (which will always be installed together) share preferences, with one of them being just a service which runs in the background and needs to use the preferences (should own the preferences but only really needs to read them) and the other app being a front-end UI app which needs to be able to write to the preferences file owned by the other app. The service will be doing things in the background (which may be determined by the preferences) and the UI will allow the user to edit the preferences and view some information from the service. However, they will be different packages/apps.
I tried following this tutorial which gave me a pretty good idea of how to have preferences in one app which can be read by another. Essentially, I create a new context through myContext = createPackageContext("com.example.package",Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); and then call myContext.getSharedPreferences("pref_name", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); However, I can't write to the preferences successfully from the outside app - (SharedPreferences.Editor).commit() returns false and I get a warning in logcat about being unable to edit pref_name.xml.bak.
How can I successfully set up my applications so both of them can read and write to the same preferences file (which is stored in the data folder of one of them)?
It is better to set private mode for the file. App needs to be signed with same set of certificates to share this file.
Set sharedUserId in both apps to be same.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hello"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="com.example">
....
Get Context from other package:
mContext = context.createPackageContext(
"com.example.otherapp",
Context.MODE_PRIVATE);
mPrefs = mContext.getSharedPreferences("sameFileNameHere", Activity.MODE_PRIVATE);
Get items as usual from SharedPreference. You can access it now.
First, I should note that this is not officially supported, although there may be a supported way to do this (i.e. it would NOT be this method) added to Android in the future (source for both claims: see second paragraph of this link).
Again, this is unsupported and is very possibly unstable. I primarily did this as an experiment to see if it was possible; take extreme caution if you are planning to actually incorporate this method into an application.
However, it appears to be possible to share preferences between applications if a few requirements are met. First, if you want App B to be able to access App A's preferences the package name of App B must be a child of App A's package name (e.g. App A: com.example.pkg App B: com.example.pkg.stuff). Additionally, they can't be wanting to access the file at the same time (I assume the same rules apply as for accessing them between activities, if you want to ensure atomic access you'll have to use additional safeguards such as .wait() and .notify(), but I won't go into that here).
Note: all of this works on the emulator on 2.2 and 2.3.3- I haven't extensively tested across devices or android versions.
Things to do in the app which is going to own the preferences (App A from above):
1.) Declare the SharedPreferences file
This is fairly simple. Simply declare a couple variables for your sharedpreferences file and the editor in your class and instantiate them in your onCreate method. You can put a string in the preferences now that you will use to make sure the other app can read it properly.
public class stuff extends Activity {
SharedPreferences mPrefs = null;
SharedPreferences.Editor mEd= null;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mPrefs = (getApplicationContext()).getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
mEd = mPrefs.edit();
mEd.putString("test", "original send from prefs owner");
mEd.commit();
2.) Set up the backup file
The getSharedPreferences method appears to check for a .bak file to load the preferences from. This is why it says in the documentation that it will not work across multiple processes; to minimize I/O, it loads the prefs ONCE when you grab them and only backs them up when you close your application/activity. However, if you call this from an outside application you will get a warning about not having the right file permissions for the folder (which is the first app's data folder). To fix this we are going to create the .bak file ourselves and make it publicly readable/writable. The way I chose to do this was to define three variables in my overall class.
final String[] copyToBackup = { "dd", "if=/data/data/com.example.pkg/shared_prefs/prefs.xml", "of=/data/data/com.example.pkg/shared_prefs/prefs.xml.bak", "bs=1024" };
final String[] mainFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml"};
final String[] bakFixPerm = {"chmod", "666", "/data/data/com.example.pkg/shared_prefs/prefs.xml.bak"};
and make a function in my main class which would take these as arguments and execute them
public void execCommand(String[] arg0){
try {
final Process pr = Runtime.getRuntime().exec(arg0);
final int retval = pr.waitFor();
if ( retval != 0 ) {
System.err.println("Error:" + retval);
}
}
catch (Exception e) {}
}
It's not terribly pretty or good but it works.
Now, in your onCreate method (right after editor.commit())you will call this function with each of the three strings.
execCommand(copyToBackup);
execCommand(mainFixPerm);
execCommand(bakFixPerm);
This will copy the file and make both the main .xml and the .xml.bak files accessible to outside programs. You should also call these three methods in your onDestroy() to make sure the database is backed up properly when your app exits, and additionally call them right before you call getSharedPreferences elsewhere in your application (as otherwise it will load the .bak file which is likely out of date if another process has been editing the main .xml file). That's all you need to do in this application though, though. You can call getSharedPreferences elsewhere in this activity and it will grab all the data from the .xml file, allowing you to then call the getdatatype("key") methods and retrieve it.
Things to do in the accessing file(s) (App B from above)
1.) Write to the file
This is even simpler. I made a button on this activity and set up code in it's onClick method which would save something to the shared preferences file. Remember that App B's package must be a child of App A's package. We will be creating a context based on App A's context and then calling getSharedPreferences on that context.
prefsbutton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Context myContext = null;
try {
// App A's context
myContext = createPackageContext("com.example.pkg", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
} catch (NameNotFoundException e) {e.printStackTrace();}
testPrefs = myContext.getSharedPreferences("svcprefs", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
testEd = testPrefs.edit();
String valueFromPrefs = testPrefs.getString("test", "read failure");
TextView test1 = (TextView)findViewById(R.id.tvprefs);
test1.setText(valueFromPrefs);
testEd.putString("test2", "testback");
boolean edit_success = testEd.commit();
This grabs the string I set in the other application and displays it (or an error message) in a textview in this application. Additionally it sets a new string in the preferences file and commits the changes. After this runs, if your other application calls getSharedPreferences it will retrieve the file including the changes from this app.

Categories

Resources