Implement state transition. The change handles a user's enter input.

Add StateView, which represents a state about the GLRootView's content pane.
Add StateManager, which stacks the StateView transitions.
Add DataManager, which manages the access of a retrieved data set.

Change-Id: Ife26d92e692120084f1b6d3907483f7d7cbbf09d
diff --git a/new3d/src/com/android/gallery3d/app/Gallery.java b/new3d/src/com/android/gallery3d/app/Gallery.java
index 74d5cc7..e44b3f6 100644
--- a/new3d/src/com/android/gallery3d/app/Gallery.java
+++ b/new3d/src/com/android/gallery3d/app/Gallery.java
@@ -19,31 +19,21 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.Message;
 import android.util.Log;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.data.DataManager;
 import com.android.gallery3d.data.ImageService;
 import com.android.gallery3d.data.MediaDbAccessor;
-import com.android.gallery3d.data.MediaSet;
-import com.android.gallery3d.ui.Compositor;
+import com.android.gallery3d.ui.GalleryView;
 import com.android.gallery3d.ui.GLRootView;
-import com.android.gallery3d.ui.GridSlotAdapter;
-import com.android.gallery3d.ui.MediaSetSlotAdapter;
-import com.android.gallery3d.ui.SlotView;
-import com.android.gallery3d.ui.SynchronizedHandler;
+import com.android.gallery3d.ui.StateManager;
 
