Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Choosing background for Live Wallpaper

I'm writing a live wallpaper that creates an effect over a background. I want the user to be able to choose the background from any of the system wallpapers and camera photos. What I would like is for the user to be able to press a button in the Settings menu, have the list of options show up just like setting the wallpaper from the home screen, minus the Live Wallpapers options. Once the user navigates the choices and picks an actually image I would load it to my canvas.

How do I do this?

I can't find an API anywhere for getting a list of wallpapers.

I've been able to come up with a list of wallpaper providers using Intents. I then get a list of live wallpaper providers also using Intents and remove those from my first list. The gives me a list of wallpaper providers that are not live.

Now what? Are there other ways to do this that I'm missing?

Please help.

like image 970
DarthNoodles Avatar asked Sep 09 '10 18:09

DarthNoodles


1 Answers

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.

like image 157
Dean Avatar answered Oct 04 '22 22:10

Dean