AOSP "Launcher2" WallpaperChooser & WallpaperChooserDialogFragment のコードをスタンドアロンのギャラリー アプリに適合させ、API レベル 7 から API 16 までで動作するようにしました。
API レベル < 11 で機能させるために、Android サポート ライブラリ (rev.10) "Compability Package"、V4 パッケージを追加しました。
ここからの提案に基づいて、互換性パッケージ (android.support.v4.app.DialogFragment.java) の変更を行いました: DialogFragments with devices api level < 11。
そうしないと、アプリがエラーでクラッシュします (「DialogFragment をコンテナー ビューにアタッチできません」)。
ただし、アプリは実際の Jelly Bean 4.1.1 デバイス (Nexus S) に展開すると意図したとおりに動作しませんが、エミュレーターでは完全に正常に動作します。
テスト済みで動作:
- Android 2.1 エミュレーター (API 7)
- Android 2.2 エミュレーター (API 8)
- Android 2.3.3 エミュレーター (API 10)
- Android 4.0.3 エミュレーター (API 15)
Android 4.1 エミュレーター (API 16)
Android 2.3.7 デバイス (API 10)
テスト済みで動作しません:
- Android 4.1.1 デバイス (API 16)
4.1.1 デバイスで実行している場合、水平ビューでギャラリー イメージをタップしても、ギャラリー イメージがロード/表示されません。
デバイスを選択して回転させた後に各画像を表示するだけで、新しい画像を選択して再度回転させると、正しく表示されます。
実際のデバイスで機能しない理由を理解できていないので、助けていただければ幸いです。互換性パッケージを削除し、API レベル +11 のみをターゲットにすると、アプリは実際の 4.1.1 デバイスで動作します。
これはアプリのコードです:
GalleryDemo.java:
package gallery.android.demo;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
public class GalleryDemo extends FragmentActivity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.wallpaper_chooser_base);
Fragment fragmentView = getSupportFragmentManager().findFragmentById(R.id.wallpaper_chooser_fragment);
if (fragmentView == null) {
DialogFragment fragment = GalleryDemoDialogFragment.newInstance();
fragment.show(getSupportFragmentManager(), "dialog");
}
}
}
GalleryDemoDialogFragment.java:
package gallery.android.demo;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.annotation.TargetApi;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;
import android.widget.Toast;
import java.util.ArrayList;
@SuppressWarnings("deprecation")
@TargetApi(16)
public class GalleryDemoDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener {
private static final String TAG = "GalleryDemoDialogFragment";
private static final String EMBEDDED_KEY = "gallery.android.demo.GalleryDemoDialogFragment.EMBEDDED_KEY";
private boolean mEmbedded;
private Bitmap mBitmap = null;
private ArrayList<Integer> mThumbs;
private ArrayList<Integer> mImages;
private WallpaperLoader mLoader;
private WallpaperDrawable mWallpaperDrawable = new WallpaperDrawable();
public static GalleryDemoDialogFragment newInstance() {
GalleryDemoDialogFragment fragment = new GalleryDemoDialogFragment();
fragment.setCancelable(true);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(EMBEDDED_KEY)) {
mEmbedded = savedInstanceState.getBoolean(EMBEDDED_KEY);
} else {
mEmbedded = isInLayout();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(EMBEDDED_KEY, mEmbedded);
}
private void cancelLoader() {
if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
mLoader.cancel(true);
mLoader = null;
}
}
@Override
public void onDetach() {
super.onDetach();
cancelLoader();
}
@Override
public void onDestroy() {
super.onDestroy();
cancelLoader();
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
FragmentActivity activity = getActivity();
if (activity != null) {
activity.finish();
}
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
findWallpapers();
return null;
}
@TargetApi(16)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
findWallpapers();
if (mEmbedded) {
View view = inflater.inflate(R.layout.wallpaper_chooser, container, false);
switch (Build.VERSION.SDK_INT) {
case Build.VERSION_CODES.ICE_CREAM_SANDWICH :
view.setBackgroundDrawable(mWallpaperDrawable);
break;
case Build.VERSION_CODES.JELLY_BEAN :
view.setBackground(mWallpaperDrawable);
break;
default:
view.setBackgroundDrawable(mWallpaperDrawable);
break;
}
final Gallery gallery = (Gallery) view.findViewById(R.id.gallery);
gallery.setCallbackDuringFling(false);
gallery.setOnItemSelectedListener(this);
gallery.setAdapter(new ImageAdapter(getActivity()));
View setButton = view.findViewById(R.id.set);
setButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int position = gallery.getSelectedItemPosition();
String[] wallpaperArray = getResources().getStringArray(R.array.wallpapers);
String name = wallpaperArray[position]+".jpg";
Log.i(TAG, "Position="+position);
Log.i(TAG, "Filename="+name);
Toast.makeText(getActivity().getApplicationContext(), "(Save2SD)\n"+
"Position="+position+"\n"+
"Filename="+name+
"", Toast.LENGTH_SHORT).show();
}
});
return view;
}
return null;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
mLoader.cancel();
}
Log.d(TAG, "onItemSelected()");
mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
private void findWallpapers() {
mThumbs = new ArrayList<Integer>(24);
mImages = new ArrayList<Integer>(24);
final Resources resources = getResources();
final String packageName = resources.getResourcePackageName(R.array.wallpapers);
addWallpapers(resources, packageName, R.array.wallpapers);
addWallpapers(resources, packageName, R.array.extra_wallpapers);
}
private void addWallpapers(Resources resources, String packageName, int list) {
final String[] extras = resources.getStringArray(list);
for (String extra : extras) {
int res = resources.getIdentifier(extra, "drawable", packageName);
if (res != 0) {
final int thumbRes = resources.getIdentifier(extra + "_small", "drawable", packageName);
if (thumbRes != 0) {
mThumbs.add(thumbRes);
mImages.add(res);
}
}
}
}
private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
private LayoutInflater mLayoutInflater;
ImageAdapter(FragmentActivity activity) {
mLayoutInflater = activity.getLayoutInflater();
}
public int getCount() {
return mThumbs.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = mLayoutInflater.inflate(R.layout.wallpaper_item, parent, false);
} else {
view = convertView;
}
ImageView iv = (ImageView) view.findViewById(R.id.wallpaper_image);
int thumbRes = mThumbs.get(position);
iv.setImageResource(thumbRes);
Drawable thumbDrawable = iv.getDrawable();
if (thumbDrawable != null) {
thumbDrawable.setDither(true);
} else {
Log.e(TAG, "Error decoding thumbnail resId=" + thumbRes + " for wallpaper #" + position);
}
return view;
}
}
private class WallpaperLoader extends AsyncTask<Integer, Void, Bitmap> {
BitmapFactory.Options mOptions;
WallpaperLoader() {
mOptions = new BitmapFactory.Options();
mOptions.inDither = false;
mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
}
@Override
protected Bitmap doInBackground(Integer... params) {
if (isCancelled()) return null;
try {
return BitmapFactory.decodeResource(getResources(), mImages.get(params[0]), mOptions);
} catch (OutOfMemoryError e) {
return null;
}
}
@Override
protected void onPostExecute(Bitmap b) {
if (b == null) return;
if (!isCancelled() && !mOptions.mCancel) {
if (mBitmap != null) {
mBitmap.recycle();
}
View v = getView();
if (v != null) {
Log.d(TAG, "WallpaperLoader.onPostExecute() getView != null");
mBitmap = b;
mWallpaperDrawable.setBitmap(b); // Doesn't appear to work on (physical) device with Jelly Bean (4.1.1)?
v.postInvalidate();
} else {
Log.d(TAG, "WallpaperLoader.onPostExecute(Bitmap b) getView == null");
mBitmap = null;
mWallpaperDrawable.setBitmap(null);
}
mLoader = null;
} else {
b.recycle();
}
}
void cancel() {
mOptions.requestCancelDecode();
super.cancel(true);
}
}
private static class WallpaperDrawable extends Drawable {
Bitmap mBitmap;
int mIntrinsicWidth;
int mIntrinsicHeight;
void setBitmap(Bitmap bitmap) {
Log.d(TAG, "WallpaperDrawable.setBitmap(Bitmap)");
mBitmap = bitmap;
if (mBitmap == null) {
Log.d(TAG, "WallpaperDrawable.setBitmap(Bitmap) mBitmap == null");
return;
}
mIntrinsicWidth = mBitmap.getWidth();
mIntrinsicHeight = mBitmap.getHeight();
}
@Override
public void draw(Canvas canvas) {
if (mBitmap == null) {
Log.d(TAG, "WallpaperDrawable.draw(Canvas) mBitmap == null");
return;
}
Log.d(TAG, "WallpaperDrawable.draw(Canvas)");
int width = canvas.getWidth();
int height = canvas.getHeight();
int x = (width - mIntrinsicWidth) / 2;
int y = (height - mIntrinsicHeight) / 2;
canvas.drawBitmap(mBitmap, x, y, null);
}
@Override
public int getOpacity() {
return android.graphics.PixelFormat.OPAQUE;
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
}
}