-public final class Gallery extends Activity implements SlotView.SlotTapListener {
+public final class Gallery extends Activity {
     public static final String REVIEW_ACTION = "com.android.gallery3d.app.REVIEW";
 
-    private static final int CHANGE_BACKGROUND = 1;
-
     private static final String TAG = "Gallery";
     private GLRootView mGLRootView;
-    private SynchronizedHandler mHandler;
-    private Compositor mCompositor;
-    private MediaSet mRootSet;
-    private SlotView mSlotView;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -51,32 +41,14 @@
         setContentView(R.layout.main);
         mGLRootView = (GLRootView) findViewById(R.id.gl_root_view);
 
+        // Initialize various managers.
+        StateManager.initialize(this, mGLRootView);
         ImageService.initialize(this);
         MediaDbAccessor.initialize(this, mGLRootView);
+        DataManager.initialize(this);
 
-        mRootSet = MediaDbAccessor.getInstance().getRootMediaSets();
-        mCompositor = new Compositor(this);
-        mSlotView = mCompositor.getSlotView();
-        mSlotView.setModel(new MediaSetSlotAdapter(this, mRootSet, mSlotView));
-        mSlotView.setSlotTapListener(this);
-        mGLRootView.setContentPane(mCompositor);
-
-        mHandler = new SynchronizedHandler(mGLRootView) {
-            @Override
-            public void handleMessage(Message message) {
-                switch (message.what) {
-                    case CHANGE_BACKGROUND:
-                        mCompositor.changeBackground();
-                        mHandler.sendEmptyMessageDelayed(CHANGE_BACKGROUND, 3000);
-                        break;
-                }
-            }
-        };
-    }
-
-    public void onSingleTapUp(int slotIndex) {
-        mCompositor.getSlotView().setModel(new GridSlotAdapter(
-                this, mRootSet.getSubMediaSet(slotIndex), mSlotView));
+        StateManager.getInstance().startStateView(
+                GalleryView.class, new Bundle());
     }
 
     @Override
@@ -93,14 +65,12 @@
     public void onResume() {
         super.onResume();
         mGLRootView.onResume();
-        mHandler.sendEmptyMessageDelayed(CHANGE_BACKGROUND, 3000);
     }
 
     @Override
     public void onPause() {
         super.onPause();
         mGLRootView.onPause();
-        mHandler.removeMessages(CHANGE_BACKGROUND);
     }
 
     @Override
diff --git a/new3d/src/com/android/gallery3d/data/DataManager.java b/new3d/src/com/android/gallery3d/data/DataManager.java
new file mode 100644
index 0000000..732f33b
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/data/DataManager.java
@@ -0,0 +1,38 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.gallery3d.data;
+
+import android.content.Context;
+
+public class DataManager {
+    private static DataManager mInstance;
+    private MediaSet mRootSet;
+    private Context mContext;
+
+    public DataManager(Context context) {
+        mContext = context;
+    }
+
+    public static void initialize(Context context) {
+        mInstance = new DataManager(context);
+    }
+
+    public static synchronized DataManager getInstance() {
+        if (mInstance == null) throw new IllegalStateException();
+        return mInstance;
+    }
+
+    public MediaSet getRootSet() {
+        if (mRootSet == null) {
+            mRootSet = MediaDbAccessor.getInstance().getRootMediaSets();
+        }
+        return mRootSet;
+    }
+
+    public MediaSet getSubMediaSet(int subSetIndex) {
+        if (mRootSet == null) {
+            mRootSet = MediaDbAccessor.getInstance().getRootMediaSets();
+        }
+        return mRootSet.getSubMediaSet(subSetIndex);
+    }
+}
diff --git a/new3d/src/com/android/gallery3d/data/ImageMediaItem.java b/new3d/src/com/android/gallery3d/data/ImageMediaItem.java
index 1c0e0a3..28e8cc0 100644
--- a/new3d/src/com/android/gallery3d/data/ImageMediaItem.java
+++ b/new3d/src/com/android/gallery3d/data/ImageMediaItem.java
@@ -6,6 +6,7 @@
 import android.graphics.BitmapFactory;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Images.ImageColumns;
+import android.util.Log;
 
 import java.io.BufferedInputStream;
 import java.io.FileInputStream;
@@ -14,10 +15,11 @@
 public class ImageMediaItem extends DatabaseMediaItem {
 
     private static final int MICRO_TARGET_PIXELS = 128 * 128;
-    private static final int JPEG_MARK_POSITION = 10 * 1024;
+    private static final int JPEG_MARK_POSITION = 60 * 1024;
 
     private static final int FULLIMAGE_TARGET_SIZE = 1024;
     private static final int FULLIMAGE_MAX_NUM_PIXELS = 2 * 1024 * 1024;
+    private static final String TAG = "ImageMediaItem";
 
     // Must preserve order between these indices and the order of the terms in
     // PROJECTION_IMAGES.
@@ -49,6 +51,7 @@
     private final BitmapFactory.Options mOptions = new BitmapFactory.Options();
 
     protected Bitmap decodeImage(String path) throws IOException {
+        // TODO: need to figure out why simply setting JPEG_MARK_POSITION doesn't work!
         BufferedInputStream bis = new BufferedInputStream(
                 new FileInputStream(path), JPEG_MARK_POSITION);
         try {
@@ -57,13 +60,14 @@
             options.inJustDecodeBounds = true;
             bis.mark(JPEG_MARK_POSITION);
             BitmapFactory.decodeStream(bis, null, options);
-
             if (options.mCancel) return null;
 
             try {
                 bis.reset();
             } catch (IOException e) {
-                throw new AssertionError();
+                Log.w(TAG, "failed in resetting the buffer after reading the jpeg header", e);
+                bis.close();
+                bis = new BufferedInputStream(new FileInputStream(path));
             }
 
             options.inSampleSize =  Utils.computeSampleSize(options,
diff --git a/new3d/src/com/android/gallery3d/ui/AlbumView.java b/new3d/src/com/android/gallery3d/ui/AlbumView.java
new file mode 100644
index 0000000..e8a7216
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/AlbumView.java
@@ -0,0 +1,125 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.gallery3d.ui;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaSet;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Message;
+
+public class AlbumView extends StateView implements SlotView.SlotTapListener {
+    public static final String KEY_BUCKET_INDEX = "keyBucketIndex";
+    private static final int CHANGE_BACKGROUND = 1;
+    private static final int MARGIN_HUD_SLOTVIEW = 5;
+    private static final int HORIZONTAL_GAP_SLOTS = 5;
+    private static final int VERTICAL_GAP_SLOTS = 5;
+
+    private AdaptiveBackground mBackground;
+    private SlotView mSlotView;
+    private HeadUpDisplay mHud;
+    private SynchronizedHandler mHandler;
+    private Bitmap mBgImages[];
+    private int mBgIndex = 0;
+    private int mBucketIndex;
+
+    public AlbumView() {}
+
+    @Override
+    public void onStart(Bundle data) {
+        initializeViews();
+        intializeData(data);
+
+        mHandler = new SynchronizedHandler(getGLRootView()) {
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case CHANGE_BACKGROUND:
+                        changeBackground();
+                        mHandler.sendEmptyMessageDelayed(
+                                CHANGE_BACKGROUND, 3000);
+                        break;
+                }
+            }
+        };
+        mHandler.sendEmptyMessage(CHANGE_BACKGROUND);
+    }
+
+    @Override
+    public void onPause() {
+        mHandler.removeMessages(CHANGE_BACKGROUND);
+    }
+
+    private void initializeViews() {
+        mBackground = new AdaptiveBackground();
+        addComponent(mBackground);
+        mSlotView = new SlotView(mContext);
+        addComponent(mSlotView);
+        mHud = new HeadUpDisplay(mContext);
+        addComponent(mHud);
+        mSlotView.setGaps(HORIZONTAL_GAP_SLOTS, VERTICAL_GAP_SLOTS);
+        mSlotView.setSlotTapListener(this);
+
+        loadBackgroundBitmap(R.drawable.square,
+                R.drawable.potrait, R.drawable.landscape);
+        mBackground.setImage(mBgImages[mBgIndex]);
+    }
+
+    private void intializeData(Bundle data) {
+        mBucketIndex = data.getInt(KEY_BUCKET_INDEX);
+        MediaSet mediaSet = DataManager.getInstance()
+                .getSubMediaSet(mBucketIndex);
+        mSlotView.setModel(new GridSlotAdapter(mContext, mediaSet, mSlotView));
+    }
+
+    public SlotView getSlotView() {
+        return mSlotView;
+    }
+
+    @Override
+    protected void onLayout(
+            boolean changed, int left, int top, int right, int bottom) {
+        mBackground.layout(0, 0, right - left, bottom - top);
+        mHud.layout(0, 0, right - left, bottom - top);
+
+        int slotViewTop = mHud.getTopBarBottomPosition() + MARGIN_HUD_SLOTVIEW;
+        int slotViewBottom = mHud.getBottomBarTopPosition()
+                - MARGIN_HUD_SLOTVIEW;
+
+        mSlotView.layout(0, slotViewTop, right - left, slotViewBottom);
+    }
+
+    public void changeBackground() {
+        mBackground.setImage(mBgImages[mBgIndex]);
+        if (++mBgIndex == mBgImages.length) mBgIndex = 0;
+    }
+
+    private void loadBackgroundBitmap(int ... ids) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = Bitmap.Config.RGB_565;
+        mBgImages = new Bitmap[ids.length];
+        Resources res = mContext.getResources();
+        for (int i = 0, n = ids.length; i < n; ++i) {
+            Bitmap bitmap = BitmapFactory.decodeResource(res, ids[i], options);
+            mBgImages[i] = mBackground.getAdaptiveBitmap(bitmap);
+            bitmap.recycle();
+        }
+    }
+
+    public void onSingleTapUp(int slotIndex) {
+        Bundle data = new Bundle();
+        data.putInt(PhotoView.KEY_SET_INDEX, mBucketIndex);
+        data.putInt(PhotoView.KEY_PHOTO_INDEX, slotIndex);
+
+        StateManager.getInstance().startStateView(PhotoView.class, data);
+    }
+
+    @Override
+    public void onResume() {
+        mHandler.sendEmptyMessage(CHANGE_BACKGROUND);
+    }
+}
\ No newline at end of file
diff --git a/new3d/src/com/android/gallery3d/ui/GalleryView.java b/new3d/src/com/android/gallery3d/ui/GalleryView.java
new file mode 100644
index 0000000..9654202
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/GalleryView.java
@@ -0,0 +1,122 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.gallery3d.ui;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaSet;
+
+public class GalleryView extends StateView implements SlotView.SlotTapListener {
+    private static final int CHANGE_BACKGROUND = 1;
+
+    private static final int MARGIN_HUD_SLOTVIEW = 5;
+    private static final int HORIZONTAL_GAP_SLOTS = 5;
+    private static final int VERTICAL_GAP_SLOTS = 5;
+
+    private AdaptiveBackground mBackground;
+    private SlotView mSlotView;
+    private HeadUpDisplay mHud;
+    private SynchronizedHandler mHandler;
+
+    private Bitmap mBgImages[];
+    private int mBgIndex = 0;
+
+    public GalleryView() {}
+
+    @Override
+    public void onStart(Bundle data) {
+        initializeViews();
+        intializeData();
+        mHandler = new SynchronizedHandler(getGLRootView()) {
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case CHANGE_BACKGROUND:
+                        changeBackground();
+                        mHandler.sendEmptyMessageDelayed(
+                                CHANGE_BACKGROUND, 3000);
+                        break;
+                }
+            }
+        };
+        mHandler.sendEmptyMessage(CHANGE_BACKGROUND);
+    }
+
+    @Override
+    public void onPause() {
+        mHandler.removeMessages(CHANGE_BACKGROUND);
+    }
+
+    @Override
+    public void onResume() {
+        mHandler.sendEmptyMessage(CHANGE_BACKGROUND);
+    }
+
+    private void intializeData() {
+        MediaSet mediaSet = DataManager.getInstance().getRootSet();
+        mSlotView.setModel(new MediaSetSlotAdapter(
+                mContext, mediaSet, mSlotView));
+    }
+
+    private void initializeViews() {
+        mBackground = new AdaptiveBackground();
+        addComponent(mBackground);
+        mSlotView = new SlotView(mContext);
+        addComponent(mSlotView);
+        mHud = new HeadUpDisplay(mContext);
+        addComponent(mHud);
+        mSlotView.setGaps(HORIZONTAL_GAP_SLOTS, VERTICAL_GAP_SLOTS);
+        mSlotView.setSlotTapListener(this);
+
+        loadBackgroundBitmap(R.drawable.square,
+                R.drawable.potrait, R.drawable.landscape);
+        mBackground.setImage(mBgImages[mBgIndex]);
+    }
+
+    public SlotView getSlotView() {
+        return mSlotView;
+    }
+
+    @Override
+    protected void onLayout(
+            boolean changed, int left, int top, int right, int bottom) {
+        mBackground.layout(0, 0, right - left, bottom - top);
+        mHud.layout(0, 0, right - left, bottom - top);
+
+        int slotViewTop = mHud.getTopBarBottomPosition() + MARGIN_HUD_SLOTVIEW;
+        int slotViewBottom = mHud.getBottomBarTopPosition()
+                - MARGIN_HUD_SLOTVIEW;
+
+        mSlotView.layout(0, slotViewTop, right - left, slotViewBottom);
+    }
+
+    public void changeBackground() {
+        mBackground.setImage(mBgImages[mBgIndex]);
+        if (++mBgIndex == mBgImages.length) mBgIndex = 0;
+    }
+
+    private void loadBackgroundBitmap(int ... ids) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inPreferredConfig = Bitmap.Config.RGB_565;
+        mBgImages = new Bitmap[ids.length];
+        Resources res = mContext.getResources();
+        for (int i = 0, n = ids.length; i < n; ++i) {
+            Bitmap bitmap = BitmapFactory.decodeResource(res, ids[i], options);
+            mBgImages[i] = mBackground.getAdaptiveBitmap(bitmap);
+            bitmap.recycle();
+        }
+    }
+
+    public void onSingleTapUp(int slotIndex) {
+        Bundle data = new Bundle();
+        data.putInt(AlbumView.KEY_BUCKET_INDEX, slotIndex);
+        StateManager.getInstance().startStateView(AlbumView.class, data);
+    }
+}
\ No newline at end of file
diff --git a/new3d/src/com/android/gallery3d/ui/PhotoView.java b/new3d/src/com/android/gallery3d/ui/PhotoView.java
new file mode 100644
index 0000000..49068be
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/PhotoView.java
@@ -0,0 +1,144 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.gallery3d.ui;
+
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.MediaSet;
+
+import java.util.ArrayList;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Bitmap.Config;
+import android.os.Bundle;
+import android.os.Message;
+
+public class PhotoView extends StateView implements SlotView.SlotTapListener {
+    public static final String KEY_SET_INDEX = "keySetIndex";
+    public static final String KEY_PHOTO_INDEX = "keyPhotoIndex";
+
+    private static final int ON_IMAGE_READY = 1;
+
+    private SynchronizedHandler mHandler;
+    private ImageViewer mImageViewer;
+    private Bitmap mScaledBitmaps[];
+    private Bitmap mBackupBitmap;
+
+    private int mSetIndex;
+    private int mPhotoIndex;
+
+    public PhotoView() {}
+
+    @Override
+    public void onStart(Bundle data) {
+        mHandler = new SynchronizedHandler(getGLRootView()) {
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case ON_IMAGE_READY:
+                        setImageViewer(new ImageViewer(
+                                mContext, mScaledBitmaps,
+                                mBackupBitmap));
+                        break;
+                }
+            }
+        };
+        initializeData(data);
+    }
+
+    @Override
+    public void onPause() {
+        synchronized (getGLRootView()) {
+            mImageViewer.close();
+        }
+    }
+
+    private void initializeData(Bundle data) {
+        mSetIndex = data.getInt(KEY_SET_INDEX);
+        mPhotoIndex = data.getInt(KEY_PHOTO_INDEX);
+        MediaSet set = DataManager.getInstance().getSubMediaSet(mSetIndex);
+        MediaItem item = set.getMediaItem(mPhotoIndex);
+        item.setListener(new MyMediaItemListener());
+        item.requestImage(MediaItem.TYPE_FULL_IMAGE);
+    }
+
+    @Override
+    protected void onLayout(
+            boolean changed, int left, int top, int right, int bottom) {
+        if (mImageViewer != null) {
+            mImageViewer.layout(0, 0, right - left, bottom - top);
+        }
+    }
+
+    public void onSingleTapUp(int slotIndex) {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void setImageViewer(ImageViewer imageViewer) {
+        // TODO modify ImageViewer to accepting a data model
+        removeComponent(mImageViewer);
+        mImageViewer = imageViewer;
+        addComponent(mImageViewer);
+        requestLayout();
+    }
+
+    private class MyMediaItemListener implements MediaItem.MediaItemListener {
+        private static final int BACKUP_SIZE = 512;
+        public MyMediaItemListener() {}
+
+        public void onImageCanceled(MediaItem abstractMediaItem, int type) {
+            // Do nothing
+        }
+
+        public void onImageError(MediaItem item, int type, Throwable error) {
+            // Do nothing
+        }
+
+        public void onImageReady(MediaItem item, int type, Bitmap bitmap) {
+            mScaledBitmaps = getScaledBitmaps(bitmap);
+            mBackupBitmap = getBackupBitmap(bitmap);
+            mHandler.sendEmptyMessage(ON_IMAGE_READY);
+        }
+
+        private Bitmap getBackupBitmap(Bitmap bitmap) {
+            Config config = bitmap.hasAlpha()
+                    ? Config.ARGB_8888 : Config.RGB_565;
+
+            int width = bitmap.getWidth();
+            int height = bitmap.getHeight();
+            float scale = (float) BACKUP_SIZE / (width > height ? width : height);
+            width = (int) (width * scale + 0.5f);
+            height = (int) (height * scale + 0.5f);
+            Bitmap result = Bitmap.createBitmap(width, height, config);
+            Canvas canvas = new Canvas(result);
+            canvas.scale(scale, scale);
+            canvas.drawBitmap(bitmap, 0, 0, null);
+            return result;
+        }
+
+        private Bitmap[] getScaledBitmaps(Bitmap bitmap) {
+            Config config = bitmap.hasAlpha()
+                    ? Config.ARGB_8888 : Config.RGB_565;
+
+            int width = bitmap.getWidth() / 2;
+            int height = bitmap.getHeight() / 2;
+
+            ArrayList<Bitmap> list = new ArrayList<Bitmap>();
+            list.add(bitmap);
+            while (width > BACKUP_SIZE || height > BACKUP_SIZE) {
+                Bitmap half = Bitmap.createBitmap(width, height, config);
+                Canvas canvas = new Canvas(half);
+                canvas.scale(0.5f, 0.5f);
+                canvas.drawBitmap(bitmap, 0, 0, null);
+                width /= 2;
+                height /= 2;
+                bitmap = half;
+                list.add(bitmap);
+            }
+            return list.toArray(new Bitmap[list.size()]);
+        }
+
+    }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/StateManager.java b/new3d/src/com/android/gallery3d/ui/StateManager.java
new file mode 100644
index 0000000..708ec21
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/StateManager.java
@@ -0,0 +1,59 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.gallery3d.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import java.util.Stack;
+
+public class StateManager {
+    private static StateManager mInstance;
+    private Context mContext;
+    private GLRootView mRootView;
+    private Stack<StateView> mStateStack = new Stack<StateView>();
+    private Stack<Bundle> mBundleStack = new Stack<Bundle>();
+
+    public StateManager(Context context, GLRootView rootView) {
+        mContext = context;
+        mRootView = rootView;
+    }
+
+    public static void initialize(Context context, GLRootView rootView) {
+        mInstance = new StateManager(context, rootView);
+    }
+
+    public static synchronized StateManager getInstance() {
+        if (mInstance == null) throw new IllegalStateException();
+        return mInstance;
+    }
+
+    public void startStateView(Class<? extends StateView> stateClass,
+            Bundle data) {
+        StateView stateView = null;
+        try {
+            stateView = stateClass.newInstance();
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+        } catch (InstantiationException e) {
+            throw new AssertionError(e);
+        }
+        stateView.setContext(mContext);
+
+        if (!mStateStack.isEmpty()) {
+            mStateStack.peek().onPause();
+        }
+
+        mRootView.setContentPane(stateView);
+        mStateStack.push(stateView);
+        mBundleStack.push(data);
+        stateView.onStart(data);
+    }
+
+    void popState() {
+        if (!mStateStack.isEmpty()) {
+            mStateStack.pop();
+            mBundleStack.pop();
+        }
+    }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/StateView.java b/new3d/src/com/android/gallery3d/ui/StateView.java
new file mode 100644
index 0000000..85edb56
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/StateView.java
@@ -0,0 +1,25 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.gallery3d.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+
+abstract public class StateView extends GLView {
+    protected Context mContext;
+
+    public StateView() {
+    }
+
+    void setContext(Context context) {
+        mContext = context;
+    }
+
+    abstract public void onStart(Bundle data);
+
+    public void onPause() {}
+
+    public void onResume() {}
+
+    public void onDestroy() {}
+}