diff options
18 files changed, 443 insertions, 444 deletions
diff --git a/res/drawable/photoeditor_arrow_back.png b/res/drawable/photoeditor_arrow_back.png Binary files differdeleted file mode 100644 index 897a1c11a..000000000 --- a/res/drawable/photoeditor_arrow_back.png +++ /dev/null diff --git a/res/layout/photoeditor_actionbar.xml b/res/layout/photoeditor_actionbar.xml new file mode 100644 index 000000000..82ed41ffc --- /dev/null +++ b/res/layout/photoeditor_actionbar.xml @@ -0,0 +1,52 @@ +<?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. +--> + +<com.android.gallery3d.photoeditor.ActionBar + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/action_bar" + style="@style/TopActionBar"> + + <LinearLayout style="@style/ActionBarLinearLayout"> + + <LinearLayout + android:id="@+id/action_bar_back" + style="@style/ActionBarBackLinearLayout"> + <ImageView style="@style/ActionBarArrow"/> + <ImageView style="@style/ActionBarIcon"/> + </LinearLayout> + + <TextView + android:id="@+id/action_bar_title" + style="@style/ActionBarTitle" + android:text="@string/edit"/> + </LinearLayout> + + <LinearLayout style="@style/ActionBarLinearLayout" android:layout_alignParentRight="true"> + + <ImageButton + android:id="@+id/undo_button" + style="@style/ImageActionButton" + android:src="@drawable/photoeditor_undo"/> + <ImageButton + android:id="@+id/redo_button" + style="@style/ImageActionButton" + android:src="@drawable/photoeditor_redo"/> + <Button + android:id="@+id/save_button" + style="@style/TextActionButton" + android:text="@string/save"/> + </LinearLayout> +</com.android.gallery3d.photoeditor.ActionBar> diff --git a/res/layout/photoeditor_main.xml b/res/layout/photoeditor_main.xml index ed4841ab3..49597ed02 100644 --- a/res/layout/photoeditor_main.xml +++ b/res/layout/photoeditor_main.xml @@ -52,37 +52,6 @@ </LinearLayout> </com.android.gallery3d.photoeditor.EffectsBar> - <com.android.gallery3d.photoeditor.ActionBar android:id="@+id/action_bar" style="@style/TopActionBar"> + <include layout="@layout/photoeditor_actionbar"/> - <LinearLayout style="@style/ActionBarLinearLayout"> - - <LinearLayout - android:id="@+id/action_bar_back" - style="@style/ActionBarBackLinearLayout"> - <ImageView style="@style/ActionBarArrow"/> - <ImageView style="@style/ActionBarIcon"/> - </LinearLayout> - - <TextView - android:id="@+id/action_bar_title" - style="@style/ActionBarText" - android:text="@string/edit"/> - </LinearLayout> - - <LinearLayout style="@style/ActionBarLinearLayout" android:layout_alignParentRight="true"> - - <ImageButton - android:id="@+id/undo_button" - style="@style/ImageActionButton" - android:src="@drawable/photoeditor_undo"/> - <ImageButton - android:id="@+id/redo_button" - style="@style/ImageActionButton" - android:src="@drawable/photoeditor_redo"/> - <Button - android:id="@+id/save_button" - style="@style/TextActionButton" - android:text="@string/save"/> - </LinearLayout> - </com.android.gallery3d.photoeditor.ActionBar> </com.android.gallery3d.photoeditor.Toolbar> diff --git a/res/values-sw320dp-land/photoeditor_dimens.xml b/res/values-sw320dp-land/photoeditor_dimens.xml new file mode 100755 index 000000000..844c928b2 --- /dev/null +++ b/res/values-sw320dp-land/photoeditor_dimens.xml @@ -0,0 +1,20 @@ +<?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. +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <dimen name="action_bar_icon_padding_vertical">4dp</dimen> + <dimen name="action_button_padding_vertical">4dp</dimen> +</resources> diff --git a/res/values-sw320dp-port/photoeditor_dimens.xml b/res/values-sw320dp-port/photoeditor_dimens.xml new file mode 100755 index 000000000..85779d664 --- /dev/null +++ b/res/values-sw320dp-port/photoeditor_dimens.xml @@ -0,0 +1,20 @@ +<?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. +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <dimen name="action_bar_icon_padding_vertical">8dp</dimen> + <dimen name="action_button_padding_vertical">8dp</dimen> +</resources> diff --git a/res/values-sw320dp/photoeditor_dimens.xml b/res/values-sw320dp/photoeditor_dimens.xml index c0f523fcc..5d894a6ce 100755 --- a/res/values-sw320dp/photoeditor_dimens.xml +++ b/res/values-sw320dp/photoeditor_dimens.xml @@ -18,22 +18,13 @@ <dimen name="effect_label_text_size">11sp</dimen> <dimen name="effect_label_width">98dp</dimen> <dimen name="effect_icon_size">72dp</dimen> - <dimen name="effect_padding_left_right">2dp</dimen> + <dimen name="effect_padding_horizontal">2dp</dimen> <dimen name="effects_container_padding">12dp</dimen> - <dimen name="action_bar_height">40dp</dimen> - <dimen name="action_bar_text_size">17sp</dimen> - <dimen name="action_bar_arrow_padding_top">12dp</dimen> - <dimen name="action_bar_arrow_padding_bottom">12dp</dimen> - <dimen name="action_bar_arrow_padding_left">3dp</dimen> - <dimen name="action_bar_arrow_padding_right">0dp</dimen> - <dimen name="action_bar_icon_padding_top">4dp</dimen> - <dimen name="action_bar_icon_padding_bottom">4dp</dimen> + <dimen name="action_bar_arrow_margin_left">3dp</dimen> + <dimen name="action_bar_arrow_margin_right">-3dp</dimen> <dimen name="action_bar_icon_padding_left">0dp</dimen> - <dimen name="action_bar_icon_padding_right">6dp</dimen> - <dimen name="action_button_padding_top">5dp</dimen> - <dimen name="action_button_padding_bottom">5dp</dimen> - <dimen name="action_button_padding_left">15dp</dimen> - <dimen name="action_button_padding_right">15dp</dimen> + <dimen name="action_bar_icon_padding_right">5dp</dimen> + <dimen name="action_button_padding_horizontal">13dp</dimen> <dimen name="effect_tool_panel_padding">12dp</dimen> <dimen name="seekbar_width">290dp</dimen> <dimen name="seekbar_height">29dp</dimen> diff --git a/res/values-sw600dp/photoeditor_dimens.xml b/res/values-sw600dp/photoeditor_dimens.xml index 413347d31..2b9cf6f9f 100755 --- a/res/values-sw600dp/photoeditor_dimens.xml +++ b/res/values-sw600dp/photoeditor_dimens.xml @@ -18,22 +18,15 @@ <dimen name="effect_label_text_size">14sp</dimen> <dimen name="effect_label_width">120dp</dimen> <dimen name="effect_icon_size">90dp</dimen> - <dimen name="effect_padding_left_right">2dp</dimen> + <dimen name="effect_padding_horizontal">2dp</dimen> <dimen name="effects_container_padding">12dp</dimen> - <dimen name="action_bar_height">56dp</dimen> - <dimen name="action_bar_text_size">18sp</dimen> - <dimen name="action_bar_arrow_padding_top">20dp</dimen> - <dimen name="action_bar_arrow_padding_bottom">20dp</dimen> - <dimen name="action_bar_arrow_padding_left">4dp</dimen> - <dimen name="action_bar_arrow_padding_right">0dp</dimen> - <dimen name="action_bar_icon_padding_top">7dp</dimen> - <dimen name="action_bar_icon_padding_bottom">7dp</dimen> + <dimen name="action_bar_arrow_margin_left">3dp</dimen> + <dimen name="action_bar_arrow_margin_right">-3dp</dimen> + <dimen name="action_bar_icon_padding_vertical">4dp</dimen> <dimen name="action_bar_icon_padding_left">0dp</dimen> - <dimen name="action_bar_icon_padding_right">8dp</dimen> - <dimen name="action_button_padding_top">9dp</dimen> - <dimen name="action_button_padding_bottom">9dp</dimen> - <dimen name="action_button_padding_left">22dp</dimen> - <dimen name="action_button_padding_right">22dp</dimen> + <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="effect_tool_panel_padding">16dp</dimen> <dimen name="seekbar_width">560dp</dimen> <dimen name="seekbar_height">33dp</dimen> diff --git a/res/values-sw800dp/photoeditor_dimens.xml b/res/values-sw800dp/photoeditor_dimens.xml index 3b054ceb4..9dab9225a 100755 --- a/res/values-sw800dp/photoeditor_dimens.xml +++ b/res/values-sw800dp/photoeditor_dimens.xml @@ -18,22 +18,15 @@ <dimen name="effect_label_text_size">15sp</dimen> <dimen name="effect_label_width">138dp</dimen> <dimen name="effect_icon_size">100dp</dimen> - <dimen name="effect_padding_left_right">3dp</dimen> + <dimen name="effect_padding_horizontal">3dp</dimen> <dimen name="effects_container_padding">18dp</dimen> - <dimen name="action_bar_height">56dp</dimen> - <dimen name="action_bar_text_size">19sp</dimen> - <dimen name="action_bar_arrow_padding_top">20dp</dimen> - <dimen name="action_bar_arrow_padding_bottom">20dp</dimen> - <dimen name="action_bar_arrow_padding_left">4dp</dimen> - <dimen name="action_bar_arrow_padding_right">0dp</dimen> - <dimen name="action_bar_icon_padding_top">7dp</dimen> - <dimen name="action_bar_icon_padding_bottom">7dp</dimen> + <dimen name="action_bar_arrow_margin_left">3dp</dimen> + <dimen name="action_bar_arrow_margin_right">-3dp</dimen> + <dimen name="action_bar_icon_padding_vertical">4dp</dimen> <dimen name="action_bar_icon_padding_left">0dp</dimen> - <dimen name="action_bar_icon_padding_right">11dp</dimen> - <dimen name="action_button_padding_top">9dp</dimen> - <dimen name="action_button_padding_bottom">9dp</dimen> - <dimen name="action_button_padding_left">28dp</dimen> - <dimen name="action_button_padding_right">28dp</dimen> + <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="effect_tool_panel_padding">18dp</dimen> <dimen name="seekbar_width">560dp</dimen> <dimen name="seekbar_height">35dp</dimen> diff --git a/res/values/photoeditor_strings.xml b/res/values/photoeditor_strings.xml index 4a5d34669..b282a45b7 100644 --- a/res/values/photoeditor_strings.xml +++ b/res/values/photoeditor_strings.xml @@ -40,7 +40,7 @@ <string name="no">No</string> <!-- Text button in the action bar for the user to save edited photo. [CHAR LIMIT=8] --> - <string name="save">Save</string> + <string name="save">SAVE</string> <!-- Name for the photo effect that auto fixes exposure. [CHAR LIMIT=15] --> <string name="autofix">Auto-fix</string> diff --git a/res/values/photoeditor_styles.xml b/res/values/photoeditor_styles.xml index 42b91d19a..fe5ad1a54 100644 --- a/res/values/photoeditor_styles.xml +++ b/res/values/photoeditor_styles.xml @@ -37,8 +37,8 @@ <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:layout_gravity">center_vertical</item> - <item name="android:paddingLeft">@dimen/effect_padding_left_right</item> - <item name="android:paddingRight">@dimen/effect_padding_left_right</item> + <item name="android:paddingLeft">@dimen/effect_padding_horizontal</item> + <item name="android:paddingRight">@dimen/effect_padding_horizontal</item> <item name="android:orientation">vertical</item> </style> <style name="EffectsContainer"> @@ -62,12 +62,11 @@ </style> <style name="ActionBar"> <item name="android:layout_width">fill_parent</item> - <item name="android:layout_height">@dimen/action_bar_height</item> + <item name="android:layout_height">?android:attr/actionBarSize</item> </style> <style name="ActionBarLinearLayout"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">fill_parent</item> - <item name="android:layout_centerVertical">true</item> <item name="android:orientation">horizontal</item> </style> <style name="ActionBarBackLinearLayout" parent="@style/ActionBarLinearLayout"> @@ -82,37 +81,39 @@ <item name="android:scaleType">centerInside</item> <item name="android:adjustViewBounds">true</item> </style> - <style name="ActionBarArrow" parent="@style/ActionBarImageView"> - <item name="android:paddingTop">@dimen/action_bar_arrow_padding_top</item> - <item name="android:paddingBottom">@dimen/action_bar_arrow_padding_bottom</item> - <item name="android:paddingLeft">@dimen/action_bar_arrow_padding_left</item> - <item name="android:paddingRight">@dimen/action_bar_arrow_padding_right</item> - <item name="android:src">@drawable/photoeditor_arrow_back</item> + <style name="ActionBarArrow"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center_vertical</item> + <item name="android:layout_marginLeft">@dimen/action_bar_arrow_margin_left</item> + <item name="android:layout_marginRight">@dimen/action_bar_arrow_margin_right</item> + <item name="android:src">?android:attr/homeAsUpIndicator</item> </style> <style name="ActionBarIcon" parent="@style/ActionBarImageView"> - <item name="android:paddingTop">@dimen/action_bar_icon_padding_top</item> - <item name="android:paddingBottom">@dimen/action_bar_icon_padding_bottom</item> + <item name="android:paddingTop">@dimen/action_bar_icon_padding_vertical</item> + <item name="android:paddingBottom">@dimen/action_bar_icon_padding_vertical</item> <item name="android:paddingLeft">@dimen/action_bar_icon_padding_left</item> <item name="android:paddingRight">@dimen/action_bar_icon_padding_right</item> <item name="android:src">@mipmap/ic_launcher_gallery</item> </style> - <style name="ActionBarText"> + <style name="ActionBarTitle" parent="android:TextAppearance.Holo.Widget.ActionBar.Title"> <item name="android:layout_width">wrap_content</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_gravity">center</item> - <item name="android:textSize">@dimen/action_bar_text_size</item> - <item name="android:textColor">#FFFFFF</item> + <item name="android:layout_height">fill_parent</item> + <item name="android:gravity">center</item> </style> <style name="ImageActionButton" parent="@style/ActionBarImageView"> - <item name="android:paddingTop">@dimen/action_button_padding_top</item> - <item name="android:paddingBottom">@dimen/action_button_padding_bottom</item> - <item name="android:paddingLeft">@dimen/action_button_padding_left</item> - <item name="android:paddingRight">@dimen/action_button_padding_right</item> + <item name="android:paddingTop">@dimen/action_button_padding_vertical</item> + <item name="android:paddingBottom">@dimen/action_button_padding_vertical</item> + <item name="android:paddingLeft">@dimen/action_button_padding_horizontal</item> + <item name="android:paddingRight">@dimen/action_button_padding_horizontal</item> <item name="android:background">?android:attr/selectableItemBackground</item> </style> - <style name="TextActionButton" parent="@style/ActionBarText"> - <item name="android:paddingLeft">@dimen/action_button_padding_left</item> - <item name="android:paddingRight">@dimen/action_button_padding_right</item> + <style name="TextActionButton" parent="android:TextAppearance.Holo.Widget.ActionBar.Menu"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">fill_parent</item> + <item name="android:gravity">center</item> + <item name="android:paddingLeft">@dimen/action_button_padding_horizontal</item> + <item name="android:paddingRight">@dimen/action_button_padding_horizontal</item> <item name="android:background">?android:attr/selectableItemBackground</item> </style> <style name="EffectsMenuActionButton" parent="@style/ImageActionButton"> diff --git a/src/com/android/gallery3d/photoeditor/ActionBar.java b/src/com/android/gallery3d/photoeditor/ActionBar.java index 32eb79274..5a423b905 100644 --- a/src/com/android/gallery3d/photoeditor/ActionBar.java +++ b/src/com/android/gallery3d/photoeditor/ActionBar.java @@ -17,66 +17,36 @@ package com.android.gallery3d.photoeditor; import android.content.Context; -import android.os.Handler; -import android.os.Message; import android.util.AttributeSet; import android.view.View; import android.widget.RelativeLayout; 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. and listens to stack changes for - * enabling/disabling buttons. + * Action bar that contains buttons such as undo, redo, save, etc. */ -public class ActionBar extends RelativeLayout implements FilterStack.StackListener { - - /** - * Listener of action button clicked. - */ - public interface ActionBarListener { - - void onUndo(); +public class ActionBar extends RelativeLayout { - void onRedo(); - - void onSave(); - } - - private static final int ENABLE_BUTTON = 1; private static final float ENABLED_ALPHA = 1; private static final float DISABLED_ALPHA = 0.47f; - private final Handler handler; - private View undo; - private View redo; - private View save; + private final HashMap<Integer, Runnable> buttonRunnables = new HashMap<Integer, Runnable>(); + private final HashSet<Integer> changedButtons = new HashSet<Integer>(); public ActionBar(Context context, AttributeSet attrs) { super(context, attrs); - - handler = new Handler() { - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case ENABLE_BUTTON: - boolean canUndo = (msg.arg1 > 0); - boolean canRedo = (msg.arg2 > 0); - enableButton(undo, canUndo); - enableButton(redo, canRedo); - enableButton(save, canUndo); - break; - } - } - }; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); - // Show action-bar title only when there's still room for it; otherwise, hide it. + // Show the action-bar title only when there's still room for it; otherwise, hide it. int width = 0; for (int i = 0; i < getChildCount(); i++) { width += getChildAt(i).getWidth(); @@ -84,67 +54,58 @@ public class ActionBar extends RelativeLayout implements FilterStack.StackListen findViewById(R.id.action_bar_title).setVisibility(((width > r - l)) ? INVISIBLE: VISIBLE); } - /** - * Initializes with a non-null ActionBarListener. - */ - public void initialize(final ActionBarListener listener) { - undo = findViewById(R.id.undo_button); - undo.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - if (isEnabled()) { - listener.onUndo(); - } - } - }); + @Override + protected void onFinishInflate() { + super.onFinishInflate(); - redo = findViewById(R.id.redo_button); - redo.setOnClickListener(new OnClickListener() { + enableButton(R.id.undo_button, false); + enableButton(R.id.redo_button, false); + enableButton(R.id.save_button, false); + } - @Override - public void onClick(View v) { - if (isEnabled()) { - listener.onRedo(); - } - } - }); + /** + * Restores the passed action-bar. + * + * @return the passed parameter. + */ + 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)); + } + return actionBar; + } - save = findViewById(R.id.save_button); - save.setOnClickListener(new OnClickListener() { + public void setRunnable(int buttonId, final Runnable r) { + findViewById(buttonId).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (isEnabled()) { - listener.onSave(); + r.run(); } } }); - - resetButtons(); + buttonRunnables.put(buttonId, r); } - public void resetButtons() { - // Disable buttons immediately instead of waiting for ENABLE_BUTTON messages which may - // happen some time later after stack changes. - enableButton(undo, false); - enableButton(redo, false); - enableButton(save, false); + public void clickButton(int buttonId) { + findViewById(buttonId).performClick(); } - public void disableSave() { - enableButton(save, false); + public boolean isButtonEnabled(int buttonId) { + return findViewById(buttonId).isEnabled(); } - private void enableButton(View button, boolean enabled) { + public void enableButton(int buttonId, boolean enabled) { + View button = findViewById(buttonId); button.setEnabled(enabled); button.setAlpha(enabled ? ENABLED_ALPHA : DISABLED_ALPHA); - } - @Override - public void onStackChanged(boolean canUndo, boolean canRedo) { - // Listens to stack changes that may come from the worker thread; send messages to enable - // buttons only in the UI thread. - handler.sendMessage(handler.obtainMessage(ENABLE_BUTTON, canUndo ? 1 : 0, canRedo ? 1 : 0)); + // Track buttons whose enabled status has been updated. + changedButtons.add(buttonId); } } diff --git a/src/com/android/gallery3d/photoeditor/FilterStack.java b/src/com/android/gallery3d/photoeditor/FilterStack.java index 3b35aa3e1..5b8d0d743 100644 --- a/src/com/android/gallery3d/photoeditor/FilterStack.java +++ b/src/com/android/gallery3d/photoeditor/FilterStack.java @@ -113,7 +113,7 @@ public class FilterStack { } private void callbackDone(final OnDoneCallback callback) { - // Call back in UI thread to report done. + // GL thread calls back to report UI thread the task is done. photoView.post(new Runnable() { @Override @@ -124,7 +124,16 @@ public class FilterStack { } private void stackChanged() { - stackListener.onStackChanged(!appliedStack.empty(), !redoStack.empty()); + // GL thread calls back to report UI thread the stack is changed. + final boolean canUndo = !appliedStack.empty(); + final boolean canRedo = !redoStack.empty(); + photoView.post(new Runnable() { + + @Override + public void run() { + stackListener.onStackChanged(canUndo, canRedo); + } + }); } public void saveBitmap(final OnDoneBitmapCallback callback) { diff --git a/src/com/android/gallery3d/photoeditor/LoadScreennailTask.java b/src/com/android/gallery3d/photoeditor/LoadScreennailTask.java index 914d50b9d..9ac85af93 100644 --- a/src/com/android/gallery3d/photoeditor/LoadScreennailTask.java +++ b/src/com/android/gallery3d/photoeditor/LoadScreennailTask.java @@ -35,7 +35,7 @@ public class LoadScreennailTask extends AsyncTask<Uri, Void, Bitmap> { */ public interface Callback { - void onComplete(Bitmap bitmap); + void onComplete(Bitmap result); } private static final int SCREENNAIL_WIDTH = 1280; @@ -61,12 +61,12 @@ public class LoadScreennailTask extends AsyncTask<Uri, Void, Bitmap> { } @Override - protected void onPostExecute(Bitmap bitmap) { - if (bitmap == null) { + protected void onPostExecute(Bitmap result) { + if (result == null) { Toast toast = Toast.makeText(context, R.string.loading_failure, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } - callback.onComplete(bitmap); + callback.onComplete(result); } } diff --git a/src/com/android/gallery3d/photoeditor/PhotoEditor.java b/src/com/android/gallery3d/photoeditor/PhotoEditor.java index c7ec48f33..e36071eea 100644 --- a/src/com/android/gallery3d/photoeditor/PhotoEditor.java +++ b/src/com/android/gallery3d/photoeditor/PhotoEditor.java @@ -17,24 +17,25 @@ package com.android.gallery3d.photoeditor; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; 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; /** - * Main activity of the photo editor. + * Main activity of the photo editor that opens a photo and prepares tools for photo editing. */ public class PhotoEditor extends Activity { private Uri uri; private FilterStack filterStack; - private Toolbar toolbar; - private View backButton; + private ActionBar actionBar; @Override public void onCreate(Bundle savedInstanceState) { @@ -44,79 +45,167 @@ public class PhotoEditor extends Activity { Intent intent = getIntent(); uri = Intent.ACTION_EDIT.equalsIgnoreCase(intent.getAction()) ? intent.getData() : null; - final ActionBar actionBar = (ActionBar) findViewById(R.id.action_bar); - filterStack = new FilterStack((PhotoView) findViewById(R.id.photo_view), actionBar); - toolbar = (Toolbar) findViewById(R.id.toolbar); - toolbar.initialize(filterStack); + actionBar = (ActionBar) findViewById(R.id.action_bar); + filterStack = new FilterStack((PhotoView) findViewById(R.id.photo_view), + new FilterStack.StackListener() { - final EffectsBar effectsBar = (EffectsBar) findViewById(R.id.effects_bar); - backButton = findViewById(R.id.action_bar_back); - backButton.setOnClickListener(new View.OnClickListener() { + @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); + } + }); + + 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.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; + } + + private SpinnerProgressDialog createProgressDialog() { + return SpinnerProgressDialog.show((ViewGroup) findViewById(R.id.toolbar)); + } + + private void openPhoto() { + final SpinnerProgressDialog progressDialog = createProgressDialog(); + LoadScreennailTask.Callback callback = new LoadScreennailTask.Callback() { @Override - public void onClick(View v) { - if (actionBar.isEnabled()) { - // Exit effects or go back to the previous activity on pressing back button. - if (!effectsBar.exit(null)) { - tryRun(new Runnable() { + public void onComplete(final Bitmap result) { + filterStack.setPhotoSource(result, new OnDoneCallback() { + + @Override + public void onDone() { + progressDialog.dismiss(); + } + }); + } + }; + new LoadScreennailTask(this, callback).execute(uri); + } + + private Runnable createUndoRedoRunnable(final boolean undo, final EffectsBar effectsBar) { + return new Runnable() { + + @Override + public void run() { + effectsBar.exit(new Runnable() { + + @Override + public void run() { + final SpinnerProgressDialog progressDialog = createProgressDialog(); + OnDoneCallback callback = new OnDoneCallback() { @Override - public void run() { - finish(); + public void onDone() { + progressDialog.dismiss(); + } + }; + if (undo) { + filterStack.undo(callback); + } else { + filterStack.redo(callback); + } + } + }); + } + }; + } + + private Runnable createSaveRunnable(final EffectsBar effectsBar) { + return new Runnable() { + + @Override + public void run() { + effectsBar.exit(new Runnable() { + + @Override + public void run() { + final SpinnerProgressDialog progressDialog = createProgressDialog(); + filterStack.saveBitmap(new OnDoneBitmapCallback() { + + @Override + public void onDone(Bitmap bitmap) { + SaveCopyTask.Callback callback = new SaveCopyTask.Callback() { + + @Override + public void onComplete(Uri result) { + progressDialog.dismiss(); + actionBar.enableButton(R.id.save_button, (result == null)); + } + }; + new SaveCopyTask(PhotoEditor.this, uri, callback).execute(bitmap); } }); } - } + }); } - }); + }; } - private void tryRun(final Runnable runnable) { - if (findViewById(R.id.save_button).isEnabled()) { - // Pop-up a dialog before executing the runnable to save unsaved photo. - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - toolbar.savePhoto(new Runnable() { - - @Override - public void run() { - runnable.run(); - } - }); - } - }) - .setNeutralButton(R.string.no, new DialogInterface.OnClickListener() { + private Runnable createBackRunnable(final EffectsBar effectsBar) { + return new Runnable() { - @Override - public void onClick(DialogInterface dialog, int which) { - runnable.run(); - } - }) - .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void run() { + // 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)) { + new YesNoCancelDialogBuilder(PhotoEditor.this, new Runnable() { - @Override - public void onClick(DialogInterface dialog, int which) { - // no-op - } - }); - builder.setMessage(R.string.save_photo).show(); - return; - } + @Override + public void run() { + actionBar.clickButton(R.id.save_button); + } + }, new Runnable() { - runnable.run(); + @Override + public void run() { + finish(); + } + }, R.string.save_photo).show(); + } else { + finish(); + } + } + } + }; } @Override public void onBackPressed() { - backButton.performClick(); + actionBar.clickButton(R.id.action_bar_back); } @Override protected void onPause() { - // TODO: Close running spinner progress dialogs as all pending operations will be paused. + // TODO: Close running progress dialogs as all pending operations will be paused. super.onPause(); filterStack.onPause(); } @@ -125,6 +214,6 @@ public class PhotoEditor extends Activity { protected void onResume() { super.onResume(); filterStack.onResume(); - toolbar.openPhoto(uri); + openPhoto(); } } diff --git a/src/com/android/gallery3d/photoeditor/SaveCopyTask.java b/src/com/android/gallery3d/photoeditor/SaveCopyTask.java index d46756335..bedd4169a 100644 --- a/src/com/android/gallery3d/photoeditor/SaveCopyTask.java +++ b/src/com/android/gallery3d/photoeditor/SaveCopyTask.java @@ -45,7 +45,7 @@ public class SaveCopyTask extends AsyncTask<Bitmap, Void, Uri> { */ public interface Callback { - void onComplete(Uri uri); + void onComplete(Uri result); } private static final String TIME_STAMP_NAME = "'IMG'_yyyyMMdd_HHmmss"; diff --git a/src/com/android/gallery3d/photoeditor/Toolbar.java b/src/com/android/gallery3d/photoeditor/Toolbar.java index 72f243bab..45ec016bb 100644 --- a/src/com/android/gallery3d/photoeditor/Toolbar.java +++ b/src/com/android/gallery3d/photoeditor/Toolbar.java @@ -17,30 +17,33 @@ package com.android.gallery3d.photoeditor; import android.content.Context; -import android.graphics.Bitmap; -import android.net.Uri; +import android.os.Handler; +import android.os.Message; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.widget.RelativeLayout; import com.android.gallery3d.R; +import java.util.ArrayList; +import java.util.List; + /** - * Toolbar that contains all tools and handles all operations for editing photo. + * Toolbar that contains all tools and controls their idle/awake behaviors from UI thread. */ public class Toolbar extends RelativeLayout { private final ToolbarIdleHandler idleHandler; - private FilterStack filterStack; - private EffectsBar effectsBar; - private ActionBar actionBar; - private Uri sourceUri; public Toolbar(Context context, AttributeSet attrs) { super(context, attrs); idleHandler = new ToolbarIdleHandler(context); setOnHierarchyChangeListener(idleHandler); + idleHandler.killIdle(); } @Override @@ -49,111 +52,61 @@ public class Toolbar extends RelativeLayout { return super.dispatchTouchEvent(ev); } - public void initialize(FilterStack filterStack) { - this.filterStack = filterStack; - effectsBar = (EffectsBar) findViewById(R.id.effects_bar); - effectsBar.initialize(filterStack); - actionBar = (ActionBar) findViewById(R.id.action_bar); - actionBar.initialize(createActionBarListener()); - idleHandler.killIdle(); - } - - private ActionBar.ActionBarListener createActionBarListener() { - actionBar = (ActionBar) findViewById(R.id.action_bar); - return new ActionBar.ActionBarListener() { - - @Override - public void onUndo() { - effectsBar.exit(new Runnable() { - - @Override - public void run() { - final SpinnerProgressDialog progressDialog = SpinnerProgressDialog.show( - Toolbar.this); - filterStack.undo(new OnDoneCallback() { - - @Override - public void onDone() { - progressDialog.dismiss(); + private static class ToolbarIdleHandler implements OnHierarchyChangeListener { + + private static final int MAKE_IDLE = 1; + private static final int TIMEOUT_IDLE = 8000; + + private final List<View> childViews = new ArrayList<View>(); + private final Handler mainHandler; + private final Animation fadeIn; + private final Animation fadeOut; + private boolean idle; + + public ToolbarIdleHandler(Context context) { + mainHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MAKE_IDLE: + if (!idle) { + idle = true; + for (View view : childViews) { + view.startAnimation(fadeOut); + } } - }); + break; } - }); + } + }; + + fadeIn = AnimationUtils.loadAnimation(context, R.anim.photoeditor_fade_in); + fadeOut = AnimationUtils.loadAnimation(context, R.anim.photoeditor_fade_out); + } + + public void killIdle() { + mainHandler.removeMessages(MAKE_IDLE); + if (idle) { + idle = false; + for (View view : childViews) { + view.startAnimation(fadeIn); + } } - - @Override - public void onRedo() { - effectsBar.exit(new Runnable() { - - @Override - public void run() { - final SpinnerProgressDialog progressDialog = SpinnerProgressDialog.show( - Toolbar.this); - filterStack.redo(new OnDoneCallback() { - - @Override - public void onDone() { - progressDialog.dismiss(); - } - }); - } - }); + mainHandler.sendEmptyMessageDelayed(MAKE_IDLE, TIMEOUT_IDLE); + } + + @Override + public void onChildViewAdded(View parent, View child) { + // All child views, except photo-view, will fade out on inactivity timeout. + if (child.getId() != R.id.photo_view) { + childViews.add(child); } + } - @Override - public void onSave() { - effectsBar.exit(new Runnable() { - - @Override - public void run() { - savePhoto(null); - } - }); - } - }; - } - - public void openPhoto(Uri uri) { - sourceUri = uri; - - final SpinnerProgressDialog progressDialog = SpinnerProgressDialog.show(this); - new LoadScreennailTask(getContext(), new LoadScreennailTask.Callback() { - - @Override - public void onComplete(final Bitmap bitmap) { - filterStack.setPhotoSource(bitmap, new OnDoneCallback() { - - @Override - public void onDone() { - progressDialog.dismiss(); - } - }); - } - }).execute(sourceUri); - } - - /** - * Saves photo and executes runnable (if provided) after saving done. - */ - public void savePhoto(final Runnable runnable) { - final SpinnerProgressDialog progressDialog = SpinnerProgressDialog.show(this); - filterStack.saveBitmap(new OnDoneBitmapCallback() { - - @Override - public void onDone(Bitmap bitmap) { - new SaveCopyTask(getContext(), sourceUri, new SaveCopyTask.Callback() { - - @Override - public void onComplete(Uri uri) { - // TODO: Handle saving failure. - progressDialog.dismiss(); - actionBar.disableSave(); - if (runnable != null) { - runnable.run(); - } - } - }).execute(bitmap); - } - }); + @Override + public void onChildViewRemoved(View parent, View child) { + childViews.remove(child); + } } } diff --git a/src/com/android/gallery3d/photoeditor/ToolbarIdleHandler.java b/src/com/android/gallery3d/photoeditor/ToolbarIdleHandler.java deleted file mode 100644 index 5cef56686..000000000 --- a/src/com/android/gallery3d/photoeditor/ToolbarIdleHandler.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.os.Handler; -import android.os.Message; -import android.view.View; -import android.view.ViewGroup.OnHierarchyChangeListener; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; - -import com.android.gallery3d.R; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handler that controls idle/awake behaviors of toolbar's child views from UI thread. - */ -class ToolbarIdleHandler implements OnHierarchyChangeListener { - - private static final int MAKE_IDLE = 1; - private static final int TIMEOUT_IDLE = 8000; - - private final List<View> childViews = new ArrayList<View>(); - private final Handler mainHandler; - private final Animation fadeIn; - private final Animation fadeOut; - private boolean idle; - - public ToolbarIdleHandler(Context context) { - mainHandler = new Handler() { - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MAKE_IDLE: - if (!idle) { - idle = true; - for (View view : childViews) { - view.startAnimation(fadeOut); - } - } - break; - } - } - }; - - fadeIn = AnimationUtils.loadAnimation(context, R.anim.photoeditor_fade_in); - fadeOut = AnimationUtils.loadAnimation(context, R.anim.photoeditor_fade_out); - } - - public void killIdle() { - mainHandler.removeMessages(MAKE_IDLE); - if (idle) { - idle = false; - for (View view : childViews) { - view.startAnimation(fadeIn); - } - } - mainHandler.sendEmptyMessageDelayed(MAKE_IDLE, TIMEOUT_IDLE); - } - - @Override - public void onChildViewAdded(View parent, View child) { - // All child views, except photo-view, will fade out on inactivity timeout. - if (child.getId() != R.id.photo_view) { - childViews.add(child); - } - } - - @Override - public void onChildViewRemoved(View parent, View child) { - childViews.remove(child); - } -} diff --git a/src/com/android/gallery3d/photoeditor/YesNoCancelDialogBuilder.java b/src/com/android/gallery3d/photoeditor/YesNoCancelDialogBuilder.java new file mode 100644 index 000000000..fcc99c0d3 --- /dev/null +++ b/src/com/android/gallery3d/photoeditor/YesNoCancelDialogBuilder.java @@ -0,0 +1,39 @@ +package com.android.gallery3d.photoeditor; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; + +import com.android.gallery3d.R; + +/** + * Alert dialog builder that builds a simple Yes/No/Cancel dialog. + */ +public class YesNoCancelDialogBuilder extends AlertDialog.Builder { + + public YesNoCancelDialogBuilder(Context context, final Runnable yes, final Runnable no, + int messageId) { + super(context); + setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + yes.run(); + } + }) + .setNeutralButton(R.string.no, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + no.run(); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + // no-op + } + }).setMessage(messageId); + } +} |
