OSDN Git Service

Fix b/5403449.
authorYuli Huang <yuli@google.com>
Tue, 18 Oct 2011 04:42:50 +0000 (12:42 +0800)
committerYuli Huang <yuli@google.com>
Tue, 18 Oct 2011 05:10:10 +0000 (13:10 +0800)
1. Extract code that recreates/restores ActionBar as RestorableView, and
make both ActionBar and EffectsMenu extend RestorableView.
2. Fix effects-menu buttons too close to each other.
3. Remove effects-menu buttons' highlight animations to look more
responsive.

Change-Id: I68e5bdcde702e611ccced5e169852c0e58a949fc

13 files changed:
res/drawable/photoeditor_toggle_button_background.xml
res/layout/photoeditor_actionbar.xml
res/layout/photoeditor_effects_menu.xml [new file with mode: 0644]
res/layout/photoeditor_main.xml
res/values-sw320dp/photoeditor_dimens.xml
res/values-sw600dp/photoeditor_dimens.xml
res/values-sw800dp/photoeditor_dimens.xml
res/values/photoeditor_styles.xml
src/com/android/gallery3d/photoeditor/ActionBar.java
src/com/android/gallery3d/photoeditor/EffectsBar.java
src/com/android/gallery3d/photoeditor/EffectsMenu.java [new file with mode: 0644]
src/com/android/gallery3d/photoeditor/PhotoEditor.java
src/com/android/gallery3d/photoeditor/RestorableView.java [new file with mode: 0644]

index e8e5406..e6cd75d 100644 (file)
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- Non focused states -->
     <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@android:color/transparent" />
     <item android:state_focused="false" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/photoeditor_tab_selected_holo" />
index 2437611..8bd51ed 100644 (file)
      limitations under the License.
 -->
 
-<com.android.gallery3d.photoeditor.ActionBar
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/action_bar"
-    style="@style/TopActionBar">
+    style="@style/ActionBarInner"
+    android:background="@drawable/photoeditor_actionbar_translucent">
 
     <LinearLayout style="@style/ActionBarLinearLayout">
 
@@ -62,4 +62,4 @@
         </ViewSwitcher>
 
     </LinearLayout>
-</com.android.gallery3d.photoeditor.ActionBar>
+</RelativeLayout>
diff --git a/res/layout/photoeditor_effects_menu.xml b/res/layout/photoeditor_effects_menu.xml
new file mode 100644 (file)
index 0000000..1688a90
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/ActionBarInner"
+    android:background="@drawable/photoeditor_actionbar_translucent_bottom">
+
+    <LinearLayout
+        android:id="@+id/toggles"
+        style="@style/EffectsMenuContainer">
+
+        <ImageButton
+            android:id="@+id/exposure_button"
+            style="@style/EffectsMenuActionButton"
+            android:src="@drawable/photoeditor_exposure"/>
+        <ImageButton
+            android:id="@+id/artistic_button"
+            style="@style/EffectsMenuActionButton"
+            android:src="@drawable/photoeditor_artistic"/>
+        <ImageButton
+            android:id="@+id/color_button"
+            style="@style/EffectsMenuActionButton"
+            android:src="@drawable/photoeditor_color"/>
+        <ImageButton
+            android:id="@+id/fix_button"
+            style="@style/EffectsMenuActionButton"
+            android:src="@drawable/photoeditor_fix"/>
+    </LinearLayout>
+
+</FrameLayout>
index 49597ed..a040ca7 100644 (file)
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"/>
 
-    <com.android.gallery3d.photoeditor.EffectsBar android:id="@+id/effects_bar" style="@style/EffectsBar">
-
-        <LinearLayout style="@style/BottomActionBar" android:gravity="center_horizontal">
-
-            <LinearLayout android:id="@+id/effects_menu" style="@style/ActionBarLinearLayout">
-
-                <ImageButton
-                    android:id="@+id/exposure_button"
-                    style="@style/EffectsMenuActionButton"
-                    android:src="@drawable/photoeditor_exposure"/>
-                <ImageButton
-                    android:id="@+id/artistic_button"
-                    style="@style/EffectsMenuActionButton"
-                    android:src="@drawable/photoeditor_artistic"/>
-                <ImageButton
-                    android:id="@+id/color_button"
-                    style="@style/EffectsMenuActionButton"
-                    android:src="@drawable/photoeditor_color"/>
-                <ImageButton
-                    android:id="@+id/fix_button"
-                    style="@style/EffectsMenuActionButton"
-                    android:src="@drawable/photoeditor_fix"/>
-            </LinearLayout>
-
-        </LinearLayout>
+    <com.android.gallery3d.photoeditor.EffectsBar
+        android:id="@+id/effects_bar"
+        style="@style/EffectsBar">
+
+        <com.android.gallery3d.photoeditor.EffectsMenu
+            android:id="@+id/effects_menu"
+            style="@style/ActionBarOuter"/>
+
     </com.android.gallery3d.photoeditor.EffectsBar>
 
