I do this by putting a preference into the Settings xml as this (mine is flash_setting.xml);
<Preference
android:key="image_custom"
android:title="Choose Background"
android:summary="Select a Custom Image"
/>
I created a custom class to take the get the OnPreferenceClick Listener and watch for the user clicking the preference as so (this is call mySettings.java) (please note that the getRealPathFromURI routine isn't mine and was found elsewhere on here);
Your class should start by extending the PreferenceActivity and implementing the Sharedpreference change listener
public class flashSettings extends PreferenceActivityimplements SharedPreferences.OnSharedPreferenceChangeListener {
Link to the preference name and register the listener
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getPreferenceManager().setSharedPreferencesName(
fingerflashpro.SHARED_PREFS_NAME);
addPreferencesFromResource(R.xml.flash_settings); getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(
this);
Next we will set the on preference listener to listen for 'image_custom'. When it is clicked we will start a new intent to display the photo picker. We start with a StartActivityForResult so we can get the URI of the image back from the intent.
getPreferenceManager().findPreference("image_custom").setOnPreferenceClickListener(new OnPreferenceClickListener()
{
@Override
public boolean onPreferenceClick(Preference preference)
{
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
Toast.makeText(getBaseContext(), "Select Image - " + (width) + " x " + height , Toast.LENGTH_LONG).show();
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, 1);
return true;
}
});}
Next we wait for the activity to return the result and we parse the URI to a real path.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
if (resultCode == Activity.RESULT_OK) {
Uri selectedImage = data.getData();
String RealPath;
SharedPreferences customSharedPreference = getSharedPreferences(fingerflashpro.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = customSharedPreference.edit ();
RealPath = getRealPathFromURI (selectedImage);
editor.putString("image_custom", RealPath);
editor.commit();
}}
The following piece of code was found on this site (by PercyPercy on this thread)and I'm only including it for completeness. It does however, work perfectly.
public String getRealPathFromURI(Uri contentUri) {
String [] proj={MediaColumns.DATA};
Cursor cursor = managedQuery( contentUri,
proj, // Which columns to return
null, // WHERE clause; which rows to return (all rows)
null, // WHERE clause selection arguments (none)
null); // Order-by clause (ascending by name)
int column_index = cursor.getColumnIndexOrThrow(MediaColumns.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);}
Make sure we implement the required Overrides;
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
getPreferenceManager().getSharedPreferences().
unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
}}
Then in your main wallpaper service activity you can extract the Path of the image from your shared preferences.
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs,
String key) {
imageBg = prefs.getString("image_custom", "Bad Image");
getBackground();}
Here is a fairly crude routine to load the image. I've attempted to put in some error trapping just in case the file is deleted, renamed or the SD card gets mounted (hence you lose your image). I've also attempted to put in some crude checks for the device orientation. I'm sure you can do better.
There also some Samplesize checking so you don't exceed the VM budget. This is the most important part of this code to prevent force closes and should definitely be included.
I also call this routine when the orientation of the phone is changed so that the background gets resized each time.
void getBackground() {
if (this.cvwidth == 0 || this.cvheight == 0 || this.visibleWidth == 0) {
this.cvwidth = 480;
this.cvheight = 854;
this.visibleWidth = 480;}
if(new File(imageBg).exists()) {
int SampleSize = 1;
do {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bg = BitmapFactory.decodeFile(imageBg, options);
SampleSize = (int) (Math.ceil(options.outWidth/(this.visibleWidth * 2))*2);
options.inJustDecodeBounds = false;
try {options.inSampleSize = SampleSize;
bg = BitmapFactory.decodeFile(imageBg, options);}
catch (OutOfMemoryError e) {
SampleSize = SampleSize * 2;
}
} while (bg == null);
bg = Bitmap.createScaledBitmap(bg, this.cvwidth/2, this.cvheight, true);}
else {bg = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
bg = Bitmap.createScaledBitmap(bg, this.cvwidth/2, this.cvheight, true);}
LoadText = "";
}
I hope this helps. It took me an absolute age to come up with all of this and I know there are still some areas I can refine but at least it should get you going.
If anyone has suggestions on making this code better then I'm all ears.