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.
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.
Nope, that's pretty much the best way to go about it. Anything that responds to android.intent.action.SET_WALLPAPER would be a wallpaper provider of one form or another. The problem is that giving them that list will take your control out of the equation. Essentially having them pick something from another provider will unset your Live Wallpaper. You could probably get around this with with some creative Manifest settings and or some "startActivityForResult" type calls.
After they choose something though it's a simple matter of something like this in your WallpaperService.Engine:
@Override
public void onSurfaceChanged( SurfaceHolder holder, int format, int width, int height )
{
super.onSurfaceChanged( holder, format, width, height );
if( isVisible() )
{
mSurfaceHolder = holder;
final Drawable drawable = WallpaperManager.getInstance( getBaseContext() ).getFastDrawable();
mSurfaceHolder.setFormat( RenderThread.pixFormat );
Canvas c = mSurfaceHolder.lockCanvas();
if( c != null )
{
drawable.draw( c );
mSurfaceHolder.unlockCanvasAndPost( c );
}
}
}
精彩评论