-    <include layout="@layout/photoeditor_actionbar"/>
+    <com.android.gallery3d.photoeditor.ActionBar
+        android:id="@+id/action_bar"
+        style="@style/ActionBarOuter"
+        android:layout_alignParentTop="true"/>
 
 </com.android.gallery3d.photoeditor.Toolbar>
index 66e9d5f..ff76e25 100755 (executable)
@@ -26,6 +26,7 @@
     <dimen name="action_bar_icon_padding_left">0dp</dimen>
     <dimen name="action_bar_icon_padding_right">5dp</dimen>
     <dimen name="action_button_padding_horizontal">13dp</dimen>
+    <dimen name="effects_menu_container_width">320dp</dimen>
     <dimen name="effect_tool_panel_padding">10dp</dimen>
     <dimen name="seekbar_width">290dp</dimen>
     <dimen name="seekbar_height">27dp</dimen>
index 9802727..29d7f53 100755 (executable)
@@ -28,6 +28,7 @@
     <dimen name="action_bar_icon_padding_right">5dp</dimen>
     <dimen name="action_button_padding_vertical">8dp</dimen>
     <dimen name="action_button_padding_horizontal">22dp</dimen>
+    <dimen name="effects_menu_container_width">400dp</dimen>
     <dimen name="effect_tool_panel_padding">13dp</dimen>
     <dimen name="seekbar_width">560dp</dimen>
     <dimen name="seekbar_height">35dp</dimen>
index 1bcc28d..e869acf 100755 (executable)
@@ -28,6 +28,7 @@
     <dimen name="action_bar_icon_padding_right">5dp</dimen>
     <dimen name="action_button_padding_vertical">8dp</dimen>
     <dimen name="action_button_padding_horizontal">28dp</dimen>
+    <dimen name="effects_menu_container_width">400dp</dimen>
     <dimen name="effect_tool_panel_padding">15dp</dimen>
     <dimen name="seekbar_width">560dp</dimen>
     <dimen name="seekbar_height">35dp</dimen>
index 964ecbf..7bd7fd0 100644 (file)
         <item name="android:layout_alignParentBottom">true</item>
         <item name="android:orientation">vertical</item>
     </style>
-    <style name="TopActionBar" parent="@style/ActionBar">
-        <item name="android:layout_alignParentTop">true</item>
-        <item name="android:background">@drawable/photoeditor_actionbar_translucent</item>
-    </style>
-    <style name="BottomActionBar" parent="@style/ActionBar">
-        <item name="android:background">@drawable/photoeditor_actionbar_translucent_bottom</item>
-    </style>
-    <style name="ActionBar">
+    <style name="ActionBarInner">
         <item name="android:layout_width">fill_parent</item>
         <item name="android:layout_height">?android:attr/actionBarSize</item>
     </style>
+    <style name="ActionBarOuter">
+        <item name="android:layout_width">fill_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
     <style name="ActionBarLinearLayout">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">fill_parent</item>
         <item name="android:paddingRight">@dimen/action_button_padding_horizontal</item>
         <item name="android:background">?android:attr/selectableItemBackground</item>
     </style>
+    <style name="EffectsMenuContainer" parent="@style/ActionBarLinearLayout">
+        <item name="android:layout_width">@dimen/effects_menu_container_width</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
     <style name="EffectsMenuActionButton" parent="@style/ImageActionButton">
+        <item name="android:layout_weight">1</item>
         <item name="android:background">@drawable/photoeditor_toggle_button_background</item>
     </style>
     <style name="FullscreenToolView">
index db18c74..814ee8e 100644 (file)
 package com.android.gallery3d.photoeditor;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
-import android.view.View;
-import android.widget.RelativeLayout;
 import android.widget.ViewSwitcher;
 
 import com.android.gallery3d.R;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map.Entry;
-
 /**
  * Action bar that contains buttons such as undo, redo, save, etc.
  */
-public class ActionBar extends RelativeLayout {
-
-    private static final float ENABLED_ALPHA = 1;
-    private static final float DISABLED_ALPHA = 0.47f;
-
-    private final HashMap<Integer, Runnable> buttonRunnables = new HashMap<Integer, Runnable>();
-    private final HashSet<Integer> changedButtons = new HashSet<Integer>();
+public class ActionBar extends RestorableView {
 
     public ActionBar(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
     @Override
+    protected int childLayoutId() {
+        return R.layout.photoeditor_actionbar;
+    }
+
+    @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
 
@@ -58,66 +52,51 @@ public class ActionBar extends RelativeLayout {
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        updateButtons(false, false);
+    }
 
-        enableButton(R.id.undo_button, false);
-        enableButton(R.id.redo_button, false);
-        enableButton(R.id.save_button, false);
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        showSaveOrShare();
     }
 
     /**
-     * Restores the passed action-bar.
-     *
-     * @return the passed parameter.
+     * Save/share button may need being switched when undo/save enabled status is changed/restored.
      */
-    public ActionBar restore(ActionBar actionBar) {
-        // Restores by runnables and enabled status of buttons that have been changed.
-        for (Entry<Integer, Runnable> entry : buttonRunnables.entrySet()) {
-            actionBar.setRunnable(entry.getKey(), entry.getValue());
-        }
-        for (int buttonId : changedButtons) {
-            actionBar.enableButton(buttonId, isButtonEnabled(buttonId));
+    private void showSaveOrShare() {
+        // Show share-button only after photo is edited and saved; otherwise, show save-button.
+        boolean showShare = findViewById(R.id.undo_button).isEnabled()
+                && !findViewById(R.id.save_button).isEnabled();
+        ViewSwitcher switcher = (ViewSwitcher) findViewById(R.id.save_share_buttons);
+        int next = switcher.getNextView().getId();
+        if ((showShare && (next == R.id.share_button))
+                || (!showShare && (next == R.id.save_button))) {
+            switcher.showNext();
         }
-        return actionBar;
     }
 
-    public void setRunnable(int buttonId, final Runnable r) {
-        findViewById(buttonId).setOnClickListener(new OnClickListener() {
-
-            @Override
-            public void onClick(View v) {
-                if (isEnabled()) {
-                    r.run();
-                }
-            }
-        });
-        buttonRunnables.put(buttonId, r);
+    public void updateButtons(boolean canUndo, boolean canRedo) {
+        setViewEnabled(R.id.undo_button, canUndo);
+        setViewEnabled(R.id.redo_button, canRedo);
+        setViewEnabled(R.id.save_button, canUndo);
+        showSaveOrShare();
     }
 
-    public void clickButton(int buttonId) {
-        findViewById(buttonId).performClick();
+    public void updateSave(boolean canSave) {
+        setViewEnabled(R.id.save_button, canSave);
+        showSaveOrShare();
     }
 
-    public boolean isButtonEnabled(int buttonId) {
-        return findViewById(buttonId).isEnabled();
+    public void clickBack() {
+        findViewById(R.id.action_bar_back).performClick();
     }
 
-    public void enableButton(int buttonId, boolean enabled) {
-        View button = findViewById(buttonId);
-        button.setEnabled(enabled);
-        button.setAlpha(enabled ? ENABLED_ALPHA : DISABLED_ALPHA);
-        // Track buttons whose enabled status has been updated.
-        changedButtons.add(buttonId);
-
-        if (buttonId == R.id.save_button) {
-            // Show share-button only after photo is edited and saved; otherwise, show save-button.
-            // TODO: Fix the assumption of undo enabled status must be updated before reaching here.
-            boolean showShare = findViewById(R.id.undo_button).isEnabled() && !enabled;
-            ViewSwitcher switcher = (ViewSwitcher) findViewById(R.id.save_share_buttons);
-            int next = switcher.getNextView().getId();
-            if ((showShare && (next == R.id.share_button))
-                    || (!showShare && (next == R.id.save_button))) {
-                switcher.showNext();
-            }
-        }
+    public void clickSave() {
+        findViewById(R.id.save_button).performClick();
+    }
+
+    public boolean canSave() {
+        return findViewById(R.id.save_button).isEnabled();
     }
 }
index 6e3178e..acb22b6 100644 (file)
@@ -33,46 +33,55 @@ import com.android.gallery3d.photoeditor.actions.EffectToolFactory;
  */
 public class EffectsBar extends LinearLayout {
 
+    private final LayoutInflater inflater;
     private FilterStack filterStack;
-    private LayoutInflater inflater;
+    private EffectsMenu effectsMenu;
     private View effectsGallery;
     private ViewGroup effectToolPanel;
     private EffectAction activeEffect;
 
     public EffectsBar(Context context, AttributeSet attrs) {
         super(context, attrs);
+        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     }
 
     public void initialize(FilterStack filterStack) {
         this.filterStack = filterStack;
-        inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
-        setupMenuToggle(R.id.exposure_button, R.layout.photoeditor_effects_exposure);
-        setupMenuToggle(R.id.artistic_button, R.layout.photoeditor_effects_artistic);
-        setupMenuToggle(R.id.color_button, R.layout.photoeditor_effects_color);
-        setupMenuToggle(R.id.fix_button, R.layout.photoeditor_effects_fix);
-
-        setEnabled(false);
-    }
-
-    private void setupMenuToggle(int toggleId, final int effectsId) {
-        final View toggle = findViewById(toggleId);
-        toggle.setOnClickListener(new View.OnClickListener() {
+        effectsMenu = (EffectsMenu) findViewById(R.id.effects_menu);
+        effectsMenu.setOnToggleListener(new EffectsMenu.OnToggleListener() {
 
             @Override
-            public void onClick(View v) {
-                // Toggle off to exit effects gallery that is showing. Or toggle on to show effects
-                // gallery after exiting an active effect if applicable.
-                exit((toggle.isSelected() && (effectsGallery != null)) ? null : new Runnable() {
+            public boolean onToggle(boolean isSelected, final int effectsId) {
+                // Create and show effects-gallery only if the clicked toggle isn't selected or it's
+                // selected but showing an active effect instead of effects-gallery. Set the clicked
+                // toggle selected only when its effects-gallery will be created and shown.
+                boolean select = !isSelected || (effectsGallery == null);
+                exit(select ? new Runnable() {
 
                     @Override
                     public void run() {
-                        toggle.setSelected(true);
-                        showEffectsGallery(effectsId);
+                        createEffectsGallery(effectsId);
                     }
-                });
+                } : null);
+                return select;
             }
         });
+
+        setEnabled(false);
+    }
+
+    private void createEffectsGallery(int effectsId) {
+        // Inflate scrollable effects-gallery and desired effects into effects-bar.
+        effectsGallery = inflater.inflate(R.layout.photoeditor_effects_gallery, this, false);
+        ViewGroup scrollView = (ViewGroup) effectsGallery.findViewById(R.id.scroll_view);
+        ViewGroup effects = (ViewGroup) inflater.inflate(effectsId, scrollView, false);
+        for (int i = 0; i < effects.getChildCount(); i++) {
+            setupEffectListener((EffectAction) effects.getChildAt(i));
+        }
+        scrollView.addView(effects);
+        scrollView.scrollTo(0, 0);
+        addView(effectsGallery, 0);
     }
 
     private void setupEffectListener(final EffectAction effect) {
@@ -86,8 +95,8 @@ public class EffectsBar extends LinearLayout {
                     exitEffectsGallery();
                     // Create effect tool panel first before the factory could create tools within.
                     createEffectToolPanel();
-                    activeEffect.begin(
-                            filterStack, new EffectToolFactory(effectToolPanel, inflater));
+                    activeEffect.begin(filterStack,
+                            new EffectToolFactory(effectToolPanel, inflater));
                 }
             }
 
@@ -105,23 +114,10 @@ public class EffectsBar extends LinearLayout {
         addView(effectToolPanel, 0);
     }
 
-    private void showEffectsGallery(int effectsId) {
-        // Inflate scrollable effects-gallery and desired effects into effects-bar.
-        effectsGallery = inflater.inflate(R.layout.photoeditor_effects_gallery, this, false);
-        ViewGroup scrollView = (ViewGroup) effectsGallery.findViewById(R.id.scroll_view);
-        ViewGroup effects = (ViewGroup) inflater.inflate(effectsId, scrollView, false);
-        for (int i = 0; i < effects.getChildCount(); i++) {
-            setupEffectListener((EffectAction) effects.getChildAt(i));
-        }
-        scrollView.addView(effects);
-        scrollView.scrollTo(0, 0);
-        addView(effectsGallery, 0);
-    }
-
     private boolean exitEffectsGallery() {
         if (effectsGallery != null) {
             if (activeEffect != null) {
-                // Detach the active effect from effects-gallery that could be recycled by gc.
+                // Detach the active effect to prevent it stopping effects-gallery from gc.
                 ViewGroup scrollView = (ViewGroup) effectsGallery.findViewById(R.id.scroll_view);
                 ((ViewGroup) scrollView.getChildAt(0)).removeView(activeEffect);
             }
@@ -165,13 +161,7 @@ public class EffectsBar extends LinearLayout {
      */
     public boolean exit(final Runnable runnableOnDone) {
         // Exit effects-menu selected states.
-        ViewGroup menu = (ViewGroup) findViewById(R.id.effects_menu);
-        for (int i = 0; i < menu.getChildCount(); i++) {
-            View toggle = menu.getChildAt(i);
-            if (toggle.isSelected()) {
-                toggle.setSelected(false);
-            }
-        }
+        effectsMenu.clearSelected();
 
         if (exitActiveEffect(runnableOnDone)) {
             return true;
diff --git a/src/com/android/gallery3d/photoeditor/EffectsMenu.java b/src/com/android/gallery3d/photoeditor/EffectsMenu.java
new file mode 100644 (file)
index 0000000..71614b1
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.photoeditor;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.gallery3d.R;
+
+/**
+ * Effects menu that contains toggles mapping to corresponding groups of effects.
+ */
+public class EffectsMenu extends RestorableView {
+
+    /**
+     * Listener of toggle changes.
+     */
+    public interface OnToggleListener {
+
+        /**
+         * Listens to the selected status and mapped effects-id of the clicked toggle.
+         *
+         * @return true to make the toggle selected; otherwise, make it unselected.
+         */
+        boolean onToggle(boolean isSelected, int effectsId);
+    }
+
+    public EffectsMenu(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected int childLayoutId() {
+        return R.layout.photoeditor_effects_menu;
+    }
+
+    public void setOnToggleListener(OnToggleListener listener) {
+        setToggleRunnalbe(listener, R.id.exposure_button, R.layout.photoeditor_effects_exposure);
+        setToggleRunnalbe(listener, R.id.artistic_button, R.layout.photoeditor_effects_artistic);
+        setToggleRunnalbe(listener, R.id.color_button, R.layout.photoeditor_effects_color);
+        setToggleRunnalbe(listener, R.id.fix_button, R.layout.photoeditor_effects_fix);
+    }
+
+    private void setToggleRunnalbe(final OnToggleListener listener, final int toggleId,
+            final int effectsId) {
+        setClickRunnable(toggleId, new Runnable() {
+
+            @Override
+            public void run() {
+                boolean selected = findViewById(toggleId).isSelected();
+                setViewSelected(toggleId, listener.onToggle(selected, effectsId));
+            }
+        });
+    }
+
+    public void clearSelected() {
+        ViewGroup menu = (ViewGroup) findViewById(R.id.toggles);
+        for (int i = 0; i < menu.getChildCount(); i++) {
+            View toggle = menu.getChildAt(i);
+            if (toggle.isSelected()) {
+                setViewSelected(toggle.getId(), false);
+            }
+        }
+    }
+}
index 3a6face..2294d03 100644 (file)
@@ -18,12 +18,9 @@ package com.android.gallery3d.photoeditor;
 
 import android.app.Activity;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.gallery3d.R;
@@ -54,41 +51,18 @@ public class PhotoEditor extends Activity {
 
                     @Override
                     public void onStackChanged(boolean canUndo, boolean canRedo) {
-                        actionBar.enableButton(R.id.undo_button, canUndo);
-                        actionBar.enableButton(R.id.redo_button, canRedo);
-                        actionBar.enableButton(R.id.save_button, canUndo);
+                        actionBar.updateButtons(canUndo, canRedo);
                     }
         });
 
         EffectsBar effectsBar = (EffectsBar) findViewById(R.id.effects_bar);
         effectsBar.initialize(filterStack);
 
-        actionBar.setRunnable(R.id.undo_button, createUndoRedoRunnable(true, effectsBar));
-        actionBar.setRunnable(R.id.redo_button, createUndoRedoRunnable(false, effectsBar));
-        actionBar.setRunnable(R.id.save_button, createSaveRunnable(effectsBar));
-        actionBar.setRunnable(R.id.share_button, createShareRunnable(effectsBar));
-        actionBar.setRunnable(R.id.action_bar_back, createBackRunnable(effectsBar));
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
-        actionBar = actionBar.restore((ActionBar) recreateView(
-                actionBar, inflater, R.layout.photoeditor_actionbar));
-    }
-
-    /**
-     * Recreates the view by inflating the given layout id.
-     */
-    private View recreateView(View view, LayoutInflater inflater, int layoutId) {
-        ViewGroup parent = (ViewGroup) view.getParent();
-        int index = parent.indexOfChild(view);
-        parent.removeViewAt(index);
-
-        View recreated = inflater.inflate(layoutId, parent, false);
-        parent.addView(recreated, index);
-        return recreated;
+        actionBar.setClickRunnable(R.id.undo_button, createUndoRedoRunnable(true, effectsBar));
+        actionBar.setClickRunnable(R.id.redo_button, createUndoRedoRunnable(false, effectsBar));
+        actionBar.setClickRunnable(R.id.save_button, createSaveRunnable(effectsBar));
+        actionBar.setClickRunnable(R.id.share_button, createShareRunnable(effectsBar));
+        actionBar.setClickRunnable(R.id.action_bar_back, createBackRunnable(effectsBar));
     }
 
     private SpinnerProgressDialog createProgressDialog() {
@@ -160,7 +134,7 @@ public class PhotoEditor extends Activity {
                                     @Override
                                     public void onComplete(Uri result) {
                                         progressDialog.dismiss();
-                                        actionBar.enableButton(R.id.save_button, (result == null));
+                                        actionBar.updateSave(result == null);
                                         saveUri = result;
                                     }
                                 };
@@ -203,12 +177,12 @@ public class PhotoEditor extends Activity {
                 // Exit effects or go back to the previous activity on pressing back button.
                 if (!effectsBar.exit(null)) {
                     // Pop-up a dialog to save unsaved photo.
-                    if (actionBar.isButtonEnabled(R.id.save_button)) {
+                    if (actionBar.canSave()) {
                         new YesNoCancelDialogBuilder(PhotoEditor.this, new Runnable() {
 
                             @Override
                             public void run() {
-                                actionBar.clickButton(R.id.save_button);
+                                actionBar.clickSave();
                             }
                         }, new Runnable() {
 
@@ -227,7 +201,7 @@ public class PhotoEditor extends Activity {
 
     @Override
     public void onBackPressed() {
-        actionBar.clickButton(R.id.action_bar_back);
+        actionBar.clickBack();
     }
 
     @Override
diff --git a/src/com/android/gallery3d/photoeditor/RestorableView.java b/src/com/android/gallery3d/photoeditor/RestorableView.java
new file mode 100644 (file)
index 0000000..fc98741
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.photoeditor;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+/**
+ * View that holds a single child and could be recreated/restored after orientation changes.
+ */
+public abstract class RestorableView extends FrameLayout {
+
+    private static final float ENABLED_ALPHA = 1;
+    private static final float DISABLED_ALPHA = 0.47f;
+
+    private final HashMap<Integer, Runnable> clickRunnables = new HashMap<Integer, Runnable>();
+    private final HashSet<Integer> changedViews = new HashSet<Integer>();
+    private final LayoutInflater inflater;
+
+    public RestorableView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+
+    protected abstract int childLayoutId();
+
+    private void recreateChildView() {
+        if (getChildCount() != 0) {
+            removeAllViews();
+        }
+        inflater.inflate(childLayoutId(), this, true);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        recreateChildView();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Remember the removing child before recreating the child.
+        View view = getChildAt(0);
+        recreateChildView();
+
+        // Restore its runnables and status of views that have been changed.
+        for (Entry<Integer, Runnable> entry : clickRunnables.entrySet()) {
+            setClickRunnable(entry.getKey(), entry.getValue());
+        }
+        for (int id : changedViews) {
+            View changed = view.findViewById(id);
+            setViewEnabled(id, changed.isEnabled());
+            setViewSelected(id, changed.isSelected());
+        }
+    }
+
+    public void setClickRunnable(int id, final Runnable r) {
+        findViewById(id).setOnClickListener(new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                if (isEnabled()) {
+                    r.run();
+                }
+            }
+        });
+        clickRunnables.put(id, r);
+    }
+
+    public void setViewEnabled(int id, boolean enabled) {
+        View view = findViewById(id);
+        view.setEnabled(enabled);
+        view.setAlpha(enabled ? ENABLED_ALPHA : DISABLED_ALPHA);
+        // Track views whose enabled status has been updated.
+        changedViews.add(id);
+    }
+
+    public void setViewSelected(int id, boolean selected) {
+        findViewById(id).setSelected(selected);
+        // Track views whose selected status has been updated.
+        changedViews.add(id);
+    }
+}