summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Hsieh <andrewhsieh@google.com>2011-07-22 11:24:17 -0700
committerAndrew Hsieh <andrewhsieh@google.com>2011-07-22 11:24:17 -0700
commit0f8a40e4cfdc5f6cd47c22e81f69ed0446067c54 (patch)
tree6f25f7ed29d5314f4161f5ad1f7df91a81d78a20
parentb2080cd3e724758f3437c210c04d65a413ea7f15 (diff)
downloadgdk-0f8a40e4cfdc5f6cd47c22e81f69ed0446067c54.tar.gz
Adding PhotoEditor & quake res/; Misc fixes
Change-Id: I3d96725b2c6c96eabddc484a62a1919ac20814be
-rw-r--r--platforms/android-portable/arch-llvm/usr/README5
l---------platforms/android-portable/arch-llvm/usr/include1
-rw-r--r--samples/PhotoEditor/Android.mk21
-rw-r--r--samples/PhotoEditor/AndroidManifest.xml29
-rw-r--r--samples/PhotoEditor/jni/Android-portable.mk46
-rw-r--r--samples/PhotoEditor/jni/Android.mk49
-rw-r--r--samples/PhotoEditor/jni/Application.mk1
-rw-r--r--samples/PhotoEditor/jni/_jni.cpp87
-rw-r--r--samples/PhotoEditor/jni/_jni.h69
-rw-r--r--samples/PhotoEditor/jni/_jnif.h23
-rw-r--r--samples/PhotoEditor/jni/backlight.cpp107
-rw-r--r--samples/PhotoEditor/jni/blur.cpp57
-rw-r--r--samples/PhotoEditor/jni/colortemp.cpp98
-rw-r--r--samples/PhotoEditor/jni/convolution.cpp116
-rw-r--r--samples/PhotoEditor/jni/convolution.h35
-rw-r--r--samples/PhotoEditor/jni/copy.cpp55
-rw-r--r--samples/PhotoEditor/jni/crossprocess.cpp89
-rw-r--r--samples/PhotoEditor/jni/duotone.cpp94
-rw-r--r--samples/PhotoEditor/jni/fisheye.cpp160
-rw-r--r--samples/PhotoEditor/jni/flip.cpp142
-rw-r--r--samples/PhotoEditor/jni/grain.cpp184
-rw-r--r--samples/PhotoEditor/jni/grayscale.cpp80
-rw-r--r--samples/PhotoEditor/jni/heq.cpp254
-rw-r--r--samples/PhotoEditor/jni/negative.cpp71
-rw-r--r--samples/PhotoEditor/jni/quantize.cpp78
-rw-r--r--samples/PhotoEditor/jni/redeye.cpp109
-rw-r--r--samples/PhotoEditor/jni/saturate.cpp150
-rw-r--r--samples/PhotoEditor/jni/sepia.cpp79
-rw-r--r--samples/PhotoEditor/jni/sharpen.cpp55
-rw-r--r--samples/PhotoEditor/jni/tint.cpp92
-rw-r--r--samples/PhotoEditor/jni/utils.cpp73
-rw-r--r--samples/PhotoEditor/jni/utils.h70
-rw-r--r--samples/PhotoEditor/jni/vignetting.cpp88
-rw-r--r--samples/PhotoEditor/jni/warmify.cpp106
-rw-r--r--samples/PhotoEditor/jni/whiteblack.cpp113
-rw-r--r--samples/PhotoEditor/res/anim/fade_in.xml21
-rw-r--r--samples/PhotoEditor/res/anim/fade_out.xml21
-rw-r--r--samples/PhotoEditor/res/anim/wheel_hide.xml20
-rw-r--r--samples/PhotoEditor/res/anim/wheel_show.xml20
-rw-r--r--samples/PhotoEditor/res/drawable/actionbar_translucent.9.pngbin0 -> 155 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/arrow_back.pngbin0 -> 602 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/arrow_down.pngbin0 -> 908 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/arrow_right.pngbin0 -> 934 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/autofix_off.pngbin0 -> 51787 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/autofix_on.pngbin0 -> 52164 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/colortemperature_off.pngbin0 -> 51109 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/colortemperature_on.pngbin0 -> 51494 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/crop_height_holo.pngbin0 -> 2390 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/crop_off.pngbin0 -> 50359 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/crop_on.pngbin0 -> 50881 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/crop_width_holo.pngbin0 -> 2413 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/crossprocess_off.pngbin0 -> 40152 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/crossprocess_on.pngbin0 -> 39963 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/documentary_off.pngbin0 -> 28936 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/documentary_on.pngbin0 -> 29415 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/doodle_off.pngbin0 -> 50595 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/doodle_on.pngbin0 -> 50991 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/duotone_off.pngbin0 -> 38379 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/duotone_on.pngbin0 -> 38614 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/filllight_off.pngbin0 -> 45771 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/filllight_on.pngbin0 -> 46019 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/fisheye_off.pngbin0 -> 47427 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/fisheye_on.pngbin0 -> 47744 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/flip_off.pngbin0 -> 50359 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/flip_on.pngbin0 -> 50881 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/grain_off.pngbin0 -> 63830 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/grain_on.pngbin0 -> 64516 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/grayscale_off.pngbin0 -> 29671 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/grayscale_on.pngbin0 -> 29990 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/group_header.9.pngbin0 -> 1239 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/highlight_off.pngbin0 -> 45963 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/highlight_on.pngbin0 -> 46247 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/icon.pngbin0 -> 6222 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/lomoish_off.pngbin0 -> 47194 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/lomoish_on.pngbin0 -> 47798 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/negative_off.pngbin0 -> 50435 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/negative_on.pngbin0 -> 50580 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/posterize_off.pngbin0 -> 41012 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/posterize_on.pngbin0 -> 41384 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/quickview.pngbin0 -> 1427 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/quickview_on.pngbin0 -> 2564 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/redeye_off.pngbin0 -> 50302 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/redeye_on.pngbin0 -> 50730 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/redo.pngbin0 -> 1791 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/rotate_off.pngbin0 -> 50379 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/rotate_on.pngbin0 -> 50717 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/saturation_off.pngbin0 -> 47988 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/saturation_on.pngbin0 -> 48481 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/saveas.pngbin0 -> 1053 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/scale_wheel_background.pngbin0 -> 79114 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/sepia_off.pngbin0 -> 33255 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/sepia_on.pngbin0 -> 33605 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/shadow_off.pngbin0 -> 49671 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/shadow_on.pngbin0 -> 50149 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/sharpen_off.pngbin0 -> 64635 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/sharpen_on.pngbin0 -> 64724 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/softfocus_off.pngbin0 -> 36563 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/softfocus_on.pngbin0 -> 36836 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/straighten_off.pngbin0 -> 49111 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/straighten_on.pngbin0 -> 49361 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/tint_off.pngbin0 -> 34825 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/tint_on.pngbin0 -> 35226 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/undo.pngbin0 -> 1303 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/vignette_off.pngbin0 -> 43507 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/vignette_on.pngbin0 -> 44018 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/warmify_off.pngbin0 -> 48761 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/warmify_on.pngbin0 -> 49250 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/wheel_knot_normal.pngbin0 -> 65929 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/wheel_knot_pressed.pngbin0 -> 57169 bytes
-rw-r--r--samples/PhotoEditor/res/drawable/wheel_knot_selector.xml20
-rw-r--r--samples/PhotoEditor/res/layout/artistic_effects.xml121
-rw-r--r--samples/PhotoEditor/res/layout/color_effects.xml121
-rw-r--r--samples/PhotoEditor/res/layout/exposure_effects.xml77
-rw-r--r--samples/PhotoEditor/res/layout/fix_effects.xml110
-rw-r--r--samples/PhotoEditor/res/layout/main.xml113
-rw-r--r--samples/PhotoEditor/res/values-ar/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-bg/strings.xml68
-rw-r--r--samples/PhotoEditor/res/values-ca/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-cs/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-da/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-de/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-el/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-en-rGB/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-es-rUS/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-es/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-fa/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-fi/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-fr/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-hr/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-hu/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-in/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-it/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-iw/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-ja/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-ko/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-lt/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-lv/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-ms/strings.xml68
-rw-r--r--samples/PhotoEditor/res/values-nb/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-nl/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-pl/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-pt-rPT/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-pt/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-ro/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-ru/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-sk/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-sl/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-sr/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-sv/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-th/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-tl/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-tr/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-uk/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-vi/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-zh-rCN/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values-zh-rTW/strings.xml63
-rw-r--r--samples/PhotoEditor/res/values/arrays.xml335
-rw-r--r--samples/PhotoEditor/res/values/attrs.xml22
-rw-r--r--samples/PhotoEditor/res/values/colors.xml47
-rwxr-xr-xsamples/PhotoEditor/res/values/dimen.xml52
-rw-r--r--samples/PhotoEditor/res/values/strings.xml159
-rw-r--r--samples/PhotoEditor/res/values/styles.xml166
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/ActionBar.java166
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/BitmapUtils.java201
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/EffectsBar.java241
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/EffectsGroup.java123
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/FilterStack.java263
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/IconIndicator.java80
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/LoadScreennailTask.java68
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/Photo.java87
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/PhotoEditor.java157
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/PhotoOutputCallback.java25
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/PhotoView.java205
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/RectUtils.java88
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/SaveCopyTask.java142
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/SpinnerProgressDialog.java69
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/Toolbar.java206
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/ToolbarIdleHandler.java102
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/ToolbarLayoutHandler.java50
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/AutoFixAction.java60
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/ColorPath.java57
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/ColorTemperatureAction.java57
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/ColorWheel.java330
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/CropAction.java77
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/CropView.java330
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/CrossProcessAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/DocumentaryAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleAction.java83
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleView.java138
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/DuotoneAction.java48
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/FillLightAction.java57
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/FilterAction.java184
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/FisheyeAction.java60
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/FlipAction.java99
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/GrainAction.java60
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/GrayscaleAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/HighlightAction.java57
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/LomoishAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/NegativeAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/PosterizeAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/RedEyeAction.java53
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/RotateAction.java115
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/RotateView.java222
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/SaturationAction.java57
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/ScaleWheel.java226
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/SepiaAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/ShadowAction.java57
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/SharpenAction.java60
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/SoftFocusAction.java57
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/StraightenAction.java88
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/TintAction.java61
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/TouchView.java117
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/VignetteAction.java60
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/actions/WarmifyAction.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/animation/AnimationPair.java41
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/animation/FadeAnimation.java38
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/animation/Rotate3DAnimation.java144
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/AutoFixFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/ColorTemperatureFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/CropFilter.java43
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/CrossProcessFilter.java34
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/DocumentaryFilter.java36
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/DoodleFilter.java63
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/DuotoneFilter.java39
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/FillLightFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/Filter.java47
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/FisheyeFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/FlipFilter.java47
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/GrainFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/GrayscaleFilter.java34
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/HighlightFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/ImageUtils.java60
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/LomoishFilter.java37
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/NegativeFilter.java34
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/PosterizeFilter.java34
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/RedEyeFilter.java49
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/RotateFilter.java40
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/SaturationFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/SepiaFilter.java34
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/ShadowFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/SharpenFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/SoftFocusFilter.java42
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/StraightenFilter.java54
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/TintFilter.java37
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/VignetteFilter.java45
-rw-r--r--samples/PhotoEditor/src/com/android/photoeditor/filters/WarmifyFilter.java34
-rw-r--r--samples/quake/res/drawable/app_quake.pngbin0 -> 8026 bytes
-rw-r--r--samples/quake/res/layout/downloader.xml75
-rw-r--r--samples/quake/res/layout/downloader_title.xml55
-rw-r--r--samples/quake/res/menu/menu.xml6
-rw-r--r--samples/quake/res/values/strings.xml43
-rw-r--r--toolchains/llvm/setup.mk3
252 files changed, 14008 insertions, 1 deletions
diff --git a/platforms/android-portable/arch-llvm/usr/README b/platforms/android-portable/arch-llvm/usr/README
new file mode 100644
index 0000000..f634f09
--- /dev/null
+++ b/platforms/android-portable/arch-llvm/usr/README
@@ -0,0 +1,5 @@
+This file is a placeholder that reminds us
+this directory should put the portable headers.
+Currently a link is poinitng to ndk/platform
+
+This README should be removed in future.
diff --git a/platforms/android-portable/arch-llvm/usr/include b/platforms/android-portable/arch-llvm/usr/include
new file mode 120000
index 0000000..914bdcf
--- /dev/null
+++ b/platforms/android-portable/arch-llvm/usr/include
@@ -0,0 +1 @@
+../../../../../ndk/platforms/android-9/arch-arm/usr/include \ No newline at end of file
diff --git a/samples/PhotoEditor/Android.mk b/samples/PhotoEditor/Android.mk
new file mode 100644
index 0000000..87acd60
--- /dev/null
+++ b/samples/PhotoEditor/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PhotoEditor
+
+LOCAL_JNI_SHARED_LIBRARIES := libjni_photoeditor
+
+LOCAL_REQUIRED_MODULES := libjni_photoeditor
+
+LOCAL_SDK_VERSION := 11
+
+include $(BUILD_PACKAGE)
+
+ifeq ($(strip $(LOCAL_PACKAGE_OVERRIDES)),)
+include $(call all-makefiles-under, $(LOCAL_PATH))
+endif
diff --git a/samples/PhotoEditor/AndroidManifest.xml b/samples/PhotoEditor/AndroidManifest.xml
new file mode 100644
index 0000000..f70bbd6
--- /dev/null
+++ b/samples/PhotoEditor/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest package="com.android.photoeditor"
+ android:versionCode="10001"
+ android:versionName="1.0.001" xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <original-package android:name="com.android.photoeditor" />
+
+ <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="11" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name"
+ android:largeHeap="true"
+ android:hardwareAccelerated="true">
+ <activity android:name=".PhotoEditor"
+ android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
+ android:clearTaskOnLaunch="true"
+ android:configChanges="orientation"
+ android:screenOrientation="landscape">
+ <intent-filter>
+ <action android:name="android.intent.action.EDIT" />
+ <data android:mimeType="image/*" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/samples/PhotoEditor/jni/Android-portable.mk b/samples/PhotoEditor/jni/Android-portable.mk
new file mode 100644
index 0000000..861ff01
--- /dev/null
+++ b/samples/PhotoEditor/jni/Android-portable.mk
@@ -0,0 +1,46 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+#LOCAL_SHARED_LIBRARIES := libm liblog libjnigraphics
+#LOCAL_LDLIBS := -lm -llog -ljnigraphics -lbcc
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libjni_photoeditor_portable
+
+LOCAL_SRC_FILES := _jni.cpp \
+ utils.cpp \
+ backlight.cpp \
+ blur.cpp \
+ colortemp.cpp \
+ convolution.cpp \
+ copy.cpp \
+ crossprocess.cpp \
+ duotone.cpp \
+ fisheye.cpp \
+ flip.cpp \
+ grain.cpp \
+ grayscale.cpp \
+ heq.cpp \
+ negative.cpp \
+ quantize.cpp \
+ redeye.cpp \
+ saturate.cpp \
+ sepia.cpp \
+ sharpen.cpp \
+ tint.cpp \
+ vignetting.cpp \
+ warmify.cpp \
+ whiteblack.cpp
+
+# This doesn't work on non-ARM yet.
+ifeq ($(TARGET_ARCH), arm)
+ LOCAL_NDK_VERSION := 5
+ LOCAL_SDK_VERSION := 9
+endif
+
+LOCAL_C_INCLUDES := $(OUT)/../../../../frameworks/compile/libbcc/include
+#LOCAL_LDFLAGS := -L$(OUT)/system/lib
+
+include $(BUILD_BITCODE)
diff --git a/samples/PhotoEditor/jni/Android.mk b/samples/PhotoEditor/jni/Android.mk
new file mode 100644
index 0000000..c94794a
--- /dev/null
+++ b/samples/PhotoEditor/jni/Android.mk
@@ -0,0 +1,49 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+#LOCAL_SHARED_LIBRARIES := libm liblog libjnigraphics
+LOCAL_LDLIBS := -lm -llog -ljnigraphics -lbcc
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libjni_photoeditor
+
+LOCAL_SRC_FILES := _jni.cpp \
+ utils.cpp \
+ backlight.cpp \
+ blur.cpp \
+ colortemp.cpp \
+ convolution.cpp \
+ copy.cpp \
+ crossprocess.cpp \
+ duotone.cpp \
+ fisheye.cpp \
+ flip.cpp \
+ grain.cpp \
+ grayscale.cpp \
+ heq.cpp \
+ negative.cpp \
+ quantize.cpp \
+ redeye.cpp \
+ saturate.cpp \
+ sepia.cpp \
+ sharpen.cpp \
+ tint.cpp \
+ vignetting.cpp \
+ warmify.cpp \
+ whiteblack.cpp
+
+# This doesn't work on non-ARM yet.
+ifeq ($(TARGET_ARCH), arm)
+ LOCAL_NDK_VERSION := 5
+ LOCAL_SDK_VERSION := 9
+endif
+
+LOCAL_CFLAGS := -Werror \
+ -I$(OUT)/../../../../frameworks/compile/libbcc/include \
+ -I$(OUT)/../../../../dalvik/libnativehelper/include
+
+LOCAL_LDFLAGS := -L$(OUT)/system/lib
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/samples/PhotoEditor/jni/Application.mk b/samples/PhotoEditor/jni/Application.mk
new file mode 100644
index 0000000..f05229c
--- /dev/null
+++ b/samples/PhotoEditor/jni/Application.mk
@@ -0,0 +1 @@
+APP_ABI := armeabi armeabi-v7a
diff --git a/samples/PhotoEditor/jni/_jni.cpp b/samples/PhotoEditor/jni/_jni.cpp
new file mode 100644
index 0000000..87263ff
--- /dev/null
+++ b/samples/PhotoEditor/jni/_jni.cpp
@@ -0,0 +1,87 @@
+#if !defined(__clang__)
+
+#include <nativehelper/jni.h>
+#include <android/log.h>
+#include <bcc/bcc.h>
+#include <dlfcn.h>
+
+
+#define LOG_TAG "libjni_photoeditor"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+#include "_jni.h"
+
+#define DEFINE(f) { #f, 0 },
+JNIFuncType JNIFunc[JNI_max] =
+{
+#include "_jnif.h"
+};
+#undef DEFINE
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ LOGI("JNI_OnLoad\n");
+
+#define DEFINE(f) JNIFunc[ JNI_ ## f ].func_ptr = (void *)f;
+ #include "_jnif.h"
+#undef DEFINE
+
+ return JNI_VERSION_1_4;
+}
+
+static void* lookupSymbol(void* pContext, const char* name)
+{
+ return (void*) dlsym(RTLD_DEFAULT, name);
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_com_android_photoeditor_filters_ImageUtils_init(
+ JNIEnv *env, jobject obj, jbyteArray scriptRef, jint length)
+{
+ void *new_func_ptr[JNI_max];
+ int i, all_func_found = 1;
+
+ BCCScriptRef script_ref = bccCreateScript();
+ jbyte* script_ptr = (jbyte *)env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0);
+ LOGI("BCC Script Len: %d", length);
+ if (bccReadBC(script_ref, "libjni_photoeditor_portable.bc", (const char*)script_ptr, length, 0)) {
+ LOGE("Error! Cannot bccReadBc");
+ return JNI_FALSE;
+ }
+ if (script_ptr) {
+ env->ReleasePrimitiveArrayCritical(scriptRef, script_ptr, 0);
+ }
+ #if 0
+ if (bccLinkFile(script_ref, "/system/lib/libclcore.bc", 0)) {
+ LOGE("Error! Cannot bccLinkBC");
+ return JNI_FALSE;
+ }
+ #endif
+ bccRegisterSymbolCallback(script_ref, lookupSymbol, NULL);
+ if (bccPrepareExecutableEx(script_ref, ".", "/data/data/com.android.photoeditor/photoeditorLLVM", 0)) {
+ LOGE("Error! Cannot bccPrepareExecutableEx");
+ return JNI_FALSE;
+ }
+ for(i=0; i<JNI_max; i++) {
+ new_func_ptr[i] = bccGetFuncAddr(script_ref, JNIFunc[i].func_name);
+ if (new_func_ptr[i] == NULL) {
+ LOGE("Error! Cannot find %s()\n", JNIFunc[i].func_name);
+ all_func_found = 0;
+ //return JNI_FALSE;
+ } else
+ LOGI("Found %s() @ 0x%x", JNIFunc[i].func_name, (unsigned)new_func_ptr[i]);
+ }
+
+ //bccDisposeScript(script_ref);
+
+ if (all_func_found)
+ {
+ LOGI("Use LLVM version");
+ for(i=0; i<JNI_max; i++)
+ JNIFunc[i].func_ptr = new_func_ptr[i];
+ }
+
+ return JNI_TRUE;
+}
+
+#endif // __clang__ \ No newline at end of file
diff --git a/samples/PhotoEditor/jni/_jni.h b/samples/PhotoEditor/jni/_jni.h
new file mode 100644
index 0000000..b806a15
--- /dev/null
+++ b/samples/PhotoEditor/jni/_jni.h
@@ -0,0 +1,69 @@
+extern "C"
+{
+ void Backlight(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat backlight);
+ void Blur(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+ void ColorTemp(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+ void Copy(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void CrossProcess(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void Duotone(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint color1, jint color2);
+ void Fisheye(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat focus_x, jfloat focus_y, jfloat scale);
+ void FlipHorizontal(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void FlipVertical(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void FlipBoth(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void Grain(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat noise_scale);
+ void Grayscale(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+ void HEQ(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+ void Negative(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void Quantize(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void RedEye(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jobjectArray redeye_positions, jfloat radius, jfloat intensity);
+ void Saturation(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+ void Sepia(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void Sharpen(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+ void Tint(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint tint);
+ void Vignetting(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat range);
+ void Warmify(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+ void WhiteBlack(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat white, jfloat black);
+};
+
+typedef void (*pBacklightType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat backlight);
+typedef void (*pBlurType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+typedef void (*pColorTempType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+typedef void (*pCopyType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pCrossProcessType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pDuotoneType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint color1, jint color2);
+typedef void (*pFisheyeType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat focus_x, jfloat focus_y, jfloat scale);
+typedef void (*pFlipHorizontalType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pFlipVerticalType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pFlipBothType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pGrainType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat noise_scale);
+typedef void (*pGrayscaleType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+typedef void (*pHEQType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+typedef void (*pNegativeType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pQuantizeType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pRedEyeType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jobjectArray redeye_positions, jfloat radius, jfloat intensity);
+typedef void (*pSaturationType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+typedef void (*pSepiaType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pSharpenType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale);
+typedef void (*pTintType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint tint);
+typedef void (*pVignettingType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat range);
+typedef void (*pWarmifyType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap);
+typedef void (*pWhiteBlackType)(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat white, jfloat black);
+
+#define DEFINE(f) JNI_ ## f,
+enum
+{
+#include "_jnif.h"
+ JNI_max
+};
+#undef DEFINE
+
+
+typedef struct
+{
+ const char *func_name;
+ void *func_ptr;
+} JNIFuncType;
+
+extern JNIFuncType JNIFunc[JNI_max];
+
+
diff --git a/samples/PhotoEditor/jni/_jnif.h b/samples/PhotoEditor/jni/_jnif.h
new file mode 100644
index 0000000..da3bf51
--- /dev/null
+++ b/samples/PhotoEditor/jni/_jnif.h
@@ -0,0 +1,23 @@
+DEFINE(Backlight)
+DEFINE(Blur)
+DEFINE(ColorTemp)
+DEFINE(Copy)
+DEFINE(CrossProcess)
+DEFINE(Duotone)
+DEFINE(Fisheye)
+DEFINE(FlipHorizontal)
+DEFINE(FlipVertical)
+DEFINE(FlipBoth)
+DEFINE(Grain)
+DEFINE(Grayscale)
+DEFINE(HEQ)
+DEFINE(Negative)
+DEFINE(Quantize)
+DEFINE(RedEye)
+DEFINE(Saturation)
+DEFINE(Sepia)
+DEFINE(Sharpen)
+DEFINE(Tint)
+DEFINE(Vignetting)
+DEFINE(Warmify)
+DEFINE(WhiteBlack)
diff --git a/samples/PhotoEditor/jni/backlight.cpp b/samples/PhotoEditor/jni/backlight.cpp
new file mode 100644
index 0000000..7beeecb
--- /dev/null
+++ b/samples/PhotoEditor/jni/backlight.cpp
@@ -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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeBacklight(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat backlight) {
+ pBacklightType f = (pBacklightType)JNIFunc[JNI_Backlight].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, backlight);
+}
+
+extern "C" void Backlight(JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat backlight) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Backlight failed, error=%d", ret);
+ return;
+ }
+
+ const float fade_gamma = 0.3f;
+ const float amt = 1.0f - backlight;
+ const float mult = 1.0f / (amt * 0.7f + 0.3f);
+ const float faded = fade_gamma + (1.0f - fade_gamma) * mult;
+ const float igamma = 1.0f / faded;
+
+ int lookup[256];
+ for (int i = 0; i < 256; i++) {
+ float value = static_cast<float>(pow(mult * i / 255.0f, igamma));
+ if (value > 256.0f) {
+ value = 256.0f;
+ }
+ lookup[i] = floor(255.0f * value) - i;
+ }
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+ while (src < src_line_end) {
+ int32_t src_red = src->rgba8[0];
+ int32_t src_green = src->rgba8[1];
+ int32_t src_blue = src->rgba8[2];
+ int32_t src_alpha = src->rgba8[3];
+
+ int32_t lightmask = (src_red + src_green * 2 + src_blue) >> 2;
+ int32_t backmask = 256 * (255 - lightmask);
+
+ int32_t diff_red = lookup[src_red] * backmask;
+ int32_t diff_green = lookup[src_green] * backmask;
+ int32_t diff_blue = lookup[src_blue] * backmask;
+
+ int32_t dst_red = src_red + (diff_red >> 16);
+ int32_t dst_green = src_green + (diff_green >> 16);
+ int32_t dst_blue = src_blue + (diff_blue >> 16);
+
+ if (dst_red > 255) {
+ dst_red = 255;
+ }
+ if (dst_green > 255) {
+ dst_green = 255;
+ }
+ if (dst_blue > 255) {
+ dst_blue = 255;
+ }
+
+ *dst = (src_alpha << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/blur.cpp b/samples/PhotoEditor/jni/blur.cpp
new file mode 100644
index 0000000..1888c35
--- /dev/null
+++ b/samples/PhotoEditor/jni/blur.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "convolution.h"
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::convolution::SpecialConvolution;
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeBlur(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ pBlurType f = (pBlurType)JNIFunc[JNI_Blur].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, scale);
+}
+
+extern "C" void Blur(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Sharpen failed, error=%d", ret);
+ return;
+ }
+
+ /* Divide blur scale by a factor of 5 which comes from trials. Due to a small (3x3)
+ * kernel is used for convolution, small blur factor causes double edge. */
+ SpecialConvolution(&src_info, &dst_info, src_pixels, dst_pixels, scale / 5);
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/colortemp.cpp b/samples/PhotoEditor/jni/colortemp.cpp
new file mode 100644
index 0000000..07a9049
--- /dev/null
+++ b/samples/PhotoEditor/jni/colortemp.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+const int k256Multiply255 = 65280;
+
+/*
+ * @param scale ranges from -0.5 to 0.5.
+ */
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeColorTemp(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ pColorTempType f = (pColorTempType)JNIFunc[JNI_ColorTemp].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, scale);
+}
+
+extern "C" void ColorTemp(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in ColorTemp failed, error=%d", ret);
+ return;
+ }
+
+ int curve[256];
+ for (int i = 0; i < 256; i++) {
+ curve[i] = i * (256 - i);
+ }
+
+ int ics = static_cast<int>(scale * 256);
+ int icsr = ics;
+ int icsg = ics > 0 ? ics : 0;
+ int icsb = ics;
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ int32_t src_red = src->rgba8[0];
+ int32_t src_green = src->rgba8[1];
+ int32_t src_blue = src->rgba8[2];
+ int32_t src_alpha = src->rgba8[3];
+
+ int32_t dst_red = src_red + ((curve[src_red] * icsr) >> 15);
+ int32_t dst_green = src_green + ((curve[src_green] * icsg) >> 17);
+ int32_t dst_blue = src_blue - ((curve[src_blue] * icsb) >> 15);
+
+ int rgb_max = MAX3(dst_red, dst_green, dst_blue);
+ if (rgb_max > 255) {
+ int invmax = k256Multiply255 / rgb_max;
+ dst_red = dst_red * invmax >> 8;
+ dst_green = dst_green * invmax >> 8;
+ dst_blue = dst_blue * invmax >> 8;
+ }
+
+ *dst = (src_alpha << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/convolution.cpp b/samples/PhotoEditor/jni/convolution.cpp
new file mode 100644
index 0000000..9be537a
--- /dev/null
+++ b/samples/PhotoEditor/jni/convolution.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+
+#include <cstdlib>
+
+#include "utils.h"
+
+using android::apps::photoeditor::utils::clamp;
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace android {
+namespace apps {
+namespace photoeditor {
+namespace convolution {
+
+/*
+ * Used for convolution of following kernel:
+ * n n n
+ * n c n
+ * n n n
+ * n: neighbor, c: center
+ */
+void SpecialConvolution(AndroidBitmapInfo *src_info, AndroidBitmapInfo *dst_info,
+ void *src_pixels, void *dst_pixels, float neighbor) {
+
+ if (neighbor == 0) {
+ memcpy(dst_pixels, src_pixels, src_info->stride * src_info->height);
+ return;
+ }
+
+ /* Copy the first row to dst_pixels */
+ memcpy(dst_pixels, src_pixels, src_info->stride);
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info->stride;
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info->stride;
+
+ const int32_t kShiftBits = 10;
+ int32_t fixed_neighbor = static_cast<int32_t>((1 << kShiftBits) * neighbor);
+ int32_t fixed_center = static_cast<int32_t>((1 << kShiftBits) * (1 - neighbor * 8));
+
+ for (uint32_t scan_line = 1; scan_line < dst_info->height - 1; scan_line++) {
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ uint32_t* src_prev = reinterpret_cast<uint32_t*>
+ (reinterpret_cast<char*>(src_pixels) - src_info->stride);
+ uint32_t* src_next = reinterpret_cast<uint32_t*>
+ (reinterpret_cast<char*>(src_pixels) + src_info->stride);
+
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ *dst++ = src->rgba32;
+ src++;
+ src_prev++;
+ src_next++;
+
+ uint32_t* dst_line_end = dst + dst_info->width - 1;
+ while (dst < dst_line_end) {
+ int32_t src_red = src->rgba8[0];
+ int32_t src_green = src->rgba8[1];
+ int32_t src_blue = src->rgba8[2];
+ int32_t src_alpha = src->rgba8[3];
+
+ uint8_t* n1 = reinterpret_cast<uint8_t*>(src_prev - 1);
+ uint8_t* n2 = reinterpret_cast<uint8_t*>(src - 1);
+ uint8_t* n3 = reinterpret_cast<uint8_t*>(src_next - 1);
+ uint8_t* n4 = reinterpret_cast<uint8_t*>(src_prev);
+ uint8_t* n5 = reinterpret_cast<uint8_t*>(src_next);
+ uint8_t* n6 = reinterpret_cast<uint8_t*>(src_prev + 1);
+ uint8_t* n7 = reinterpret_cast<uint8_t*>(src + 1);
+ uint8_t* n8 = reinterpret_cast<uint8_t*>(src_next + 1);
+
+ int32_t red = n1[0] + n2[0] + n3[0] + n4[0] + n5[0] + n6[0] + n7[0] + n8[0];
+ int32_t green = n1[1] + n2[1] + n3[1] + n4[1] + n5[1] + n6[1] + n7[1] + n8[1];
+ int32_t blue = n1[2] + n2[2] + n3[2] + n4[2] + n5[2] + n6[2] + n7[2] + n8[2];
+
+ red = (fixed_neighbor * red + fixed_center * src_red) >> kShiftBits;
+ green = (fixed_neighbor * green + fixed_center * src_green) >> kShiftBits;
+ blue = (fixed_neighbor * blue + fixed_center * src_blue) >> kShiftBits;
+
+ red = clamp(red, 0, 255);
+ green = clamp(green, 0, 255);
+ blue = clamp(blue, 0, 255);
+
+ *dst = (src_alpha << 24) | (blue << 16) | (green << 8) | red;
+ dst++;
+ src++;
+ src_prev++;
+ src_next++;
+ }
+ *dst = src->rgba32;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info->stride;
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info->stride;
+ }
+
+ /* Copy the last row to dst_pixels */
+ memcpy(dst_pixels, src_pixels, src_info->stride);
+}
+
+} // namespace convolution
+} // namespace photoeditor
+} // namespace apps
+} // namespace android
diff --git a/samples/PhotoEditor/jni/convolution.h b/samples/PhotoEditor/jni/convolution.h
new file mode 100644
index 0000000..fd6d5f2
--- /dev/null
+++ b/samples/PhotoEditor/jni/convolution.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef PHOTOEDITOR_JNI_CONVOLUTION_H_
+#define PHOTOEDITOR_JNI_CONVOLUTION_H_
+
+#include <android/bitmap.h>
+
+namespace android {
+namespace apps {
+namespace photoeditor {
+namespace convolution {
+
+void SpecialConvolution(AndroidBitmapInfo *src_info, AndroidBitmapInfo *dst_info,
+ void *src_pixels, void *dst_pixels, float neighbor);
+
+} // namespace convolution
+} // namespace photoeditor
+} // namespace apps
+} // namespace android
+
+#endif // PHOTOEDITOR_JNI_CONVOLUTION_H_
diff --git a/samples/PhotoEditor/jni/copy.cpp b/samples/PhotoEditor/jni/copy.cpp
new file mode 100644
index 0000000..01707de
--- /dev/null
+++ b/samples/PhotoEditor/jni/copy.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cstdlib>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeCopy(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pCopyType f = (pCopyType)JNIFunc[JNI_Copy].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void Copy(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Copy failed, error=%d", ret);
+ return;
+ }
+
+ memcpy(dst_pixels, src_pixels, src_info.stride * src_info.height);
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/crossprocess.cpp b/samples/PhotoEditor/jni/crossprocess.cpp
new file mode 100644
index 0000000..e150693
--- /dev/null
+++ b/samples/PhotoEditor/jni/crossprocess.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeCrossProcess(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pCrossProcessType f = (pCrossProcessType)JNIFunc[JNI_CrossProcess].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void CrossProcess(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Crossprocess failed, error=%d", ret);
+ return;
+ }
+
+ const int k128SquareDouble = 32768; // 2 * 128^2
+ const int k128CubicDouble = 4194304; // 2 * 128^3
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ // Enhance red contrast
+ uint32_t r = src->rgba8[0];
+ uint32_t dst_red;
+ if (r <= 128) {
+ dst_red = 255 * r * r * r / k128CubicDouble;
+ } else {
+ uint32_t diff_r = 255 - r;
+ dst_red = 255 - 255 * diff_r * diff_r * diff_r / k128CubicDouble;
+ }
+ // Enhance green contrast
+ int g = src->rgba8[1];
+ uint32_t dst_green;
+ if (g <= 128) {
+ dst_green = 255 * g * g / k128SquareDouble;
+ } else {
+ int diff_g = 255 - g;
+ dst_green = 255 - 255 * diff_g * diff_g / k128SquareDouble;
+ }
+ // Narrow the blue channel, from 64 to 192.
+ int dst_blue = src->rgba8[2] * 128 / 255 + 64;
+
+ *dst = (src->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/duotone.cpp b/samples/PhotoEditor/jni/duotone.cpp
new file mode 100644
index 0000000..daae4de
--- /dev/null
+++ b/samples/PhotoEditor/jni/duotone.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeDuotone(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint color1, jint color2) {
+ pDuotoneType f = (pDuotoneType)JNIFunc[JNI_Duotone].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, color1, color2);
+}
+
+extern "C" void Duotone(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint color1, jint color2) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Duotone failed, error=%d", ret);
+ return;
+ }
+
+ uint8_t red1 = static_cast<uint8_t>(color1 >> 16) & 0xff;
+ uint8_t green1 = static_cast<uint8_t>(color1 >> 8) & 0xff;
+ uint8_t blue1 = static_cast<uint8_t>(color1) & 0xff;
+
+ uint8_t red2 = (uint8_t)(color2 >> 16) & 0xff;
+ uint8_t green2 = (uint8_t)(color2 >> 8) & 0xff;
+ uint8_t blue2 = (uint8_t)color2 & 0xff;
+
+ int r_delta = red2 - red1;
+ int g_delta = green2 - green1;
+ int b_delta = blue2 - blue1;
+
+ const uint32_t kEnergyLevels = 255 * 3 + 1;
+ uint8_t r_table[kEnergyLevels];
+ uint8_t g_table[kEnergyLevels];
+ uint8_t b_table[kEnergyLevels];
+ for (uint32_t energy = 0; energy < kEnergyLevels; energy++) {
+ r_table[energy] = (uint8_t)(red1 + energy * r_delta / kEnergyLevels);
+ g_table[energy] = (uint8_t)(green1 + energy * g_delta / kEnergyLevels);
+ b_table[energy] = (uint8_t)(blue1 + energy * b_delta / kEnergyLevels);
+ }
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ uint32_t energy = src->rgba8[0] + src->rgba8[1] + src->rgba8[2];
+
+ int dst_red = r_table[energy];
+ int dst_green = g_table[energy];
+ int dst_blue = b_table[energy];
+
+ *dst = (src->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/fisheye.cpp b/samples/PhotoEditor/jni/fisheye.cpp
new file mode 100644
index 0000000..5e26a3a
--- /dev/null
+++ b/samples/PhotoEditor/jni/fisheye.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+#include <cstdlib>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+uint32_t BilinearPixelInterpolation(
+ float x, float y, uint8_t *p00, uint8_t *p01, uint8_t *p10, uint8_t *p11) {
+
+ float coef4 = x * y;
+ float coef2 = x - coef4; // x * (1 - y)
+ float coef3 = y - coef4; // (1 - x) * y
+ float coef1 = 1 - x - coef3; // (1 - x) * (1 - y)
+
+ int dst_red = p00[0] * coef1 + p10[0] * coef2 + p01[0] * coef3 + p11[0] * coef4;
+ int dst_green = p00[1] * coef1 + p10[1] * coef2 + p01[1] * coef3 + p11[1] * coef4;
+ int dst_blue = p00[2] * coef1 + p10[2] * coef2 + p01[2] * coef3 + p11[2] * coef4;
+ if (dst_red > 255) {
+ dst_red = 255;
+ }
+ if (dst_green > 255) {
+ dst_green = 255;
+ }
+ if (dst_blue > 255) {
+ dst_blue = 255;
+ }
+
+ // alpha is not calculated, directly from any point should do.
+ return p00[3] << 24 | (dst_blue << 16) | (dst_green << 8) | dst_red;
+}
+
+void FisheyeMapPixels(
+ float px, float py, uint32_t x, uint32_t y, AndroidBitmapInfo *src_info,
+ AndroidBitmapInfo *dst_info, void *src_pixels, void *dst_pixels) {
+ if (x >= src_info->width || y >= src_info->height) {
+ return;
+ }
+
+ uint32_t px_floor = floor(px);
+ uint32_t py_floor = floor(py);
+
+ uint32_t *p00 = (uint32_t*)((char*)src_pixels + src_info->stride * py_floor) + px_floor;
+ uint32_t *p01, *p10, *p11;
+ if (py_floor + 1 < src_info->height) {
+ p01 = (uint32_t*)((char*)p00 + src_info->stride);
+ } else {
+ p01 = p00;
+ }
+ if (py_floor + 1 < src_info->width) {
+ p10 = p00 + 1;
+ p11 = p01 + 1;
+ } else {
+ p10 = p00;
+ p11 = p01;
+ }
+
+ uint32_t *dst = (uint32_t*)((char*)dst_pixels + dst_info->stride * y) + x;
+ *dst = BilinearPixelInterpolation(px - px_floor, py - py_floor,
+ (uint8_t*)p00, (uint8_t*)p01, (uint8_t*)p10, (uint8_t*)p11);
+}
+
+/*
+ * @param scale ranges from 0.0 to 1.0.
+ * @param focus_x is the X-coord of the center of the projection ranging from 0 to 1.
+ * @param focus_y is the Y-coord of the center of the projection ranging from 0 to 1.
+ */
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeFisheye(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap,
+ jfloat focus_x, jfloat focus_y, jfloat scale) {
+ pFisheyeType f = (pFisheyeType)JNIFunc[JNI_Fisheye].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, focus_x, focus_y, scale);
+}
+
+extern "C" void Fisheye(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap,
+ jfloat focus_x, jfloat focus_y, jfloat scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in FishEye failed, error=%d", ret);
+ return;
+ }
+
+ if (scale == 0) {
+ memcpy(dst_pixels, src_pixels, src_info.stride * src_info.height);
+ return;
+ }
+
+ float far_point = hypotf(src_info.height * focus_y, src_info.width * focus_x);
+ const float r = far_point * 1.15; // radius
+ const float r2 = r * r;
+ const uint32_t center_x = round(src_info.width * focus_x);
+ const uint32_t center_y = round(src_info.height * focus_y);
+ float alpha = 0.75 + scale * 2.0;
+ float linear_scale = far_point /
+ (M_PI_2 - atan(alpha * sqrtf(r2 - far_point * far_point) / far_point));
+
+ for (uint32_t scan_line = 0; scan_line <= center_y; scan_line++) {
+
+ int y = scan_line - center_y;
+ int y2 = y * y;
+
+ for (uint32_t dst_x = 0; dst_x <= center_x; dst_x++) {
+ int x = dst_x - center_x;
+ float xy2 = (x * x + y2);
+ float s_xy2 = sqrtf(xy2);
+ float r_scale = linear_scale * (M_PI_2 - atan(alpha * sqrtf(r2 - xy2) / s_xy2)) / s_xy2;
+ float scaled_x = x * r_scale;
+ float scaled_y = y * r_scale;
+ int nx = center_x - x;
+ int ny = center_y - y;
+ float fpx = center_x + scaled_x;
+ float fnx = center_x - scaled_x;
+ float fpy = center_y + scaled_y;
+ float fny = center_y - scaled_y;
+
+ FisheyeMapPixels(fpx, fpy, dst_x, scan_line, &src_info, &dst_info, src_pixels, dst_pixels);
+ FisheyeMapPixels(fpx, fny, dst_x, ny, &src_info, &dst_info, src_pixels, dst_pixels);
+ FisheyeMapPixels(fnx, fpy, nx, scan_line, &src_info, &dst_info, src_pixels, dst_pixels);
+ FisheyeMapPixels(fnx, fny, nx, ny, &src_info, &dst_info, src_pixels, dst_pixels);
+ }
+ }
+ // Deal with the condition when x = 0 and y = 0 (dst_x = center_x and
+ // scan_line = center_y.
+ uint32_t *dst = (uint32_t*)((char*)dst_pixels + dst_info.stride * center_y) + center_x;
+ *dst = *((uint32_t*)((char*)src_pixels + src_info.stride * center_y) + center_x);
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/flip.cpp b/samples/PhotoEditor/jni/flip.cpp
new file mode 100644
index 0000000..cdeb6d9
--- /dev/null
+++ b/samples/PhotoEditor/jni/flip.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeFlipHorizontal(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pFlipHorizontalType f = (pFlipHorizontalType)JNIFunc[JNI_FlipHorizontal].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void FlipHorizontal(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in FlipHorizontal failed, error=%d", ret);
+ return;
+ }
+
+ for (uint32_t scan_line = 0; scan_line < src_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels) + src_info.width - 1;
+ uint32_t* src = reinterpret_cast<uint32_t*>(src_pixels);
+ uint32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ *dst = *src;
+ src++;
+ dst--;
+ }
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeFlipVertical(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pFlipVerticalType f = (pFlipVerticalType)JNIFunc[JNI_FlipVertical].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void FlipVertical(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in FlipVertical failed, error=%d", ret);
+ return;
+ }
+
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride * (dst_info.height - 1);
+ for (uint32_t scan_line = 0; scan_line < src_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ uint32_t* src = reinterpret_cast<uint32_t*>(src_pixels);
+ uint32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) - dst_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeFlipBoth(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pFlipBothType f = (pFlipBothType)JNIFunc[JNI_FlipBoth].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void FlipBoth(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in FlipBoth failed, error=%d", ret);
+ return;
+ }
+
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride * (dst_info.height - 1);
+ for (uint32_t scan_line = 0; scan_line < src_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels) + src_info.width - 1;
+ uint32_t* src = reinterpret_cast<uint32_t*>(src_pixels);
+ uint32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ *dst = *src;
+ src++;
+ dst--;
+ }
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) - dst_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/grain.cpp b/samples/PhotoEditor/jni/grain.cpp
new file mode 100644
index 0000000..b051660
--- /dev/null
+++ b/samples/PhotoEditor/jni/grain.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+#include <cstdlib>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+const uint32_t kShiftBits = 10;
+const uint32_t kShiftValue = (1 << kShiftBits);
+
+/*
+ * Convolution matrix of distance 2 with fixed point of 'kShiftBits' bits
+ * shifted. Thus the sum of this matrix should be 'kShiftValue'. Entries of
+ * small values are not calculated to gain efficiency.
+ * The order ot pixels represented in this matrix is:
+ * 1 2 3
+ * 4 0 5
+ * 6 7 8
+ * and the matrix should be: {230, 56, 114, 56, 114, 114, 56, 114, 56}.
+ * However, since most of the valus are identical, we only use the first three
+ * entries and the entries corresponding to the pixels is:
+ * 1 2 1
+ * 2 0 2
+ * 1 2 1
+ */
+const uint32_t convolution_matrix[3] = {230, 56, 114};
+
+/*
+ * Generate a blurred random noise bitmap.
+ */
+void GenerateBlurredNoise(void* dst_pixels, AndroidBitmapInfo* dst_info,
+ float noise_scale) {
+ uint32_t fixed_noise_scale = noise_scale * kShiftValue;
+
+ // Clear dst bitmap to 0 for storing generated random noise.
+ memset(dst_pixels, 0, dst_info->stride * dst_info->height);
+
+ // 0.5 is a empirical value and could be tuned.
+ int random_threshold = RAND_MAX * 0.5;
+ for (uint32_t y = 0; y < dst_info->height; y++) {
+ uint32_t* dp_line = reinterpret_cast<uint32_t*>(
+ reinterpret_cast<char*>(dst_pixels) + y * dst_info->stride);
+
+ for (uint32_t x = 0; x < dst_info->width; x++) {
+ if (rand() < random_threshold) {
+ uint32_t* dp = dp_line + x;
+ uint32_t* dp_prev = (y == 0) ? dp : (dp - dst_info->width);
+ uint32_t* dp_next = (y == dst_info->height - 1) ? dp : (dp + dst_info->width);
+
+ /*
+ * 1 2 3
+ * 4 0 5
+ * 6 7 8
+ */
+ uint32_t* n[9];
+ n[0] = dp;
+ n[2] = dp_prev;
+ n[7] = dp_next;
+ if (x == 0) {
+ n[1] = n[2];
+ n[4] = n[0];
+ n[6] = n[7];
+ } else {
+ n[1] = n[2] - 1;
+ n[4] = n[0] - 1;
+ n[6] = n[7] - 1;
+ }
+ if (x == dst_info->width - 1) {
+ n[3] = n[2];
+ n[5] = n[0];
+ n[8] = n[7];
+ } else {
+ n[3] = n[2] + 1;
+ n[5] = n[0] + 1;
+ n[8] = n[7] + 1;
+ }
+
+ // noise randomness uniformly distributed between 0.5 to 1.5,
+ // 0.5 is an empirical value.
+ uint32_t random_noise_scale = fixed_noise_scale
+ * (static_cast<double>(rand()) / RAND_MAX + 0.5);
+
+ *n[0] = *n[0] + ((convolution_matrix[0] * random_noise_scale) >> kShiftBits);
+ // The value in convolution_matrix is identical (56) for indexes 1, 3, 6, 8.
+ uint32_t normal_scaled_noise = (convolution_matrix[1] * random_noise_scale) >> kShiftBits;
+ *n[1] += normal_scaled_noise;
+ *n[3] += normal_scaled_noise;
+ *n[6] += normal_scaled_noise;
+ *n[8] += normal_scaled_noise;
+ // Likewise, the computation could be saved for indexes 2, 4, 5, 7;
+ normal_scaled_noise = (convolution_matrix[2] * random_noise_scale) >> kShiftBits;
+ *n[2] += normal_scaled_noise;
+ *n[4] += normal_scaled_noise;
+ *n[5] += normal_scaled_noise;
+ *n[7] += normal_scaled_noise;
+ }
+ }
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeGrain(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat noise_scale) {
+ pGrainType f = (pGrainType)JNIFunc[JNI_Grain].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, noise_scale);
+}
+
+extern "C" void Grain(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat noise_scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in grain failed, error=%d", ret);
+ return;
+ }
+
+ GenerateBlurredNoise(dst_pixels, &dst_info, noise_scale);
+
+ for (uint32_t scan_line = 0; scan_line < src_info.height; scan_line++) {
+ uint32_t* src = reinterpret_cast<uint32_t*>(
+ reinterpret_cast<char*>(src_pixels) + src_info.stride * scan_line);
+ uint32_t* dst = reinterpret_cast<uint32_t*>(
+ reinterpret_cast<char*>(dst_pixels) + dst_info.stride * scan_line);
+
+ uint32_t* src_line_end = src + src_info.width;
+ while (src < src_line_end) {
+ pixel32_t* sp = reinterpret_cast<pixel32_t*>(src);
+ pixel32_t* dp = reinterpret_cast<pixel32_t*>(dst);
+
+ // energy_mask is used to constrain the noise according to the energy
+ // level. Film grain appear more in dark part.
+ // The energy level (from 0 to 765) is square-rooted and should in the
+ // range from 0 to 27.659 (sqrt(765)), so 28 is used for normalization.
+ uint32_t energy_level = sp->rgba8[0] + sp->rgba8[1] + sp->rgba8[2];
+ uint32_t energy_mask = 28 - static_cast<uint32_t>(sqrtf(energy_level));
+
+ // The intensity of each channel of RGB is affected by the random
+ // noise previously produced and stored in dp->pixel.
+ // dp->pixel should be in the range of [1.3 * noise_scale * kShiftValue,
+ // 0]. Therefore 'scale' should be in the range of
+ // [kShiftValue, kShiftValue - 1.3 * noise_scale * kShiftValue]
+ uint32_t scale = (kShiftValue - dp->rgba32 * energy_mask / 28);
+ uint32_t red = (sp->rgba8[0] * scale) >> kShiftBits;
+ uint32_t green = (sp->rgba8[1] * scale) >> kShiftBits;
+ uint32_t blue = (sp->rgba8[2] * scale) >> kShiftBits;
+ dp->rgba32 = (sp->rgba8[3] << 24) | (blue << 16) | (green << 8) | red;
+
+ dst++;
+ src++;
+ }
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/grayscale.cpp b/samples/PhotoEditor/jni/grayscale.cpp
new file mode 100644
index 0000000..8b528f8
--- /dev/null
+++ b/samples/PhotoEditor/jni/grayscale.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeGrayscale(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ pGrayscaleType f = (pGrayscaleType)JNIFunc[JNI_Grayscale].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, scale);
+}
+
+extern "C" void Grayscale(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in GrayScale failed, error=%d", ret);
+ return;
+ }
+
+ const int kShiftBits = 20;
+ const int32_t kRedRatio = static_cast<int32_t>((1 << kShiftBits) * 0.21f);
+ const int32_t kGreenRatio = static_cast<int32_t>((1 << kShiftBits) * 0.71f);
+ const int32_t kBlueRatio = static_cast<int32_t>((1 << kShiftBits) * 0.07f);
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+ while (src < src_line_end) {
+ int32_t src_red = src->rgba8[0];
+ int32_t src_green = src->rgba8[1];
+ int32_t src_blue = src->rgba8[2];
+ int32_t src_alpha = src->rgba8[3];
+
+ int32_t dst_color = (kRedRatio * src_red + kGreenRatio * src_green +
+ kBlueRatio * src_blue) >> kShiftBits;
+ if (dst_color > 255) {
+ dst_color = 255;
+ }
+ *dst = (src_alpha << 24) | (dst_color << 16) | (dst_color << 8) | dst_color;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/heq.cpp b/samples/PhotoEditor/jni/heq.cpp
new file mode 100644
index 0000000..eb4d28d
--- /dev/null
+++ b/samples/PhotoEditor/jni/heq.cpp
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::clamp;
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+ // Array of approximated CDF of normal distribution.
+ // 1024 Entries in total. The array is approximated by sigmoid function and
+ // the exact command in "octave" is:
+ // x = [2/1029:1/1029:1025/1029];
+ // y = (-1/11.5*log(1./x-0.9999)+0.5)*766*0.9+766*0.05;
+ const int kCDFEntries = 1024;
+ const uint32_t normal_cdf[] = {
+ 9, 33, 50, 64, 75, 84, 92, 99, 106, 112, 117, 122, 126, 130, 134, 138, 142,
+ 145, 148, 150, 154, 157, 159, 162, 164, 166, 169, 170, 173, 175, 177, 179,
+ 180, 182, 184, 186, 188, 189, 190, 192, 194, 195, 197, 198, 199, 200, 202,
+ 203, 205, 206, 207, 208, 209, 210, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 229, 230, 231, 232, 233,
+ 234, 235, 236, 236, 237, 238, 239, 239, 240, 240, 242, 242, 243, 244, 245,
+ 245, 246, 247, 247, 248, 249, 249, 250, 250, 251, 252, 253, 253, 254, 255,
+ 255, 256, 256, 257, 258, 258, 259, 259, 259, 260, 261, 262, 262, 263, 263,
+ 264, 264, 265, 265, 266, 267, 267, 268, 268, 269, 269, 269, 270, 270, 271,
+ 272, 272, 273, 273, 274, 274, 275, 275, 276, 276, 277, 277, 277, 278, 278,
+ 279, 279, 279, 280, 280, 281, 282, 282, 282, 283, 283, 284, 284, 285, 285,
+ 285, 286, 286, 287, 287, 288, 288, 288, 289, 289, 289, 290, 290, 290, 291,
+ 292, 292, 292, 293, 293, 294, 294, 294, 295, 295, 296, 296, 296, 297, 297,
+ 297, 298, 298, 298, 299, 299, 299, 299, 300, 300, 301, 301, 302, 302, 302,
+ 303, 303, 304, 304, 304, 305, 305, 305, 306, 306, 306, 307, 307, 307, 308,
+ 308, 308, 309, 309, 309, 309, 310, 310, 310, 310, 311, 312, 312, 312, 313,
+ 313, 313, 314, 314, 314, 315, 315, 315, 315, 316, 316, 316, 317, 317, 317,
+ 318, 318, 318, 319, 319, 319, 319, 319, 320, 320, 320, 321, 321, 322, 322,
+ 322, 323, 323, 323, 323, 324, 324, 324, 325, 325, 325, 325, 326, 326, 326,
+ 327, 327, 327, 327, 328, 328, 328, 329, 329, 329, 329, 329, 330, 330, 330,
+ 330, 331, 331, 332, 332, 332, 333, 333, 333, 333, 334, 334, 334, 334, 335,
+ 335, 335, 336, 336, 336, 336, 337, 337, 337, 337, 338, 338, 338, 339, 339,
+ 339, 339, 339, 339, 340, 340, 340, 340, 341, 341, 342, 342, 342, 342, 343,
+ 343, 343, 344, 344, 344, 344, 345, 345, 345, 345, 346, 346, 346, 346, 347,
+ 347, 347, 347, 348, 348, 348, 348, 349, 349, 349, 349, 349, 349, 350, 350,
+ 350, 350, 351, 351, 352, 352, 352, 352, 353, 353, 353, 353, 354, 354, 354,
+ 354, 355, 355, 355, 355, 356, 356, 356, 356, 357, 357, 357, 357, 358, 358,
+ 358, 358, 359, 359, 359, 359, 359, 359, 359, 360, 360, 360, 360, 361, 361,
+ 362, 362, 362, 362, 363, 363, 363, 363, 364, 364, 364, 364, 365, 365, 365,
+ 365, 366, 366, 366, 366, 366, 367, 367, 367, 367, 368, 368, 368, 368, 369,
+ 369, 369, 369, 369, 369, 370, 370, 370, 370, 370, 371, 371, 372, 372, 372,
+ 372, 373, 373, 373, 373, 374, 374, 374, 374, 374, 375, 375, 375, 375, 376,
+ 376, 376, 376, 377, 377, 377, 377, 378, 378, 378, 378, 378, 379, 379, 379,
+ 379, 379, 379, 380, 380, 380, 380, 381, 381, 381, 382, 382, 382, 382, 383,
+ 383, 383, 383, 384, 384, 384, 384, 385, 385, 385, 385, 385, 386, 386, 386,
+ 386, 387, 387, 387, 387, 388, 388, 388, 388, 388, 389, 389, 389, 389, 389,
+ 389, 390, 390, 390, 390, 391, 391, 392, 392, 392, 392, 392, 393, 393, 393,
+ 393, 394, 394, 394, 394, 395, 395, 395, 395, 396, 396, 396, 396, 396, 397,
+ 397, 397, 397, 398, 398, 398, 398, 399, 399, 399, 399, 399, 399, 400, 400,
+ 400, 400, 400, 401, 401, 402, 402, 402, 402, 403, 403, 403, 403, 404, 404,
+ 404, 404, 405, 405, 405, 405, 406, 406, 406, 406, 406, 407, 407, 407, 407,
+ 408, 408, 408, 408, 409, 409, 409, 409, 409, 409, 410, 410, 410, 410, 411,
+ 411, 412, 412, 412, 412, 413, 413, 413, 413, 414, 414, 414, 414, 415, 415,
+ 415, 415, 416, 416, 416, 416, 417, 417, 417, 417, 418, 418, 418, 418, 419,
+ 419, 419, 419, 419, 419, 420, 420, 420, 420, 421, 421, 422, 422, 422, 422,
+ 423, 423, 423, 423, 424, 424, 424, 425, 425, 425, 425, 426, 426, 426, 426,
+ 427, 427, 427, 427, 428, 428, 428, 429, 429, 429, 429, 429, 429, 430, 430,
+ 430, 430, 431, 431, 432, 432, 432, 433, 433, 433, 433, 434, 434, 434, 435,
+ 435, 435, 435, 436, 436, 436, 436, 437, 437, 437, 438, 438, 438, 438, 439,
+ 439, 439, 439, 439, 440, 440, 440, 441, 441, 442, 442, 442, 443, 443, 443,
+ 443, 444, 444, 444, 445, 445, 445, 446, 446, 446, 446, 447, 447, 447, 448,
+ 448, 448, 449, 449, 449, 449, 449, 450, 450, 450, 451, 451, 452, 452, 452,
+ 453, 453, 453, 454, 454, 454, 455, 455, 455, 456, 456, 456, 457, 457, 457,
+ 458, 458, 458, 459, 459, 459, 459, 460, 460, 460, 461, 461, 462, 462, 462,
+ 463, 463, 463, 464, 464, 465, 465, 465, 466, 466, 466, 467, 467, 467, 468,
+ 468, 469, 469, 469, 469, 470, 470, 470, 471, 472, 472, 472, 473, 473, 474,
+ 474, 474, 475, 475, 476, 476, 476, 477, 477, 478, 478, 478, 479, 479, 479,
+ 480, 480, 480, 481, 482, 482, 483, 483, 484, 484, 484, 485, 485, 486, 486,
+ 487, 487, 488, 488, 488, 489, 489, 489, 490, 490, 491, 492, 492, 493, 493,
+ 494, 494, 495, 495, 496, 496, 497, 497, 498, 498, 499, 499, 499, 500, 501,
+ 502, 502, 503, 503, 504, 504, 505, 505, 506, 507, 507, 508, 508, 509, 509,
+ 510, 510, 511, 512, 513, 513, 514, 515, 515, 516, 517, 517, 518, 519, 519,
+ 519, 520, 521, 522, 523, 524, 524, 525, 526, 526, 527, 528, 529, 529, 530,
+ 531, 532, 533, 534, 535, 535, 536, 537, 538, 539, 539, 540, 542, 543, 544,
+ 545, 546, 547, 548, 549, 549, 550, 552, 553, 554, 555, 556, 558, 559, 559,
+ 561, 562, 564, 565, 566, 568, 569, 570, 572, 574, 575, 577, 578, 579, 582,
+ 583, 585, 587, 589, 590, 593, 595, 597, 599, 602, 604, 607, 609, 612, 615,
+ 618, 620, 624, 628, 631, 635, 639, 644, 649, 654, 659, 666, 673, 680, 690,
+ 700, 714};
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeHEQ(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ pHEQType f = (pHEQType)JNIFunc[JNI_HEQ].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, scale);
+}
+
+extern "C" void HEQ(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in HEQ failed, error=%d", ret);
+ return;
+ }
+
+ const int kEnergyLevels = 766; // 255 * 3 + 1
+
+ // The energy has the range of [0, kEnergyLevels]
+ int accumulated_histogram[kEnergyLevels];
+ memset(accumulated_histogram, 0, sizeof(accumulated_histogram));
+
+ // Store all the energy in the dst_pixels
+ pixel32_t* dst = reinterpret_cast<pixel32_t*>(dst_pixels);
+ pixel32_t const* src = reinterpret_cast<pixel32_t const*>(src_pixels);
+ pixel32_t const* src_end = reinterpret_cast<pixel32_t const*>(
+ reinterpret_cast<char const*>(src) + src_info.stride * src_info.height);
+ while (src < src_end) {
+ dst->rgba32 = src->rgba8[0] + src->rgba8[1] + src->rgba8[2];
+ ++src;
+ ++dst;
+ }
+
+ // Build up the accumulated histogram table by ignoring borders (1/20 = 5% width).
+ float border_thickness_ratio = 0.05;
+ int y_border_thickness = dst_info.height * border_thickness_ratio;
+ pixel32_t* dst_line = reinterpret_cast<pixel32_t*>(
+ reinterpret_cast<char*>(dst_pixels) + dst_info.stride * y_border_thickness);
+ pixel32_t* dst_line_end = reinterpret_cast<pixel32_t*>(
+ reinterpret_cast<char*>(dst_pixels) + dst_info.stride *
+ (dst_info.height - y_border_thickness));
+ int x_border_thickness = dst_info.width * border_thickness_ratio;
+ int x_border_end = dst_info.width - x_border_thickness;
+ while (dst_line < dst_line_end) {
+ pixel32_t* dp = dst_line + x_border_thickness;
+ pixel32_t* dp_end = dst_line + x_border_end;
+ while (dp < dp_end) {
+ ++accumulated_histogram[dp->rgba32];
+ ++dp;
+ }
+ dst_line = reinterpret_cast<pixel32_t*>(
+ reinterpret_cast<char*>(dst_line) + dst_info.stride);
+ }
+
+ for (int i = 1; i < kEnergyLevels; i++) {
+ accumulated_histogram[i] += accumulated_histogram[i - 1];
+ }
+
+ uint32_t const* src_line =
+ reinterpret_cast<uint32_t const*>(src_pixels);
+ dst_line = reinterpret_cast<pixel32_t*>(dst_pixels);
+ dst_line_end = reinterpret_cast<pixel32_t*>(reinterpret_cast<char*>(dst_line) +
+ dst_info.height * dst_info.stride);
+ // The whole process is done by apply the HEQ result with a mask.
+ // The mask is a curve segmented by the energy_middle which could be tuned
+ // based on each bitmap. For the lower part, the mask tries to make the change
+ // significant for greater energy. For the smaller part, the mask does the
+ // contrary. The two curve should be continuous on the energy_middle so the
+ // final result is more natural. In this implementation, what I defined is two
+ // curve based on the energy 'e', for the smaller part, e^2 is used. For the
+ // greater part, e^1.5 is used. That is, for pixel with energy 'e', the mask
+ // is obtained by:
+ // if e > energy_middle
+ // (e - energy_middle)^1.5 / (765 - energy_middle)^1.5
+ // else
+ // (e - energy_middle)^2 / (energy_middle)^2
+ const int kShiftBits = 10;
+ const int kShiftValue = (1 << kShiftBits);
+ const int scale_shifted = scale * kShiftValue;
+ const int normalization_scale_shifted = (1.0 - scale) * kShiftValue;
+ const int energy_middle = 382; // 765 / 2 = 382.5
+ const int normalization_low = 7481; // (765 - 382.5)^1.5
+ const int normalization_high = 146307; // 382.5^2
+ int total_pixels = accumulated_histogram[kEnergyLevels - 1];
+ while (dst_line < dst_line_end) {
+ pixel32_t const* sp = reinterpret_cast<pixel32_t const*>(src_line);
+ pixel32_t* dp = dst_line;
+ pixel32_t* dp_end = dp + dst_info.width;
+ while (dp < dp_end) {
+ if (!dp->rgba32) { // the energy is 0, no changes will be made.
+ dp->rgba32 = sp->rgba32;
+ } else {
+ uint32_t energy = dp->rgba32;
+ int mask_normalization;
+ int mask_value = energy - energy_middle;
+
+ if (mask_value > 0) {
+ mask_value = mask_value * sqrt(mask_value);
+ mask_normalization = normalization_low;
+ } else {
+ mask_value *= mask_value;
+ mask_normalization = normalization_high;
+ }
+ mask_value = ((mask_value * scale_shifted) +
+ (mask_normalization * (normalization_scale_shifted))) >> kShiftBits;
+ // The final picture is masked by the original energy.
+ // Assumption: Lower energy can result in low-confidence information and
+ // higher energy indicates good confidence.
+ // Therefore, pixels with low and high confidence should not be changed
+ // greatly.
+ uint32_t dst_energy = normal_cdf[
+ kCDFEntries * accumulated_histogram[energy] / total_pixels];
+ dst_energy = (energy * mask_value + dst_energy * (mask_normalization - mask_value)) /
+ mask_normalization;
+
+ // Ensure there is no RGB value will be greater than 255.
+ uint32_t max_energy = energy * 255 / MAX3(sp->rgba8[0], sp->rgba8[1], sp->rgba8[2]);
+ if (dst_energy > max_energy) {
+ dst_energy = max_energy;
+ }
+
+ dst_energy = (dst_energy << kShiftBits) / energy;
+ uint32_t dst_red = (sp->rgba8[0] * dst_energy) >> kShiftBits;
+ uint32_t dst_green = (sp->rgba8[1] * dst_energy) >> kShiftBits;
+ uint32_t dst_blue = (sp->rgba8[2] * dst_energy) >> kShiftBits;
+ dp->rgba32 = (sp->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ }
+ dp++;
+ sp++;
+ }
+ src_line = reinterpret_cast<uint32_t const*>(
+ reinterpret_cast<char const*>(src_line) + src_info.stride);
+ dst_line = reinterpret_cast<pixel32_t*>(
+ reinterpret_cast<char*>(dst_line) + dst_info.stride);
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/negative.cpp b/samples/PhotoEditor/jni/negative.cpp
new file mode 100644
index 0000000..ee90609
--- /dev/null
+++ b/samples/PhotoEditor/jni/negative.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeNegative(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pNegativeType f = (pNegativeType)JNIFunc[JNI_Negative].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void Negative(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Negative failed, error=%d", ret);
+ return;
+ }
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ uint32_t dst_red = 0xff - src->rgba8[0];
+ uint32_t dst_green = 0xff - src->rgba8[1];
+ uint32_t dst_blue = 0xff - src->rgba8[2];
+ uint32_t src_alpha = src->rgba8[3];
+
+ *dst = (src_alpha << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/quantize.cpp b/samples/PhotoEditor/jni/quantize.cpp
new file mode 100644
index 0000000..cd43f66
--- /dev/null
+++ b/samples/PhotoEditor/jni/quantize.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+#include <cstdlib>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeQuantize(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pQuantizeType f = (pQuantizeType)JNIFunc[JNI_Quantize].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void Quantize(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in quantize failed, error=%d", ret);
+ return;
+ }
+
+ uint8_t quantize_map[256];
+
+ for (uint32_t i = 0; i < 256; i++) {
+ quantize_map[i] = i / 128 * 128 + 64;
+ }
+
+ for (uint32_t scan_line = 0; scan_line < src_info.height; scan_line++) {
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* dst = reinterpret_cast<pixel32_t*>(dst_pixels);
+
+ pixel32_t* src_line_end = src + src_info.width;
+ while (src < src_line_end) {
+ dst->rgba8[0] = quantize_map[src->rgba8[0]];
+ dst->rgba8[1] = quantize_map[src->rgba8[1]];
+ dst->rgba8[2] = quantize_map[src->rgba8[2]];
+ dst->rgba8[3] = src->rgba8[3];
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/redeye.cpp b/samples/PhotoEditor/jni/redeye.cpp
new file mode 100644
index 0000000..af828d8
--- /dev/null
+++ b/samples/PhotoEditor/jni/redeye.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+#include <cstdlib>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeRedEye(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap,
+ jobjectArray redeye_positions, jfloat radius, jfloat intensity) {
+ pRedEyeType f = (pRedEyeType)JNIFunc[JNI_RedEye].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, redeye_positions, radius, intensity);
+}
+
+extern "C" void RedEye(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap,
+ jobjectArray redeye_positions, jfloat radius, jfloat intensity) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in RedEye failed, error=%d", ret);
+ return;
+ }
+
+ memcpy(dst_pixels, src_pixels, src_info.stride * src_info.height);
+
+ // get the class reference.
+ jclass cls = env->FindClass("android/graphics/PointF");
+ jfieldID xid = env->GetFieldID(cls, "x", "F");
+ jfieldID yid = env->GetFieldID(cls, "y", "F");
+ int eye_number = env->GetArrayLength(redeye_positions);
+ for (int eye = 0; eye < eye_number; eye++) {
+ jobject point_f = env->GetObjectArrayElement(redeye_positions, eye);
+ float focus_x = env->GetFloatField(point_f, xid);
+ float focus_y = env->GetFloatField(point_f, yid);
+ if (focus_x < 0.0 || focus_x > 1.0 || focus_y < 0.0 || focus_y > 1.0) {
+ // did not tap on the picture
+ continue;
+ }
+
+ float x = focus_x * src_info.width;
+ float y = focus_y * src_info.height;
+ uint32_t bound_y = y + radius;
+ if (bound_y > dst_info.height) {
+ bound_y = dst_info.height;
+ }
+ uint32_t start_x = (x > radius) ? x - radius: 0;
+ uint32_t bound_x = x + radius;
+ if (bound_x > dst_info.width) {
+ bound_x = dst_info.width;
+ }
+ uint32_t dst_y = (y > radius) ? y - radius : 0;
+ for (; dst_y < bound_y; dst_y++) {
+ uint32_t *dst = reinterpret_cast<uint32_t*>(
+ reinterpret_cast<char*>(dst_pixels) + dst_info.stride * dst_y) + start_x;
+ pixel32_t *src = reinterpret_cast<pixel32_t*>(
+ reinterpret_cast<char*>(src_pixels) + src_info.stride * dst_y) + start_x;
+ for (uint32_t dst_x = start_x; dst_x < bound_x; dst_x++) {
+ if (hypotf(dst_x - x, dst_y - y) <= radius) {
+ int32_t red = src->rgba8[0];
+ int32_t green = src->rgba8[1];
+ int32_t blue = src->rgba8[2];
+ int32_t alpha = src->rgba8[3];
+
+ float redIntensity = static_cast<float>(red) / (green + blue);
+ if (redIntensity > intensity) {
+ red = (green + blue) / 2.0f;
+ *dst = alpha << 24 | (blue << 16) | (green << 8) | red;
+ }
+ }
+ dst++;
+ src++;
+ } // dst_x
+ } // dst_y
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/saturate.cpp b/samples/PhotoEditor/jni/saturate.cpp
new file mode 100644
index 0000000..29693e7
--- /dev/null
+++ b/samples/PhotoEditor/jni/saturate.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+const int k256Multiply255 = 65280;
+
+void BenSaturate(AndroidBitmapInfo *src_info, AndroidBitmapInfo *dst_info,
+ void *src_pixels, void *dst_pixels, int scale) {
+ for (uint32_t scan_line = 0; scan_line < dst_info->height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_end = src + src_info->width;
+
+ while (src < src_end) {
+ int kv = (src->rgba8[0] + src->rgba8[0] + src->rgba8[1] * 5 + src->rgba8[2] + 4) >> 3;
+
+ int dst_red = kv + (((src->rgba8[0] - kv) * scale) >> 8);
+ int dst_green = kv + (((src->rgba8[1] - kv) * scale) >> 8);
+ int dst_blue = kv + (((src->rgba8[2] - kv) * scale) >> 8);
+
+ *dst = (src->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info->stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info->stride;
+ }
+}
+
+void HerfSaturate(AndroidBitmapInfo *src_info, AndroidBitmapInfo *dst_info,
+ void *src_pixels, void *dst_pixels, float scale) {
+ const float kMapScale = 8.0;
+ const int kMapSize = 2048;
+ int rmap[kMapSize];
+ int gmap[kMapSize];
+ int bmap[kMapSize];
+
+ const float kR = 0.3;
+ const float kG = 0.7;
+ const float kB = 0.9;
+ const float amtR = (kR * scale) + 1;
+ const float amtG = (kG * scale) + 1;
+ const float amtB = (kB * scale) + 1;
+
+ for (int i = 0; i < kMapSize; i++) {
+ float inv = kMapScale * static_cast<float>(i) / kMapSize;
+ rmap[i] = static_cast<int>(pow(inv, amtR) * 256);
+ gmap[i] = static_cast<int>(pow(inv, amtG) * 256);
+ bmap[i] = static_cast<int>(pow(inv, amtB) * 256);
+ }
+
+ int kMapFactor256 = static_cast<int>(kMapSize / kMapScale) * 256;
+ for (uint32_t scan_line = 0; scan_line < dst_info->height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_end = src + src_info->width;
+
+ while (src < src_end) {
+ int de = (src->rgba8[0] + src->rgba8[0] + src->rgba8[1] * 5 + src->rgba8[2]) >> 3;
+ if (!de) {
+ *dst = src->rgba8[3] << 24;
+ } else {
+ int invde = (kMapFactor256 - 1) / de;
+ int r = invde * src->rgba8[0] >> 8;
+ int g = invde * src->rgba8[1] >> 8;
+ int b = invde * src->rgba8[2] >> 8;
+
+ int dst_red = de * rmap[r] >> 8;
+ int dst_green = de * gmap[g] >> 8;
+ int dst_blue = de * bmap[b] >> 8;
+
+ int rgb_max = MAX3(dst_red, dst_green, dst_blue);
+ if (rgb_max > 255) {
+ int invmax = k256Multiply255 / rgb_max;
+ dst_red = dst_red * invmax >> 8;
+ dst_green = dst_green * invmax >> 8;
+ dst_blue = dst_blue * invmax >> 8;
+ }
+
+ *dst = (src->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ }
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info->stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info->stride;
+ }
+}
+
+/*
+ * @param scale ranges from -1 to 1.
+ */
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeSaturation(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ pSaturationType f = (pSaturationType)JNIFunc[JNI_Saturation].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, scale);
+}
+
+extern "C" void Saturation(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Saturation failed, error=%d", ret);
+ return;
+ }
+
+ if (scale >= 0) {
+ HerfSaturate(&src_info, &dst_info, src_pixels, dst_pixels, scale * 3);
+ } else {
+ BenSaturate(&src_info, &dst_info, src_pixels, dst_pixels,
+ static_cast<int>((1 + scale) * 256));
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/sepia.cpp b/samples/PhotoEditor/jni/sepia.cpp
new file mode 100644
index 0000000..ea54d4c
--- /dev/null
+++ b/samples/PhotoEditor/jni/sepia.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeSepia(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pSepiaType f = (pSepiaType)JNIFunc[JNI_Sepia].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void Sepia(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Sepia failed, error=%d", ret);
+ return;
+ }
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+ while (src < src_line_end) {
+ int dst_red = (src->rgba8[0] * 805 + src->rgba8[1] * 1575 + src->rgba8[2] * 387) >> 11;
+ int dst_green = (src->rgba8[0] * 715 + src->rgba8[1] * 1405 + src->rgba8[2] * 344) >> 11;
+ int dst_blue = (src->rgba8[0] * 557 + src->rgba8[1] * 1094 + src->rgba8[2] * 268) >> 11;
+
+ if (dst_red > 255) {
+ dst_red = 255;
+ }
+ if (dst_green > 255) {
+ dst_green = 255;
+ }
+ if (dst_blue > 255) {
+ dst_blue = 255;
+ }
+
+ *dst = (src->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/sharpen.cpp b/samples/PhotoEditor/jni/sharpen.cpp
new file mode 100644
index 0000000..3c0514e
--- /dev/null
+++ b/samples/PhotoEditor/jni/sharpen.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "convolution.h"
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::convolution::SpecialConvolution;
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeSharpen(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ pSharpenType f = (pSharpenType)JNIFunc[JNI_Sharpen].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, scale);
+}
+
+extern "C" void Sharpen(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat scale) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Sharpen failed, error=%d", ret);
+ return;
+ }
+
+ SpecialConvolution(&src_info, &dst_info, src_pixels, dst_pixels, -scale);
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/tint.cpp b/samples/PhotoEditor/jni/tint.cpp
new file mode 100644
index 0000000..23c7fe4
--- /dev/null
+++ b/samples/PhotoEditor/jni/tint.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeTint(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint tint) {
+ pTintType f = (pTintType)JNIFunc[JNI_Tint].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, tint);
+}
+
+extern "C" void Tint(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jint tint) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Tint failed, error=%d", ret);
+ return;
+ }
+
+ const int kShiftBits = 10;
+ const int kRedRatio = static_cast<int>(0.8f * (1 << kShiftBits) * 0.21f);
+ const int kGreenRatio = static_cast<int>(0.8f * (1 << kShiftBits) * 0.71f);
+ const int kBlueRatio = static_cast<int>(0.8f * (1 << kShiftBits) * 0.07f);
+ const int kTintRatio = static_cast<int>(0.2f * (1 << kShiftBits));
+
+ uint32_t tint_red = kTintRatio * ((tint >> 16) & 0xff);
+ uint32_t tint_green = kTintRatio * ((tint >> 8) & 0xff);
+ uint32_t tint_blue = kTintRatio * (tint & 0xff);
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+ while (src < src_line_end) {
+ int32_t avg = kRedRatio * src->rgba8[0] + kGreenRatio * src->rgba8[1] +
+ kBlueRatio * src->rgba8[2];
+
+ int32_t dst_red = (avg + tint_red) >> kShiftBits;
+ int32_t dst_green = (avg + tint_green) >> kShiftBits;
+ int32_t dst_blue = (avg + tint_blue) >> kShiftBits;
+
+ if (dst_red > 255) {
+ dst_red = 255;
+ }
+ if (dst_green > 255) {
+ dst_green = 255;
+ }
+ if (dst_blue > 255) {
+ dst_blue = 255;
+ }
+
+ *dst = (src->rgba8[3] << 24) | (dst_blue << 16) | (dst_green << 8) | dst_red;
+ dst++;
+ src++;
+ }
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/utils.cpp b/samples/PhotoEditor/jni/utils.cpp
new file mode 100644
index 0000000..d16eceb
--- /dev/null
+++ b/samples/PhotoEditor/jni/utils.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#include "utils.h"
+
+#include <android/bitmap.h>
+
+namespace android {
+namespace apps {
+namespace photoeditor {
+namespace utils {
+
+int ExtractInfoFromBitmap(JNIEnv *env,
+ AndroidBitmapInfo *src_info, AndroidBitmapInfo *dst_info,
+ jobject src_bitmap, jobject dst_bitmap) {
+ int ret;
+ /*
+ * The info->format of both bitmap should be ANDROID_BITMAP_FORMAT_RGBA_8888.
+ * Both bitmaps shall also be in the same dimension.
+ */
+ if ((ret = AndroidBitmap_getInfo(env, src_bitmap, src_info)) < 0) {
+ LOGE("AndroidBitmap_getInfo(src_bitmap) failed, error=%d", ret);
+ return ret;
+ }
+ if ((ret = AndroidBitmap_getInfo(env, dst_bitmap, dst_info)) < 0) {
+ LOGE("AndroidBitmap_getInfo(dst_bitmap) failed, error=%d", ret);
+ return ret;
+ }
+ return 0;
+}
+
+int LockBitmaps(JNIEnv* env, jobject src_bitmap, jobject dst_bitmap,
+ AndroidBitmapInfo* src_info, AndroidBitmapInfo* dst_info,
+ void** src_pixels, void** dst_pixels) {
+ int ret;
+ if ((ret = ExtractInfoFromBitmap(env, src_info, dst_info, src_bitmap, dst_bitmap)) < 0) {
+ return ret;
+ }
+ if ((ret = AndroidBitmap_lockPixels(env, src_bitmap, src_pixels)) < 0) {
+ LOGE("AndroidBitmap_lockPixels(src_bitmap) failed, error=%d", ret);
+ return ret;
+ }
+ if ((ret = AndroidBitmap_lockPixels(env, dst_bitmap, dst_pixels)) < 0) {
+ LOGE("AndroidBitmap_lockPixels(dst_bitmap) failed, error=%d", ret);
+ AndroidBitmap_unlockPixels(env, src_bitmap);
+ return ret;
+ }
+ return 0;
+}
+
+void UnlockBitmaps(JNIEnv* env, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmap_unlockPixels(env, src_bitmap);
+ AndroidBitmap_unlockPixels(env, dst_bitmap);
+}
+
+} // namespace utils
+} // namespace photoeditor
+} // namespace apps
+} // namespace android
+
diff --git a/samples/PhotoEditor/jni/utils.h b/samples/PhotoEditor/jni/utils.h
new file mode 100644
index 0000000..af4bb54
--- /dev/null
+++ b/samples/PhotoEditor/jni/utils.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef PHOTOEDITOR_JNI_UTILS_H_
+#define PHOTOEDITOR_JNI_UTILS_H_
+
+#include <android/bitmap.h>
+#include <android/log.h>
+#include <jni.h>
+
+#define LOG_TAG "libjni_photoeditor"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+#define MIN3(x, y, z) ((y) <= (z) ? \
+ ((x) <= (y) ? (x) : (y)) \
+ : \
+ ((x) <= (z) ? (x) : (z)))
+
+#define MAX3(x, y, z) ((y) >= (z) ? \
+ ((x) >= (y) ? (x) : (y)) \
+ : \
+ ((x) >= (z) ? (x) : (z)))
+
+namespace android {
+namespace apps {
+namespace photoeditor {
+namespace utils {
+
+inline int clamp(int x, int min, int max) {
+ return (x < min ? min : ((x > max) ? max : x));
+}
+
+union pixel32_t {
+ uint32_t rgba32;
+ uint8_t rgba8[4]; // 0: red 1:green 2:blue 3:alpha
+};
+
+typedef union pixel32_t pixel32_t;
+
+int ExtractInfoFromBitmap(JNIEnv *env,
+ AndroidBitmapInfo *src_info, AndroidBitmapInfo *dst_info,
+ jobject src_bitmap, jobject dst_bitmap);
+
+int LockBitmaps(JNIEnv* env, jobject src_bitmap, jobject dst_bitmap,
+ AndroidBitmapInfo* src_info, AndroidBitmapInfo* dst_info,
+ void** src_pixels, void** dst_pixels);
+
+void UnlockBitmaps(JNIEnv* env, jobject src_bitmap, jobject dst_bitmap);
+
+} // namespace utils
+} // namespace photoeditor
+} // namespace apps
+} // namespace android
+
+
+#endif // PHOTOEDITOR_JNI_UTILS_H_
diff --git a/samples/PhotoEditor/jni/vignetting.cpp b/samples/PhotoEditor/jni/vignetting.cpp
new file mode 100644
index 0000000..3451e9d
--- /dev/null
+++ b/samples/PhotoEditor/jni/vignetting.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+/**
+ * @param range the position of the significant falloff of light (50% luminosity).
+ */
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeVignetting(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat range) {
+ pVignettingType f = (pVignettingType)JNIFunc[JNI_Vignetting].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, range);
+}
+
+extern "C" void Vignetting(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat range) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ const float slope = 20.0f; // how fast the light changes, the bigger the faster.
+ range *= slope;
+ const int kShiftBits = 10;
+ const uint32_t shade = 0.85 * (1 << kShiftBits); // the intensity of the shade
+ const uint32_t shade_offset = (1 << kShiftBits) - shade;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in Vignetting failed, error=%d", ret);
+ return;
+ }
+
+ const uint32_t center_x = src_info.width / 2;
+ const uint32_t center_y = src_info.height / 2;
+ const uint32_t hypot = sqrt(center_x * center_x + center_y * center_y) / slope;
+
+ for (uint32_t dst_y = 0; dst_y < dst_info.height; dst_y++) {
+ uint32_t *dst = reinterpret_cast<uint32_t*>(
+ reinterpret_cast<char*>(dst_pixels) + dst_info.stride * dst_y);
+ pixel32_t *src = reinterpret_cast<pixel32_t*>(
+ reinterpret_cast<char*>(src_pixels) + src_info.stride * dst_y);
+ int dy = dst_y - center_y;
+ for (uint32_t dst_x = 0; dst_x < dst_info.width; dst_x++) {
+ int dx = dst_x - center_x;
+ int dist_square = dx * dx + dy * dy;
+ uint32_t luminousity = shade / (1 + exp(sqrt(dist_square) / hypot - range)) +
+ shade_offset;
+ uint32_t red = (luminousity * src->rgba8[0]) >> kShiftBits;
+ uint32_t green = (luminousity * src->rgba8[1]) >> kShiftBits;
+ uint32_t blue = (luminousity * src->rgba8[2]) >> kShiftBits;
+ uint32_t alpha = src->rgba8[3];
+ *dst = (alpha << 24) | (blue << 16) | (green << 8) | red;
+ dst++;
+ src++;
+ } // dst_x
+ } // dst_y
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/warmify.cpp b/samples/PhotoEditor/jni/warmify.cpp
new file mode 100644
index 0000000..5bf702a
--- /dev/null
+++ b/samples/PhotoEditor/jni/warmify.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+const int map[256] = {
+ 0xff000000,0xff010101,0xff030201,0xff040302,0xff050303,0xff060403,0xff080504,0xff090605,
+ 0xff0a0705,0xff0c0806,0xff0d0907,0xff0e0907,0xff0f0a08,0xff110b09,0xff120c09,0xff130d0a,
+ 0xff140e0b,0xff160f0b,0xff17100c,0xff18100d,0xff1a110d,0xff1b120e,0xff1c130f,0xff1d140f,
+ 0xff1f1510,0xff201611,0xff211711,0xff221812,0xff241913,0xff251a13,0xff261b14,0xff271c15,
+ 0xff291d15,0xff2a1e16,0xff2b1f17,0xff2c2017,0xff2e2118,0xff2f2119,0xff30221a,0xff31231a,
+ 0xff32241b,0xff34251c,0xff35261c,0xff36271d,0xff37281e,0xff38291e,0xff3a2a1f,0xff3b2b20,
+ 0xff3c2c20,0xff3d2d21,0xff3e2e22,0xff402f23,0xff413023,0xff423124,0xff433225,0xff443325,
+ 0xff463426,0xff473527,0xff483628,0xff493728,0xff4a3829,0xff4b392a,0xff4c3a2b,0xff4e3b2b,
+ 0xff4f3c2c,0xff503d2d,0xff513e2d,0xff523f2e,0xff53402f,0xff544130,0xff554130,0xff564231,
+ 0xff574332,0xff594433,0xff5a4533,0xff5b4634,0xff5c4735,0xff5d4836,0xff5e4937,0xff5f4a37,
+ 0xff604b38,0xff614c39,0xff624c3a,0xff634d3a,0xff644e3b,0xff654f3c,0xff66503d,0xff67513e,
+ 0xff68523e,0xff69533f,0xff6a5340,0xff6b5441,0xff6c5542,0xff6d5642,0xff6e5743,0xff6f5844,
+ 0xff705845,0xff715946,0xff715a47,0xff725b47,0xff735c48,0xff745c49,0xff755d4a,0xff765e4b,
+ 0xff775f4c,0xff785f4c,0xff79604d,0xff7a614e,0xff7a624f,0xff7b6350,0xff7c6351,0xff7d6452,
+ 0xff7e6552,0xff7f6653,0xff806654,0xff806755,0xff816856,0xff826957,0xff836958,0xff846a59,
+ 0xff856b5a,0xff856c5a,0xff866c5b,0xff876d5c,0xff886e5d,0xff896f5e,0xff896f5f,0xff8a7060,
+ 0xff8b7161,0xff8c7262,0xff8d7263,0xff8d7364,0xff8e7465,0xff8f7565,0xff907566,0xff907667,
+ 0xff917768,0xff927869,0xff93796a,0xff94796b,0xff947a6c,0xff957b6d,0xff967c6e,0xff977c6f,
+ 0xff977d70,0xff987e71,0xff997f72,0xff9a8073,0xff9a8074,0xff9b8175,0xff9c8276,0xff9d8377,
+ 0xff9d8478,0xff9e8479,0xff9f857a,0xffa0867b,0xffa0877c,0xffa1887d,0xffa2897e,0xffa3897f,
+ 0xffa38a80,0xffa48b81,0xffa58c82,0xffa68d83,0xffa68e84,0xffa78f85,0xffa89086,0xffa89087,
+ 0xffa99188,0xffaa9289,0xffab938a,0xffab948b,0xffac958c,0xffad968e,0xffae978f,0xffae9890,
+ 0xffaf9991,0xffb09992,0xffb19a93,0xffb19b94,0xffb29c95,0xffb39d96,0xffb49e97,0xffb49f98,
+ 0xffb5a099,0xffb6a19a,0xffb7a29c,0xffb8a39d,0xffb8a49e,0xffb9a59f,0xffbaa6a0,0xffbba7a1,
+ 0xffbba8a2,0xffbca9a3,0xffbdaaa4,0xffbeaba5,0xffbfaba7,0xffbfaca8,0xffc0ada9,0xffc1aeaa,
+ 0xffc2afab,0xffc3b0ac,0xffc3b1ad,0xffc4b2ae,0xffc5b3af,0xffc6b4b1,0xffc7b5b2,0xffc7b6b3,
+ 0xffc8b7b4,0xffc9b8b5,0xffcab9b6,0xffcbbab7,0xffccbbb8,0xffcdbcba,0xffcdbdbb,0xffcebebc,
+ 0xffcfbfbd,0xffd0c0be,0xffd1c1bf,0xffd2c3c0,0xffd3c4c2,0xffd3c5c3,0xffd4c6c4,0xffd5c7c5,
+ 0xffd6c8c6,0xffd7c9c7,0xffd8cac8,0xffd9cbca,0xffdacccb,0xffdacdcc,0xffdbcecd,0xffdccfce,
+ 0xffddd0cf,0xffded1d1,0xffdfd2d2,0xffe0d3d3,0xffe1d4d4,0xffe2d5d5,0xffe3d6d6,0xffe3d7d7,
+ 0xffe4d8d9,0xffe5d9da,0xffe6dadb,0xffe7dbdc,0xffe8dcdd,0xffe9ddde,0xffeadfe0,0xffebe0e1,
+ 0xffece1e2,0xffede2e3,0xffede3e4,0xffeee4e5,0xffefe5e7,0xfff0e6e8,0xfff1e7e9,0xfff2e8ea
+};
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeWarmify(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ pWarmifyType f = (pWarmifyType)JNIFunc[JNI_Warmify].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap);
+}
+
+extern "C" void Warmify(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in nativeWarmify failed, error=%d", ret);
+ return;
+ }
+
+ for (uint32_t scan_line = 0; scan_line < dst_info.height; scan_line++) {
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+
+ while (src < src_line_end) {
+ int dst_red = (map[src->rgba8[0]] & 0xFF0000) >> 16;
+ int dst_green = map[src->rgba8[1]] & 0xFF00;
+ int dst_blue = (map[src->rgba8[2]] & 0xFF) << 16;
+
+ *dst = (src->rgba8[3] << 24) | dst_blue | dst_green | dst_red;
+
+ src++;
+ dst++;
+ }
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/jni/whiteblack.cpp b/samples/PhotoEditor/jni/whiteblack.cpp
new file mode 100644
index 0000000..6bc6c36
--- /dev/null
+++ b/samples/PhotoEditor/jni/whiteblack.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <jni.h>
+
+#include <cmath>
+#include <cstdlib>
+#include <ctime>
+
+#include "utils.h"
+#include "_jni.h"
+
+using android::apps::photoeditor::utils::clamp;
+using android::apps::photoeditor::utils::LockBitmaps;
+using android::apps::photoeditor::utils::pixel32_t;
+using android::apps::photoeditor::utils::UnlockBitmaps;
+
+namespace {
+
+const int k256Multiply255 = 65280;
+
+void WhiteblackXF(float white, float black, int xform[]) {
+ float scale = (black != white) ? 1.0f / (white - black) : 2000.0f;
+ float offset = k256Multiply255 * black;
+ for (int i = 0; i < 256; i++) {
+ float vgamma = (256.0f * i - offset) * scale;
+ xform[i] = clamp(floor(vgamma), 0, k256Multiply255);
+ }
+ xform[256] = xform[255];
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_photoeditor_filters_ImageUtils_nativeWhiteBlack(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat white, jfloat black) {
+ pWhiteBlackType f = (pWhiteBlackType)JNIFunc[JNI_WhiteBlack].func_ptr;
+ return f(env, obj, src_bitmap, dst_bitmap, white, black);
+}
+
+extern "C" void WhiteBlack(
+ JNIEnv *env, jobject obj, jobject src_bitmap, jobject dst_bitmap, jfloat white, jfloat black) {
+ AndroidBitmapInfo src_info;
+ AndroidBitmapInfo dst_info;
+ void* src_pixels;
+ void* dst_pixels;
+
+ int ret = LockBitmaps(
+ env, src_bitmap, dst_bitmap, &src_info, &dst_info, &src_pixels, &dst_pixels);
+ if (ret < 0) {
+ LOGE("LockBitmaps in WhiteBlack failed, error=%d", ret);
+ return;
+ }
+
+ srandom(time(NULL));
+
+ int xform[257];
+ WhiteblackXF(white, black, xform);
+
+ for (uint32_t scan_line = 0; scan_line < src_info.height; scan_line++) {
+ pixel32_t* src = reinterpret_cast<pixel32_t*>(src_pixels);
+ uint32_t* dst = reinterpret_cast<uint32_t*>(dst_pixels);
+ pixel32_t* src_line_end = src + src_info.width;
+ while (src < src_line_end) {
+ int32_t red = src->rgba8[0];
+ int32_t green = src->rgba8[1];
+ int32_t blue = src->rgba8[2];
+ int32_t alpha = src->rgba8[3];
+
+ int32_t xform_red = xform[red];
+ int32_t xform_green = xform[green];
+ int32_t xform_blue = xform[blue];
+
+ red = xform[red + 1] - xform_red;
+ green = xform[green + 1] - xform_green;
+ blue = xform[blue + 1] - xform_blue;
+
+ int32_t dither = random() % 256;
+ int32_t diff_red = red * dither >> 8;
+ int32_t diff_green = green * dither >> 8;
+ int32_t diff_blue = blue * dither >> 8;
+
+ red = (xform_red + diff_red - (red >> 1)) >> 8;
+ green = (xform_green + diff_green - (green >> 1)) >> 8;
+ blue = (xform_blue + diff_blue - (blue >> 1)) >> 8;
+
+ red = clamp(red, 0, 255);
+ green = clamp(green, 0, 255);
+ blue = clamp(blue, 0, 255);
+
+ *dst = (alpha << 24) | (blue << 16) | (green << 8) | red;
+ src++;
+ dst++;
+ }
+ src_pixels = reinterpret_cast<char*>(src_pixels) + src_info.stride;
+ dst_pixels = reinterpret_cast<char*>(dst_pixels) + dst_info.stride;
+ }
+
+ UnlockBitmaps(env, src_bitmap, dst_bitmap);
+}
+
+} // namespace
diff --git a/samples/PhotoEditor/res/anim/fade_in.xml b/samples/PhotoEditor/res/anim/fade_in.xml
new file mode 100644
index 0000000..7334041
--- /dev/null
+++ b/samples/PhotoEditor/res/anim/fade_in.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="0.25"
+ android:toAlpha="1.0"
+ android:duration="500"
+ android:fillAfter="true"/>
diff --git a/samples/PhotoEditor/res/anim/fade_out.xml b/samples/PhotoEditor/res/anim/fade_out.xml
new file mode 100644
index 0000000..ad97c49
--- /dev/null
+++ b/samples/PhotoEditor/res/anim/fade_out.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.25"
+ android:duration="500"
+ android:fillAfter="true"/>
diff --git a/samples/PhotoEditor/res/anim/wheel_hide.xml b/samples/PhotoEditor/res/anim/wheel_hide.xml
new file mode 100644
index 0000000..e79e346
--- /dev/null
+++ b/samples/PhotoEditor/res/anim/wheel_hide.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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0%"
+ android:toXDelta="-100%"
+ android:duration="300"/>
diff --git a/samples/PhotoEditor/res/anim/wheel_show.xml b/samples/PhotoEditor/res/anim/wheel_show.xml
new file mode 100644
index 0000000..9fef1e2
--- /dev/null
+++ b/samples/PhotoEditor/res/anim/wheel_show.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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="-100%"
+ android:toXDelta="0%"
+ android:duration="300"/>
diff --git a/samples/PhotoEditor/res/drawable/actionbar_translucent.9.png b/samples/PhotoEditor/res/drawable/actionbar_translucent.9.png
new file mode 100644
index 0000000..f18761f
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/actionbar_translucent.9.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/arrow_back.png b/samples/PhotoEditor/res/drawable/arrow_back.png
new file mode 100644
index 0000000..897a1c1
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/arrow_back.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/arrow_down.png b/samples/PhotoEditor/res/drawable/arrow_down.png
new file mode 100644
index 0000000..ef6dd97
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/arrow_down.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/arrow_right.png b/samples/PhotoEditor/res/drawable/arrow_right.png
new file mode 100644
index 0000000..87893f7
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/arrow_right.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/autofix_off.png b/samples/PhotoEditor/res/drawable/autofix_off.png
new file mode 100644
index 0000000..f0e2d46
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/autofix_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/autofix_on.png b/samples/PhotoEditor/res/drawable/autofix_on.png
new file mode 100644
index 0000000..9f1da69
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/autofix_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/colortemperature_off.png b/samples/PhotoEditor/res/drawable/colortemperature_off.png
new file mode 100644
index 0000000..d4f1355
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/colortemperature_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/colortemperature_on.png b/samples/PhotoEditor/res/drawable/colortemperature_on.png
new file mode 100644
index 0000000..7569b57
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/colortemperature_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/crop_height_holo.png b/samples/PhotoEditor/res/drawable/crop_height_holo.png
new file mode 100644
index 0000000..19fdb87
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/crop_height_holo.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/crop_off.png b/samples/PhotoEditor/res/drawable/crop_off.png
new file mode 100644
index 0000000..cdea423
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/crop_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/crop_on.png b/samples/PhotoEditor/res/drawable/crop_on.png
new file mode 100644
index 0000000..4f5c298
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/crop_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/crop_width_holo.png b/samples/PhotoEditor/res/drawable/crop_width_holo.png
new file mode 100644
index 0000000..3c82e11
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/crop_width_holo.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/crossprocess_off.png b/samples/PhotoEditor/res/drawable/crossprocess_off.png
new file mode 100644
index 0000000..f9e596a
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/crossprocess_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/crossprocess_on.png b/samples/PhotoEditor/res/drawable/crossprocess_on.png
new file mode 100644
index 0000000..5564db3
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/crossprocess_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/documentary_off.png b/samples/PhotoEditor/res/drawable/documentary_off.png
new file mode 100644
index 0000000..92c40f9
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/documentary_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/documentary_on.png b/samples/PhotoEditor/res/drawable/documentary_on.png
new file mode 100644
index 0000000..ff63406
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/documentary_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/doodle_off.png b/samples/PhotoEditor/res/drawable/doodle_off.png
new file mode 100644
index 0000000..5b63178
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/doodle_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/doodle_on.png b/samples/PhotoEditor/res/drawable/doodle_on.png
new file mode 100644
index 0000000..f8a7592
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/doodle_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/duotone_off.png b/samples/PhotoEditor/res/drawable/duotone_off.png
new file mode 100644
index 0000000..994e104
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/duotone_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/duotone_on.png b/samples/PhotoEditor/res/drawable/duotone_on.png
new file mode 100644
index 0000000..6f71b75
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/duotone_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/filllight_off.png b/samples/PhotoEditor/res/drawable/filllight_off.png
new file mode 100644
index 0000000..999b7f7
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/filllight_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/filllight_on.png b/samples/PhotoEditor/res/drawable/filllight_on.png
new file mode 100644
index 0000000..eff5c59
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/filllight_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/fisheye_off.png b/samples/PhotoEditor/res/drawable/fisheye_off.png
new file mode 100644
index 0000000..5fd1b93
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/fisheye_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/fisheye_on.png b/samples/PhotoEditor/res/drawable/fisheye_on.png
new file mode 100644
index 0000000..851141f
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/fisheye_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/flip_off.png b/samples/PhotoEditor/res/drawable/flip_off.png
new file mode 100644
index 0000000..cdea423
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/flip_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/flip_on.png b/samples/PhotoEditor/res/drawable/flip_on.png
new file mode 100644
index 0000000..4f5c298
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/flip_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/grain_off.png b/samples/PhotoEditor/res/drawable/grain_off.png
new file mode 100644
index 0000000..f6ff7fa
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/grain_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/grain_on.png b/samples/PhotoEditor/res/drawable/grain_on.png
new file mode 100644
index 0000000..7533af3
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/grain_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/grayscale_off.png b/samples/PhotoEditor/res/drawable/grayscale_off.png
new file mode 100644
index 0000000..3c09cd3
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/grayscale_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/grayscale_on.png b/samples/PhotoEditor/res/drawable/grayscale_on.png
new file mode 100644
index 0000000..4df7c4d
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/grayscale_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/group_header.9.png b/samples/PhotoEditor/res/drawable/group_header.9.png
new file mode 100644
index 0000000..a535678
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/group_header.9.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/highlight_off.png b/samples/PhotoEditor/res/drawable/highlight_off.png
new file mode 100644
index 0000000..1d59fb4
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/highlight_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/highlight_on.png b/samples/PhotoEditor/res/drawable/highlight_on.png
new file mode 100644
index 0000000..88c580e
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/highlight_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/icon.png b/samples/PhotoEditor/res/drawable/icon.png
new file mode 100644
index 0000000..34410f8
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/icon.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/lomoish_off.png b/samples/PhotoEditor/res/drawable/lomoish_off.png
new file mode 100644
index 0000000..ee347e6
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/lomoish_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/lomoish_on.png b/samples/PhotoEditor/res/drawable/lomoish_on.png
new file mode 100644
index 0000000..b6059ec
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/lomoish_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/negative_off.png b/samples/PhotoEditor/res/drawable/negative_off.png
new file mode 100644
index 0000000..96da62c
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/negative_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/negative_on.png b/samples/PhotoEditor/res/drawable/negative_on.png
new file mode 100644
index 0000000..cc732d9
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/negative_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/posterize_off.png b/samples/PhotoEditor/res/drawable/posterize_off.png
new file mode 100644
index 0000000..ac5384b
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/posterize_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/posterize_on.png b/samples/PhotoEditor/res/drawable/posterize_on.png
new file mode 100644
index 0000000..eed1284
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/posterize_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/quickview.png b/samples/PhotoEditor/res/drawable/quickview.png
new file mode 100644
index 0000000..cefd851
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/quickview.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/quickview_on.png b/samples/PhotoEditor/res/drawable/quickview_on.png
new file mode 100644
index 0000000..eb7c77f
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/quickview_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/redeye_off.png b/samples/PhotoEditor/res/drawable/redeye_off.png
new file mode 100644
index 0000000..ec21a96
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/redeye_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/redeye_on.png b/samples/PhotoEditor/res/drawable/redeye_on.png
new file mode 100644
index 0000000..df83f11
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/redeye_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/redo.png b/samples/PhotoEditor/res/drawable/redo.png
new file mode 100644
index 0000000..780871e
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/redo.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/rotate_off.png b/samples/PhotoEditor/res/drawable/rotate_off.png
new file mode 100644
index 0000000..153d733
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/rotate_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/rotate_on.png b/samples/PhotoEditor/res/drawable/rotate_on.png
new file mode 100644
index 0000000..c2e26c7
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/rotate_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/saturation_off.png b/samples/PhotoEditor/res/drawable/saturation_off.png
new file mode 100644
index 0000000..d151d5c
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/saturation_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/saturation_on.png b/samples/PhotoEditor/res/drawable/saturation_on.png
new file mode 100644
index 0000000..415305d
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/saturation_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/saveas.png b/samples/PhotoEditor/res/drawable/saveas.png
new file mode 100644
index 0000000..5f572ac
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/saveas.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/scale_wheel_background.png b/samples/PhotoEditor/res/drawable/scale_wheel_background.png
new file mode 100644
index 0000000..c3dc5c8
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/scale_wheel_background.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/sepia_off.png b/samples/PhotoEditor/res/drawable/sepia_off.png
new file mode 100644
index 0000000..236b990
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/sepia_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/sepia_on.png b/samples/PhotoEditor/res/drawable/sepia_on.png
new file mode 100644
index 0000000..9fb3b66
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/sepia_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/shadow_off.png b/samples/PhotoEditor/res/drawable/shadow_off.png
new file mode 100644
index 0000000..e3caa44
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/shadow_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/shadow_on.png b/samples/PhotoEditor/res/drawable/shadow_on.png
new file mode 100644
index 0000000..5d7e0da
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/shadow_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/sharpen_off.png b/samples/PhotoEditor/res/drawable/sharpen_off.png
new file mode 100644
index 0000000..89ce896
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/sharpen_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/sharpen_on.png b/samples/PhotoEditor/res/drawable/sharpen_on.png
new file mode 100644
index 0000000..dc030fa
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/sharpen_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/softfocus_off.png b/samples/PhotoEditor/res/drawable/softfocus_off.png
new file mode 100644
index 0000000..e7b63f2
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/softfocus_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/softfocus_on.png b/samples/PhotoEditor/res/drawable/softfocus_on.png
new file mode 100644
index 0000000..cfd23c5
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/softfocus_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/straighten_off.png b/samples/PhotoEditor/res/drawable/straighten_off.png
new file mode 100644
index 0000000..d558ed8
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/straighten_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/straighten_on.png b/samples/PhotoEditor/res/drawable/straighten_on.png
new file mode 100644
index 0000000..7a81448
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/straighten_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/tint_off.png b/samples/PhotoEditor/res/drawable/tint_off.png
new file mode 100644
index 0000000..6a6bb33
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/tint_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/tint_on.png b/samples/PhotoEditor/res/drawable/tint_on.png
new file mode 100644
index 0000000..9ade695
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/tint_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/undo.png b/samples/PhotoEditor/res/drawable/undo.png
new file mode 100644
index 0000000..2fa3df6
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/undo.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/vignette_off.png b/samples/PhotoEditor/res/drawable/vignette_off.png
new file mode 100644
index 0000000..e529563
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/vignette_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/vignette_on.png b/samples/PhotoEditor/res/drawable/vignette_on.png
new file mode 100644
index 0000000..e5d762d
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/vignette_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/warmify_off.png b/samples/PhotoEditor/res/drawable/warmify_off.png
new file mode 100644
index 0000000..770746a
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/warmify_off.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/warmify_on.png b/samples/PhotoEditor/res/drawable/warmify_on.png
new file mode 100644
index 0000000..443fb7a
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/warmify_on.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/wheel_knot_normal.png b/samples/PhotoEditor/res/drawable/wheel_knot_normal.png
new file mode 100644
index 0000000..5a3b27d
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/wheel_knot_normal.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/wheel_knot_pressed.png b/samples/PhotoEditor/res/drawable/wheel_knot_pressed.png
new file mode 100644
index 0000000..3b95aa5
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/wheel_knot_pressed.png
Binary files differ
diff --git a/samples/PhotoEditor/res/drawable/wheel_knot_selector.xml b/samples/PhotoEditor/res/drawable/wheel_knot_selector.xml
new file mode 100644
index 0000000..52ac39e
--- /dev/null
+++ b/samples/PhotoEditor/res/drawable/wheel_knot_selector.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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="false" android:drawable="@drawable/wheel_knot_normal" />
+ <item android:state_pressed="true" android:drawable="@drawable/wheel_knot_pressed" />
+</selector>
diff --git a/samples/PhotoEditor/res/layout/artistic_effects.xml b/samples/PhotoEditor/res/layout/artistic_effects.xml
new file mode 100644
index 0000000..1d34450
--- /dev/null
+++ b/samples/PhotoEditor/res/layout/artistic_effects.xml
@@ -0,0 +1,121 @@
+<?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.photoeditor.EffectsGroup
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:photoeditor="http://schemas.android.com/apk/res/com.android.photoeditor"
+ style="@style/EffectsBarLinearLayout">
+
+ <FrameLayout android:id="@+id/group_header" style="@style/GroupHeader">
+
+ <TextView android:text="@string/artistic_group" style="@style/GroupLabel"/>
+ <ImageView android:id="@+id/group_arrow" style="@style/GroupArrow"/>
+
+ </FrameLayout>
+
+ <LinearLayout android:id="@+id/grouped_effects" style="@style/GroupContent">
+
+ <LinearLayout style="@style/Effect" android:id="@+id/fisheye_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/fisheye_modes"
+ photoeditor:icons="@array/fisheye_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/fisheye"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/vignette_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/vignette_modes"
+ photoeditor:icons="@array/vignette_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/vignette"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/crossprocess_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/crossprocess_modes"
+ photoeditor:icons="@array/crossprocess_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/crossprocess"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/documentary_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/documentary_modes"
+ photoeditor:icons="@array/documentary_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/documentary"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/lomoish_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/lomoish_modes"
+ photoeditor:icons="@array/lomoish_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/lomoish"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/grain_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/grain_modes"
+ photoeditor:icons="@array/grain_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/grain"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/posterize_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/posterize_modes"
+ photoeditor:icons="@array/posterize_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/posterize"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/doodle_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/doodle_modes"
+ photoeditor:icons="@array/doodle_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/doodle"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+
+ </LinearLayout>
+</com.android.photoeditor.EffectsGroup>
diff --git a/samples/PhotoEditor/res/layout/color_effects.xml b/samples/PhotoEditor/res/layout/color_effects.xml
new file mode 100644
index 0000000..7fe0669
--- /dev/null
+++ b/samples/PhotoEditor/res/layout/color_effects.xml
@@ -0,0 +1,121 @@
+<?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.photoeditor.EffectsGroup
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:photoeditor="http://schemas.android.com/apk/res/com.android.photoeditor"
+ style="@style/EffectsBarLinearLayout">
+
+ <FrameLayout android:id="@+id/group_header" style="@style/GroupHeader">
+
+ <TextView android:text="@string/color_group" style="@style/GroupLabel"/>
+ <ImageView android:id="@+id/group_arrow" style="@style/GroupArrow"/>
+
+ </FrameLayout>
+
+ <LinearLayout android:id="@+id/grouped_effects" style="@style/GroupContent">
+
+ <LinearLayout style="@style/Effect" android:id="@+id/warmify_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/warmify_modes"
+ photoeditor:icons="@array/warmify_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/warmify"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/sepia_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/sepia_modes"
+ photoeditor:icons="@array/sepia_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/sepia"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/grayscale_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/grayscale_modes"
+ photoeditor:icons="@array/grayscale_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/grayscale"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/negative_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/negative_modes"
+ photoeditor:icons="@array/negative_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/negative"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/duotone_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/duotone_modes"
+ photoeditor:icons="@array/duotone_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/duotone"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/tint_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/tint_modes"
+ photoeditor:icons="@array/tint_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/tint"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/temperature_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/temperature_modes"
+ photoeditor:icons="@array/temperature_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/temperature"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/saturation_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/saturation_modes"
+ photoeditor:icons="@array/saturation_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/saturation"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+
+ </LinearLayout>
+</com.android.photoeditor.EffectsGroup>
diff --git a/samples/PhotoEditor/res/layout/exposure_effects.xml b/samples/PhotoEditor/res/layout/exposure_effects.xml
new file mode 100644
index 0000000..b1d3476
--- /dev/null
+++ b/samples/PhotoEditor/res/layout/exposure_effects.xml
@@ -0,0 +1,77 @@
+<?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.photoeditor.EffectsGroup
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:photoeditor="http://schemas.android.com/apk/res/com.android.photoeditor"
+ style="@style/EffectsBarLinearLayout">
+
+ <FrameLayout android:id="@+id/group_header" style="@style/GroupHeader">
+
+ <TextView android:text="@string/exposure_group" style="@style/GroupLabel"/>
+ <ImageView android:id="@+id/group_arrow" style="@style/GroupArrow"/>
+
+ </FrameLayout>
+
+ <LinearLayout android:id="@+id/grouped_effects" style="@style/GroupContent">
+
+ <LinearLayout style="@style/Effect" android:id="@+id/filllight_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/filllight_modes"
+ photoeditor:icons="@array/filllight_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/filllight"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/highlight_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/highlight_modes"
+ photoeditor:icons="@array/highlight_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/highlight"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/shadow_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/shadow_modes"
+ photoeditor:icons="@array/shadow_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/shadow"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/autofix_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/autofix_modes"
+ photoeditor:icons="@array/autofix_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/autofix"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+
+ </LinearLayout>
+</com.android.photoeditor.EffectsGroup>
diff --git a/samples/PhotoEditor/res/layout/fix_effects.xml b/samples/PhotoEditor/res/layout/fix_effects.xml
new file mode 100644
index 0000000..7892646
--- /dev/null
+++ b/samples/PhotoEditor/res/layout/fix_effects.xml
@@ -0,0 +1,110 @@
+<?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.photoeditor.EffectsGroup
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:photoeditor="http://schemas.android.com/apk/res/com.android.photoeditor"
+ style="@style/EffectsBarLinearLayout">
+
+ <FrameLayout android:id="@+id/group_header" style="@style/GroupHeader">
+
+ <TextView android:text="@string/fix_group" style="@style/GroupLabel"/>
+ <ImageView android:id="@+id/group_arrow" style="@style/GroupArrow"/>
+
+ </FrameLayout>
+
+ <LinearLayout android:id="@+id/grouped_effects" style="@style/GroupContent">
+
+ <LinearLayout style="@style/Effect" android:id="@+id/crop_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/crop_modes"
+ photoeditor:icons="@array/crop_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/crop"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/rotate_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/rotate_modes"
+ photoeditor:icons="@array/rotate_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/rotate"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/straighten_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/straighten_modes"
+ photoeditor:icons="@array/straighten_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/straighten"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/flip_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/flip_modes"
+ photoeditor:icons="@array/flip_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/flip"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/redeye_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/redeye_modes"
+ photoeditor:icons="@array/redeye_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/redeye"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <LinearLayout style="@style/Effect" android:id="@+id/sharpen_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/sharpen_modes"
+ photoeditor:icons="@array/sharpen_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/sharpen"
+ style="@style/EffectLabel"/>
+ </LinearLayout>
+ <!--<LinearLayout style="@style/Effect" android:id="@+id/softfocus_effect">
+ <com.android.photoeditor.IconIndicator
+ android:id="@+id/effect_button"
+ style="@style/EffectIcon"
+ photoeditor:modes="@array/softfocus_modes"
+ photoeditor:icons="@array/softfocus_icons"/>
+ <TextView
+ android:id="@+id/effect_label"
+ android:text="@string/softfocus"
+ style="@style/EffectLabel"/>
+ </LinearLayout>-->
+
+ </LinearLayout>
+</com.android.photoeditor.EffectsGroup>
diff --git a/samples/PhotoEditor/res/layout/main.xml b/samples/PhotoEditor/res/layout/main.xml
new file mode 100644
index 0000000..dee7f50
--- /dev/null
+++ b/samples/PhotoEditor/res/layout/main.xml
@@ -0,0 +1,113 @@
+<?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.photoeditor.Toolbar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/toolbar"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <com.android.photoeditor.PhotoView
+ android:id="@+id/photo_view"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
+
+ <com.android.photoeditor.actions.TouchView
+ android:id="@+id/touch_view"
+ style="@style/FullscreenToolView"/>
+ <com.android.photoeditor.actions.DoodleView
+ android:id="@+id/doodle_view"
+ style="@style/FullscreenToolView"/>
+ <com.android.photoeditor.actions.RotateView
+ android:id="@+id/rotate_view"
+ style="@style/FullscreenToolView"/>
+ <com.android.photoeditor.actions.CropView
+ android:id="@+id/crop_view"
+ style="@style/FullscreenToolView"/>
+ <com.android.photoeditor.actions.ScaleWheel
+ android:id="@+id/scale_wheel"
+ android:tag="fadeOnIdle"
+ style="@style/Wheel"/>
+ <com.android.photoeditor.actions.ColorWheel
+ android:id="@+id/color_wheel"
+ android:tag="fadeOnIdle"
+ style="@style/Wheel"/>
+
+ <com.android.photoeditor.EffectsBar
+ android:id="@+id/effects_bar"
+ android:layout_below="@+id/action_bar"
+ style="@style/EffectsBar">
+
+ <LinearLayout
+ android:id="@+id/effects_container"
+ style="@style/EffectsBarLinearLayout">
+
+ <include layout="@layout/exposure_effects"/>
+ <include layout="@layout/fix_effects"/>
+ <include layout="@layout/artistic_effects"/>
+ <include layout="@layout/color_effects"/>
+ </LinearLayout>
+ </com.android.photoeditor.EffectsBar>
+
+ <com.android.photoeditor.ActionBar
+ android:id="@+id/action_bar"
+ android:tag="fadeOnIdle"
+ style="@style/ActionBar">
+
+ <RelativeLayout style="@style/ActionBarTranslucent">
+ <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_effect_name" style="@style/ActionBarText"/>
+ </LinearLayout>
+
+ <LinearLayout
+ style="@style/ActionBarLinearLayout"
+ android:layout_alignParentRight="true">
+
+ <ImageButton
+ android:id="@+id/save_button"
+ style="@style/ActionButton"
+ android:src="@drawable/saveas"/>
+ <ImageButton
+ android:id="@+id/undo_button"
+ style="@style/ActionButton"
+ android:src="@drawable/undo"/>
+ <ImageButton
+ android:id="@+id/redo_button"
+ style="@style/ActionButton"
+ android:src="@drawable/redo"/>
+ <ImageButton
+ android:id="@+id/quickview_button"
+ style="@style/RightmostActionButton"
+ android:src="@drawable/quickview"/>
+ </LinearLayout>
+ </RelativeLayout>
+
+ <ImageButton
+ android:id="@+id/quickview_on_button"
+ style="@style/RightmostActionButton"
+ android:layout_gravity="center_vertical|right"
+ android:src="@drawable/quickview_on"/>
+
+ </com.android.photoeditor.ActionBar>
+</com.android.photoeditor.Toolbar>
diff --git a/samples/PhotoEditor/res/values-ar/strings.xml b/samples/PhotoEditor/res/values-ar/strings.xml
new file mode 100644
index 0000000..4405989
--- /dev/null
+++ b/samples/PhotoEditor/res/values-ar/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"تعذر تحميل الصورة للتعديل"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"حدث خطأ أثناء الحفظ"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"تم حفظ <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="save_photo" msgid="3125109368779997862">"هل تريد حفظ الصورة التي تم تعديلها؟"</string>
+ <string name="yes" msgid="5402582493291792293">"نعم"</string>
+ <string name="no" msgid="5595408018304861875">"لا"</string>
+ <string name="cancel" msgid="6286688759430767307">"إلغاء"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"صور معدلة"</string>
+ <string name="artistic_group" msgid="573091775825402562">"فني"</string>
+ <string name="color_group" msgid="3390757893475444688">"اللون"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"التعرض للضوء"</string>
+ <string name="fix_group" msgid="971450155810247383">"إصلاح"</string>
+ <string name="autofix" msgid="1223859191856172755">"ضربة حظ"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"معالجة متقاطعة"</string>
+ <string name="documentary" msgid="50396326708699797">"وثائقي"</string>
+ <string name="doodle" msgid="1686409894518940990">"رسومات مبتكرة"</string>
+ <string name="duotone" msgid="8145893940788467106">"درجة لونين"</string>
+ <string name="filllight" msgid="2644989991700022526">"إضاءة تكميلية"</string>
+ <string name="fisheye" msgid="6037488646928998921">"عين سمكة"</string>
+ <string name="flip" msgid="2357692401826287480">"عكس"</string>
+ <string name="grain" msgid="7487585304579789098">"تحبب الفيلم"</string>
+ <string name="grayscale" msgid="615169770671286699">"تدرج الرمادي"</string>
+ <string name="highlight" msgid="3902653944386623972">"لمحات"</string>
+ <string name="lomoish" msgid="7793824845892532976">"متعلق بـ Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"سلبي"</string>
+ <string name="posterize" msgid="4139212359561383385">"تتالٍ"</string>
+ <string name="redeye" msgid="4958448806369928239">"العين الحمراء"</string>
+ <string name="rotate" msgid="6607597269792373083">"تدوير"</string>
+ <string name="saturation" msgid="8621322012271169931">"تشبع اللون"</string>
+ <string name="sepia" msgid="7978093531824705601">"بني داكن"</string>
+ <string name="shadow" msgid="8235188588101973090">"ظلال"</string>
+ <string name="sharpen" msgid="8449662378104403230">"زيادة الحدة"</string>
+ <string name="softfocus" msgid="6334228862566408303">"تركيز ضعيف"</string>
+ <string name="straighten" msgid="5217801513491493491">"تسوية"</string>
+ <string name="temperature" msgid="3589958696423284897">"درجة الحرارة"</string>
+ <string name="tint" msgid="154435943863418434">"تلوين خفيف"</string>
+ <string name="vignette" msgid="7648125924662648282">"نقوش صورة نصفية"</string>
+ <string name="warmify" msgid="2404474974251014984">"زيادة دفء اللون"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"اسحب الصورة لإنشاء رسومات مبتكرة"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"اسحب الصورة لقلبها"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"انقر لإزالة الأعين الحمراء"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"اسحب الصورة لتدويرها"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"اسحب الصورة لتسويتها"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-bg/strings.xml b/samples/PhotoEditor/res/values-bg/strings.xml
new file mode 100644
index 0000000..f28f7d7
--- /dev/null
+++ b/samples/PhotoEditor/res/values-bg/strings.xml
@@ -0,0 +1,68 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Снимката не може да бъде заредена за редактиране"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Възникна проблем при запазването"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"„<xliff:g id="FILENAME">%s</xliff:g>“ е запазено"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Да се запази ли редактираната снимка?"</string>
+ <string name="yes" msgid="5402582493291792293">"Да"</string>
+ <string name="no" msgid="5595408018304861875">"Нe"</string>
+ <string name="cancel" msgid="6286688759430767307">"Отказ"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Редактирани"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Художеств."</string>
+ <string name="color_group" msgid="3390757893475444688">"Цвят"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Експонация"</string>
+ <string name="fix_group" msgid="971450155810247383">"Корекция"</string>
+ <string name="autofix" msgid="1223859191856172755">"С късмет"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Кроспроцес"</string>
+ <string name="documentary" msgid="50396326708699797">"Документален филм"</string>
+ <string name="doodle" msgid="1686409894518940990">"Рисунка"</string>
+ <string name="duotone" msgid="8145893940788467106">"В два цвята"</string>
+ <string name="filllight" msgid="2644989991700022526">"Запълв. светл."</string>
+ <string name="fisheye" msgid="6037488646928998921">"Рибешко око"</string>
+ <string name="flip" msgid="2357692401826287480">"Обръщане"</string>
+ <string name="grain" msgid="7487585304579789098">"Зърнистост"</string>
+ <string name="grayscale" msgid="615169770671286699">"Сива скала"</string>
+ <string name="highlight" msgid="3902653944386623972">"Светли участъци"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Като „Ломо“"</string>
+ <string name="negative" msgid="1985508917342811252">"Негатив"</string>
+ <string name="posterize" msgid="4139212359561383385">"Плакат"</string>
+ <string name="redeye" msgid="4958448806369928239">"Червени очи"</string>
+ <string name="rotate" msgid="6607597269792373083">"Завъртане"</string>
+ <string name="saturation" msgid="8621322012271169931">"Насищане"</string>
+ <string name="sepia" msgid="7978093531824705601">"Сепия"</string>
+ <string name="shadow" msgid="8235188588101973090">"Тъмни участъци"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Отчетливост"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Мек фокус"</string>
+ <string name="straighten" msgid="5217801513491493491">"Изправяне"</string>
+ <string name="temperature" msgid="3589958696423284897">"Температура"</string>
+ <string name="tint" msgid="154435943863418434">"Нюансиране"</string>
+ <string name="vignette" msgid="7648125924662648282">"Винетиране"</string>
+ <string name="warmify" msgid="2404474974251014984">"Затопляне"</string>
+ <!-- no translation found for doodle_tooltip (2902117272374362915) -->
+ <skip />
+ <!-- no translation found for flip_tooltip (2700943256714731737) -->
+ <skip />
+ <!-- no translation found for redeye_tooltip (9112774042113471358) -->
+ <skip />
+ <!-- no translation found for rotate_tooltip (7008602969130734229) -->
+ <skip />
+ <!-- no translation found for straighten_tooltip (4846317027139212339) -->
+ <skip />
+</resources>
diff --git a/samples/PhotoEditor/res/values-ca/strings.xml b/samples/PhotoEditor/res/values-ca/strings.xml
new file mode 100644
index 0000000..0dcb2e6
--- /dev/null
+++ b/samples/PhotoEditor/res/values-ca/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Estudi de fotografia"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"La foto no es pot carregar per editar-la"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"S\'ha produït un problema en desar"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> desada"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Vols desar la foto editada?"</string>
+ <string name="yes" msgid="5402582493291792293">"Sí"</string>
+ <string name="no" msgid="5595408018304861875">"No"</string>
+ <string name="cancel" msgid="6286688759430767307">"Cancel·la"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Editades"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artístics"</string>
+ <string name="color_group" msgid="3390757893475444688">"Color"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposició"</string>
+ <string name="fix_group" msgid="971450155810247383">"Correcció"</string>
+ <string name="autofix" msgid="1223859191856172755">"Correcc. autom."</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Procés creuat"</string>
+ <string name="documentary" msgid="50396326708699797">"Documental"</string>
+ <string name="doodle" msgid="1686409894518940990">"Gargots"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dos tons"</string>
+ <string name="filllight" msgid="2644989991700022526">"Retroil·lumin."</string>
+ <string name="fisheye" msgid="6037488646928998921">"Ull de peix"</string>
+ <string name="flip" msgid="2357692401826287480">"Inversió"</string>
+ <string name="grain" msgid="7487585304579789098">"Gra pel·lícula"</string>
+ <string name="grayscale" msgid="615169770671286699">"Escala grisos"</string>
+ <string name="highlight" msgid="3902653944386623972">"Realçaments"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Estil Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatiu"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterització"</string>
+ <string name="redeye" msgid="4958448806369928239">"Ulls vermells"</string>
+ <string name="rotate" msgid="6607597269792373083">"Gir"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturació"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sèpia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Ombres"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Nitidesa"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Focus suau"</string>
+ <string name="straighten" msgid="5217801513491493491">"Redreçament"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Tint"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinyeta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Calidesa"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Arrossega el cursor sobre la foto per dibuixar"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Arrossega la foto per invertir-la"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Toca per eliminar els ulls vermells"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Arrossega la foto per girar-la"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Arrossega la foto per redreçar-la"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-cs/strings.xml b/samples/PhotoEditor/res/values-cs/strings.xml
new file mode 100644
index 0000000..df0b474
--- /dev/null
+++ b/samples/PhotoEditor/res/values-cs/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Fotografii nelze načíst k úpravám"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Při ukládání došlo k problému"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Fotografie <xliff:g id="FILENAME">%s</xliff:g> byla uložena"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Chcete upravenou fotografii uložit?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ano"</string>
+ <string name="no" msgid="5595408018304861875">"Ne"</string>
+ <string name="cancel" msgid="6286688759430767307">"Zrušit"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Upraveno"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Umělecké"</string>
+ <string name="color_group" msgid="3390757893475444688">"Barva"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Expozice"</string>
+ <string name="fix_group" msgid="971450155810247383">"Korekce"</string>
+ <string name="autofix" msgid="1223859191856172755">"Zkusím štěstí"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Křížové zpracování"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokument"</string>
+ <string name="doodle" msgid="1686409894518940990">"Kreslení"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duo-tone"</string>
+ <string name="filllight" msgid="2644989991700022526">"Zadní osvětlení"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Rybí oko"</string>
+ <string name="flip" msgid="2357692401826287480">"Převrácení"</string>
+ <string name="grain" msgid="7487585304579789098">"Zrnitý film"</string>
+ <string name="grayscale" msgid="615169770671286699">"Stupně šedi"</string>
+ <string name="highlight" msgid="3902653944386623972">"Přisvícení"</string>
+ <string name="lomoish" msgid="7793824845892532976">"LOMO"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativ"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterizace"</string>
+ <string name="redeye" msgid="4958448806369928239">"Červené oči"</string>
+ <string name="rotate" msgid="6607597269792373083">"Otočení"</string>
+ <string name="saturation" msgid="8621322012271169931">"Sytost"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sépiové barvy"</string>
+ <string name="shadow" msgid="8235188588101973090">"Stíny"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Zaostření"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Jemné rozostření"</string>
+ <string name="straighten" msgid="5217801513491493491">"Vyrovnání"</string>
+ <string name="temperature" msgid="3589958696423284897">"Teplota"</string>
+ <string name="tint" msgid="154435943863418434">"Tónování"</string>
+ <string name="vignette" msgid="7648125924662648282">"Medailonek"</string>
+ <string name="warmify" msgid="2404474974251014984">"Teplejší barvy"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Na fotografii můžete kreslit prstem"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Táhnutím prstem fotografii převrátíte"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Klepnutím odstraníte červené oči"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Táhnutím prstem fotografii otočíte"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Táhnutím prstem fotografii narovnáte"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-da/strings.xml b/samples/PhotoEditor/res/values-da/strings.xml
new file mode 100644
index 0000000..0dd390f
--- /dev/null
+++ b/samples/PhotoEditor/res/values-da/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Fotostudie"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Billedet kan ikke indlæses til redigering"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Der opstod et problem ved lagring"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> er gemt"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Vil du gemme det redigerede billede?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ja"</string>
+ <string name="no" msgid="5595408018304861875">"Nej"</string>
+ <string name="cancel" msgid="6286688759430767307">"Annuller"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Redigeret"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Kunstnerisk"</string>
+ <string name="color_group" msgid="3390757893475444688">"Farve"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Eksponering"</string>
+ <string name="fix_group" msgid="971450155810247383">"Ret"</string>
+ <string name="autofix" msgid="1223859191856172755">"Prøv lykken"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Krydsfremkald"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentar"</string>
+ <string name="doodle" msgid="1686409894518940990">"Krusedulle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Tofarvet"</string>
+ <string name="filllight" msgid="2644989991700022526">"Oplys mørke"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fiskeøje"</string>
+ <string name="flip" msgid="2357692401826287480">"Vend"</string>
+ <string name="grain" msgid="7487585304579789098">"Filmkorn"</string>
+ <string name="grayscale" msgid="615169770671286699">"Gråtoner"</string>
+ <string name="highlight" msgid="3902653944386623972">"Fremhævning"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo-effekt"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativ"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterisering"</string>
+ <string name="redeye" msgid="4958448806369928239">"Røde øjne"</string>
+ <string name="rotate" msgid="6607597269792373083">"Roter"</string>
+ <string name="saturation" msgid="8621322012271169931">"Mætning"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Skygger"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Gør skarpere"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Blødt fokus"</string>
+ <string name="straighten" msgid="5217801513491493491">"Ret op"</string>
+ <string name="temperature" msgid="3589958696423284897">"Farvetemperatur"</string>
+ <string name="tint" msgid="154435943863418434">"Nuance"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignet"</string>
+ <string name="warmify" msgid="2404474974251014984">"Gør varmere"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Tegn på billedet for at lave kruseduller"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Træk billedet for at vende det"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Tryk for at fjerne røde øjne"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Træk billedet for at rotere det"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Træk i billedet for at rette det op"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-de/strings.xml b/samples/PhotoEditor/res/values-de/strings.xml
new file mode 100644
index 0000000..0e5df5f
--- /dev/null
+++ b/samples/PhotoEditor/res/values-de/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Laden des Fotos zum Bearbeiten nicht möglich."</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Beim Speichern ist ein Problem aufgetreten."</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> gespeichert"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Bearbeitetes Foto speichern?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ja"</string>
+ <string name="no" msgid="5595408018304861875">"Nein"</string>
+ <string name="cancel" msgid="6286688759430767307">"Abbrechen"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Bearbeitet"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Kunstvoll"</string>
+ <string name="color_group" msgid="3390757893475444688">"Farbe"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Belichtung"</string>
+ <string name="fix_group" msgid="971450155810247383">"Korrektur"</string>
+ <string name="autofix" msgid="1223859191856172755">"Gute Stimmung"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Cross-Entwickl."</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentation"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duoton"</string>
+ <string name="filllight" msgid="2644989991700022526">"Aufhellen"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fischauge"</string>
+ <string name="flip" msgid="2357692401826287480">"Spiegeln"</string>
+ <string name="grain" msgid="7487585304579789098">"Filmkörnung"</string>
+ <string name="grayscale" msgid="615169770671286699">"Graustufen"</string>
+ <string name="highlight" msgid="3902653944386623972">"Highlights"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomoesk"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativ"</string>
+ <string name="posterize" msgid="4139212359561383385">"Tontrennung"</string>
+ <string name="redeye" msgid="4958448806369928239">"Rote Augen"</string>
+ <string name="rotate" msgid="6607597269792373083">"Drehen"</string>
+ <string name="saturation" msgid="8621322012271169931">"Sättigung"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Schatten"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Scharf stellen"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Weichzeichnen"</string>
+ <string name="straighten" msgid="5217801513491493491">"Ausrichten"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatur"</string>
+ <string name="tint" msgid="154435943863418434">"Färbung"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignettierung"</string>
+ <string name="warmify" msgid="2404474974251014984">"Wärmer gestalten"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Zum Erstellen eines Doodles auf dem Foto zeichnen"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Foto ziehen, um es umzudrehen"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Zum Entfernen roter Augen tippen"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Foto ziehen, um es zu drehen"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Foto ziehen, um es zu strecken"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-el/strings.xml b/samples/PhotoEditor/res/values-el/strings.xml
new file mode 100644
index 0000000..30651ba
--- /dev/null
+++ b/samples/PhotoEditor/res/values-el/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Δεν είναι δυνατή η φόρτωση της φωτογραφίας για επεξεργασία"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Παρουσιάστηκε πρόβλημα κατά την αποθήκευση"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Το <xliff:g id="FILENAME">%s</xliff:g> αποθηκεύτηκε"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Αποθήκευση επεξεργασμένης φωτογραφίας;"</string>
+ <string name="yes" msgid="5402582493291792293">"Ναι"</string>
+ <string name="no" msgid="5595408018304861875">"Όχι"</string>
+ <string name="cancel" msgid="6286688759430767307">"Ακύρωση"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Επεξεργασμ."</string>
+ <string name="artistic_group" msgid="573091775825402562">"Καλλιτεχν."</string>
+ <string name="color_group" msgid="3390757893475444688">"Χρώμα"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Έκθεση"</string>
+ <string name="fix_group" msgid="971450155810247383">"Διόρθωση"</string>
+ <string name="autofix" msgid="1223859191856172755">"Νιώθω τυχερός"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Cross Process"</string>
+ <string name="documentary" msgid="50396326708699797">"Ντοκιμαντέρ"</string>
+ <string name="doodle" msgid="1686409894518940990">"Σκετσάκι"</string>
+ <string name="duotone" msgid="8145893940788467106">"Δύο τόνοι"</string>
+ <string name="filllight" msgid="2644989991700022526">"Γέμισμα με φως"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fisheye"</string>
+ <string name="flip" msgid="2357692401826287480">"Αναστροφή"</string>
+ <string name="grain" msgid="7487585304579789098">"Κόκκος φιλμ"</string>
+ <string name="grayscale" msgid="615169770671286699">"Κλίμακα του γκρι"</string>
+ <string name="highlight" msgid="3902653944386623972">"Επισημάνσεις"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Φίλτρο lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Αρνητικό"</string>
+ <string name="posterize" msgid="4139212359561383385">"Ποστεροποίηση"</string>
+ <string name="redeye" msgid="4958448806369928239">"Κόκκινα μάτια"</string>
+ <string name="rotate" msgid="6607597269792373083">"Περιστροφή"</string>
+ <string name="saturation" msgid="8621322012271169931">"Κορεσμός"</string>
+ <string name="sepia" msgid="7978093531824705601">"Σέπια"</string>
+ <string name="shadow" msgid="8235188588101973090">"Σκιές"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Όξυνση"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Απαλή εστίαση"</string>
+ <string name="straighten" msgid="5217801513491493491">"Ευθυγράμμιση"</string>
+ <string name="temperature" msgid="3589958696423284897">"Θερμοκρασία"</string>
+ <string name="tint" msgid="154435943863418434">"Απόχρωση"</string>
+ <string name="vignette" msgid="7648125924662648282">"Βινιετάρισμα"</string>
+ <string name="warmify" msgid="2404474974251014984">"Ζεστή απόχρωση"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Σχεδιάστε στη φωτογραφία για να δημιουργήσετε ένα σκετσάκι"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Σύρετε τη φωτογραφία για να την αναστρέψετε"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Πατήστε για να αφαιρέσετε τα κόκκινα μάτια"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Σύρετε τη φωτογραφία για να την περιστρέψετε"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Σύρετε τη φωτογραφία για να την ευθυγραμμίσετε"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-en-rGB/strings.xml b/samples/PhotoEditor/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..14df5a7
--- /dev/null
+++ b/samples/PhotoEditor/res/values-en-rGB/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Cannot load the photo for editing"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"A problem occurred during saving"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> saved"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Save edited photo?"</string>
+ <string name="yes" msgid="5402582493291792293">"Yes"</string>
+ <string name="no" msgid="5595408018304861875">"No"</string>
+ <string name="cancel" msgid="6286688759430767307">"Cancel"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Edited"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistic"</string>
+ <string name="color_group" msgid="3390757893475444688">"Colour"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposure"</string>
+ <string name="fix_group" msgid="971450155810247383">"Fix"</string>
+ <string name="autofix" msgid="1223859191856172755">"Feel Lucky"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Cross-Process"</string>
+ <string name="documentary" msgid="50396326708699797">"Documentary"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duo-tone"</string>
+ <string name="filllight" msgid="2644989991700022526">"Fill Light"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fisheye"</string>
+ <string name="flip" msgid="2357692401826287480">"Flip"</string>
+ <string name="grain" msgid="7487585304579789098">"Film Grain"</string>
+ <string name="grayscale" msgid="615169770671286699">"Grey Scale"</string>
+ <string name="highlight" msgid="3902653944386623972">"Highlights"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo-ish"</string>
+ <string name="negative" msgid="1985508917342811252">"Negative"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterise"</string>
+ <string name="redeye" msgid="4958448806369928239">"Red Eye"</string>
+ <string name="rotate" msgid="6607597269792373083">"Rotate"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturation"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Shadows"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Sharpen"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Soft Focus"</string>
+ <string name="straighten" msgid="5217801513491493491">"Straighten"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperature"</string>
+ <string name="tint" msgid="154435943863418434">"Tint"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignette"</string>
+ <string name="warmify" msgid="2404474974251014984">"Warmify"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Draw on photo to doodle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Drag photo to flip it"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Tap to remove red eyes"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Drag photo to rotate it"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Drag photo to straighten it"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-es-rUS/strings.xml b/samples/PhotoEditor/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..02c54ef
--- /dev/null
+++ b/samples/PhotoEditor/res/values-es-rUS/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Estudio de fotografía"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"No se puede cargar la foto para editarla"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Se produjo un problema al guardar"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> guardado"</string>
+ <string name="save_photo" msgid="3125109368779997862">"¿Guardar foto editada?"</string>
+ <string name="yes" msgid="5402582493291792293">"Sí"</string>
+ <string name="no" msgid="5595408018304861875">"No"</string>
+ <string name="cancel" msgid="6286688759430767307">"Cancelar"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Editadas"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artísticos"</string>
+ <string name="color_group" msgid="3390757893475444688">"Color"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposición."</string>
+ <string name="fix_group" msgid="971450155810247383">"Con arreglos"</string>
+ <string name="autofix" msgid="1223859191856172755">"Me siento con suerte"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Proceso cruzado"</string>
+ <string name="documentary" msgid="50396326708699797">"Documental"</string>
+ <string name="doodle" msgid="1686409894518940990">"Garabatear"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dos tonos"</string>
+ <string name="filllight" msgid="2644989991700022526">"Aumentar brillo"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Ojo de pez"</string>
+ <string name="flip" msgid="2357692401826287480">"Cambiar"</string>
+ <string name="grain" msgid="7487585304579789098">"Grano de la película"</string>
+ <string name="grayscale" msgid="615169770671286699">"Escala de grises"</string>
+ <string name="highlight" msgid="3902653944386623972">"Destacadas"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Estilo Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"negativo"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterizar"</string>
+ <string name="redeye" msgid="4958448806369928239">"Ojos rojos"</string>
+ <string name="rotate" msgid="6607597269792373083">"Girar"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturación"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Sombras"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Mejorar nitidez"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Desenfocar"</string>
+ <string name="straighten" msgid="5217801513491493491">"Enderezar"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Matiz"</string>
+ <string name="vignette" msgid="7648125924662648282">"Viñeta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Más calidez"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Dibuja en la foto para hacer garabatos"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Arrastra la foto para darla vuelta"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Toca para eliminar los ojos rojos"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Arrastra la foto para hacerla girar"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Arrastra la foto para enderezarla"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-es/strings.xml b/samples/PhotoEditor/res/values-es/strings.xml
new file mode 100644
index 0000000..08b808e
--- /dev/null
+++ b/samples/PhotoEditor/res/values-es/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"No se puede cargar la foto para editarla."</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Error al guardar"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> guardado"</string>
+ <string name="save_photo" msgid="3125109368779997862">"¿Quieres guardar la foto editada?"</string>
+ <string name="yes" msgid="5402582493291792293">"Sí"</string>
+ <string name="no" msgid="5595408018304861875">"No"</string>
+ <string name="cancel" msgid="6286688759430767307">"Cancelar"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Editadas"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artísticos"</string>
+ <string name="color_group" msgid="3390757893475444688">"Color"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposición"</string>
+ <string name="fix_group" msgid="971450155810247383">"Fijos"</string>
+ <string name="autofix" msgid="1223859191856172755">"Con suerte"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Proceso cruzado"</string>
+ <string name="documentary" msgid="50396326708699797">"Documental"</string>
+ <string name="doodle" msgid="1686409894518940990">"Garabatos"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dos tonos"</string>
+ <string name="filllight" msgid="2644989991700022526">"Luz de relleno"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Ojo de pez"</string>
+ <string name="flip" msgid="2357692401826287480">"Dar la vuelta"</string>
+ <string name="grain" msgid="7487585304579789098">"Grano de película"</string>
+ <string name="grayscale" msgid="615169770671286699">"Escala de grises"</string>
+ <string name="highlight" msgid="3902653944386623972">"Reflejos"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Estilo lomográfico"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativo"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterizar"</string>
+ <string name="redeye" msgid="4958448806369928239">"Ojos rojos"</string>
+ <string name="rotate" msgid="6607597269792373083">"Girar"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturación"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Sombras"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Enfocar"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Desenfocar"</string>
+ <string name="straighten" msgid="5217801513491493491">"Enderezar"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Tinte"</string>
+ <string name="vignette" msgid="7648125924662648282">"Viñeta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Más calidez"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Dibuja en la foto para hacer garabatos"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Arrastra la foto para darle la vuelta"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Toca para eliminar los ojos rojos"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Arrastra la foto para girarla"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Arrastra la foto para enderezarla"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-fa/strings.xml b/samples/PhotoEditor/res/values-fa/strings.xml
new file mode 100644
index 0000000..b1ce3f2
--- /dev/null
+++ b/samples/PhotoEditor/res/values-fa/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"بارگیری عکس برای ویرایش ممکن نیست"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"هنگام ذخیره کردن خطایی رخ داد"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> ذخیره شد"</string>
+ <string name="save_photo" msgid="3125109368779997862">"عکس ویرایش شده ذخیره شود؟"</string>
+ <string name="yes" msgid="5402582493291792293">"بله"</string>
+ <string name="no" msgid="5595408018304861875">"خیر"</string>
+ <string name="cancel" msgid="6286688759430767307">"لغو"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"ویرایش شده"</string>
+ <string name="artistic_group" msgid="573091775825402562">"هنرمندانه"</string>
+ <string name="color_group" msgid="3390757893475444688">"رنگ"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"نوردهی"</string>
+ <string name="fix_group" msgid="971450155810247383">"ثابت"</string>
+ <string name="autofix" msgid="1223859191856172755">"احساس خوشبختی"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"فرآیند همگذاری"</string>
+ <string name="documentary" msgid="50396326708699797">"مستند"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"دو تن رنگ"</string>
+ <string name="filllight" msgid="2644989991700022526">"پر کردن نور"</string>
+ <string name="fisheye" msgid="6037488646928998921">"فیش آی"</string>
+ <string name="flip" msgid="2357692401826287480">"برعکس کردن"</string>
+ <string name="grain" msgid="7487585304579789098">"بافت فیلم"</string>
+ <string name="grayscale" msgid="615169770671286699">"طیف خاکستری"</string>
+ <string name="highlight" msgid="3902653944386623972">"قسمت های برجسته"</string>
+ <string name="lomoish" msgid="7793824845892532976">"شبیه Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"نگاتیو"</string>
+ <string name="posterize" msgid="4139212359561383385">"پوستری کردن"</string>
+ <string name="redeye" msgid="4958448806369928239">"قرمزی چشم"</string>
+ <string name="rotate" msgid="6607597269792373083">"چرخش"</string>
+ <string name="saturation" msgid="8621322012271169931">"اشباع"</string>
+ <string name="sepia" msgid="7978093531824705601">"سپیا"</string>
+ <string name="shadow" msgid="8235188588101973090">"سایه ها"</string>
+ <string name="sharpen" msgid="8449662378104403230">"وضوح"</string>
+ <string name="softfocus" msgid="6334228862566408303">"فوکوس نرم"</string>
+ <string name="straighten" msgid="5217801513491493491">"صاف کردن"</string>
+ <string name="temperature" msgid="3589958696423284897">"دما"</string>
+ <string name="tint" msgid="154435943863418434">"ته رنگ"</string>
+ <string name="vignette" msgid="7648125924662648282">"وینت"</string>
+ <string name="warmify" msgid="2404474974251014984">"گرم کردن"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"برای doodle کردن روی عکس رسم کنید"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"برای وارونه کردن عکس آن را بکشید"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"برای حذف قرمزی چشم ضربه بزنید"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"برای چرخاندن عکس آن را بکشید"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"برای صاف کردن عکس آن را بکشید"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-fi/strings.xml b/samples/PhotoEditor/res/values-fi/strings.xml
new file mode 100644
index 0000000..4afa2d8
--- /dev/null
+++ b/samples/PhotoEditor/res/values-fi/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Kuvaa ei voi ladata muokattavaksi"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Ongelma tallennuksessa"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> tallennettu"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Tallenna muokattu valokuva?"</string>
+ <string name="yes" msgid="5402582493291792293">"Kyllä"</string>
+ <string name="no" msgid="5595408018304861875">"Ei"</string>
+ <string name="cancel" msgid="6286688759430767307">"Peruuta"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Muokattu"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Taiteellinen"</string>
+ <string name="color_group" msgid="3390757893475444688">"Väri"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Valotus"</string>
+ <string name="fix_group" msgid="971450155810247383">"Korjaus"</string>
+ <string name="autofix" msgid="1223859191856172755">"Kokeilen onneani"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Ristiinkäsittely"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentti"</string>
+ <string name="doodle" msgid="1686409894518940990">"Piirtely"</string>
+ <string name="duotone" msgid="8145893940788467106">"Kaksoissävy"</string>
+ <string name="filllight" msgid="2644989991700022526">"Täytä vaaleat kohdat"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Kalansilmä"</string>
+ <string name="flip" msgid="2357692401826287480">"Käännä"</string>
+ <string name="grain" msgid="7487585304579789098">"Filmin rakeisuus"</string>
+ <string name="grayscale" msgid="615169770671286699">"Harmaasävy"</string>
+ <string name="highlight" msgid="3902653944386623972">"Kohokohdat"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomomainen"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatiivinen"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterisaatio"</string>
+ <string name="redeye" msgid="4958448806369928239">"Punasilmäisyys"</string>
+ <string name="rotate" msgid="6607597269792373083">"Kierrä"</string>
+ <string name="saturation" msgid="8621322012271169931">"Värikylläisyys"</string>
+ <string name="sepia" msgid="7978093531824705601">"Seepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Varjostus"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Tarkennus"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Pehmeäpiirto"</string>
+ <string name="straighten" msgid="5217801513491493491">"Suorista"</string>
+ <string name="temperature" msgid="3589958696423284897">"Lämpötila"</string>
+ <string name="tint" msgid="154435943863418434">"Sävytys"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinjetti"</string>
+ <string name="warmify" msgid="2404474974251014984">"Lämmitä sävyt"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Piirrä valokuvaan siirtämällä osoitin sen päälle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Käännä kuva vetämällä"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Poista punaiset silmät napauttamalla"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Käännä valokuvaa vetämällä"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Suorista valokuva vetämällä"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-fr/strings.xml b/samples/PhotoEditor/res/values-fr/strings.xml
new file mode 100644
index 0000000..be8d971
--- /dev/null
+++ b/samples/PhotoEditor/res/values-fr/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Impossible de charger la photo pour la retoucher."</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Un problème est survenu lors de l\'enregistrement."</string>
+ <string name="photo_saved" msgid="5093564498138145422">"\"<xliff:g id="FILENAME">%s</xliff:g>\" enregistré"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Enregistrer photo retouchée ?"</string>
+ <string name="yes" msgid="5402582493291792293">"Oui"</string>
+ <string name="no" msgid="5595408018304861875">"Non"</string>
+ <string name="cancel" msgid="6286688759430767307">"Annuler"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Modifiées"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistique"</string>
+ <string name="color_group" msgid="3390757893475444688">"Couleur"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposition"</string>
+ <string name="fix_group" msgid="971450155810247383">"Retouches"</string>
+ <string name="autofix" msgid="1223859191856172755">"J\'ai de la chance"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Traitement croisé"</string>
+ <string name="documentary" msgid="50396326708699797">"Documentaire"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Deux tons"</string>
+ <string name="filllight" msgid="2644989991700022526">"Éclairage d\'appoint"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Objectif fisheye"</string>
+ <string name="flip" msgid="2357692401826287480">"Retourner"</string>
+ <string name="grain" msgid="7487585304579789098">"Grain"</string>
+ <string name="grayscale" msgid="615169770671286699">"Nuances de gris"</string>
+ <string name="highlight" msgid="3902653944386623972">"Reflets"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Effet Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Négatif"</string>
+ <string name="posterize" msgid="4139212359561383385">"Postérisation"</string>
+ <string name="redeye" msgid="4958448806369928239">"Yeux rouges"</string>
+ <string name="rotate" msgid="6607597269792373083">"Rotation"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturation"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sépia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Ombres"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Netteté"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Flou artistique"</string>
+ <string name="straighten" msgid="5217801513491493491">"Redresser"</string>
+ <string name="temperature" msgid="3589958696423284897">"Température"</string>
+ <string name="tint" msgid="154435943863418434">"Coloration"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignetage"</string>
+ <string name="warmify" msgid="2404474974251014984">"Couleurs chaudes"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Dessiner sur la photo pour gribouiller"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Faire glisser la photo pour la retourner"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Appuyer pour supprimer les yeux rouges"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Faire glisser la photo pour la faire pivoter"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Faire glisser la photo pour la redresser"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-hr/strings.xml b/samples/PhotoEditor/res/values-hr/strings.xml
new file mode 100644
index 0000000..bfbe0ea
--- /dev/null
+++ b/samples/PhotoEditor/res/values-hr/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Foto studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Ne mogu učitati fotografiju za uređivanje"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Došlo je do problema tijekom spremanja"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> spremljena"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Spremiti uređenu fotografiju?"</string>
+ <string name="yes" msgid="5402582493291792293">"Da"</string>
+ <string name="no" msgid="5595408018304861875">"Ne"</string>
+ <string name="cancel" msgid="6286688759430767307">"Odustani"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Uređeno"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Umjetnički"</string>
+ <string name="color_group" msgid="3390757893475444688">"Boja"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Ekspozicija"</string>
+ <string name="fix_group" msgid="971450155810247383">"Popravi"</string>
+ <string name="autofix" msgid="1223859191856172755">"Sretan"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Pomak boja"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentarni"</string>
+ <string name="doodle" msgid="1686409894518940990">"Škrabotine"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duo-ton"</string>
+ <string name="filllight" msgid="2644989991700022526">"Ispuni svjetlom"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Riblje oko"</string>
+ <string name="flip" msgid="2357692401826287480">"Okreni"</string>
+ <string name="grain" msgid="7487585304579789098">"Zrnatost filma"</string>
+ <string name="grayscale" msgid="615169770671286699">"Nijanse sive"</string>
+ <string name="highlight" msgid="3902653944386623972">"Isticanja"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomografski"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativan"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterizacija"</string>
+ <string name="redeye" msgid="4958448806369928239">"Crvene oči"</string>
+ <string name="rotate" msgid="6607597269792373083">"Rotiraj"</string>
+ <string name="saturation" msgid="8621322012271169931">"Zasićenje"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepija"</string>
+ <string name="shadow" msgid="8235188588101973090">"Sjenčanje"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Izoštri"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Mekani fokus"</string>
+ <string name="straighten" msgid="5217801513491493491">"Poravnaj"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Svjetlija nijansa"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinjeta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Stavi topliju nijansu"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Crtanje na fotografiju za doodle logotip"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Povucite sliku da biste je preokrenuli"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Dotaknite za uklanjanje crvenih očiju"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Povucite fotografiju da biste je rotirali"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Povucite fotografiju da biste je izravnali"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-hu/strings.xml b/samples/PhotoEditor/res/values-hu/strings.xml
new file mode 100644
index 0000000..3dfc431
--- /dev/null
+++ b/samples/PhotoEditor/res/values-hu/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"A fotó nem tölthető be szerkesztésre"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Hiba történt mentés közben"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> mentve"</string>
+ <string name="save_photo" msgid="3125109368779997862">"A szerkesztett kép mentése?"</string>
+ <string name="yes" msgid="5402582493291792293">"Igen"</string>
+ <string name="no" msgid="5595408018304861875">"Nem"</string>
+ <string name="cancel" msgid="6286688759430767307">"Mégse"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Szerkesztve"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Művészi"</string>
+ <string name="color_group" msgid="3390757893475444688">"Szín"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Expozíció"</string>
+ <string name="fix_group" msgid="971450155810247383">"Javítás"</string>
+ <string name="autofix" msgid="1223859191856172755">"Jó napom van"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Áttűnés"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentumfilm"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Kéttónusú"</string>
+ <string name="filllight" msgid="2644989991700022526">"Háttérfény"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Halszem"</string>
+ <string name="flip" msgid="2357692401826287480">"Tükrözés"</string>
+ <string name="grain" msgid="7487585304579789098">"Filmszemcse"</string>
+ <string name="grayscale" msgid="615169770671286699">"Szürkeárnyalat"</string>
+ <string name="highlight" msgid="3902653944386623972">"Kiemelt részek"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo-hatás"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatív"</string>
+ <string name="posterize" msgid="4139212359561383385">"Poszterizálás"</string>
+ <string name="redeye" msgid="4958448806369928239">"Vörösszem"</string>
+ <string name="rotate" msgid="6607597269792373083">"Forgatás"</string>
+ <string name="saturation" msgid="8621322012271169931">"Telítettség"</string>
+ <string name="sepia" msgid="7978093531824705601">"Szépia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Árnyékok"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Élesítés"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Lágy fókusz"</string>
+ <string name="straighten" msgid="5217801513491493491">"Kiegyenesítés"</string>
+ <string name="temperature" msgid="3589958696423284897">"Hőmérséklet"</string>
+ <string name="tint" msgid="154435943863418434">"Árnyalás"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignetta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Melegítés"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Rajzoljon a fotóra embléma létrehozásához"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Húzza a fotót annak tükrözéséhez"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Érintse meg a vörös szem hatás eltávolításához"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Húzza a fényképet annak forgatásához"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Húzza a fényképet annak kiegyenesítéséhez"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-in/strings.xml b/samples/PhotoEditor/res/values-in/strings.xml
new file mode 100644
index 0000000..7f3345c
--- /dev/null
+++ b/samples/PhotoEditor/res/values-in/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Tidak dapat memuat foto untuk diedit"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Terjadi masalah saat menyimpan"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> tersimpan"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Simpan foto yang diedit?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ya"</string>
+ <string name="no" msgid="5595408018304861875">"Tidak"</string>
+ <string name="cancel" msgid="6286688759430767307">"Batal"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Hasil edit"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistik"</string>
+ <string name="color_group" msgid="3390757893475444688">"Warna"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Pencahayaan"</string>
+ <string name="fix_group" msgid="971450155810247383">"Diperbaiki"</string>
+ <string name="autofix" msgid="1223859191856172755">"Lagi Beruntung"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Proses silang"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumenter"</string>
+ <string name="doodle" msgid="1686409894518940990">"Orat-oret"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duo-warna"</string>
+ <string name="filllight" msgid="2644989991700022526">"Isi Cahaya"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Mata Ikan"</string>
+ <string name="flip" msgid="2357692401826287480">"Balik"</string>
+ <string name="grain" msgid="7487585304579789098">"Butiran Film"</string>
+ <string name="grayscale" msgid="615169770671286699">"Gradasi kelabu"</string>
+ <string name="highlight" msgid="3902653944386623972">"Sorotan"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Gaya Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatif"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterisasi"</string>
+ <string name="redeye" msgid="4958448806369928239">"Mata Merah"</string>
+ <string name="rotate" msgid="6607597269792373083">"Putar"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturasi"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Bayangan"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Pertajam"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Fokus Lembut"</string>
+ <string name="straighten" msgid="5217801513491493491">"Luruskan"</string>
+ <string name="temperature" msgid="3589958696423284897">"Suhu"</string>
+ <string name="tint" msgid="154435943863418434">"Warna"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinyet"</string>
+ <string name="warmify" msgid="2404474974251014984">"Perhangat"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Menggambar pada foto untuk membuat orat-oret"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Seret foto untuk membaliknya"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Ketuk untuk menghapus mata merah"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Seret foto untuk memutarnya"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Seret foto untuk meluruskannya"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-it/strings.xml b/samples/PhotoEditor/res/values-it/strings.xml
new file mode 100644
index 0000000..e60d281
--- /dev/null
+++ b/samples/PhotoEditor/res/values-it/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Impossibile caricare la foto da modificare"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Si è verificato un problema durante il salvataggio"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> salvato"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Salvare la foto modificata?"</string>
+ <string name="yes" msgid="5402582493291792293">"Sì"</string>
+ <string name="no" msgid="5595408018304861875">"No"</string>
+ <string name="cancel" msgid="6286688759430767307">"Annulla"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Modificata"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistici"</string>
+ <string name="color_group" msgid="3390757893475444688">"Colore"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Esposizione"</string>
+ <string name="fix_group" msgid="971450155810247383">"Correggi"</string>
+ <string name="autofix" msgid="1223859191856172755">"Mi sento fortunato"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Cross process"</string>
+ <string name="documentary" msgid="50396326708699797">"Documentario"</string>
+ <string name="doodle" msgid="1686409894518940990">"Scarabocchio"</string>
+ <string name="duotone" msgid="8145893940788467106">"Bitonale"</string>
+ <string name="filllight" msgid="2644989991700022526">"Luce di riempimento"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fisheye"</string>
+ <string name="flip" msgid="2357692401826287480">"Capovolgi"</string>
+ <string name="grain" msgid="7487585304579789098">"Grana pellicola"</string>
+ <string name="grayscale" msgid="615169770671286699">"Scala di grigi"</string>
+ <string name="highlight" msgid="3902653944386623972">"Luci"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Effetto Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativo"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterizza"</string>
+ <string name="redeye" msgid="4958448806369928239">"Occhi rossi"</string>
+ <string name="rotate" msgid="6607597269792373083">"Ruota"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturazione"</string>
+ <string name="sepia" msgid="7978093531824705601">"Seppia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Ombre"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Nitidezza"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Sfocatura diffusa"</string>
+ <string name="straighten" msgid="5217801513491493491">"Raddrizza"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Tinta"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignetta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Colori caldi"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Disegna sulla foto per scarabocchiarla"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Trascina la foto per girarla"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Tocca per rimuovere gli occhi rossi"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Trascina la foto per ruotarla"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Trascina la foto per raddrizzarla"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-iw/strings.xml b/samples/PhotoEditor/res/values-iw/strings.xml
new file mode 100644
index 0000000..ea6a0fe
--- /dev/null
+++ b/samples/PhotoEditor/res/values-iw/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"לא ניתן לטעון את התמונה לעריכה"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"אירעה בעיה במהלך השמירה"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> נשמר"</string>
+ <string name="save_photo" msgid="3125109368779997862">"לשמור את התמונה הערוכה?"</string>
+ <string name="yes" msgid="5402582493291792293">"כן"</string>
+ <string name="no" msgid="5595408018304861875">"לא"</string>
+ <string name="cancel" msgid="6286688759430767307">"ביטול"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"תמונות ערוכות"</string>
+ <string name="artistic_group" msgid="573091775825402562">"אמנותי"</string>
+ <string name="color_group" msgid="3390757893475444688">"צבע"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"חשיפה"</string>
+ <string name="fix_group" msgid="971450155810247383">"תיקונים"</string>
+ <string name="autofix" msgid="1223859191856172755">"יותר מזל משכל"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"פיתוח מוצלב"</string>
+ <string name="documentary" msgid="50396326708699797">"דוקומנטרי"</string>
+ <string name="doodle" msgid="1686409894518940990">"ציור"</string>
+ <string name="duotone" msgid="8145893940788467106">"שני גוונים"</string>
+ <string name="filllight" msgid="2644989991700022526">"אור מילוי"</string>
+ <string name="fisheye" msgid="6037488646928998921">"עדשת עין הדג"</string>
+ <string name="flip" msgid="2357692401826287480">"הפוך"</string>
+ <string name="grain" msgid="7487585304579789098">"גרעיניות סרט"</string>
+ <string name="grayscale" msgid="615169770671286699">"גווני אפור"</string>
+ <string name="highlight" msgid="3902653944386623972">"הבהרה"</string>
+ <string name="lomoish" msgid="7793824845892532976">"בסגנון Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"נגטיב"</string>
+ <string name="posterize" msgid="4139212359561383385">"מצב פוסטר (Posterize)"</string>
+ <string name="redeye" msgid="4958448806369928239">"עין אדומה"</string>
+ <string name="rotate" msgid="6607597269792373083">"סיבוב"</string>
+ <string name="saturation" msgid="8621322012271169931">"רוויה"</string>
+ <string name="sepia" msgid="7978093531824705601">"חום-ספיה"</string>
+ <string name="shadow" msgid="8235188588101973090">"צלליות"</string>
+ <string name="sharpen" msgid="8449662378104403230">"שפר חדות"</string>
+ <string name="softfocus" msgid="6334228862566408303">"מיקוד רך"</string>
+ <string name="straighten" msgid="5217801513491493491">"יישר"</string>
+ <string name="temperature" msgid="3589958696423284897">"טמפרטורה"</string>
+ <string name="tint" msgid="154435943863418434">"גוון"</string>
+ <string name="vignette" msgid="7648125924662648282">"שוליים דהויים"</string>
+ <string name="warmify" msgid="2404474974251014984">"הפוך לחמים יותר"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"גרור על גבי התמונה כדי לצייר עליה"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"גרור את התמונה כדי להפוך אותה"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"הקש כדי להסיר עיניים אדומות"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"גרור את התמונה כדי לסובב אותה"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"גרור את התמונה כדי ליישר אותה"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-ja/strings.xml b/samples/PhotoEditor/res/values-ja/strings.xml
new file mode 100644
index 0000000..fcf47cc
--- /dev/null
+++ b/samples/PhotoEditor/res/values-ja/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"フォトスタジオ"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"写真を編集用に読み込むことができません。"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"保存中に問題が発生しました"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g>を保存しました"</string>
+ <string name="save_photo" msgid="3125109368779997862">"編集した写真を保存しますか?"</string>
+ <string name="yes" msgid="5402582493291792293">"はい"</string>
+ <string name="no" msgid="5595408018304861875">"いいえ"</string>
+ <string name="cancel" msgid="6286688759430767307">"キャンセル"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"編集済み"</string>
+ <string name="artistic_group" msgid="573091775825402562">"芸術的"</string>
+ <string name="color_group" msgid="3390757893475444688">"色"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"露出"</string>
+ <string name="fix_group" msgid="971450155810247383">"修正"</string>
+ <string name="autofix" msgid="1223859191856172755">"ラッキー"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"クロスプロセス"</string>
+ <string name="documentary" msgid="50396326708699797">"ドキュメンタリー"</string>
+ <string name="doodle" msgid="1686409894518940990">"落書き"</string>
+ <string name="duotone" msgid="8145893940788467106">"デュオトーン"</string>
+ <string name="filllight" msgid="2644989991700022526">"明るさ調整"</string>
+ <string name="fisheye" msgid="6037488646928998921">"魚眼レンズ"</string>
+ <string name="flip" msgid="2357692401826287480">"反転"</string>
+ <string name="grain" msgid="7487585304579789098">"フィルムグレイン"</string>
+ <string name="grayscale" msgid="615169770671286699">"グレースケール"</string>
+ <string name="highlight" msgid="3902653944386623972">"ハイライト"</string>
+ <string name="lomoish" msgid="7793824845892532976">"LOMO風"</string>
+ <string name="negative" msgid="1985508917342811252">"白黒反転"</string>
+ <string name="posterize" msgid="4139212359561383385">"ポスタライズ"</string>
+ <string name="redeye" msgid="4958448806369928239">"赤目処理"</string>
+ <string name="rotate" msgid="6607597269792373083">"回転"</string>
+ <string name="saturation" msgid="8621322012271169931">"彩度"</string>
+ <string name="sepia" msgid="7978093531824705601">"セピア"</string>
+ <string name="shadow" msgid="8235188588101973090">"シャドウ"</string>
+ <string name="sharpen" msgid="8449662378104403230">"シャープ"</string>
+ <string name="softfocus" msgid="6334228862566408303">"ソフトフォーカス"</string>
+ <string name="straighten" msgid="5217801513491493491">"傾き調整"</string>
+ <string name="temperature" msgid="3589958696423284897">"温度"</string>
+ <string name="tint" msgid="154435943863418434">"色合い"</string>
+ <string name="vignette" msgid="7648125924662648282">"周辺減光"</string>
+ <string name="warmify" msgid="2404474974251014984">"ウォーム"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"画像の上に書き込みます"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"画像をめくるにはドラッグします"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"赤目を補正するにはタップします"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"画像を回転させるにはドラッグします"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"画像をまっすぐにするにはドラッグします"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-ko/strings.xml b/samples/PhotoEditor/res/values-ko/strings.xml
new file mode 100644
index 0000000..2eaa100
--- /dev/null
+++ b/samples/PhotoEditor/res/values-ko/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"수정할 사진을 로드할 수 없습니다."</string>
+ <string name="saving_failure" msgid="3116717307036082369">"저장하는 동안 문제가 발생했습니다."</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g>이(가) 저장되었습니다."</string>
+ <string name="save_photo" msgid="3125109368779997862">"수정한 사진을 저장하시겠습니까?"</string>
+ <string name="yes" msgid="5402582493291792293">"예"</string>
+ <string name="no" msgid="5595408018304861875">"아니요"</string>
+ <string name="cancel" msgid="6286688759430767307">"취소"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"수정된 사진"</string>
+ <string name="artistic_group" msgid="573091775825402562">"예술적"</string>
+ <string name="color_group" msgid="3390757893475444688">"색상"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"노출"</string>
+ <string name="fix_group" msgid="971450155810247383">"조절"</string>
+ <string name="autofix" msgid="1223859191856172755">"Feel Lucky"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"크로스 프로세스"</string>
+ <string name="documentary" msgid="50396326708699797">"다큐멘터리"</string>
+ <string name="doodle" msgid="1686409894518940990">"낙서"</string>
+ <string name="duotone" msgid="8145893940788467106">"더블톤"</string>
+ <string name="filllight" msgid="2644989991700022526">"밝게"</string>
+ <string name="fisheye" msgid="6037488646928998921">"어안렌즈"</string>
+ <string name="flip" msgid="2357692401826287480">"방향 바꾸기"</string>
+ <string name="grain" msgid="7487585304579789098">"거친 필름"</string>
+ <string name="grayscale" msgid="615169770671286699">"그레이 스케일"</string>
+ <string name="highlight" msgid="3902653944386623972">"하이라이트"</string>
+ <string name="lomoish" msgid="7793824845892532976">"로모 스타일"</string>
+ <string name="negative" msgid="1985508917342811252">"네거티브"</string>
+ <string name="posterize" msgid="4139212359561383385">"포스터"</string>
+ <string name="redeye" msgid="4958448806369928239">"적목현상"</string>
+ <string name="rotate" msgid="6607597269792373083">"회전"</string>
+ <string name="saturation" msgid="8621322012271169931">"채도"</string>
+ <string name="sepia" msgid="7978093531824705601">"세피아"</string>
+ <string name="shadow" msgid="8235188588101973090">"그림자"</string>
+ <string name="sharpen" msgid="8449662378104403230">"선명하게"</string>
+ <string name="softfocus" msgid="6334228862566408303">"소프트 포커스"</string>
+ <string name="straighten" msgid="5217801513491493491">"똑바르게"</string>
+ <string name="temperature" msgid="3589958696423284897">"색온도"</string>
+ <string name="tint" msgid="154435943863418434">"농담"</string>
+ <string name="vignette" msgid="7648125924662648282">"비네트"</string>
+ <string name="warmify" msgid="2404474974251014984">"따뜻하게"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"사진에 그림을 그려 낙서하기"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"사진을 드래그하여 방향 바꾸기"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"터치하여 적목 현상 제거"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"드래그하여 사진 회전"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"사진을 드래그하여 반듯하게 하기"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-lt/strings.xml b/samples/PhotoEditor/res/values-lt/strings.xml
new file mode 100644
index 0000000..f49fe84
--- /dev/null
+++ b/samples/PhotoEditor/res/values-lt/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Neįmanoma įkelti nuotr., kad ją būtų g. redaguoti."</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Išsaugant iškilo problema"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> išsaugota"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Išsaugoti redaguotą nuotrauką?"</string>
+ <string name="yes" msgid="5402582493291792293">"Taip"</string>
+ <string name="no" msgid="5595408018304861875">"Ne"</string>
+ <string name="cancel" msgid="6286688759430767307">"Atšaukti"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Redaguota"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Meniška"</string>
+ <string name="color_group" msgid="3390757893475444688">"Spalva"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Išlaikymas"</string>
+ <string name="fix_group" msgid="971450155810247383">"Pataisyti"</string>
+ <string name="autofix" msgid="1223859191856172755">"Man sekasi"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Kryžm. apdor."</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentika"</string>
+ <string name="doodle" msgid="1686409894518940990">"Logotipų puoš."</string>
+ <string name="duotone" msgid="8145893940788467106">"Du tonai"</string>
+ <string name="filllight" msgid="2644989991700022526">"Užpild. šviesa"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Žuvies akis"</string>
+ <string name="flip" msgid="2357692401826287480">"Apversti"</string>
+ <string name="grain" msgid="7487585304579789098">"Juostos grūd."</string>
+ <string name="grayscale" msgid="615169770671286699">"Pilkumo tonas"</string>
+ <string name="highlight" msgid="3902653944386623972">"Paryškinimai"</string>
+ <string name="lomoish" msgid="7793824845892532976">"„Lomo“ tipo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatyvas"</string>
+ <string name="posterize" msgid="4139212359561383385">"Mažinti spalv."</string>
+ <string name="redeye" msgid="4958448806369928239">"Raudonos akys"</string>
+ <string name="rotate" msgid="6607597269792373083">"Pasukti"</string>
+ <string name="saturation" msgid="8621322012271169931">"Spalvų sodr."</string>
+ <string name="sepia" msgid="7978093531824705601">"Tamsiai rusv."</string>
+ <string name="shadow" msgid="8235188588101973090">"Šešėliai"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Paryškinti"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Švelnus fokus."</string>
+ <string name="straighten" msgid="5217801513491493491">"Tiesinti"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatūra"</string>
+ <string name="tint" msgid="154435943863418434">"Atspalvis"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinjetė"</string>
+ <string name="warmify" msgid="2404474974251014984">"Naud. šilt. sp."</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Pieškite ant nuotraukos, jei norite taikyti logotipų puošybą"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Vilkite nuotrauką, kad ją apverstumėte"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Palieskite, kad pašalintumėte raudonas akis"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Vilkite nuotrauką, kad ją apsuktumėte"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Vilkite nuotrauką, kad ją ištiesintumėte"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-lv/strings.xml b/samples/PhotoEditor/res/values-lv/strings.xml
new file mode 100644
index 0000000..465d12b
--- /dev/null
+++ b/samples/PhotoEditor/res/values-lv/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Fotostudija"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Nevar ielādēt fotoattēlu, lai to rediģētu."</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Saglabāšanas laikā radās problēma."</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Fails <xliff:g id="FILENAME">%s</xliff:g> ir saglabāts."</string>
+ <string name="save_photo" msgid="3125109368779997862">"Vai saglabāt rediģēto fotoattēlu?"</string>
+ <string name="yes" msgid="5402582493291792293">"Jā"</string>
+ <string name="no" msgid="5595408018304861875">"Nē"</string>
+ <string name="cancel" msgid="6286688759430767307">"Atcelt"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Rediģēti"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Māksla"</string>
+ <string name="color_group" msgid="3390757893475444688">"Krāsa"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Ekspozīcija"</string>
+ <string name="fix_group" msgid="971450155810247383">"Labot"</string>
+ <string name="autofix" msgid="1223859191856172755">"Ticu veiksmei"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Kļūd. apstrāde"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentārs"</string>
+ <string name="doodle" msgid="1686409894518940990">"Kricelējums"</string>
+ <string name="duotone" msgid="8145893940788467106">"Divtoņu"</string>
+ <string name="filllight" msgid="2644989991700022526">"Izm. pretgaismu"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Platleņķis"</string>
+ <string name="flip" msgid="2357692401826287480">"Apvērst"</string>
+ <string name="grain" msgid="7487585304579789098">"Graudaina filma"</string>
+ <string name="grayscale" msgid="615169770671286699">"Pelēktoņu"</string>
+ <string name="highlight" msgid="3902653944386623972">"Gaismas efekti"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomogrāfija"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatīvs"</string>
+ <string name="posterize" msgid="4139212359561383385">"Veidot plakātu"</string>
+ <string name="redeye" msgid="4958448806369928239">"Sarkanās acis"</string>
+ <string name="rotate" msgid="6607597269792373083">"Pagriezt"</string>
+ <string name="saturation" msgid="8621322012271169931">"Piesātinājums"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sēpija"</string>
+ <string name="shadow" msgid="8235188588101973090">"Ēnas"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Padarīt asāku"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Neass attēls"</string>
+ <string name="straighten" msgid="5217801513491493491">"Iztaisnot"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatūra"</string>
+ <string name="tint" msgid="154435943863418434">"Nianse"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinjete"</string>
+ <string name="warmify" msgid="2404474974251014984">"Siltināt"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Velciet uz fotoattēla, lai kricelētu."</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Velciet fotoattēlu, lai to apgrieztu."</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Pieskarieties, lai noņemtu sarkano acu efektu."</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Velciet fotoattēlu, lai to pagrieztu."</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Velciet fotoattēlu, lai to iztaisnotu."</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-ms/strings.xml b/samples/PhotoEditor/res/values-ms/strings.xml
new file mode 100644
index 0000000..f6c2e62
--- /dev/null
+++ b/samples/PhotoEditor/res/values-ms/strings.xml
@@ -0,0 +1,68 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Tidak boleh memuatkan foto untuk pengeditan"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Ralat berlaku semasa menyimpan"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> disimpan"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Simpan foto yang diedit?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ya"</string>
+ <string name="no" msgid="5595408018304861875">"Tidak"</string>
+ <string name="cancel" msgid="6286688759430767307">"Batal"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Diedit"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistik"</string>
+ <string name="color_group" msgid="3390757893475444688">"Warna"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Dedahan"</string>
+ <string name="fix_group" msgid="971450155810247383">"Baiki"</string>
+ <string name="autofix" msgid="1223859191856172755">"Rasa Bertuah"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Proses silang"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentari"</string>
+ <string name="doodle" msgid="1686409894518940990">"Coretan"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dua tona"</string>
+ <string name="filllight" msgid="2644989991700022526">"Cahaya Pemenuh"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Mata ikan"</string>
+ <string name="flip" msgid="2357692401826287480">"Balikkan"</string>
+ <string name="grain" msgid="7487585304579789098">"Ira Filem"</string>
+ <string name="grayscale" msgid="615169770671286699">"Skala kelabu"</string>
+ <string name="highlight" msgid="3902653944386623972">"Serlahan"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Seperti Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatif"</string>
+ <string name="posterize" msgid="4139212359561383385">"Pemposteran"</string>
+ <string name="redeye" msgid="4958448806369928239">"Mata Merah"</string>
+ <string name="rotate" msgid="6607597269792373083">"Putar"</string>
+ <string name="saturation" msgid="8621322012271169931">"Ketepuan"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Bayang-bayang"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Tajamkan"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Fokus Lembut"</string>
+ <string name="straighten" msgid="5217801513491493491">"Luruskan"</string>
+ <string name="temperature" msgid="3589958696423284897">"Suhu"</string>
+ <string name="tint" msgid="154435943863418434">"Seri warna"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignet"</string>
+ <string name="warmify" msgid="2404474974251014984">"Hangatkan"</string>
+ <!-- no translation found for doodle_tooltip (2902117272374362915) -->
+ <skip />
+ <!-- no translation found for flip_tooltip (2700943256714731737) -->
+ <skip />
+ <!-- no translation found for redeye_tooltip (9112774042113471358) -->
+ <skip />
+ <!-- no translation found for rotate_tooltip (7008602969130734229) -->
+ <skip />
+ <!-- no translation found for straighten_tooltip (4846317027139212339) -->
+ <skip />
+</resources>
diff --git a/samples/PhotoEditor/res/values-nb/strings.xml b/samples/PhotoEditor/res/values-nb/strings.xml
new file mode 100644
index 0000000..b36ca86
--- /dev/null
+++ b/samples/PhotoEditor/res/values-nb/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Kan ikke laste inn bildet for redigering"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Det oppstod et problem under lagring"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> er lagret"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Vil du lagre det redigerte bildet?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ja"</string>
+ <string name="no" msgid="5595408018304861875">"Nei"</string>
+ <string name="cancel" msgid="6286688759430767307">"Avbryt"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Redigert"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Kunstnerisk"</string>
+ <string name="color_group" msgid="3390757893475444688">"Farge"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Eksponering"</string>
+ <string name="fix_group" msgid="971450155810247383">"Fiks"</string>
+ <string name="autofix" msgid="1223859191856172755">"Ta sjansen"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Kryssfremkall."</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentar"</string>
+ <string name="doodle" msgid="1686409894518940990">"Drodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Tofargebilde"</string>
+ <string name="filllight" msgid="2644989991700022526">"Utfyllingslys"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fiskeøye"</string>
+ <string name="flip" msgid="2357692401826287480">"Vend"</string>
+ <string name="grain" msgid="7487585304579789098">"Kornete oppløs."</string>
+ <string name="grayscale" msgid="615169770671286699">"Gråtoner"</string>
+ <string name="highlight" msgid="3902653944386623972">"Gjør lysere"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo-aktig"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativt"</string>
+ <string name="posterize" msgid="4139212359561383385">"Plakateffekt"</string>
+ <string name="redeye" msgid="4958448806369928239">"Røde øyne"</string>
+ <string name="rotate" msgid="6607597269792373083">"Rotering"</string>
+ <string name="saturation" msgid="8621322012271169931">"Metning"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Skygger"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Gjør skarpere"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Kantdiffusering"</string>
+ <string name="straighten" msgid="5217801513491493491">"Rett opp"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatur"</string>
+ <string name="tint" msgid="154435943863418434">"Fargetone"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignettering"</string>
+ <string name="warmify" msgid="2404474974251014984">"Gjør varmere"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Tegn på bildet for å drodle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Dra bildet for å snu det"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Trykk for å fjerne røde øyne"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Dra bildet for å rotere det"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Dra bilde for å rette på det"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-nl/strings.xml b/samples/PhotoEditor/res/values-nl/strings.xml
new file mode 100644
index 0000000..9c7e946
--- /dev/null
+++ b/samples/PhotoEditor/res/values-nl/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Kan de foto niet laden om te bewerken"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Er is een probleem opgetreden tijdens het opslaan"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> opgeslagen"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Bewerkte foto opslaan?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ja"</string>
+ <string name="no" msgid="5595408018304861875">"Nee"</string>
+ <string name="cancel" msgid="6286688759430767307">"Annuleren"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Bewerkt"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistiek"</string>
+ <string name="color_group" msgid="3390757893475444688">"Kleur"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Belichting"</string>
+ <string name="fix_group" msgid="971450155810247383">"Verbeteren"</string>
+ <string name="autofix" msgid="1223859191856172755">"Ik doe een gok"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Kruisverwerking"</string>
+ <string name="documentary" msgid="50396326708699797">"Documentaire"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duotone"</string>
+ <string name="filllight" msgid="2644989991700022526">"Licht invullen"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Visooglens"</string>
+ <string name="flip" msgid="2357692401826287480">"Spiegelen"</string>
+ <string name="grain" msgid="7487585304579789098">"Korrelstructuur"</string>
+ <string name="grayscale" msgid="615169770671286699">"Grijstinten"</string>
+ <string name="highlight" msgid="3902653944386623972">"Lichter maken"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo-achtig"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatief"</string>
+ <string name="posterize" msgid="4139212359561383385">"Postereffect"</string>
+ <string name="redeye" msgid="4958448806369928239">"Rode ogen"</string>
+ <string name="rotate" msgid="6607597269792373083">"Draaien"</string>
+ <string name="saturation" msgid="8621322012271169931">"Verzadiging"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Schaduwen"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Scherper maken"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Zachte focus"</string>
+ <string name="straighten" msgid="5217801513491493491">"Recht maken"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatuur"</string>
+ <string name="tint" msgid="154435943863418434">"Tint"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vervloeiende randen"</string>
+ <string name="warmify" msgid="2404474974251014984">"Warmer maken"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Teken op de foto om hierop te krabbelen"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Sleep de foto om deze te spiegelen"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Tik om rode ogen te verwijderen"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Sleep de foto om deze te draaien"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Sleep de foto om deze recht te zetten"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-pl/strings.xml b/samples/PhotoEditor/res/values-pl/strings.xml
new file mode 100644
index 0000000..c14104c
--- /dev/null
+++ b/samples/PhotoEditor/res/values-pl/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Studio fotograficzne"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Nie można wczytać zdjęcia do edytowania."</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Podczas zapisu wystąpił problem."</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Plik <xliff:g id="FILENAME">%s</xliff:g> został zapisany."</string>
+ <string name="save_photo" msgid="3125109368779997862">"Czy zapisać edytowane zdjęcie?"</string>
+ <string name="yes" msgid="5402582493291792293">"Tak"</string>
+ <string name="no" msgid="5595408018304861875">"Nie"</string>
+ <string name="cancel" msgid="6286688759430767307">"Anuluj"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Edytowane"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artystyczne"</string>
+ <string name="color_group" msgid="3390757893475444688">"Kolor"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Ekspozycja"</string>
+ <string name="fix_group" msgid="971450155810247383">"Poprawki"</string>
+ <string name="autofix" msgid="1223859191856172755">"Szczęśliwy traf"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Proces krosowy"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokument"</string>
+ <string name="doodle" msgid="1686409894518940990">"Bazgroły"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dwuton"</string>
+ <string name="filllight" msgid="2644989991700022526">"Doświetlenie"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Rybie oko"</string>
+ <string name="flip" msgid="2357692401826287480">"Odwrócenie"</string>
+ <string name="grain" msgid="7487585304579789098">"Ziarno kliszy"</string>
+ <string name="grayscale" msgid="615169770671286699">"Skala szarości"</string>
+ <string name="highlight" msgid="3902653944386623972">"Podświetlenie"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Łomografia"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatyw"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posteryzacja"</string>
+ <string name="redeye" msgid="4958448806369928239">"Czerwone oczy"</string>
+ <string name="rotate" msgid="6607597269792373083">"Obrót"</string>
+ <string name="saturation" msgid="8621322012271169931">"Nasycenie"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Cienie"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Wyostrzenie"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Nieostrość"</string>
+ <string name="straighten" msgid="5217801513491493491">"Wyprostowanie"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Odcień"</string>
+ <string name="vignette" msgid="7648125924662648282">"Winietowanie"</string>
+ <string name="warmify" msgid="2404474974251014984">"Ocieplenie"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Rysuj po zdjęciu, aby dodać bazgroły."</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Przeciągnij zdjęcie, aby je odwrócić."</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Dotknij, aby usunąć efekt czerwonych oczu."</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Przeciągnij zdjęcie, aby je obrócić."</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Przeciągnij zdjęcie, aby je wyprostować."</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-pt-rPT/strings.xml b/samples/PhotoEditor/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..9547595
--- /dev/null
+++ b/samples/PhotoEditor/res/values-pt-rPT/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Não é possível carregar a fotografia para edição"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Ocorreu um problema ao guardar"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> guardado"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Guardar a fotografia editada?"</string>
+ <string name="yes" msgid="5402582493291792293">"Sim"</string>
+ <string name="no" msgid="5595408018304861875">"Não"</string>
+ <string name="cancel" msgid="6286688759430767307">"Cancelar"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Editado"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artístico"</string>
+ <string name="color_group" msgid="3390757893475444688">"Cor"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposição"</string>
+ <string name="fix_group" msgid="971450155810247383">"Corrigir"</string>
+ <string name="autofix" msgid="1223859191856172755">"Sente-se com Sorte"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Processo cruzado"</string>
+ <string name="documentary" msgid="50396326708699797">"Documentário"</string>
+ <string name="doodle" msgid="1686409894518940990">"Rabiscar"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dois tons"</string>
+ <string name="filllight" msgid="2644989991700022526">"Preench. Claro"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Olho de peixe"</string>
+ <string name="flip" msgid="2357692401826287480">"Inverter"</string>
+ <string name="grain" msgid="7487585304579789098">"Filme Granulado"</string>
+ <string name="grayscale" msgid="615169770671286699">"Escala de cinzentos"</string>
+ <string name="highlight" msgid="3902653944386623972">"Realces"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Pseudo Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativo"</string>
+ <string name="posterize" msgid="4139212359561383385">"Póster"</string>
+ <string name="redeye" msgid="4958448806369928239">"Olhos Vermelhos"</string>
+ <string name="rotate" msgid="6607597269792373083">"Rodar"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturação"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sépia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Sombras"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Aumentar nitidez"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Focagem suave"</string>
+ <string name="straighten" msgid="5217801513491493491">"Endireitar"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Tonalidade"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinheta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Tons quentes"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Desenhe na fotografia para rabiscar"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Arraste a fotografia para virá-la"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Toque para remover o efeito de olhos vermelhos"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Arraste a fotografia para rodá-la"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Arraste a fotografia para endireitá-la"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-pt/strings.xml b/samples/PhotoEditor/res/values-pt/strings.xml
new file mode 100644
index 0000000..132fb68
--- /dev/null
+++ b/samples/PhotoEditor/res/values-pt/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Não é possível carregar a foto para edição"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Ocorreu um problema durante a ação de salvar"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> salvo"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Salvar foto editada?"</string>
+ <string name="yes" msgid="5402582493291792293">"Sim"</string>
+ <string name="no" msgid="5595408018304861875">"Não"</string>
+ <string name="cancel" msgid="6286688759430767307">"Cancelar"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Editado"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artístico"</string>
+ <string name="color_group" msgid="3390757893475444688">"Cor"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposição"</string>
+ <string name="fix_group" msgid="971450155810247383">"Fixar"</string>
+ <string name="autofix" msgid="1223859191856172755">"Se sente sort."</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Proc. cruzado"</string>
+ <string name="documentary" msgid="50396326708699797">"Documentário"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duotone"</string>
+ <string name="filllight" msgid="2644989991700022526">"Luz de preench."</string>
+ <string name="fisheye" msgid="6037488646928998921">"Olho de peixe"</string>
+ <string name="flip" msgid="2357692401826287480">"Inverter"</string>
+ <string name="grain" msgid="7487585304579789098">"Granulado"</string>
+ <string name="grayscale" msgid="615169770671286699">"Escala de cinza"</string>
+ <string name="highlight" msgid="3902653944386623972">"Destaques"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Est. câm. Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativo"</string>
+ <string name="posterize" msgid="4139212359561383385">"Transf. em pôster"</string>
+ <string name="redeye" msgid="4958448806369928239">"Olhos vermelhos"</string>
+ <string name="rotate" msgid="6607597269792373083">"Girar"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturação"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sépia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Sombras"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Aument. nitidez"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Filtro difusor"</string>
+ <string name="straighten" msgid="5217801513491493491">"Endireitar"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Tingir"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinheta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Tons + quentes"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Desenhe na foto para rabiscar"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Arraste a foto para invertê-la"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Toque para remover olhos vermelhos"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Arraste a foto para girá-la"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Arraste a foto para endireitá-la"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-ro/strings.xml b/samples/PhotoEditor/res/values-ro/strings.xml
new file mode 100644
index 0000000..997e41c
--- /dev/null
+++ b/samples/PhotoEditor/res/values-ro/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Studio foto"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Nu se poate încărca fotografia pentru editare"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"A apărut o problemă la salvare"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> salvate"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Salvaţi fotografia editată?"</string>
+ <string name="yes" msgid="5402582493291792293">"Da"</string>
+ <string name="no" msgid="5595408018304861875">"Nu"</string>
+ <string name="cancel" msgid="6286688759430767307">"Anulaţi"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Editate"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistic"</string>
+ <string name="color_group" msgid="3390757893475444688">"Culoare"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Expunere"</string>
+ <string name="fix_group" msgid="971450155810247383">"Remediere"</string>
+ <string name="autofix" msgid="1223859191856172755">"Mă simt norocos"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Suprapunere"</string>
+ <string name="documentary" msgid="50396326708699797">"Documentar"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"În două culori"</string>
+ <string name="filllight" msgid="2644989991700022526">"Lumină complet."</string>
+ <string name="fisheye" msgid="6037488646928998921">"Ochi de peşte"</string>
+ <string name="flip" msgid="2357692401826287480">"Răsturnare"</string>
+ <string name="grain" msgid="7487585304579789098">"Granulaţie film"</string>
+ <string name="grayscale" msgid="615169770671286699">"Tonuri de gri"</string>
+ <string name="highlight" msgid="3902653944386623972">"Evidenţieri"</string>
+ <string name="lomoish" msgid="7793824845892532976">"În stilul Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativ"</string>
+ <string name="posterize" msgid="4139212359561383385">"Poster"</string>
+ <string name="redeye" msgid="4958448806369928239">"Ochi roşii"</string>
+ <string name="rotate" msgid="6607597269792373083">"Rotaţie"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturaţie"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Umbre"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Clarificare"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Defocalizare"</string>
+ <string name="straighten" msgid="5217801513491493491">"Îndreptare"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatură"</string>
+ <string name="tint" msgid="154435943863418434">"Haşură"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignetare"</string>
+ <string name="warmify" msgid="2404474974251014984">"Tonuri mai calde"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Desenaţi pe fotografie pentru a aplica efectul doodle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Trageţi o fotografie pentru a o răsturna"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Apăsaţi pentru a elimina ochii roşii"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Trageţi fotografia pentru a o roti"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Trageţi de fotografie pentru a o îndrepta"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-ru/strings.xml b/samples/PhotoEditor/res/values-ru/strings.xml
new file mode 100644
index 0000000..f8f19e9
--- /dev/null
+++ b/samples/PhotoEditor/res/values-ru/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Фотостудия"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Не удается загрузить фото для изменения"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"При сохранении произошла ошибка"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Фото <xliff:g id="FILENAME">%s</xliff:g> сохранено"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Сохранить изменения?"</string>
+ <string name="yes" msgid="5402582493291792293">"Да"</string>
+ <string name="no" msgid="5595408018304861875">"Нет"</string>
+ <string name="cancel" msgid="6286688759430767307">"Отмена"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Измененные"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Художник"</string>
+ <string name="color_group" msgid="3390757893475444688">"Цвет"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Экспозиция"</string>
+ <string name="fix_group" msgid="971450155810247383">"Исправление"</string>
+ <string name="autofix" msgid="1223859191856172755">"Мне повезет!"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Кроссобработка"</string>
+ <string name="documentary" msgid="50396326708699797">"Документальные"</string>
+ <string name="doodle" msgid="1686409894518940990">"Подмалевок"</string>
+ <string name="duotone" msgid="8145893940788467106">"Двойной тон"</string>
+ <string name="filllight" msgid="2644989991700022526">"Осветление"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Рыбий глаз"</string>
+ <string name="flip" msgid="2357692401826287480">"Отразить"</string>
+ <string name="grain" msgid="7487585304579789098">"Зернистость"</string>
+ <string name="grayscale" msgid="615169770671286699">"Оттенки серого"</string>
+ <string name="highlight" msgid="3902653944386623972">"Блики"</string>
+ <string name="lomoish" msgid="7793824845892532976">"В стиле Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Негатив"</string>
+ <string name="posterize" msgid="4139212359561383385">"Постеризация"</string>
+ <string name="redeye" msgid="4958448806369928239">"Красные глаза"</string>
+ <string name="rotate" msgid="6607597269792373083">"Поворот"</string>
+ <string name="saturation" msgid="8621322012271169931">"Насыщенность"</string>
+ <string name="sepia" msgid="7978093531824705601">"Сепия"</string>
+ <string name="shadow" msgid="8235188588101973090">"Затенение"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Резкость"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Мягкий фокус"</string>
+ <string name="straighten" msgid="5217801513491493491">"Выравнивание"</string>
+ <string name="temperature" msgid="3589958696423284897">"Температура"</string>
+ <string name="tint" msgid="154435943863418434">"Оттенок"</string>
+ <string name="vignette" msgid="7648125924662648282">"Виньетка"</string>
+ <string name="warmify" msgid="2404474974251014984">"Теплые тона"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Рисуйте на фото пальцем"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Чтобы отразить фото, перетащите его край"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Нажмите для устранения эффекта красных глаз"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Чтобы повернуть фото, перетащите его край"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Чтобы выровнять фото, перетащите его"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-sk/strings.xml b/samples/PhotoEditor/res/values-sk/strings.xml
new file mode 100644
index 0000000..c6379d0
--- /dev/null
+++ b/samples/PhotoEditor/res/values-sk/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Fotografiu nie je možné načítať na úpravy"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Pri ukladaní sa vyskytol problém"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Fotografie <xliff:g id="FILENAME">%s</xliff:g> boli uložené"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Chcete upravenú fotografiu uložiť?"</string>
+ <string name="yes" msgid="5402582493291792293">"Áno"</string>
+ <string name="no" msgid="5595408018304861875">"Nie"</string>
+ <string name="cancel" msgid="6286688759430767307">"Zrušiť"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Upravené"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Umelecké"</string>
+ <string name="color_group" msgid="3390757893475444688">"Farba"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Expozícia"</string>
+ <string name="fix_group" msgid="971450155810247383">"Korekcia"</string>
+ <string name="autofix" msgid="1223859191856172755">"Skúsim šťastie"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Prelínanie"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokument"</string>
+ <string name="doodle" msgid="1686409894518940990">"Kreslenie"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dvojtónový efekt"</string>
+ <string name="filllight" msgid="2644989991700022526">"Zad. osvetlenie"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Rybie oko"</string>
+ <string name="flip" msgid="2357692401826287480">"Prevrátenie"</string>
+ <string name="grain" msgid="7487585304579789098">"Zrnitý film"</string>
+ <string name="grayscale" msgid="615169770671286699">"Stupne sivej"</string>
+ <string name="highlight" msgid="3902653944386623972">"Prisvietenie"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Štýl Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatív"</string>
+ <string name="posterize" msgid="4139212359561383385">"Posterizácia"</string>
+ <string name="redeye" msgid="4958448806369928239">"Červené oči"</string>
+ <string name="rotate" msgid="6607597269792373083">"Otočenie"</string>
+ <string name="saturation" msgid="8621322012271169931">"Sýtosť"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sépiové farby"</string>
+ <string name="shadow" msgid="8235188588101973090">"Tiene"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Zaostrenie"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Jemné rozostr."</string>
+ <string name="straighten" msgid="5217801513491493491">"Vyrovnanie"</string>
+ <string name="temperature" msgid="3589958696423284897">"Teplota"</string>
+ <string name="tint" msgid="154435943863418434">"Tónovanie"</string>
+ <string name="vignette" msgid="7648125924662648282">"Medailónik"</string>
+ <string name="warmify" msgid="2404474974251014984">"Teplejšie farby"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Na fotografii môžete kresliť prstom"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Pretiahnutím prstom fotografiu prevrátite"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Klepnutím odstráňte efekt červených očí"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Potiahnutím prstom fotografiu otočíte"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Potiahnutím prstom fotografiu vyrovnáte"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-sl/strings.xml b/samples/PhotoEditor/res/values-sl/strings.xml
new file mode 100644
index 0000000..1f2b906
--- /dev/null
+++ b/samples/PhotoEditor/res/values-sl/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Fotografije ni mogoče naložiti za urejanje"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Med shranjevanjem je prišlo do težave"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Datoteka <xliff:g id="FILENAME">%s</xliff:g> je shranjena"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Želite shraniti urejene fotografije?"</string>
+ <string name="yes" msgid="5402582493291792293">"Da"</string>
+ <string name="no" msgid="5595408018304861875">"Ne"</string>
+ <string name="cancel" msgid="6286688759430767307">"Prekliči"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Urejeno"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Umetniško"</string>
+ <string name="color_group" msgid="3390757893475444688">"Barva"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Osvetlitev"</string>
+ <string name="fix_group" msgid="971450155810247383">"Popravki"</string>
+ <string name="autofix" msgid="1223859191856172755">"Samodejno"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Navzkr. obdel."</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentarno"</string>
+ <string name="doodle" msgid="1686409894518940990">"Čačka"</string>
+ <string name="duotone" msgid="8145893940788467106">"Dvotonsko"</string>
+ <string name="filllight" msgid="2644989991700022526">"Dosvetlitev"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Ribje oko"</string>
+ <string name="flip" msgid="2357692401826287480">"Obrnjeno"</string>
+ <string name="grain" msgid="7487585304579789098">"Zrnatost filma"</string>
+ <string name="grayscale" msgid="615169770671286699">"Sivine"</string>
+ <string name="highlight" msgid="3902653944386623972">"Poudarki"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Slog lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativ"</string>
+ <string name="posterize" msgid="4139212359561383385">"Poster"</string>
+ <string name="redeye" msgid="4958448806369928239">"Rdeče oči"</string>
+ <string name="rotate" msgid="6607597269792373083">"Zasukaj"</string>
+ <string name="saturation" msgid="8621322012271169931">"Nasičenost"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepija"</string>
+ <string name="shadow" msgid="8235188588101973090">"Sence"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Izostri"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Megleno"</string>
+ <string name="straighten" msgid="5217801513491493491">"Poravnavanje"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Obarvanje"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinjeta"</string>
+ <string name="warmify" msgid="2404474974251014984">"Toplejši toni"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Povlecite po fotografiji za prostoročno risanje"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Povlecite fotografijo, da jo obrnete"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Tapnite, da odstranite rdeče oči"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Povlecite fotografijo, da jo zavrtite"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Povlecite fotografijo, da jo poravnate"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-sr/strings.xml b/samples/PhotoEditor/res/values-sr/strings.xml
new file mode 100644
index 0000000..67cef0f
--- /dev/null
+++ b/samples/PhotoEditor/res/values-sr/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Фото-студио"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Није могуће учитати фотографију за измену"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Дошло је до проблема приликом чувања"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Сачувана је датотека <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Желите ли да сачувате измењену фотографију?"</string>
+ <string name="yes" msgid="5402582493291792293">"Да"</string>
+ <string name="no" msgid="5595408018304861875">"Не"</string>
+ <string name="cancel" msgid="6286688759430767307">"Откажи"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Измењено"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Уметнички"</string>
+ <string name="color_group" msgid="3390757893475444688">"Боја"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Експозиција"</string>
+ <string name="fix_group" msgid="971450155810247383">"Фиксирање"</string>
+ <string name="autofix" msgid="1223859191856172755">"Аутоматски"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Унакрсна обрада"</string>
+ <string name="documentary" msgid="50396326708699797">"Документарни"</string>
+ <string name="doodle" msgid="1686409894518940990">"Дудл логотип"</string>
+ <string name="duotone" msgid="8145893940788467106">"Дуо-тон"</string>
+ <string name="filllight" msgid="2644989991700022526">"Светлина"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Рибље око"</string>
+ <string name="flip" msgid="2357692401826287480">"Обрни"</string>
+ <string name="grain" msgid="7487585304579789098">"Зрнастост филма"</string>
+ <string name="grayscale" msgid="615169770671286699">"Црно-бело"</string>
+ <string name="highlight" msgid="3902653944386623972">"Истакнуте ставке"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Ломографски"</string>
+ <string name="negative" msgid="1985508917342811252">"Негатив"</string>
+ <string name="posterize" msgid="4139212359561383385">"Постеризуј"</string>
+ <string name="redeye" msgid="4958448806369928239">"Црвене очи"</string>
+ <string name="rotate" msgid="6607597269792373083">"Ротирај"</string>
+ <string name="saturation" msgid="8621322012271169931">"Засићење"</string>
+ <string name="sepia" msgid="7978093531824705601">"Сепија"</string>
+ <string name="shadow" msgid="8235188588101973090">"Сенке"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Изоштри"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Меки фокус"</string>
+ <string name="straighten" msgid="5217801513491493491">"Исправи"</string>
+ <string name="temperature" msgid="3589958696423284897">"Температура"</string>
+ <string name="tint" msgid="154435943863418434">"Сенка"</string>
+ <string name="vignette" msgid="7648125924662648282">"Вињета"</string>
+ <string name="warmify" msgid="2404474974251014984">"Топлије боје"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Превуците слику на дудл логотип"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Превуците фотографију да бисте је окренули"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Додирните да бисте уклонили ефекат црвених очију"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Превуците фотографију да бисте је ротирали"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Превуците фотографију да бисте је исправили"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-sv/strings.xml b/samples/PhotoEditor/res/values-sv/strings.xml
new file mode 100644
index 0000000..07091f1
--- /dev/null
+++ b/samples/PhotoEditor/res/values-sv/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Fotostudio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Kan inte läsa in fotot för redigering"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Det gick inte att spara"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> sparades"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Spara redigerat foto?"</string>
+ <string name="yes" msgid="5402582493291792293">"Ja"</string>
+ <string name="no" msgid="5595408018304861875">"Nej"</string>
+ <string name="cancel" msgid="6286688759430767307">"Avbryt"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Redigerade"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Konstnärlig"</string>
+ <string name="color_group" msgid="3390757893475444688">"Färg"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exponering"</string>
+ <string name="fix_group" msgid="971450155810247383">"Korrigera"</string>
+ <string name="autofix" msgid="1223859191856172755">"Jag har tur"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Cross Process"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentär"</string>
+ <string name="doodle" msgid="1686409894518940990">"Klotter"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duoton"</string>
+ <string name="filllight" msgid="2644989991700022526">"Fyll belysning"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fisheye"</string>
+ <string name="flip" msgid="2357692401826287480">"Vänd"</string>
+ <string name="grain" msgid="7487585304579789098">"Filmkornighet"</string>
+ <string name="grayscale" msgid="615169770671286699">"Gråskala"</string>
+ <string name="highlight" msgid="3902653944386623972">"Högdager"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo-stil"</string>
+ <string name="negative" msgid="1985508917342811252">"Negativ"</string>
+ <string name="posterize" msgid="4139212359561383385">"Färgreduktion"</string>
+ <string name="redeye" msgid="4958448806369928239">"Röda ögon"</string>
+ <string name="rotate" msgid="6607597269792373083">"Rotera"</string>
+ <string name="saturation" msgid="8621322012271169931">"Mättnad"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Skuggor"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Skarpare"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Minska fokus"</string>
+ <string name="straighten" msgid="5217801513491493491">"Räta ut"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatur"</string>
+ <string name="tint" msgid="154435943863418434">"Ton"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignette"</string>
+ <string name="warmify" msgid="2404474974251014984">"Värme"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Rita på bilden om du vill doodle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Tryck och dra bilden om du vill vända den"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Knacka lätt om du vill ta bort röda ögon"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Tryck och dra bilden om du vill rotera den"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Tryck och dra bilden om du vill räta upp den"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-th/strings.xml b/samples/PhotoEditor/res/values-th/strings.xml
new file mode 100644
index 0000000..dcd2853
--- /dev/null
+++ b/samples/PhotoEditor/res/values-th/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"สตูดิโอถ่ายภาพ"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"ไม่สามารถโหลดภาพเพื่อแก้ไข"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"เกิดปัญหาในระหว่างการบันทึก"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"บันทึก <xliff:g id="FILENAME">%s</xliff:g> แล้ว"</string>
+ <string name="save_photo" msgid="3125109368779997862">"บันทึกภาพที่แก้ไขหรือไม่"</string>
+ <string name="yes" msgid="5402582493291792293">"ใช่"</string>
+ <string name="no" msgid="5595408018304861875">"ไม่"</string>
+ <string name="cancel" msgid="6286688759430767307">"ยกเลิก"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"แก้ไขแล้ว"</string>
+ <string name="artistic_group" msgid="573091775825402562">"ศิลปะ"</string>
+ <string name="color_group" msgid="3390757893475444688">"สี"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"การรับแสง"</string>
+ <string name="fix_group" msgid="971450155810247383">"คงที่"</string>
+ <string name="autofix" msgid="1223859191856172755">"ดีใจจัง"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"ครอสโพรเซส"</string>
+ <string name="documentary" msgid="50396326708699797">"สารคดี"</string>
+ <string name="doodle" msgid="1686409894518940990">"doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"ดูโอโทน"</string>
+ <string name="filllight" msgid="2644989991700022526">"เติมแสง"</string>
+ <string name="fisheye" msgid="6037488646928998921">"ฟิชอาย"</string>
+ <string name="flip" msgid="2357692401826287480">"ฟลิป"</string>
+ <string name="grain" msgid="7487585304579789098">"เนื้อฟิล์ม"</string>
+ <string name="grayscale" msgid="615169770671286699">"โทนสีเทา"</string>
+ <string name="highlight" msgid="3902653944386623972">"ไฮไลต์"</string>
+ <string name="lomoish" msgid="7793824845892532976">"แนวโลโม"</string>
+ <string name="negative" msgid="1985508917342811252">"สีตรงข้าม"</string>
+ <string name="posterize" msgid="4139212359561383385">"ลดจำนวนสีในภาพ"</string>
+ <string name="redeye" msgid="4958448806369928239">"ตาแดง"</string>
+ <string name="rotate" msgid="6607597269792373083">"หมุน"</string>
+ <string name="saturation" msgid="8621322012271169931">"ความอิ่มตัว"</string>
+ <string name="sepia" msgid="7978093531824705601">"ซีเปีย"</string>
+ <string name="shadow" msgid="8235188588101973090">"เงา"</string>
+ <string name="sharpen" msgid="8449662378104403230">"ทำให้ชัด"</string>
+ <string name="softfocus" msgid="6334228862566408303">"ซอฟต์โฟกัส"</string>
+ <string name="straighten" msgid="5217801513491493491">"ทำให้ตรง"</string>
+ <string name="temperature" msgid="3589958696423284897">"อุณหภูมิ"</string>
+ <string name="tint" msgid="154435943863418434">"แต้มสี"</string>
+ <string name="vignette" msgid="7648125924662648282">"วิกเน็ตต์"</string>
+ <string name="warmify" msgid="2404474974251014984">"ทำให้ภาพดูอบอุ่นขึ้น"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"วาดบนภาพเพื่อ doodle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"ลากภาพเพื่อพลิก"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"แตะเพื่อลบตาแดง"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"ลากภาพเพื่อหมุน"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"ลากภาพเพื่อยืดให้ตรง"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-tl/strings.xml b/samples/PhotoEditor/res/values-tl/strings.xml
new file mode 100644
index 0000000..bdfd5d5
--- /dev/null
+++ b/samples/PhotoEditor/res/values-tl/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Hindi ma-load ang larawan para sa pag-edit"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"May naganap na problema habang nagse-save"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Na-save ang <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="save_photo" msgid="3125109368779997862">"I-save ang na-edit na larawan?"</string>
+ <string name="yes" msgid="5402582493291792293">"Oo"</string>
+ <string name="no" msgid="5595408018304861875">"Hindi"</string>
+ <string name="cancel" msgid="6286688759430767307">"Kanselahin"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Na-edit"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Artistic"</string>
+ <string name="color_group" msgid="3390757893475444688">"Kulay"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Exposure"</string>
+ <string name="fix_group" msgid="971450155810247383">"Ayusin"</string>
+ <string name="autofix" msgid="1223859191856172755">"Masuwerte"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"I-cross-process"</string>
+ <string name="documentary" msgid="50396326708699797">"Dokumentaryo"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Duo-tone"</string>
+ <string name="filllight" msgid="2644989991700022526">"Pampunong Ilaw"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Fisheye"</string>
+ <string name="flip" msgid="2357692401826287480">"Baliktarin"</string>
+ <string name="grain" msgid="7487585304579789098">"Film Grain"</string>
+ <string name="grayscale" msgid="615169770671286699">"Grayscale"</string>
+ <string name="highlight" msgid="3902653944386623972">"Mga Highlight"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo-ish"</string>
+ <string name="negative" msgid="1985508917342811252">"Negative"</string>
+ <string name="posterize" msgid="4139212359561383385">"I-posterize"</string>
+ <string name="redeye" msgid="4958448806369928239">"Pulang Mata"</string>
+ <string name="rotate" msgid="6607597269792373083">"Paikutin"</string>
+ <string name="saturation" msgid="8621322012271169931">"Saturation"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepia"</string>
+ <string name="shadow" msgid="8235188588101973090">"Mga Shadow"</string>
+ <string name="sharpen" msgid="8449662378104403230">"I-sharpen"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Soft Focus"</string>
+ <string name="straighten" msgid="5217801513491493491">"Ituwid"</string>
+ <string name="temperature" msgid="3589958696423284897">"Temperatura"</string>
+ <string name="tint" msgid="154435943863418434">"Tint"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vignette"</string>
+ <string name="warmify" msgid="2404474974251014984">"Gawing Warm"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Gumuhit sa larawan upang mag-doodle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"I-drag ang larawan upang i-flip ito"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Tapikin upang alisin ang mga pulang mata"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"I-drag ang larawan upang i-rotate ito"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"I-drag ang larawan upang iunat ito"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-tr/strings.xml b/samples/PhotoEditor/res/values-tr/strings.xml
new file mode 100644
index 0000000..b3df612
--- /dev/null
+++ b/samples/PhotoEditor/res/values-tr/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Fotoğraf Stüdyosu"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Fotoğraf düzenlenmek üzere yüklenemiyor"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Kaydederken sorun oluştu"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> kaydedildi"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Düzenlenmiş fotoğraf kaydedilsin mi?"</string>
+ <string name="yes" msgid="5402582493291792293">"Evet"</string>
+ <string name="no" msgid="5595408018304861875">"Hayır"</string>
+ <string name="cancel" msgid="6286688759430767307">"İptal"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Düzenlendi"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Sanatsal"</string>
+ <string name="color_group" msgid="3390757893475444688">"Renk"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Pozlama"</string>
+ <string name="fix_group" msgid="971450155810247383">"Düzelt"</string>
+ <string name="autofix" msgid="1223859191856172755">"Şanslı Hissdyrm"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Çapraz Banyo"</string>
+ <string name="documentary" msgid="50396326708699797">"Belgesel"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"İki tonlu"</string>
+ <string name="filllight" msgid="2644989991700022526">"Dolgu Işığı"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Balıkgözü"</string>
+ <string name="flip" msgid="2357692401826287480">"Çevir"</string>
+ <string name="grain" msgid="7487585304579789098">"Film Greni"</string>
+ <string name="grayscale" msgid="615169770671286699">"Gri Tonlama"</string>
+ <string name="highlight" msgid="3902653944386623972">"Parlak noktalar"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo Tarzı"</string>
+ <string name="negative" msgid="1985508917342811252">"Negatif"</string>
+ <string name="posterize" msgid="4139212359561383385">"Poster Efkt Uyg"</string>
+ <string name="redeye" msgid="4958448806369928239">"Kırmızı Göz"</string>
+ <string name="rotate" msgid="6607597269792373083">"Döndür"</string>
+ <string name="saturation" msgid="8621322012271169931">"Doygunluk"</string>
+ <string name="sepia" msgid="7978093531824705601">"Sepya Tonu"</string>
+ <string name="shadow" msgid="8235188588101973090">"Gölgeler"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Keskinleştir"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Yumuşak Odak"</string>
+ <string name="straighten" msgid="5217801513491493491">"Düzelt"</string>
+ <string name="temperature" msgid="3589958696423284897">"Sıcaklık"</string>
+ <string name="tint" msgid="154435943863418434">"Tonlama"</string>
+ <string name="vignette" msgid="7648125924662648282">"Vinyet"</string>
+ <string name="warmify" msgid="2404474974251014984">"Sıcak Göster"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Doodle için fotoğrafın üzerinde çizin"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Çevirmek için fotoğrafı sürükleyin"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Kırmızı gözleri kaldırmak için hafifçe vurun"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Döndürmek için fotoğrafı sürükleyin"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Düzleştirmek için fotoğrafı sürükleyin"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-uk/strings.xml b/samples/PhotoEditor/res/values-uk/strings.xml
new file mode 100644
index 0000000..8c71d57
--- /dev/null
+++ b/samples/PhotoEditor/res/values-uk/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Неможливо завантажити фотографію для редагування"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Під час збереження виникла проблема"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Файл \"<xliff:g id="FILENAME">%s</xliff:g>\" збережено"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Зберегти відредаговану фотографію?"</string>
+ <string name="yes" msgid="5402582493291792293">"Так"</string>
+ <string name="no" msgid="5595408018304861875">"Ні"</string>
+ <string name="cancel" msgid="6286688759430767307">"Скасувати"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Редаговано"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Художнє фото"</string>
+ <string name="color_group" msgid="3390757893475444688">"Колір"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Експозиція"</string>
+ <string name="fix_group" msgid="971450155810247383">"Виправлення"</string>
+ <string name="autofix" msgid="1223859191856172755">"Мені пощастить"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Крос-процес"</string>
+ <string name="documentary" msgid="50396326708699797">"Чорно-білі тони"</string>
+ <string name="doodle" msgid="1686409894518940990">"Doodle"</string>
+ <string name="duotone" msgid="8145893940788467106">"Два тони"</string>
+ <string name="filllight" msgid="2644989991700022526">"Доповн. світло"</string>
+ <string name="fisheye" msgid="6037488646928998921">"\"Риб’яче око\""</string>
+ <string name="flip" msgid="2357692401826287480">"Перевернути"</string>
+ <string name="grain" msgid="7487585304579789098">"Зернистість плівки"</string>
+ <string name="grayscale" msgid="615169770671286699">"Сірі тони"</string>
+ <string name="highlight" msgid="3902653944386623972">"Світлові ефекти"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Ломо-ефект"</string>
+ <string name="negative" msgid="1985508917342811252">"Негатив"</string>
+ <string name="posterize" msgid="4139212359561383385">"Ефект плаката"</string>
+ <string name="redeye" msgid="4958448806369928239">"Червоні очі"</string>
+ <string name="rotate" msgid="6607597269792373083">"Обертання"</string>
+ <string name="saturation" msgid="8621322012271169931">"Насиченість"</string>
+ <string name="sepia" msgid="7978093531824705601">"Сепія"</string>
+ <string name="shadow" msgid="8235188588101973090">"Тіні"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Різкість"</string>
+ <string name="softfocus" msgid="6334228862566408303">"М’який фокус"</string>
+ <string name="straighten" msgid="5217801513491493491">"Вирівняти"</string>
+ <string name="temperature" msgid="3589958696423284897">"Температура"</string>
+ <string name="tint" msgid="154435943863418434">"Відтінок"</string>
+ <string name="vignette" msgid="7648125924662648282">"Віньєтка"</string>
+ <string name="warmify" msgid="2404474974251014984">"Тепліші тони"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Малюйте на фото, щоб створити doodle"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Перетягніть фото, щоб перевернути його"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Натисніть, щоб усунути ефект червоних очей"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Перетягніть фото, щоб обертати його"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Перетягніть фото, щоб вирівняти його"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-vi/strings.xml b/samples/PhotoEditor/res/values-vi/strings.xml
new file mode 100644
index 0000000..92f119f
--- /dev/null
+++ b/samples/PhotoEditor/res/values-vi/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Studio ảnh"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"Không thể tải ảnh để chỉnh sửa"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"Đã xảy ra lỗi trong khi lưu"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"Đã lưu <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="save_photo" msgid="3125109368779997862">"Lưu ảnh đã chỉnh sửa?"</string>
+ <string name="yes" msgid="5402582493291792293">"Có"</string>
+ <string name="no" msgid="5595408018304861875">"Không"</string>
+ <string name="cancel" msgid="6286688759430767307">"Hủy"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"Đã chỉnh sửa"</string>
+ <string name="artistic_group" msgid="573091775825402562">"Nghệ thuật"</string>
+ <string name="color_group" msgid="3390757893475444688">"Màu sắc"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"Phơi sáng"</string>
+ <string name="fix_group" msgid="971450155810247383">"Cố định"</string>
+ <string name="autofix" msgid="1223859191856172755">"Tự chỉnh ph.sáng"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"Tạo màu sắc khác lạ"</string>
+ <string name="documentary" msgid="50396326708699797">"Phim tài liệu"</string>
+ <string name="doodle" msgid="1686409894518940990">"Hình vẽ nguệch ngoạc"</string>
+ <string name="duotone" msgid="8145893940788467106">"Có hai màu"</string>
+ <string name="filllight" msgid="2644989991700022526">"Tô sáng"</string>
+ <string name="fisheye" msgid="6037488646928998921">"Mắt cá"</string>
+ <string name="flip" msgid="2357692401826287480">"Lật"</string>
+ <string name="grain" msgid="7487585304579789098">"Hiệu ứng hạt mỏng"</string>
+ <string name="grayscale" msgid="615169770671286699">"Thang màu xám"</string>
+ <string name="highlight" msgid="3902653944386623972">"Thêm điểm sáng"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Kiểu Lomo"</string>
+ <string name="negative" msgid="1985508917342811252">"Tạo âm bản"</string>
+ <string name="posterize" msgid="4139212359561383385">"Ảnh áp phích"</string>
+ <string name="redeye" msgid="4958448806369928239">"Mắt đỏ"</string>
+ <string name="rotate" msgid="6607597269792373083">"Xoay"</string>
+ <string name="saturation" msgid="8621322012271169931">"Bão hòa"</string>
+ <string name="sepia" msgid="7978093531824705601">"Nâu đỏ"</string>
+ <string name="shadow" msgid="8235188588101973090">"Bóng"</string>
+ <string name="sharpen" msgid="8449662378104403230">"Làm sắc nét"</string>
+ <string name="softfocus" msgid="6334228862566408303">"Tiêu điểm mềm"</string>
+ <string name="straighten" msgid="5217801513491493491">"Làm thẳng"</string>
+ <string name="temperature" msgid="3589958696423284897">"Nhiệt độ"</string>
+ <string name="tint" msgid="154435943863418434">"Phủ màu"</string>
+ <string name="vignette" msgid="7648125924662648282">"Làm mờ nét ảnh"</string>
+ <string name="warmify" msgid="2404474974251014984">"Làm nóng"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"Kéo trên ảnh để vẽ ngoạch ngoạc"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"Kéo ảnh để lật"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"Chạm để loại bỏ mắt đỏ"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"Kéo ảnh để xoay"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"Kéo ảnh để sắp thẳng"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-zh-rCN/strings.xml b/samples/PhotoEditor/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..50c7cc3
--- /dev/null
+++ b/samples/PhotoEditor/res/values-zh-rCN/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"照相馆"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"无法加载照片以供修改"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"保存时出现问题"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g>​已保存"</string>
+ <string name="save_photo" msgid="3125109368779997862">"要保存已修改的照片吗?"</string>
+ <string name="yes" msgid="5402582493291792293">"是"</string>
+ <string name="no" msgid="5595408018304861875">"否"</string>
+ <string name="cancel" msgid="6286688759430767307">"取消"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"已修改"</string>
+ <string name="artistic_group" msgid="573091775825402562">"艺术效果"</string>
+ <string name="color_group" msgid="3390757893475444688">"颜色"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"曝光"</string>
+ <string name="fix_group" msgid="971450155810247383">"修正"</string>
+ <string name="autofix" msgid="1223859191856172755">"修正曝光"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"交叉冲印"</string>
+ <string name="documentary" msgid="50396326708699797">"纪实"</string>
+ <string name="doodle" msgid="1686409894518940990">"涂鸦"</string>
+ <string name="duotone" msgid="8145893940788467106">"双色"</string>
+ <string name="filllight" msgid="2644989991700022526">"补光"</string>
+ <string name="fisheye" msgid="6037488646928998921">"鱼眼镜头"</string>
+ <string name="flip" msgid="2357692401826287480">"翻转"</string>
+ <string name="grain" msgid="7487585304579789098">"胶片颗粒"</string>
+ <string name="grayscale" msgid="615169770671286699">"灰度"</string>
+ <string name="highlight" msgid="3902653944386623972">"加亮区"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo 风格"</string>
+ <string name="negative" msgid="1985508917342811252">"反色"</string>
+ <string name="posterize" msgid="4139212359561383385">"色调分离"</string>
+ <string name="redeye" msgid="4958448806369928239">"红眼"</string>
+ <string name="rotate" msgid="6607597269792373083">"旋转"</string>
+ <string name="saturation" msgid="8621322012271169931">"饱和度"</string>
+ <string name="sepia" msgid="7978093531824705601">"深褐色"</string>
+ <string name="shadow" msgid="8235188588101973090">"阴影"</string>
+ <string name="sharpen" msgid="8449662378104403230">"锐化"</string>
+ <string name="softfocus" msgid="6334228862566408303">"柔焦"</string>
+ <string name="straighten" msgid="5217801513491493491">"拉直"</string>
+ <string name="temperature" msgid="3589958696423284897">"色温"</string>
+ <string name="tint" msgid="154435943863418434">"着色"</string>
+ <string name="vignette" msgid="7648125924662648282">"晕影"</string>
+ <string name="warmify" msgid="2404474974251014984">"暖色化"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"在照片上绘制即可涂鸦"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"拖动照片可将其翻转"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"点按可消除红眼"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"拖动照片可将其旋转"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"拖动照片可将其拉直"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values-zh-rTW/strings.xml b/samples/PhotoEditor/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..aada7e2
--- /dev/null
+++ b/samples/PhotoEditor/res/values-zh-rTW/strings.xml
@@ -0,0 +1,63 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="6794833895507358569">"Photo Studio"</string>
+ <string name="loading_failure" msgid="8575831322115054185">"無法載入要編輯的相片"</string>
+ <string name="saving_failure" msgid="3116717307036082369">"儲存時發生問題"</string>
+ <string name="photo_saved" msgid="5093564498138145422">"<xliff:g id="FILENAME">%s</xliff:g> 已儲存"</string>
+ <string name="save_photo" msgid="3125109368779997862">"要儲存已編輯的相片嗎?"</string>
+ <string name="yes" msgid="5402582493291792293">"是"</string>
+ <string name="no" msgid="5595408018304861875">"否"</string>
+ <string name="cancel" msgid="6286688759430767307">"取消"</string>
+ <string name="edited_photo_bucket_name" msgid="3777746536831799890">"已編輯"</string>
+ <string name="artistic_group" msgid="573091775825402562">"藝術"</string>
+ <string name="color_group" msgid="3390757893475444688">"色彩"</string>
+ <string name="exposure_group" msgid="7019148007708100078">"曝光"</string>
+ <string name="fix_group" msgid="971450155810247383">"修正"</string>
+ <string name="autofix" msgid="1223859191856172755">"好幸運"</string>
+ <string name="crossprocess" msgid="4173724489001742342">"融合處理"</string>
+ <string name="documentary" msgid="50396326708699797">"黑白紀錄片"</string>
+ <string name="doodle" msgid="1686409894518940990">"塗鴉"</string>
+ <string name="duotone" msgid="8145893940788467106">"雙色調"</string>
+ <string name="filllight" msgid="2644989991700022526">"調整亮度"</string>
+ <string name="fisheye" msgid="6037488646928998921">"魚眼"</string>
+ <string name="flip" msgid="2357692401826287480">"翻轉"</string>
+ <string name="grain" msgid="7487585304579789098">"膠片顆粒"</string>
+ <string name="grayscale" msgid="615169770671286699">"灰階"</string>
+ <string name="highlight" msgid="3902653944386623972">"強光"</string>
+ <string name="lomoish" msgid="7793824845892532976">"Lomo 風格"</string>
+ <string name="negative" msgid="1985508917342811252">"負片"</string>
+ <string name="posterize" msgid="4139212359561383385">"色調分離"</string>
+ <string name="redeye" msgid="4958448806369928239">"紅眼"</string>
+ <string name="rotate" msgid="6607597269792373083">"旋轉"</string>
+ <string name="saturation" msgid="8621322012271169931">"飽和度"</string>
+ <string name="sepia" msgid="7978093531824705601">"深褐色"</string>
+ <string name="shadow" msgid="8235188588101973090">"陰影"</string>
+ <string name="sharpen" msgid="8449662378104403230">"銳化"</string>
+ <string name="softfocus" msgid="6334228862566408303">"柔焦"</string>
+ <string name="straighten" msgid="5217801513491493491">"拉正"</string>
+ <string name="temperature" msgid="3589958696423284897">"色溫"</string>
+ <string name="tint" msgid="154435943863418434">"著色"</string>
+ <string name="vignette" msgid="7648125924662648282">"暈影"</string>
+ <string name="warmify" msgid="2404474974251014984">"加強暖色"</string>
+ <string name="doodle_tooltip" msgid="2902117272374362915">"在相片上畫圖即可塗鴉"</string>
+ <string name="flip_tooltip" msgid="2700943256714731737">"拖曳照片即可翻轉"</string>
+ <string name="redeye_tooltip" msgid="9112774042113471358">"輕按即可消除紅眼"</string>
+ <string name="rotate_tooltip" msgid="7008602969130734229">"拖曳照片即可旋轉"</string>
+ <string name="straighten_tooltip" msgid="4846317027139212339">"拖曳照片即可拉正"</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values/arrays.xml b/samples/PhotoEditor/res/values/arrays.xml
new file mode 100644
index 0000000..9877b98
--- /dev/null
+++ b/samples/PhotoEditor/res/values/arrays.xml
@@ -0,0 +1,335 @@
+<?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>
+ <string-array name="autofix_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="autofix_icons">
+ <item>@drawable/autofix_off</item>
+ <item>@drawable/autofix_on</item>
+ </array>
+
+ <string-array name="crop_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="crop_icons">
+ <item>@drawable/crop_off</item>
+ <item>@drawable/crop_on</item>
+ </array>
+
+ <string-array name="crossprocess_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="crossprocess_icons">
+ <item>@drawable/crossprocess_off</item>
+ <item>@drawable/crossprocess_on</item>
+ </array>
+
+ <string-array name="documentary_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="documentary_icons">
+ <item>@drawable/documentary_off</item>
+ <item>@drawable/documentary_on</item>
+ </array>
+
+ <string-array name="doodle_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="doodle_icons">
+ <item>@drawable/doodle_off</item>
+ <item>@drawable/doodle_on</item>
+ </array>
+
+ <string-array name="duotone_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="duotone_icons">
+ <item>@drawable/duotone_off</item>
+ <item>@drawable/duotone_on</item>
+ </array>
+
+ <string-array name="filllight_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="filllight_icons">
+ <item>@drawable/filllight_off</item>
+ <item>@drawable/filllight_on</item>
+ </array>
+
+ <string-array name="fisheye_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="fisheye_icons">
+ <item>@drawable/fisheye_off</item>
+ <item>@drawable/fisheye_on</item>
+ </array>
+
+ <string-array name="flip_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="flip_icons">
+ <item>@drawable/flip_off</item>
+ <item>@drawable/flip_on</item>
+ </array>
+
+ <string-array name="grain_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="grain_icons">
+ <item>@drawable/grain_off</item>
+ <item>@drawable/grain_on</item>
+ </array>
+
+ <string-array name="grayscale_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="grayscale_icons">
+ <item>@drawable/grayscale_off</item>
+ <item>@drawable/grayscale_on</item>
+ </array>
+
+ <string-array name="highlight_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="highlight_icons">
+ <item>@drawable/highlight_off</item>
+ <item>@drawable/highlight_on</item>
+ </array>
+
+ <string-array name="lomoish_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="lomoish_icons">
+ <item>@drawable/lomoish_off</item>
+ <item>@drawable/lomoish_on</item>
+ </array>
+
+ <string-array name="negative_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="negative_icons">
+ <item>@drawable/negative_off</item>
+ <item>@drawable/negative_on</item>
+ </array>
+
+ <string-array name="posterize_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="posterize_icons">
+ <item>@drawable/posterize_off</item>
+ <item>@drawable/posterize_on</item>
+ </array>
+
+ <string-array name="redeye_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="redeye_icons">
+ <item>@drawable/redeye_off</item>
+ <item>@drawable/redeye_on</item>
+ </array>
+
+ <string-array name="rotate_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="rotate_icons">
+ <item>@drawable/rotate_off</item>
+ <item>@drawable/rotate_on</item>
+ </array>
+
+ <string-array name="saturation_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="saturation_icons">
+ <item>@drawable/saturation_off</item>
+ <item>@drawable/saturation_on</item>
+ </array>
+
+ <string-array name="sepia_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="sepia_icons">
+ <item>@drawable/sepia_off</item>
+ <item>@drawable/sepia_on</item>
+ </array>
+
+ <string-array name="shadow_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="shadow_icons">
+ <item>@drawable/shadow_off</item>
+ <item>@drawable/shadow_on</item>
+ </array>
+
+ <string-array name="sharpen_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="sharpen_icons">
+ <item>@drawable/sharpen_off</item>
+ <item>@drawable/sharpen_on</item>
+ </array>
+
+ <string-array name="softfocus_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="softfocus_icons">
+ <item>@drawable/softfocus_off</item>
+ <item>@drawable/softfocus_on</item>
+ </array>
+
+ <string-array name="straighten_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="straighten_icons">
+ <item>@drawable/straighten_off</item>
+ <item>@drawable/straighten_on</item>
+ </array>
+
+ <string-array name="temperature_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="temperature_icons">
+ <item>@drawable/colortemperature_off</item>
+ <item>@drawable/colortemperature_on</item>
+ </array>
+
+ <string-array name="tint_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="tint_icons">
+ <item>@drawable/tint_off</item>
+ <item>@drawable/tint_on</item>
+ </array>
+
+ <string-array name="vignette_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="vignette_icons">
+ <item>@drawable/vignette_off</item>
+ <item>@drawable/vignette_on</item>
+ </array>
+
+ <string-array name="warmify_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="warmify_icons">
+ <item>@drawable/warmify_off</item>
+ <item>@drawable/warmify_on</item>
+ </array>
+
+ <string-array name="undo_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="undo_icons">
+ <item>@drawable/undo</item>
+ <item>@drawable/undo</item>
+ </array>
+
+ <string-array name="redo_modes" translatable="false">
+ <item>off</item>
+ <item>on</item>
+ </string-array>
+
+ <array name="redo_icons">
+ <item>@drawable/redo</item>
+ <item>@drawable/redo</item>
+ </array>
+
+ <array name="color_picker_wheel_colors">
+ <item>@color/color_picker_preset_color1</item>
+ <item>@color/color_picker_preset_color2</item>
+ <item>@color/color_picker_preset_color3</item>
+ <item>@color/color_picker_preset_color4</item>
+ <item>@color/color_picker_preset_color5</item>
+ <item>@color/color_picker_preset_color6</item>
+ <item>@color/color_picker_preset_color7</item>
+ <item>@color/color_picker_preset_color8</item>
+ <item>@color/color_picker_preset_color9</item>
+ <item>@color/color_picker_preset_color10</item>
+ <item>@color/color_picker_preset_color11</item>
+ <item>@color/color_picker_preset_color12</item>
+ <item>@color/color_picker_preset_color13</item>
+ <item>@color/color_picker_preset_color14</item>
+ <item>@color/color_picker_preset_color15</item>
+ <item>@color/color_picker_preset_color16</item>
+ <item>@color/color_picker_preset_color17</item>
+ <item>@color/color_picker_preset_color18</item>
+ <item>@color/color_picker_preset_color19</item>
+ <item>@color/color_picker_preset_color20</item>
+ <item>@color/color_picker_preset_color21</item>
+ <item>@color/color_picker_preset_color22</item>
+ <item>@color/color_picker_preset_color23</item>
+ <item>@color/color_picker_preset_color24</item>
+ </array>
+
+</resources>
diff --git a/samples/PhotoEditor/res/values/attrs.xml b/samples/PhotoEditor/res/values/attrs.xml
new file mode 100644
index 0000000..052534c
--- /dev/null
+++ b/samples/PhotoEditor/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?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>
+ <declare-styleable name="IconIndicator">
+ <attr name="icons" format="reference" />
+ <attr name="modes" format="reference" />
+ </declare-styleable>
+</resources>
diff --git a/samples/PhotoEditor/res/values/colors.xml b/samples/PhotoEditor/res/values/colors.xml
new file mode 100644
index 0000000..f699c1b
--- /dev/null
+++ b/samples/PhotoEditor/res/values/colors.xml
@@ -0,0 +1,47 @@
+<?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>
+ <color name="scale_wheel_interior_color">#40808080</color>
+
+ <color name="color_picker_indicator_color">#ffff3377</color>
+ <color name="color_picker_border_color">#ffbbbbbb</color>
+
+ <color name="color_picker_preset_color1">#ddff1e00</color>
+ <color name="color_picker_preset_color2">#ddff5b00</color>
+ <color name="color_picker_preset_color3">#ddff9900</color>
+ <color name="color_picker_preset_color4">#ddffd600</color>
+ <color name="color_picker_preset_color5">#ddeaff00</color>
+ <color name="color_picker_preset_color6">#ddadff00</color>
+ <color name="color_picker_preset_color7">#dd70ff00</color>
+ <color name="color_picker_preset_color8">#dd32ff00</color>
+ <color name="color_picker_preset_color9">#dd00ff0a</color>
+ <color name="color_picker_preset_color10">#dd00ff47</color>
+ <color name="color_picker_preset_color11">#dd00ff84</color>
+ <color name="color_picker_preset_color12">#dd00ffc1</color>
+ <color name="color_picker_preset_color13">#dd00ffff</color>
+ <color name="color_picker_preset_color14">#dd00c1ff</color>
+ <color name="color_picker_preset_color15">#dd0084ff</color>
+ <color name="color_picker_preset_color16">#dd0047ff</color>
+ <color name="color_picker_preset_color17">#dd000aff</color>
+ <color name="color_picker_preset_color18">#dd3300ff</color>
+ <color name="color_picker_preset_color19">#dd7000ff</color>
+ <color name="color_picker_preset_color20">#ddad00ff</color>
+ <color name="color_picker_preset_color21">#ddea00ff</color>
+ <color name="color_picker_preset_color22">#ddff00d6</color>
+ <color name="color_picker_preset_color23">#ddff0098</color>
+ <color name="color_picker_preset_color24">#ddff005b</color>
+</resources>
diff --git a/samples/PhotoEditor/res/values/dimen.xml b/samples/PhotoEditor/res/values/dimen.xml
new file mode 100755
index 0000000..cbadea0
--- /dev/null
+++ b/samples/PhotoEditor/res/values/dimen.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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <dimen name="group_cotent_padding_top">10dp</dimen>
+ <dimen name="group_cotent_padding_bottom">15dp</dimen>
+ <dimen name="group_header_height">48dp</dimen>
+ <dimen name="group_header_padding_bottom">10dp</dimen>
+ <dimen name="group_header_padding_left">11dp</dimen>
+ <dimen name="group_header_padding_right">5dp</dimen>
+ <dimen name="group_arrow_size">20dp</dimen>
+ <dimen name="group_label_text_size">15sp</dimen>
+ <dimen name="effect_label_text_size">14sp</dimen>
+ <dimen name="effect_icon_size">92dp</dimen>
+ <dimen name="effect_padding_top">10dp</dimen>
+ <dimen name="effect_padding_bottom">15dp</dimen>
+ <dimen name="effects_bar_width">124dp</dimen>
+ <dimen name="effects_bar_margin_top">4dp</dimen>
+ <dimen name="effects_bar_margin_bottom">4dp</dimen>
+ <dimen name="action_bar_height">50dp</dimen>
+ <dimen name="action_bar_text_size">18sp</dimen>
+ <dimen name="action_bar_arrow_padding_top">17dp</dimen>
+ <dimen name="action_bar_arrow_padding_bottom">17dp</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">5dp</dimen>
+ <dimen name="action_bar_icon_padding_bottom">5dp</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">7dp</dimen>
+ <dimen name="action_button_padding_bottom">7dp</dimen>
+ <dimen name="action_button_padding_left">22dp</dimen>
+ <dimen name="action_button_padding_right">22dp</dimen>
+ <dimen name="action_button_padding_right_most">45dp</dimen>
+ <dimen name="wheel_layout_marginLeft">-48dp</dimen>
+ <dimen name="wheel_layout_size">240dp</dimen>
+ <dimen name="wheel_thumb_size">51dp</dimen>
+ <dimen name="crop_indicator_size">32dp</dimen>
+</resources>
diff --git a/samples/PhotoEditor/res/values/strings.xml b/samples/PhotoEditor/res/values/strings.xml
new file mode 100644
index 0000000..c4f587c
--- /dev/null
+++ b/samples/PhotoEditor/res/values/strings.xml
@@ -0,0 +1,159 @@
+<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name appearing in the application launcher. [CHAR LIMIT=NONE] -->
+ <string name="app_name">Photo Studio</string>
+
+ <!-- Toast shown when selected photo could not be loaded. [CHAR LIMIT=50] -->
+ <string name="loading_failure">Cannot load the photo for editing</string>
+
+ <!-- Toast shown when edited photo could not be saved. [CHAR LIMIT=50] -->
+ <string name="saving_failure">A problem occurred during saving</string>
+
+ <!-- Toast shown when edited photo is successfully saved with filename %s [CHAR LIMIT=50] -->
+ <string name="photo_saved"><xliff:g id="filename">%s</xliff:g> saved</string>
+
+ <!-- Dialog message prompted when the user is abandoning unsaved changes. [CHAR LIMIT=50] -->
+ <string name="save_photo">Save edited photo?</string>
+
+ <!-- Dialog yes button for the user to accept text presented in a dialog. [CHAR LIMIT=12] -->
+ <string name="yes">Yes</string>
+
+ <!-- Dialog no button for the user to decline text presented in a dialog. [CHAR LIMIT=12] -->
+ <string name="no">No</string>
+
+ <!-- Dialog cancel button for the user to cancel a dialog. [CHAR LIMIT=12] -->
+ <string name="cancel">Cancel</string>
+
+ <!-- Directory name where edited photo will be saved in the storage. [CHAR LIMIT=12] -->
+ <string name="edited_photo_bucket_name">Edited</string>
+
+ <!-- Menu label to group all artistic-related photo effects, e.g. vignette, posterize, etc.
+ [CHAR LIMIT=11] -->
+ <string name="artistic_group">Artistic</string>
+
+ <!-- Menu label to group all color-related photo effects, e.g. color-temperature, tint, etc.
+ [CHAR LIMIT=11] -->
+ <string name="color_group">Color</string>
+
+ <!-- Menu label to group all exposure-related photo effects, e.g. fill-light, highlight, etc.
+ [CHAR LIMIT=11] -->
+ <string name="exposure_group">Exposure</string>
+
+ <!-- Menu label to group all fix-related photo effects, e.g. straighten, red-eye removal, etc.
+ [CHAR LIMIT=11] -->
+ <string name="fix_group">Fix</string>
+
+ <!-- Name for the photo effect 'Feel Lucky' that auto-fixes exposure. [CHAR LIMIT=15] -->
+ <string name="autofix">Feel Lucky</string>
+
+ <!-- Name for the photo effect that crops photo. [CHAR LIMIT=15] -->
+ <string name="crop">Crop</string>
+
+ <!-- Name for the photo effect that applies cross-process effect. [CHAR LIMIT=15] -->
+ <string name="crossprocess">Cross-process</string>
+
+ <!-- Name for the photo effect that applies black/white documentary styles. [CHAR LIMIT=15] -->
+ <string name="documentary">Documentary</string>
+
+ <!-- Name for the photo effect that doodles on photo. [CHAR LIMIT=15] -->
+ <string name="doodle">Doodle</string>
+
+ <!-- Name for the photo effect that makes photo only two color tones. [CHAR LIMIT=15] -->
+ <string name="duotone">Duo-tone</string>
+
+ <!-- Name for the photo effect that fills back-light. [CHAR LIMIT=15] -->
+ <string name="filllight">Fill Light</string>
+
+ <!-- Name for the photo effect that applies fisheye lens distortion. [CHAR LIMIT=15] -->
+ <string name="fisheye">Fisheye</string>
+
+ <!-- Name for the photo effect that flips photo vertically or horizontally. [CHAR LIMIT=15] -->
+ <string name="flip">Flip</string>
+
+ <!-- Name for the photo effect that adds grains and noises. [CHAR LIMIT=15] -->
+ <string name="grain">Film Grain</string>
+
+ <!-- Name for the photo effect that converts colors to grayscales. [CHAR LIMIT=15] -->
+ <string name="grayscale">Grayscale</string>
+
+ <!-- Name for the photo effect that adds highlights. [CHAR LIMIT=15] -->
+ <string name="highlight">Highlights</string>
+
+ <!-- Name for the photo effect that applies lomo-camera styles. [CHAR LIMIT=15] -->
+ <string name="lomoish">Lomo-ish</string>
+
+ <!-- Name for the photo effect that inverts photo to negative images. [CHAR LIMIT=15] -->
+ <string name="negative">Negative</string>
+
+ <!-- Name for the photo effect that applies posterization. [CHAR LIMIT=15] -->
+ <string name="posterize">Posterize</string>
+
+ <!-- Name for the photo effect that remove red eyes. [CHAR LIMIT=15] -->
+ <string name="redeye">Red Eye</string>
+
+ <!-- Name for the photo effect that rotates photo. [CHAR LIMIT=15] -->
+ <string name="rotate">Rotate</string>
+
+ <!-- Name for the photo effect that adjusts color saturation. [CHAR LIMIT=15] -->
+ <string name="saturation">Saturation</string>
+
+ <!-- Name for the photo effect that applies sepia color toning. [CHAR LIMIT=15] -->
+ <string name="sepia">Sepia</string>
+
+ <!-- Name for the photo effect that adds shadows. [CHAR LIMIT=15] -->
+ <string name="shadow">Shadows</string>
+
+ <!-- Name for the photo effect that sharpens blurred photo. [CHAR LIMIT=15] -->
+ <string name="sharpen">Sharpen</string>
+
+ <!-- Name for the photo effect that applies soft-focus. [CHAR LIMIT=15] -->
+ <string name="softfocus">Soft Focus</string>
+
+ <!-- Name for the photo effect that straightens crooked photo. [CHAR LIMIT=15] -->
+ <string name="straighten">Straighten</string>
+
+ <!-- Name for the photo effect that adjusts color-temperature. [CHAR LIMIT=15] -->
+ <string name="temperature">Temperature</string>
+
+ <!-- Name for the photo effect that tints photo with various colors. [CHAR LIMIT=15] -->
+ <string name="tint">Tint</string>
+
+ <!-- Name for the photo effect that fades off photo edges. [CHAR LIMIT=15] -->
+ <string name="vignette">Vignette</string>
+
+ <!-- Name for the photo effect that applies warmify color toning. [CHAR LIMIT=15] -->
+ <string name="warmify">Warmify</string>
+
+ <!-- Tool-tip toast shown when the user chooses to crop photo. [CHAR LIMIT=50] -->
+ <string name="crop_tooltip">Drag photo to crop it</string>
+
+ <!-- Tool-tip toast shown when the user chooses to doodle on photo. [CHAR LIMIT=50] -->
+ <string name="doodle_tooltip">Draw on photo to doodle</string>
+
+ <!-- Tool-tip toast shown when the user chooses to flip photo. [CHAR LIMIT=50] -->
+ <string name="flip_tooltip">Drag photo to flip it</string>
+
+ <!-- Tool-tip toast shown when the user chooses to remove red eyes. [CHAR LIMIT=50] -->
+ <string name="redeye_tooltip">Tap to remove red eyes</string>
+
+ <!-- Tool-tip toast shown when the user chooses to rotate photo. [CHAR LIMIT=50] -->
+ <string name="rotate_tooltip">Drag photo to rotate it</string>
+
+ <!-- Tool-tip toast shown when the user chooses to straighten photo. [CHAR LIMIT=50] -->
+ <string name="straighten_tooltip">Drag photo to straighten it</string>
+</resources>
diff --git a/samples/PhotoEditor/res/values/styles.xml b/samples/PhotoEditor/res/values/styles.xml
new file mode 100644
index 0000000..35bca10
--- /dev/null
+++ b/samples/PhotoEditor/res/values/styles.xml
@@ -0,0 +1,166 @@
+<?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">
+ <style name="GroupHeader">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">@dimen/group_header_height</item>
+ <item name="android:paddingBottom">@dimen/group_header_padding_bottom</item>
+ <item name="android:paddingLeft">@dimen/group_header_padding_left</item>
+ <item name="android:paddingRight">@dimen/group_header_padding_right</item>
+ <item name="android:background">@drawable/group_header</item>
+ </style>
+ <style name="GroupArrow">
+ <item name="android:layout_width">@dimen/group_arrow_size</item>
+ <item name="android:layout_height">@dimen/group_arrow_size</item>
+ <item name="android:layout_gravity">center_vertical|right</item>
+ <item name="android:paddingTop">2dp</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:src">@drawable/arrow_down</item>
+ </style>
+ <style name="GroupLabel">
+ <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:gravity">center_vertical</item>
+ <item name="android:textSize">@dimen/group_label_text_size</item>
+ <item name="android:textColor">#FFFFFF</item>
+ <item name="android:shadowColor">#000000</item>
+ <item name="android:shadowRadius">2.0</item>
+ </style>
+ <style name="GroupContent" parent="@style/EffectsBarLinearLayout">
+ <item name="android:paddingTop">@dimen/group_cotent_padding_top</item>
+ <item name="android:paddingBottom">@dimen/group_cotent_padding_bottom</item>
+ </style>
+ <style name="EffectIcon">
+ <item name="android:layout_width">@dimen/effect_icon_size</item>
+ <item name="android:layout_height">@dimen/effect_icon_size</item>
+ <item name="android:layout_gravity">center_horizontal</item>
+ <item name="android:scaleType">fitCenter</item>
+ </style>
+ <style name="EffectLabel">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center_horizontal</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textSize">@dimen/effect_label_text_size</item>
+ <item name="android:textColor">#FFFFFF</item>
+ <item name="android:shadowColor">#000000</item>
+ <item name="android:shadowRadius">2.0</item>
+ </style>
+ <style name="Effect">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center_horizontal</item>
+ <item name="android:paddingTop">@dimen/effect_padding_top</item>
+ <item name="android:paddingBottom">@dimen/effect_padding_bottom</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+ <style name="EffectsBarLinearLayout">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+ <style name="EffectsBar">
+ <item name="android:layout_width">@dimen/effects_bar_width</item>
+ <item name="android:layout_height">fill_parent</item>
+ <item name="android:layout_alignParentRight">true</item>
+ <item name="android:layout_marginTop">@dimen/effects_bar_margin_top</item>
+ <item name="android:layout_marginBottom">@dimen/effects_bar_margin_bottom</item>
+ <item name="android:scrollbars">none</item>
+ </style>
+ <style name="ActionBar">
+ <item name="android:layout_alignParentTop">true</item>
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">@dimen/action_bar_height</item>
+ <item name="android:background">@null</item>
+ </style>
+ <style name="ActionBarTranslucent">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">fill_parent</item>
+ <item name="android:background">@drawable/actionbar_translucent</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">
+ <item name="android:clickable">true</item>
+ <item name="android:focusable">true</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ </style>
+ <style name="ActionBarImageView">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">fill_parent</item>
+ <item name="android:layout_gravity">center</item>
+ <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/arrow_back</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:paddingLeft">@dimen/action_bar_icon_padding_left</item>
+ <item name="android:paddingRight">@dimen/action_bar_icon_padding_right</item>
+ <item name="android:src">@drawable/icon</item>
+ </style>
+ <style name="ActionBarText">
+ <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>
+ </style>
+ <style name="ActionButton" 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:background">?android:attr/selectableItemBackground</item>
+ </style>
+ <style name="RightmostActionButton" parent="@style/ActionButton">
+ <item name="android:paddingRight">@dimen/action_button_padding_right_most</item>
+ </style>
+ <style name="Wheel">
+ <item name="android:layout_width">@dimen/wheel_layout_size</item>
+ <item name="android:layout_height">@dimen/wheel_layout_size</item>
+ <item name="android:layout_alignParentLeft">true</item>
+ <item name="android:layout_marginLeft">@dimen/wheel_layout_marginLeft</item>
+ <item name="android:background">@null</item>
+ <item name="android:visibility">invisible</item>
+ </style>
+ <style name="FullscreenToolView">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">fill_parent</item>
+ <item name="android:visibility">invisible</item>
+ </style>
+ <style name="SpinnerProgressDialog">
+ <item name="android:background">@android:color/transparent</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowTitleStyle">@null</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
+</resources>
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/ActionBar.java b/samples/PhotoEditor/src/com/android/photoeditor/ActionBar.java
new file mode 100644
index 0000000..4465741
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/ActionBar.java
@@ -0,0 +1,166 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ViewSwitcher;
+
+/**
+ * Action bar that contains buttons such as undo, redo, save, etc. and listens to stack changes for
+ * enabling/disabling buttons.
+ */
+public class ActionBar extends ViewSwitcher implements FilterStack.StackListener {
+
+ /**
+ * Listener of action button clicked.
+ */
+ public interface ActionBarListener {
+
+ void onQuickview(boolean on);
+
+ void onUndo();
+
+ void onRedo();
+
+ void onSave();
+ }
+
+ private static final int ENABLE_BUTTON = 1;
+ private static final int ENABLED_ALPHA = 255;
+ private static final int DISABLED_ALPHA = 120;
+
+ private final Handler handler;
+ private ImageButton save;
+ private ImageButton undo;
+ private ImageButton redo;
+ private ImageButton quickview;
+
+ 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(quickview, canUndo);
+ enableButton(save, canUndo);
+ enableButton(undo, canUndo);
+ enableButton(redo, canRedo);
+ break;
+ }
+ }
+ };
+ }
+
+ /**
+ * Initializes with a non-null ActionBarListener.
+ */
+ public void initialize(final ActionBarListener listener) {
+ save = (ImageButton) findViewById(R.id.save_button);
+ save.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isEnabled()) {
+ listener.onSave();
+ }
+ }
+ });
+
+ undo = (ImageButton) findViewById(R.id.undo_button);
+ undo.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isEnabled()) {
+ listener.onUndo();
+ }
+ }
+ });
+
+ redo = (ImageButton) findViewById(R.id.redo_button);
+ redo.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isEnabled()) {
+ listener.onRedo();
+ }
+ }
+ });
+
+ quickview = (ImageButton) findViewById(R.id.quickview_button);
+ quickview.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isEnabled()) {
+ ActionBar.this.showNext();
+ listener.onQuickview(true);
+ }
+ }
+ });
+
+ View quickviewOn = findViewById(R.id.quickview_on_button);
+ quickviewOn.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isEnabled()) {
+ ActionBar.this.showNext();
+ listener.onQuickview(false);
+ }
+ }
+ });
+
+ resetButtons();
+ }
+
+ public void resetButtons() {
+ // Disable buttons immediately instead of waiting for ENABLE_BUTTON messages which may
+ // happen some time later after stack changes.
+ enableButton(save, false);
+ enableButton(undo, false);
+ enableButton(redo, false);
+ enableButton(quickview, false);
+ }
+
+ public void disableSave() {
+ enableButton(save, false);
+ }
+
+ private void enableButton(ImageButton button, boolean enabled) {
+ 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));
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/BitmapUtils.java b/samples/PhotoEditor/src/com/android/photoeditor/BitmapUtils.java
new file mode 100644
index 0000000..8678f10
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/BitmapUtils.java
@@ -0,0 +1,201 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.provider.MediaStore.Images.ImageColumns;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Utils for bitmap operations.
+ */
+public class BitmapUtils {
+
+ private static final String TAG = "BitmapUtils";
+ private static final int DEFAULT_COMPRESS_QUALITY = 90;
+ private static final int INDEX_ORIENTATION = 0;
+
+ private static final String[] IMAGE_PROJECTION = new String[] {
+ ImageColumns.ORIENTATION
+ };
+
+ private final Context context;
+
+ public BitmapUtils(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns an immutable bitmap from subset of source bitmap transformed by the given matrix.
+ */
+ public static Bitmap createBitmap(Bitmap bitmap, Matrix m) {
+ // TODO: Re-implement createBitmap to avoid ARGB -> RBG565 conversion on platforms
+ // prior to honeycomb.
+ return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
+ }
+
+ private void closeStream(Closeable stream) {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private Rect getBitmapBounds(Uri uri) {
+ Rect bounds = new Rect();
+ InputStream is = null;
+
+ try {
+ is = context.getContentResolver().openInputStream(uri);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, options);
+
+ bounds.right = options.outWidth;
+ bounds.bottom = options.outHeight;
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ closeStream(is);
+ }
+
+ return bounds;
+ }
+
+ private int getOrientation(Uri uri) {
+ int orientation = 0;
+ Cursor cursor = context.getContentResolver().query(uri, IMAGE_PROJECTION, null, null, null);
+ if ((cursor != null) && cursor.moveToNext()) {
+ orientation = cursor.getInt(INDEX_ORIENTATION);
+ }
+ return orientation;
+ }
+
+ /**
+ * Decodes immutable bitmap that keeps aspect-ratio and spans most within the given rectangle.
+ */
+ private Bitmap decodeBitmap(Uri uri, int width, int height) {
+ InputStream is = null;
+ Bitmap bitmap = null;
+
+ try {
+ // TODO: Take max pixels allowed into account for calculation to avoid possible OOM.
+ Rect bounds = getBitmapBounds(uri);
+ int sampleSize = Math.max(bounds.width() / width, bounds.height() / height);
+ sampleSize = Math.min(sampleSize,
+ Math.max(bounds.width() / height, bounds.height() / width));
+
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = Math.max(sampleSize, 1);
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+
+ is = context.getContentResolver().openInputStream(uri);
+ bitmap = BitmapFactory.decodeStream(is, null, options);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "FileNotFoundException: " + uri);
+ } finally {
+ closeStream(is);
+ }
+
+ // Scale down the sampled bitmap if it's still larger than the desired dimension.
+ if (bitmap != null) {
+ float scale = Math.min((float) width / bitmap.getWidth(),
+ (float) height / bitmap.getHeight());
+ scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(),
+ (float) width / bitmap.getHeight()));
+ if (scale < 1) {
+ Matrix m = new Matrix();
+ m.setScale(scale, scale);
+ Bitmap transformed = createBitmap(bitmap, m);
+ bitmap.recycle();
+ return transformed;
+ }
+ }
+ return bitmap;
+ }
+
+ /**
+ * Gets decoded bitmap that keeps orientation as well.
+ */
+ public Bitmap getBitmap(Uri uri, int width, int height) {
+ Bitmap bitmap = decodeBitmap(uri, width, height);
+
+ // Rotate the decoded bitmap according to its orientation if it's necessary.
+ if (bitmap != null) {
+ int orientation = getOrientation(uri);
+ if (orientation != 0) {
+ Matrix m = new Matrix();
+ m.setRotate(orientation);
+ Bitmap transformed = createBitmap(bitmap, m);
+ bitmap.recycle();
+ return transformed;
+ }
+ }
+ return bitmap;
+ }
+
+ /**
+ * Saves the bitmap by given directory, filename, and format; if the directory is given null,
+ * then saves it under the cache directory.
+ */
+ public File saveBitmap(
+ Bitmap bitmap, String directory, String filename, CompressFormat format) {
+
+ if (directory == null) {
+ directory = context.getCacheDir().getAbsolutePath();
+ } else {
+ // Check if the given directory exists or try to create it.
+ File file = new File(directory);
+ if (!file.isDirectory() && !file.mkdirs()) {
+ return null;
+ }
+ }
+
+ File file = null;
+ OutputStream os = null;
+
+ try {
+ filename = (format == CompressFormat.PNG) ? filename + ".png" : filename + ".jpg";
+ file = new File(directory, filename);
+ os = new FileOutputStream(file);
+ bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, os);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ closeStream(os);
+ }
+ return file;
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/EffectsBar.java b/samples/PhotoEditor/src/com/android/photoeditor/EffectsBar.java
new file mode 100644
index 0000000..63cbf3f
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/EffectsBar.java
@@ -0,0 +1,241 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import com.android.photoeditor.actions.AutoFixAction;
+import com.android.photoeditor.actions.ColorTemperatureAction;
+import com.android.photoeditor.actions.CropAction;
+import com.android.photoeditor.actions.CrossProcessAction;
+import com.android.photoeditor.actions.DocumentaryAction;
+import com.android.photoeditor.actions.DoodleAction;
+import com.android.photoeditor.actions.DuotoneAction;
+import com.android.photoeditor.actions.FillLightAction;
+import com.android.photoeditor.actions.FilterAction;
+import com.android.photoeditor.actions.FisheyeAction;
+import com.android.photoeditor.actions.FlipAction;
+import com.android.photoeditor.actions.GrainAction;
+import com.android.photoeditor.actions.GrayscaleAction;
+import com.android.photoeditor.actions.HighlightAction;
+import com.android.photoeditor.actions.LomoishAction;
+import com.android.photoeditor.actions.NegativeAction;
+import com.android.photoeditor.actions.PosterizeAction;
+import com.android.photoeditor.actions.RedEyeAction;
+import com.android.photoeditor.actions.RotateAction;
+import com.android.photoeditor.actions.SaturationAction;
+import com.android.photoeditor.actions.SepiaAction;
+import com.android.photoeditor.actions.ShadowAction;
+import com.android.photoeditor.actions.SharpenAction;
+import com.android.photoeditor.actions.StraightenAction;
+import com.android.photoeditor.actions.TintAction;
+import com.android.photoeditor.actions.VignetteAction;
+import com.android.photoeditor.actions.WarmifyAction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Scroll view that contains all effects for editing photo by mapping each effect to trigger one
+ * corresponding FilterAction.
+ */
+public class EffectsBar extends ScrollView {
+
+ private final List<Effect> effects = new ArrayList<Effect>();
+ private TextView effectName;
+
+ public EffectsBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void initialize(FilterStack filterStack, PhotoView photoView, ViewGroup tools) {
+ effects.add(new Effect(R.id.autofix_effect,
+ new AutoFixAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.crop_effect,
+ new CropAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.crossprocess_effect,
+ new CrossProcessAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.documentary_effect,
+ new DocumentaryAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.doodle_effect,
+ new DoodleAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.duotone_effect,
+ new DuotoneAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.filllight_effect,
+ new FillLightAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.fisheye_effect,
+ new FisheyeAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.flip_effect,
+ new FlipAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.grain_effect,
+ new GrainAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.grayscale_effect,
+ new GrayscaleAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.highlight_effect,
+ new HighlightAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.lomoish_effect,
+ new LomoishAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.negative_effect,
+ new NegativeAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.posterize_effect,
+ new PosterizeAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.redeye_effect,
+ new RedEyeAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.rotate_effect,
+ new RotateAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.saturation_effect,
+ new SaturationAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.sepia_effect,
+ new SepiaAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.shadow_effect,
+ new ShadowAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.sharpen_effect,
+ new SharpenAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.straighten_effect,
+ new StraightenAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.temperature_effect,
+ new ColorTemperatureAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.tint_effect,
+ new TintAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.vignette_effect,
+ new VignetteAction(filterStack, tools)));
+
+ effects.add(new Effect(R.id.warmify_effect,
+ new WarmifyAction(filterStack, tools)));
+
+ effectName = (TextView) tools.findViewById(R.id.action_effect_name);
+
+ // Disable hardware acceleration on this view to make alpha animations work for idle fading.
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+
+ setEnabled(false);
+ }
+
+ public void effectsOff(Runnable runnableOnEffectsOff) {
+ for (Effect effect : effects) {
+ if (effect.on) {
+ effect.turnOff(runnableOnEffectsOff);
+ return;
+ }
+ }
+ // Just execute the runnable right away if all effects are already off.
+ if (runnableOnEffectsOff != null) {
+ runnableOnEffectsOff.run();
+ }
+ }
+
+ public boolean hasEffectOn() {
+ for (Effect effect : effects) {
+ if (effect.on) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private class Effect implements FilterAction.FilterActionListener {
+
+ private final FilterAction action;
+ private final CharSequence name;
+ private final IconIndicator button;
+ private boolean on;
+ private Runnable runnableOnODone;
+
+ public Effect(int effectId, FilterAction action) {
+ this.action = action;
+
+ View view = findViewById(effectId);
+ name = ((TextView) view.findViewById(R.id.effect_label)).getText();
+ button = (IconIndicator) view.findViewById(R.id.effect_button);
+ button.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isEnabled()) {
+ if (on) {
+ turnOff(null);
+ } else {
+ // Have other effects done turning off first and then turn on itself.
+ effectsOff(new Runnable() {
+
+ @Override
+ public void run() {
+ turnOn();
+ }
+ });
+ }
+ }
+ }
+ });
+ }
+
+ private void turnOn() {
+ effectName.setText(name);
+ button.setMode("on");
+ on = true;
+ action.begin(this);
+ }
+
+ private void turnOff(Runnable runnableOnODone) {
+ this.runnableOnODone = runnableOnODone;
+ action.end();
+ }
+
+ @Override
+ public void onDone() {
+ if (on) {
+ effectName.setText("");
+ button.setMode("off");
+ on = false;
+
+ if (runnableOnODone != null) {
+ runnableOnODone.run();
+ runnableOnODone = null;
+ }
+ }
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/EffectsGroup.java b/samples/PhotoEditor/src/com/android/photoeditor/EffectsGroup.java
new file mode 100644
index 0000000..7c1f9af
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/EffectsGroup.java
@@ -0,0 +1,123 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Groups effects in an accordion menu style that could either expand or fold effects.
+ */
+public class EffectsGroup extends LinearLayout {
+
+ private static final int ANIMATION_INTERVAL = 75;
+
+ private final Drawable downArrow;
+ private final Drawable rightArrow;
+
+ private boolean expandEffects;
+
+ public EffectsGroup(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ downArrow = context.getResources().getDrawable(R.drawable.arrow_down);
+ rightArrow = context.getResources().getDrawable(R.drawable.arrow_right);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ if (expandEffects) {
+ requestRectangleOnScreen(new Rect(0, 0, 0, getHeight()));
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ final ImageView arrow = (ImageView) findViewById(R.id.group_arrow);
+ final ViewGroup container = (ViewGroup) findViewById(R.id.grouped_effects);
+ final List<View> effects = new ArrayList<View>();
+ for (int i = 0; i < container.getChildCount(); i++) {
+ effects.add(container.getChildAt(i));
+ }
+
+ findViewById(R.id.group_header).setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ if (container.getVisibility() == VISIBLE) {
+ expandEffects = false;
+
+ int delay = 0;
+ for (int i = effects.size() - 1; i >= 0; i--) {
+ final View effect = effects.get(i);
+
+ postDelayed(new Runnable() {
+
+ @Override
+ public void run() {
+ effect.setVisibility(GONE);
+ }
+ }, delay);
+ delay += ANIMATION_INTERVAL;
+ }
+
+ postDelayed(new Runnable() {
+
+ @Override
+ public void run() {
+ container.setVisibility(GONE);
+ arrow.setImageDrawable(rightArrow);
+ }
+ }, delay - ANIMATION_INTERVAL);
+ } else {
+ expandEffects = true;
+
+ arrow.setImageDrawable(downArrow);
+ container.setVisibility(VISIBLE);
+
+ int delay = 0;
+ for (int i = 0; i < effects.size(); i++) {
+ final View effect = effects.get(i);
+
+ postDelayed(new Runnable() {
+
+ @Override
+ public void run() {
+ effect.setVisibility(VISIBLE);
+ }
+ }, delay);
+ delay += ANIMATION_INTERVAL;
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/FilterStack.java b/samples/PhotoEditor/src/com/android/photoeditor/FilterStack.java
new file mode 100644
index 0000000..9a9b8d7
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/FilterStack.java
@@ -0,0 +1,263 @@
+/*
+ * 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.photoeditor;
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+
+import com.android.photoeditor.filters.Filter;
+
+import java.util.Stack;
+import java.util.Vector;
+
+/**
+ * A stack of filters to be applied onto a photo.
+ */
+public class FilterStack {
+
+ /**
+ * Listener of stack changes.
+ */
+ public interface StackListener {
+
+ void onStackChanged(boolean canUndo, boolean canRedo);
+ }
+
+ private static class OutputMessageObj {
+ PhotoOutputCallback callback;
+ Photo result;
+ }
+
+ private static final int COPY_SOURCE = 1;
+ private static final int COPY_RESULT = 2;
+ private static final int SET_SOURCE = 3;
+ private static final int CLEAR_SOURCE = 4;
+ private static final int CLEAR_STACKS = 5;
+ private static final int PUSH_FILTER = 6;
+ private static final int UNDO = 7;
+ private static final int REDO = 8;
+ private static final int TOP_FILTER_CHANGE = 9;
+ private static final int OUTPUT = 10;
+
+ private final Stack<Filter> appliedStack = new Stack<Filter>();
+ private final Stack<Filter> redoStack = new Stack<Filter>();
+ private final Vector<Message> pendingMessages = new Vector<Message>();
+ private final Handler mainHandler;
+ private final Handler workerHandler;
+
+ // Use two photo buffers as in and out in turns to apply filters in the stack.
+ private final Photo[] buffers = new Photo[2];
+
+ private Photo source;
+ private StackListener stackListener;
+
+ public FilterStack() {
+ HandlerThread workerThread = new HandlerThread("FilterStackWorker");
+ workerThread.start();
+
+ mainHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OUTPUT:
+ OutputMessageObj obj = (OutputMessageObj) msg.obj;
+ obj.callback.onReady(obj.result);
+ break;
+ }
+ }
+ };
+ workerHandler = new Handler(workerThread.getLooper()) {
+
+ private void output(PhotoOutputCallback callback, Photo target) {
+ // Copy target photo in rgb-565 format to update photo-view or save.
+ OutputMessageObj obj = new OutputMessageObj();
+ obj.callback = callback;
+ obj.result = (target != null) ? target.copy(Bitmap.Config.RGB_565) : null;
+ mainHandler.sendMessage(mainHandler.obtainMessage(OUTPUT, obj));
+ }
+
+ private void clearBuffers() {
+ pendingMessages.clear();
+ workerHandler.removeMessages(TOP_FILTER_CHANGE);
+ mainHandler.removeMessages(OUTPUT);
+ for (int i = 0; i < buffers.length; i++) {
+ if (buffers[i] != null) {
+ buffers[i].clear();
+ buffers[i] = null;
+ }
+ }
+ }
+
+ private void reallocateBuffer(int target) {
+ int other = target ^ 1;
+ buffers[target] = Photo.create(Bitmap.createBitmap(
+ buffers[other].width(), buffers[other].height(), Bitmap.Config.ARGB_8888));
+ }
+
+ private void invalidate() {
+ // In/out buffers need redrawn by reloading source photo and re-applying filters.
+ clearBuffers();
+ buffers[0] = (source != null) ? source.copy(Bitmap.Config.ARGB_8888) : null;
+ if (buffers[0] != null) {
+ reallocateBuffer(1);
+
+ int out = 1;
+ for (Filter filter : appliedStack) {
+ runFilter(filter, out);
+ out = out ^ 1;
+ }
+ }
+ }
+
+ private void runFilter(Filter filter, int out) {
+ if ((buffers[0] != null) && (buffers[1] != null)) {
+ int in = out ^ 1;
+ filter.process(buffers[in], buffers[out]);
+ if (!buffers[in].matchDimension(buffers[out])) {
+ buffers[in].clear();
+ reallocateBuffer(in);
+ }
+ }
+ }
+
+ private int getOutBufferIndex() {
+ // buffers[0] and buffers[1] are swapped in turns as the in/out buffers for
+ // processing stacked filters. For example, the first filter reads buffer[0] and
+ // writes buffer[1]; the second filter then reads buffer[1] and writes buffer[0].
+ return appliedStack.size() % 2;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case COPY_SOURCE:
+ output((PhotoOutputCallback) msg.obj, source);
+ break;
+
+ case COPY_RESULT:
+ output((PhotoOutputCallback) msg.obj, buffers[getOutBufferIndex()]);
+ break;
+
+ case SET_SOURCE:
+ source = (Photo) msg.obj;
+ invalidate();
+ break;
+
+ case CLEAR_SOURCE:
+ if (source != null) {
+ source.clear();
+ source = null;
+ }
+ clearBuffers();
+ break;
+
+ case CLEAR_STACKS:
+ redoStack.clear();
+ appliedStack.clear();
+ break;
+
+ case PUSH_FILTER:
+ redoStack.clear();
+ appliedStack.push((Filter) msg.obj);
+ stackChanged();
+ break;
+
+ case UNDO:
+ if (!appliedStack.empty()) {
+ redoStack.push(appliedStack.pop());
+ stackChanged();
+ invalidate();
+ }
+ output((PhotoOutputCallback) msg.obj, buffers[getOutBufferIndex()]);
+ break;
+
+ case REDO:
+ if (!redoStack.empty()) {
+ Filter filter = redoStack.pop();
+ appliedStack.push(filter);
+ stackChanged();
+ runFilter(filter, getOutBufferIndex());
+ }
+ output((PhotoOutputCallback) msg.obj, buffers[getOutBufferIndex()]);
+ break;
+
+ case TOP_FILTER_CHANGE:
+ if (pendingMessages.remove(msg) && !appliedStack.empty()) {
+ int out = getOutBufferIndex();
+ runFilter(appliedStack.peek(), out);
+ output((PhotoOutputCallback) msg.obj, buffers[out]);
+ }
+ break;
+ }
+ }
+ };
+ }
+
+ public void getSourceCopy(PhotoOutputCallback callback) {
+ workerHandler.sendMessage(workerHandler.obtainMessage(COPY_SOURCE, callback));
+ }
+
+ public void getResultCopy(PhotoOutputCallback callback) {
+ workerHandler.sendMessage(workerHandler.obtainMessage(COPY_RESULT, callback));
+ }
+
+ public void setPhotoSource(Photo source) {
+ workerHandler.sendMessage(workerHandler.obtainMessage(SET_SOURCE, source));
+ }
+
+ public void clearPhotoSource() {
+ workerHandler.sendMessage(workerHandler.obtainMessage(CLEAR_SOURCE));
+ }
+
+ public void clearStacks() {
+ workerHandler.sendMessage(workerHandler.obtainMessage(CLEAR_STACKS));
+ }
+
+ public void pushFilter(Filter filter) {
+ workerHandler.sendMessage(workerHandler.obtainMessage(PUSH_FILTER, filter));
+ }
+
+ public void undo(PhotoOutputCallback callback) {
+ workerHandler.sendMessage(workerHandler.obtainMessage(UNDO, callback));
+ }
+
+ public void redo(PhotoOutputCallback callback) {
+ workerHandler.sendMessage(workerHandler.obtainMessage(REDO, callback));
+ }
+
+ public void topFilterChanged(PhotoOutputCallback callback) {
+ // Flush outdated top-filter messages before sending new ones.
+ Message msg = workerHandler.obtainMessage(TOP_FILTER_CHANGE, callback);
+ pendingMessages.clear();
+ pendingMessages.add(msg);
+ workerHandler.removeMessages(TOP_FILTER_CHANGE);
+ workerHandler.sendMessage(msg);
+ }
+
+ public synchronized void setStackListener(StackListener listener) {
+ stackListener = listener;
+ }
+
+ private synchronized void stackChanged() {
+ if (stackListener != null) {
+ stackListener.onStackChanged(!appliedStack.empty(), !redoStack.empty());
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/IconIndicator.java b/samples/PhotoEditor/src/com/android/photoeditor/IconIndicator.java
new file mode 100644
index 0000000..173e88e
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/IconIndicator.java
@@ -0,0 +1,80 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * Represents image icons/buttons having various modes.
+ */
+public class IconIndicator extends ImageView {
+
+ private Drawable[] mIcons;
+ private CharSequence[] mModes;
+
+ public IconIndicator(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ TypedArray a = context
+ .obtainStyledAttributes(attrs, R.styleable.IconIndicator, defStyle, 0);
+ Drawable icons[] = loadIcons(context.getResources(), a.getResourceId(
+ R.styleable.IconIndicator_icons, 0));
+ CharSequence modes[] = a.getTextArray(R.styleable.IconIndicator_modes);
+ a.recycle();
+
+ setModesAndIcons(modes, icons);
+ setImageDrawable(mIcons.length > 0 ? mIcons[0] : null);
+ }
+
+ public IconIndicator(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ private Drawable[] loadIcons(Resources resources, int iconsId) {
+ TypedArray array = resources.obtainTypedArray(iconsId);
+ int n = array.length();
+ Drawable drawable[] = new Drawable[n];
+ for (int i = 0; i < n; ++i) {
+ int id = array.getResourceId(i, 0);
+ drawable[i] = id == 0 ? null : resources.getDrawable(id);
+ }
+ array.recycle();
+ return drawable;
+ }
+
+ private void setModesAndIcons(CharSequence[] modes, Drawable icons[]) {
+ if (modes.length != icons.length || icons.length == 0) {
+ throw new IllegalArgumentException();
+ }
+ mIcons = icons;
+ mModes = modes;
+ }
+
+ public void setMode(String mode) {
+ for (int i = 0, n = mModes.length; i < n; ++i) {
+ if (mModes[i].equals(mode)) {
+ setImageDrawable(mIcons[i]);
+ return;
+ }
+ }
+ throw new IllegalArgumentException("unknown mode: " + mode);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/LoadScreennailTask.java b/samples/PhotoEditor/src/com/android/photoeditor/LoadScreennailTask.java
new file mode 100644
index 0000000..89ba0e2
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/LoadScreennailTask.java
@@ -0,0 +1,68 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.widget.Toast;
+
+/**
+ * Asynchronous task for loading source photo screennail.
+ */
+public class LoadScreennailTask extends AsyncTask<Uri, Void, Bitmap> {
+
+ /**
+ * Callback for the completed asynchronous task.
+ */
+ public interface Callback {
+
+ void onComplete(Bitmap bitmap);
+ }
+
+ // TODO: Support 1280x960 once OOM is fixed.
+ private static final int SCREENNAIL_WIDTH = 1024;
+ private static final int SCREENNAIL_HEIGHT = 768;
+
+ private final Context context;
+ private final Callback callback;
+
+ public LoadScreennailTask(Context context, Callback callback) {
+ this.context = context;
+ this.callback = callback;
+ }
+
+ /**
+ * The task should be executed with one given source photo uri.
+ */
+ @Override
+ protected Bitmap doInBackground(Uri... params) {
+ if (params[0] == null) {
+ return null;
+ }
+ return new BitmapUtils(context).getBitmap(params[0], SCREENNAIL_WIDTH, SCREENNAIL_HEIGHT);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (bitmap == null) {
+ Toast.makeText(context, R.string.loading_failure, Toast.LENGTH_SHORT).show();
+ }
+ callback.onComplete(bitmap);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/Photo.java b/samples/PhotoEditor/src/com/android/photoeditor/Photo.java
new file mode 100644
index 0000000..07898a9
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/Photo.java
@@ -0,0 +1,87 @@
+/*
+ * 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.photoeditor;
+
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+
+/**
+ * Photo that is used for editing/display and should be synchronized for concurrent access.
+ */
+public class Photo {
+
+ private Bitmap bitmap;
+
+ /**
+ * Factory method to ensure every Photo instance holds a non-null bitmap.
+ */
+ public static Photo create(Bitmap bitmap) {
+ return (bitmap != null) ? new Photo(bitmap) : null;
+ }
+
+ private Photo(Bitmap bitmap) {
+ this.bitmap = bitmap;
+ }
+
+ public Bitmap bitmap() {
+ return bitmap;
+ }
+
+ public Photo copy(Bitmap.Config config) {
+ Bitmap copy = bitmap.copy(config, true);
+ return (copy != null) ? new Photo(copy) : null;
+ }
+
+ public boolean matchDimension(Photo photo) {
+ return ((photo.width() == width()) && (photo.height() == height()));
+ }
+
+ public int width() {
+ return bitmap.getWidth();
+ }
+
+ public int height() {
+ return bitmap.getHeight();
+ }
+
+ public void transform(Matrix matrix) {
+ // Copy immutable transformed photo; no-op if it fails to ensure bitmap isn't assigned null.
+ Bitmap transformed = BitmapUtils.createBitmap(bitmap, matrix).copy(
+ bitmap.getConfig(), true);
+ if (transformed != null) {
+ bitmap.recycle();
+ bitmap = transformed;
+ }
+ }
+
+ public void crop(int left, int top, int width, int height) {
+ // Copy immutable cropped photo; no-op if it fails to ensure bitmap isn't assigned null.
+ Bitmap cropped = Bitmap.createBitmap(bitmap, left, top, width, height).copy(
+ bitmap.getConfig(), true);
+ if (cropped != null) {
+ bitmap.recycle();
+ bitmap = cropped;
+ }
+ }
+
+ /**
+ * Recycles bitmaps; this instance should not be used after its clear() is called.
+ */
+ public void clear() {
+ bitmap.recycle();
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/PhotoEditor.java b/samples/PhotoEditor/src/com/android/photoeditor/PhotoEditor.java
new file mode 100644
index 0000000..928f523
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/PhotoEditor.java
@@ -0,0 +1,157 @@
+/*
+ * 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.photoeditor;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.android.photoeditor.filters.ImageUtils;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * Main activity of the photo editor.
+ */
+public class PhotoEditor extends Activity {
+
+ private Toolbar toolbar;
+ private View backButton;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ {
+ // HACK: create faked view in order to read bitcode in resource
+ View view = new View(getApplication());
+ byte[] pgm;
+ int pgmLength;
+
+ // read bitcode in res
+ InputStream is = view.getResources().openRawResource(R.raw.libjni_photoeditor_portable);
+ try {
+ try {
+ pgm = new byte[1024];
+ pgmLength = 0;
+
+ while(true) {
+ int bytesLeft = pgm.length - pgmLength;
+ if (bytesLeft == 0) {
+ byte[] buf2 = new byte[pgm.length * 2];
+ System.arraycopy(pgm, 0, buf2, 0, pgm.length);
+ pgm = buf2;
+ bytesLeft = pgm.length - pgmLength;
+ }
+ int bytesRead = is.read(pgm, pgmLength, bytesLeft);
+ if (bytesRead <= 0) {
+ break;
+ }
+ pgmLength += bytesRead;
+ }
+ ImageUtils.init(pgm, pgmLength);
+ } finally {
+ is.close();
+ }
+ } catch(IOException e) {
+ throw new Resources.NotFoundException();
+ }
+ }
+
+ toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar.initialize();
+
+ final EffectsBar effectsBar = (EffectsBar) findViewById(R.id.effects_bar);
+ final View actionBar = findViewById(R.id.action_bar);
+ final View quickviewOn = findViewById(R.id.quickview_on_button);
+ backButton = findViewById(R.id.action_bar_back);
+ backButton.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (actionBar.isEnabled()) {
+ if (quickviewOn.getVisibility() == View.VISIBLE) {
+ quickviewOn.performClick();
+ } else if (effectsBar.hasEffectOn()) {
+ effectsBar.effectsOff(null);
+ } else {
+ tryRun(new Runnable() {
+
+ @Override
+ public void run() {
+ finish();
+ }
+ });
+ }
+ }
+ }
+ });
+
+ Intent intent = getIntent();
+ if (Intent.ACTION_EDIT.equalsIgnoreCase(intent.getAction()) && (intent.getData() != null)) {
+ toolbar.openPhoto(intent.getData());
+ }
+ }
+
+ 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() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ runnable.run();
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // no-op
+ }
+ });
+ builder.setMessage(R.string.save_photo).show();
+ return;
+ }
+
+ runnable.run();
+ }
+
+ @Override
+ public void onBackPressed() {
+ backButton.performClick();
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/PhotoOutputCallback.java b/samples/PhotoEditor/src/com/android/photoeditor/PhotoOutputCallback.java
new file mode 100644
index 0000000..75651f3
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/PhotoOutputCallback.java
@@ -0,0 +1,25 @@
+/*
+ * 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.photoeditor;
+
+/**
+ * Callback of photo output that will only be called in UI thread.
+ */
+public interface PhotoOutputCallback {
+
+ void onReady(Photo photo);
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/PhotoView.java b/samples/PhotoEditor/src/com/android/photoeditor/PhotoView.java
new file mode 100644
index 0000000..1853d7c
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/PhotoView.java
@@ -0,0 +1,205 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Animation;
+
+import com.android.photoeditor.animation.AnimationPair;
+
+/**
+ * Displays photo in the view. All its methods should be called from UI thread.
+ */
+public class PhotoView extends View {
+
+ private final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
+ private final Matrix displayMatrix = new Matrix();
+ private Photo photo;
+ private RectF clipBounds;
+ private AnimationPair transitions;
+
+ public PhotoView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (photo != null) {
+ canvas.save();
+ if (clipBounds != null) {
+ canvas.clipRect(clipBounds);
+ }
+ canvas.concat(displayMatrix);
+ canvas.drawBitmap(photo.bitmap(), 0, 0, paint);
+ canvas.restore();
+ }
+ }
+
+ /**
+ * Maps x and y to a percentage position relative to displayed photo.
+ */
+ public PointF mapPhotoPoint(float x, float y) {
+ if ((photo == null) || (photo.width() == 0) || (photo.height() == 0)) {
+ return new PointF();
+ }
+ float[] point = new float[] {x, y};
+ Matrix matrix = new Matrix();
+ displayMatrix.invert(matrix);
+ matrix.mapPoints(point);
+ return new PointF(point[0] / photo.width(), point[1] / photo.height());
+ }
+
+ public void mapPhotoPath(Path src, Path dst) {
+ // TODO: Use percentages representing paths for saving photo larger than previewed photo.
+ Matrix matrix = new Matrix();
+ displayMatrix.invert(matrix);
+ src.transform(matrix, dst);
+ }
+
+ public RectF getPhotoDisplayBounds() {
+ RectF bounds = getPhotoBounds();
+ displayMatrix.mapRect(bounds);
+ return bounds;
+ }
+
+ public RectF getPhotoBounds() {
+ return (photo != null) ? new RectF(0, 0, photo.width(), photo.height()) : new RectF();
+ }
+
+ /**
+ * Transforms display by replacing the display matrix of photo-view with the given matrix.
+ */
+ public void transformDisplay(Matrix matrix) {
+ RectF bounds = getPhotoBounds();
+ matrix.mapRect(bounds);
+ displayMatrix.set(matrix);
+ RectUtils.postCenterMatrix(bounds, this, displayMatrix);
+ invalidate();
+ }
+
+ public void clipPhoto(RectF bounds) {
+ clipBounds = bounds;
+ invalidate();
+ }
+
+ /**
+ * Updates the photo with animations (if any) and also updates photo display-matrix.
+ */
+ public void update(final Photo photo) {
+ if (transitions == null) {
+ setPhoto(photo);
+ invalidate();
+ } else if (getAnimation() != null) {
+ // Clear old running transitions.
+ clearTransitionAnimations();
+ setPhoto(photo);
+ invalidate();
+ } else {
+ // TODO: Use AnimationSet to chain two animations.
+ transitions.first().setAnimationListener(new Animation.AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(final Animation animation) {
+ post(new Runnable() {
+
+ @Override
+ public void run() {
+ if ((transitions != null) && (animation == transitions.first())) {
+ startAnimation(transitions.second());
+ }
+ }
+ });
+ }
+ });
+ transitions.second().setAnimationListener(new Animation.AnimationListener() {
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ setPhoto(photo);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ post(new Runnable() {
+
+ @Override
+ public void run() {
+ clearTransitionAnimations();
+ }
+ });
+ }
+ });
+ startAnimation(transitions.first());
+ }
+ }
+
+ private void setPhoto(Photo photo) {
+ if (this.photo != null) {
+ this.photo.clear();
+ this.photo = null;
+ }
+ this.photo = photo;
+
+ // Scale-down (if necessary) and center the photo for display.
+ displayMatrix.reset();
+ RectF bounds = getPhotoBounds();
+ float scale = RectUtils.getDisplayScale(bounds, this);
+ displayMatrix.setScale(scale, scale);
+ displayMatrix.mapRect(bounds);
+ RectUtils.postCenterMatrix(bounds, this, displayMatrix);
+ }
+
+ private void clearTransitionAnimations() {
+ if (transitions != null) {
+ transitions.first().setAnimationListener(null);
+ transitions.second().setAnimationListener(null);
+ transitions = null;
+ clearAnimation();
+ }
+ }
+
+ /**
+ * Sets transition animations in the next update() to transit out the current bitmap and
+ * transit in the new replacing one. Transition animations will be cleared once done.
+ */
+ public void setTransitionAnimations(AnimationPair transitions) {
+ clearTransitionAnimations();
+ this.transitions = transitions;
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/RectUtils.java b/samples/PhotoEditor/src/com/android/photoeditor/RectUtils.java
new file mode 100644
index 0000000..e3945ca
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/RectUtils.java
@@ -0,0 +1,88 @@
+/*
+ * 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.photoeditor;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.view.View;
+
+/**
+ * Utils for rectangles/bounds related calculations.
+ */
+public class RectUtils {
+
+ private static final float MATH_PI = (float) Math.PI;
+ private static final float DEGREES_TO_RADIAN = MATH_PI / 180.0f;
+
+ /**
+ * Gets straighten matrix for the given bounds and degrees.
+ */
+ public static void getStraightenMatrix(RectF bounds, float degrees, Matrix matrix) {
+ matrix.reset();
+ if ((degrees != 0) && !bounds.isEmpty()) {
+ float w = bounds.width() / 2;
+ float h = bounds.height() / 2;
+ float adjustAngle;
+ if ((degrees < 0 && w > h) || (degrees > 0 && w <= h)) {
+ // The top left point is the boundary.
+ adjustAngle = (float) Math.atan(h / -w) + MATH_PI + degrees * DEGREES_TO_RADIAN;
+ } else {
+ // The top right point is the boundary.
+ adjustAngle = (float) Math.atan(h / w) - MATH_PI + degrees * DEGREES_TO_RADIAN;
+ }
+ float radius = (float) Math.hypot(w, h);
+ float scaleX = (float) Math.abs(radius * Math.cos(adjustAngle)) / w;
+ float scaleY = (float) Math.abs(radius * Math.sin(adjustAngle)) / h;
+ float scale = Math.max(scaleX, scaleY);
+
+ postRotateMatrix(degrees, new RectF(bounds), matrix);
+ matrix.postScale(scale, scale);
+ }
+ }
+
+ /**
+ * Post rotates the matrix and bounds for the given bounds and degrees.
+ */
+ public static void postRotateMatrix(float degrees, RectF bounds, Matrix matrix) {
+ matrix.postRotate(degrees);
+ matrix.mapRect(bounds);
+ matrix.postTranslate(-bounds.left, -bounds.top);
+ }
+
+ /**
+ * Post translates the matrix to center the given bounds inside the view.
+ */
+ public static void postCenterMatrix(RectF contentBounds, View view, Matrix matrix) {
+ matrix.postTranslate((view.getWidth() - contentBounds.width()) / 2,
+ (view.getHeight() - contentBounds.height()) / 2);
+ }
+
+ /**
+ * Gets the proper scale value that scales down the content and keeps its aspect ratio to
+ * display inside the view.
+ */
+ public static float getDisplayScale(RectF contentBounds, View view) {
+ if (contentBounds.isEmpty()) {
+ return 1;
+ }
+
+ float scale = Math.min(view.getWidth() / contentBounds.width(),
+ view.getHeight() / contentBounds.height());
+ // Avoid scaling up the content.
+ return Math.min(scale, 1);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/SaveCopyTask.java b/samples/PhotoEditor/src/com/android/photoeditor/SaveCopyTask.java
new file mode 100644
index 0000000..3706600
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/SaveCopyTask.java
@@ -0,0 +1,142 @@
+/*
+ * 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.photoeditor;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap.CompressFormat;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Images.ImageColumns;
+import android.widget.Toast;
+
+import java.io.File;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+
+/**
+ * Asynchronous task for loading source photo in target dimensions and saving edits as a new copy.
+ */
+public class SaveCopyTask extends AsyncTask<Photo, Void, Uri> {
+
+ /**
+ * Callback for the completed asynchronous task.
+ */
+ public interface Callback {
+
+ void onComplete(Uri uri);
+ }
+
+ private static final String TIME_STAMP_NAME = "'IMG'_yyyyMMdd_HHmmss";
+ private static final int INDEX_DATE_TAKEN = 0;
+ private static final int INDEX_LATITUDE = 1;
+ private static final int INDEX_LONGITUDE = 2;
+
+ private static final String[] IMAGE_PROJECTION = new String[] {
+ ImageColumns.DATE_TAKEN,
+ ImageColumns.LATITUDE,
+ ImageColumns.LONGITUDE,
+ };
+
+ private final Context context;
+ private final Uri sourceUri;
+ private final Callback callback;
+ private final String saveFileName;
+
+ public SaveCopyTask(Context context, Uri sourceUri, Callback callback) {
+ this.context = context;
+ this.sourceUri = sourceUri;
+ this.callback = callback;
+
+ saveFileName = new SimpleDateFormat(TIME_STAMP_NAME).format(
+ new Date(System.currentTimeMillis()));
+ }
+
+ /**
+ * The task should be executed with one given photo to be saved.
+ */
+ @Override
+ protected Uri doInBackground(Photo... params) {
+ // TODO: Support larger dimensions for photo saving.
+ if (params[0] == null) {
+ return null;
+ }
+ Photo photo = params[0];
+ File file = save(photo);
+ Uri uri = (file != null) ? insertContent(file) : null;
+ photo.clear();
+ return uri;
+ }
+
+ @Override
+ protected void onPostExecute(Uri result) {
+ String message = (result == null) ? context.getString(R.string.saving_failure)
+ : context.getString(R.string.photo_saved, saveFileName);
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+
+ callback.onComplete(result);
+ }
+
+ private File save(Photo photo) {
+ String directory = Environment.getExternalStorageDirectory().toString() + "/"
+ + context.getString(R.string.edited_photo_bucket_name);
+
+ return new BitmapUtils(context).saveBitmap(
+ photo.bitmap(), directory, saveFileName, CompressFormat.JPEG);
+ }
+
+ /**
+ * Insert the content (saved file) with proper source photo properties.
+ */
+ private Uri insertContent(File file) {
+ long now = System.currentTimeMillis() / 1000;
+ long dateTaken = now;
+ double latitude = 0f;
+ double longitude = 0f;
+
+ ContentResolver contentResolver = context.getContentResolver();
+ Cursor cursor = contentResolver.query(
+ sourceUri, IMAGE_PROJECTION, null, null, null);
+ if ((cursor != null) && cursor.moveToNext()) {
+ dateTaken = cursor.getLong(INDEX_DATE_TAKEN);
+ latitude = cursor.getDouble(INDEX_LATITUDE);
+ longitude = cursor.getDouble(INDEX_LONGITUDE);
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Images.Media.TITLE, saveFileName);
+ values.put(Images.Media.DISPLAY_NAME, saveFileName);
+ values.put(Images.Media.MIME_TYPE, "image/jpeg");
+ values.put(Images.Media.DATE_TAKEN, dateTaken);
+ values.put(Images.Media.DATE_MODIFIED, now);
+ values.put(Images.Media.DATE_ADDED, now);
+ values.put(Images.Media.ORIENTATION, 0);
+ values.put(Images.Media.DATA, file.getAbsolutePath());
+ values.put(Images.Media.SIZE, file.length());
+
+ // TODO: Change || to && after the default location issue is fixed.
+ if ((latitude != 0f) || (longitude != 0f)) {
+ values.put(Images.Media.LATITUDE, latitude);
+ values.put(Images.Media.LONGITUDE, longitude);
+ }
+ return contentResolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/SpinnerProgressDialog.java b/samples/PhotoEditor/src/com/android/photoeditor/SpinnerProgressDialog.java
new file mode 100644
index 0000000..4e29298
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/SpinnerProgressDialog.java
@@ -0,0 +1,69 @@
+/*
+ * 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.photoeditor;
+
+import android.app.Dialog;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ProgressBar;
+
+/**
+ * Spinner model progress dialog that disables all tools for user interaction after it shows up and
+ * and re-enables them after it dismisses.
+ */
+public class SpinnerProgressDialog extends Dialog {
+
+ private final ViewGroup tools;
+
+ public static SpinnerProgressDialog show(ViewGroup tools) {
+ SpinnerProgressDialog dialog = new SpinnerProgressDialog(tools);
+ dialog.show();
+ return dialog;
+ }
+
+ private SpinnerProgressDialog(ViewGroup tools) {
+ super(tools.getContext(), R.style.SpinnerProgressDialog);
+
+ addContentView(new ProgressBar(tools.getContext()), new LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ this.tools = tools;
+ enableTools(false);
+ }
+
+ @Override
+ public void dismiss() {
+ super.dismiss();
+
+ enableTools(true);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ // Pass touch events to tools for killing idle even when the progress dialog is shown.
+ return tools.onInterceptTouchEvent(event);
+ }
+
+ private void enableTools(boolean enabled) {
+ for (int i = 0; i < tools.getChildCount(); i++) {
+ tools.getChildAt(i).setEnabled(enabled);
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/Toolbar.java b/samples/PhotoEditor/src/com/android/photoeditor/Toolbar.java
new file mode 100644
index 0000000..106cf75
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/Toolbar.java
@@ -0,0 +1,206 @@
+/*
+ * 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.photoeditor;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.RelativeLayout;
+
+import com.android.photoeditor.animation.FadeAnimation;
+import com.android.photoeditor.animation.Rotate3DAnimation;
+import com.android.photoeditor.animation.Rotate3DAnimation.Rotate;
+
+/**
+ * Toolbar that contains all tools and handles all operations for editing photo.
+ */
+public class Toolbar extends RelativeLayout {
+
+ private static final int UNDO_REDO_ANIMATION_DURATION = 100;
+ private static final int QUICKVIEW_ANIMATION_DURATION = 150;
+
+ private final FilterStack filterStack = new FilterStack();
+ private ToolbarLayoutHandler layoutHandler;
+ private ToolbarIdleHandler idleHandler;
+ private EffectsBar effectsBar;
+ private ActionBar actionBar;
+ private PhotoView photoView;
+ private SpinnerProgressDialog progressDialog;
+ private Uri sourceUri;
+
+ public Toolbar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ idleHandler.killIdle();
+ return false;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ layoutHandler.extraLayout(l, t, r, b);
+ }
+
+ public void initialize() {
+ photoView = (PhotoView) findViewById(R.id.photo_view);
+ initializeEffectsBar();
+ initializeActionBar();
+ layoutHandler = new ToolbarLayoutHandler(this);
+ idleHandler = new ToolbarIdleHandler(this);
+ idleHandler.killIdle();
+ }
+
+ private void initializeEffectsBar() {
+ effectsBar = (EffectsBar) findViewById(R.id.effects_bar);
+ effectsBar.initialize(filterStack, photoView, this);
+ }
+
+ private void initializeActionBar() {
+ final PhotoOutputCallback callback = new PhotoOutputCallback() {
+
+ @Override
+ public void onReady(Photo photo) {
+ photoView.setTransitionAnimations(
+ FadeAnimation.getFadeOutInAnimations(UNDO_REDO_ANIMATION_DURATION));
+ photoView.update(photo);
+ progressDialog.dismiss();
+ }
+ };
+
+ actionBar = (ActionBar) findViewById(R.id.action_bar);
+ actionBar.initialize(new ActionBar.ActionBarListener() {
+
+ @Override
+ public void onUndo() {
+ effectsBar.effectsOff(new Runnable() {
+
+ @Override
+ public void run() {
+ progressDialog = SpinnerProgressDialog.show(Toolbar.this);
+ filterStack.undo(callback);
+ }
+ });
+ }
+
+ @Override
+ public void onRedo() {
+ effectsBar.effectsOff(new Runnable() {
+
+ @Override
+ public void run() {
+ progressDialog = SpinnerProgressDialog.show(Toolbar.this);
+ filterStack.redo(callback);
+ }
+ });
+ }
+
+ @Override
+ public void onQuickview(final boolean on) {
+ final PhotoOutputCallback callback = new PhotoOutputCallback() {
+
+ @Override
+ public void onReady(Photo photo) {
+ photoView.setTransitionAnimations(Rotate3DAnimation.getFlipAnimations(
+ on ? Rotate.RIGHT : Rotate.LEFT, QUICKVIEW_ANIMATION_DURATION));
+ photoView.update(photo);
+ }
+ };
+
+ if (on) {
+ effectsBar.effectsOff(new Runnable() {
+
+ @Override
+ public void run() {
+ effectsBar.setVisibility(INVISIBLE);
+ filterStack.getSourceCopy(callback);
+ }
+ });
+ } else {
+ effectsBar.setVisibility(VISIBLE);
+ filterStack.getResultCopy(callback);
+ }
+ }
+
+ @Override
+ public void onSave() {
+ effectsBar.effectsOff(new Runnable() {
+
+ @Override
+ public void run() {
+ savePhoto(null);
+ }
+ });
+ }
+ });
+ }
+
+ public void openPhoto(Uri uri) {
+ sourceUri = uri;
+ filterStack.setStackListener(actionBar);
+
+ // clearPhotoSource() should be called before loading a new source photo to avoid OOM.
+ progressDialog = SpinnerProgressDialog.show(this);
+ filterStack.clearPhotoSource();
+ new LoadScreennailTask(getContext(), new LoadScreennailTask.Callback() {
+
+ @Override
+ public void onComplete(Bitmap bitmap) {
+ filterStack.setPhotoSource(Photo.create(bitmap));
+ filterStack.getResultCopy(new PhotoOutputCallback() {
+
+ @Override
+ public void onReady(Photo photo) {
+ photoView.update(photo);
+ progressDialog.dismiss();
+ effectsBar.setEnabled(photo != null);
+ }
+ });
+ }
+ }).execute(sourceUri);
+ }
+
+ /**
+ * Saves photo and executes runnable (if provided) after saving done.
+ */
+ public void savePhoto(final Runnable runnable) {
+ progressDialog = SpinnerProgressDialog.show(this);
+ filterStack.getResultCopy(new PhotoOutputCallback() {
+
+ @Override
+ public void onReady(Photo photo) {
+ 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(photo);
+ }
+ });
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/ToolbarIdleHandler.java b/samples/PhotoEditor/src/com/android/photoeditor/ToolbarIdleHandler.java
new file mode 100644
index 0000000..07b8f50
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/ToolbarIdleHandler.java
@@ -0,0 +1,102 @@
+/*
+ * 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.photoeditor;
+
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handler that controls idle/awake behaviors of toolbar's child views.
+ */
+class ToolbarIdleHandler {
+
+ private static final String IDLE_VIEW_TAG = "fadeOnIdle";
+ 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;
+
+ /**
+ * Constructor should only be invoked after toolbar has done inflation and added all its child
+ * views; then its child views could be found by findViewById calls.
+ */
+ public ToolbarIdleHandler(ViewGroup toolbar) {
+ mainHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MAKE_IDLE:
+ if (!idle) {
+ idle = true;
+ for (View view : childViews) {
+ makeIdle(view);
+ }
+ }
+ break;
+ }
+ }
+ };
+
+ fadeIn = AnimationUtils.loadAnimation(toolbar.getContext(), R.anim.fade_in);
+ fadeOut = AnimationUtils.loadAnimation(toolbar.getContext(), R.anim.fade_out);
+
+ for (int i = 0; i < toolbar.getChildCount(); i++) {
+ View view = toolbar.getChildAt(i);
+ String tag = (String) view.getTag();
+ if ((tag != null) && tag.equals(IDLE_VIEW_TAG)) {
+ childViews.add(view);
+ }
+ }
+ // Alpha animations don't work well on scroll-view; apply them on container linear-layout.
+ childViews.add(toolbar.findViewById(R.id.effects_container));
+ }
+
+ public void killIdle() {
+ mainHandler.removeMessages(MAKE_IDLE);
+ if (idle) {
+ idle = false;
+ for (View view : childViews) {
+ makeAwake(view);
+ }
+ }
+ mainHandler.sendEmptyMessageDelayed(MAKE_IDLE, TIMEOUT_IDLE);
+ }
+
+ private void makeAwake(View view) {
+ if (view.getVisibility() == View.VISIBLE) {
+ view.startAnimation(fadeIn);
+ }
+ }
+
+ private void makeIdle(View view) {
+ if (view.getVisibility() == View.VISIBLE) {
+ view.startAnimation(fadeOut);
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/ToolbarLayoutHandler.java b/samples/PhotoEditor/src/com/android/photoeditor/ToolbarLayoutHandler.java
new file mode 100644
index 0000000..93253d7
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/ToolbarLayoutHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.photoeditor;
+
+import android.view.View;
+
+/**
+ * Handler that adjusts layouts of toolbar's child views whose positions need being calculated
+ * according to the current screen dimensions.
+ */
+class ToolbarLayoutHandler {
+
+ private final View tools;
+
+ /**
+ * Constructor should only be invoked after toolbar has done inflation and added all its child
+ * views; then its child views could be found by findViewById calls.
+ */
+ public ToolbarLayoutHandler(View toolbar) {
+ this.tools = toolbar;
+ }
+
+ /**
+ * Layouts child tool views' positions that need being updated when toolbar is being layout.
+ */
+ public void extraLayout(int left, int top, int right, int bottom) {
+ // Wheels need being centered vertically.
+ int height = bottom - top;
+
+ View scaleWheel = tools.findViewById(R.id.scale_wheel);
+ scaleWheel.offsetTopAndBottom((height - scaleWheel.getHeight()) / 2);
+
+ View colorWheel = tools.findViewById(R.id.color_wheel);
+ colorWheel.offsetTopAndBottom((height - colorWheel.getHeight()) / 2);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/AutoFixAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/AutoFixAction.java
new file mode 100644
index 0000000..0c9f174
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/AutoFixAction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.AutoFixFilter;
+
+/**
+ * An action handling auto-fix effect.
+ */
+public class AutoFixAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0.5f;
+
+ public AutoFixAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final AutoFixFilter filter = new AutoFixFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setScale(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+
+ filter.setScale(DEFAULT_SCALE);
+ notifyFilterChanged(filter, true);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorPath.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorPath.java
new file mode 100644
index 0000000..1aedfb6
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorPath.java
@@ -0,0 +1,57 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+
+/**
+ * Colored path that could be painted on canvas.
+ */
+public class ColorPath {
+
+ private final int color;
+ private final Path path;
+
+ public ColorPath(int color, Path path) {
+ this.color = Color.argb(192, Color.red(color), Color.green(color), Color.blue(color));
+ this.path = path;
+ }
+
+ public Path path() {
+ return path;
+ }
+
+ public void draw(Canvas canvas, Paint paint) {
+ paint.setColor(color);
+ canvas.drawPath(path, paint);
+ }
+
+ /**
+ * Creates a paint to draw color paths.
+ */
+ public static Paint createPaint() {
+ Paint paint = new Paint(Paint.DITHER_FLAG | Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeJoin(Paint.Join.ROUND);
+ paint.setStrokeCap(Paint.Cap.ROUND);
+ paint.setStrokeWidth(12);
+ return paint;
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorTemperatureAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorTemperatureAction.java
new file mode 100644
index 0000000..db2850f
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorTemperatureAction.java
@@ -0,0 +1,57 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.ColorTemperatureFilter;
+
+/**
+ * An action handling color temperature effect.
+ */
+public class ColorTemperatureAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0.5f;
+
+ public ColorTemperatureAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final ColorTemperatureFilter filter = new ColorTemperatureFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setColorTemperature(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorWheel.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorWheel.java
new file mode 100644
index 0000000..7c7e715
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/ColorWheel.java
@@ -0,0 +1,330 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+
+import com.android.photoeditor.R;
+
+/**
+ * Wheel that has a draggable thumb to set and get the predefined color set.
+ */
+class ColorWheel extends View {
+
+ /**
+ * Listens to color changes.
+ */
+ public interface OnColorChangeListener {
+
+ void onColorChanged(int color, boolean fromUser);
+ }
+
+ private static final float MATH_PI = (float) Math.PI;
+ private static final float MATH_HALF_PI = MATH_PI / 2;
+
+ // All angles used in this object are defined between PI and -PI.
+ private static final float ANGLE_SPANNED = MATH_PI * 4 / 3;
+ private static final float ANGLE_BEGIN = ANGLE_SPANNED / 2.0f;
+ private static final float DEGREES_BEGIN = 360 - (float) Math.toDegrees(ANGLE_BEGIN);
+ private static final float STROKE_WIDTH = 3.0f;
+
+ private static final float THUMB_RADIUS_RATIO = 0.363f;
+ private static final float INNER_RADIUS_RATIO = 0.173f;
+
+ private static final int PADDING = 4;
+ private static final int COLOR_METER_THICKNESS = 18;
+
+ private final Drawable thumb;
+ private final Paint fillPaint;
+ private final Paint strokePaint;
+ private final int thumbSize;
+ private final int borderColor;
+ private final int[] colorsDefined;
+ private final float radiantInterval;
+ private Bitmap background;
+ private int thumbRadius;
+ private int innerRadius;
+ private int centerXY;
+ private int colorIndex;
+ private float angle;
+ private boolean dragThumb;
+ private OnColorChangeListener listener;
+
+ public ColorWheel(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ Resources resources = context.getResources();
+ thumbSize = (int) resources.getDimension(R.dimen.wheel_thumb_size);
+
+ // Set the number of total colors and compute the radiant interval between colors.
+ TypedArray colors = resources.obtainTypedArray(R.array.color_picker_wheel_colors);
+ colorsDefined = new int[colors.length()];
+ for (int c = 0; c < colors.length(); c++) {
+ colorsDefined[c] = colors.getColor(c, 0x000000);
+ }
+ colors.recycle();
+
+ radiantInterval = ANGLE_SPANNED / colorsDefined.length;
+
+ thumb = resources.getDrawable(R.drawable.wheel_knot_selector);
+ borderColor = resources.getColor(R.color.color_picker_border_color);
+
+ fillPaint = new Paint();
+ fillPaint.setAntiAlias(true);
+ fillPaint.setStyle(Paint.Style.FILL);
+ strokePaint = new Paint();
+ strokePaint.setAntiAlias(true);
+ strokePaint.setStrokeWidth(STROKE_WIDTH);
+ strokePaint.setStyle(Paint.Style.STROKE);
+ }
+
+ public void setColorIndex(int colorIndex) {
+ if (updateColorIndex(colorIndex, false)) {
+ updateThumbPositionByColorIndex();
+ }
+ }
+
+ public int getColor() {
+ return colorsDefined[colorIndex];
+ }
+
+ public void setOnColorChangeListener(OnColorChangeListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+
+ startAnimation(AnimationUtils.loadAnimation(getContext(),
+ (visibility == VISIBLE) ? R.anim.wheel_show : R.anim.wheel_hide));
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ int wheelSize = Math.min(w, h) - PADDING;
+ thumbRadius = (int) (wheelSize * THUMB_RADIUS_RATIO);
+ innerRadius = (int) (wheelSize * INNER_RADIUS_RATIO);
+
+ // The wheel would be centered at (centerXY, centerXY) and have outer-radius centerXY.
+ centerXY = wheelSize / 2;
+ updateThumbPositionByColorIndex();
+ }
+
+ private Bitmap prepareBackground() {
+ int diameter = centerXY * 2;
+ Bitmap bitmap = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ // the colors to be selected.
+ float radiantDegrees = (float) Math.toDegrees(radiantInterval);
+ RectF drawBound = new RectF(0, 0, diameter, diameter);
+ for (int c = 0; c < colorsDefined.length; c++) {
+ fillPaint.setColor(colorsDefined[c]);
+ canvas.drawArc(drawBound, DEGREES_BEGIN + radiantDegrees * c,
+ radiantDegrees, true, fillPaint);
+ }
+
+ // clear the inner area.
+ fillPaint.setColor(Color.BLACK);
+ fillPaint.setAlpha(160);
+ canvas.drawCircle(centerXY, centerXY, centerXY - COLOR_METER_THICKNESS, fillPaint);
+
+ // the border for the inner ball
+ fillPaint.setColor(borderColor);
+ canvas.drawCircle(centerXY, centerXY, innerRadius + STROKE_WIDTH, fillPaint);
+
+ return bitmap;
+ }
+
+ private void drawBackground(Canvas canvas) {
+ if (background == null) {
+ background = prepareBackground();
+ }
+ canvas.drawBitmap(background, 0, 0, fillPaint);
+ }
+
+ private void drawHighlighter(Canvas canvas) {
+ strokePaint.setColor(borderColor);
+ int diameter = centerXY * 2;
+ RectF drawBound = new RectF(0, 0, diameter, diameter);
+ float radiantDegrees = (float) Math.toDegrees(radiantInterval);
+ float startAngle = DEGREES_BEGIN + radiantDegrees * colorIndex;
+ canvas.drawArc(drawBound, startAngle, radiantDegrees, false, strokePaint);
+ drawBound.inset(COLOR_METER_THICKNESS, COLOR_METER_THICKNESS);
+ canvas.drawArc(drawBound, startAngle, radiantDegrees, false, strokePaint);
+
+ float lineAngle = ANGLE_BEGIN - radiantInterval * colorIndex;
+ float cosAngle = (float) Math.cos(lineAngle);
+ float sinAngle = (float) Math.sin(lineAngle);
+ int innerRadius = centerXY - COLOR_METER_THICKNESS;
+ canvas.drawLine(centerXY + centerXY * cosAngle, centerXY - centerXY * sinAngle,
+ centerXY + innerRadius * cosAngle,
+ centerXY - innerRadius * sinAngle, strokePaint);
+
+ lineAngle -= radiantInterval;
+ cosAngle = (float) Math.cos(lineAngle);
+ sinAngle = (float) Math.sin(lineAngle);
+ canvas.drawLine(centerXY + centerXY * cosAngle, centerXY - centerXY * sinAngle,
+ centerXY + innerRadius * cosAngle,
+ centerXY - innerRadius * sinAngle, strokePaint);
+ }
+
+ private void drawInnerCircle(Canvas canvas) {
+ fillPaint.setColor(colorsDefined[colorIndex]);
+ canvas.drawCircle(centerXY, centerXY, innerRadius, fillPaint);
+ }
+
+ private void drawThumb(Canvas canvas) {
+ int thumbX = (int) (thumbRadius * Math.cos(angle) + centerXY);
+ int thumbY = (int) (centerXY - thumbRadius * Math.sin(angle));
+ int halfSize = thumbSize / 2;
+ thumb.setBounds(thumbX - halfSize, thumbY - halfSize, thumbX + halfSize, thumbY + halfSize);
+ thumb.draw(canvas);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ drawBackground(canvas);
+ drawInnerCircle(canvas);
+ drawHighlighter(canvas);
+ drawThumb(canvas);
+ }
+
+ private boolean updateAngle(float x, float y) {
+ float angle;
+ if (x == 0) {
+ if (y >= 0) {
+ angle = MATH_HALF_PI;
+ } else {
+ angle = -MATH_HALF_PI;
+ }
+ } else {
+ angle = (float) Math.atan((double) y / x);
+ }
+
+ if (angle >= 0 && x < 0) {
+ angle = angle - MATH_PI;
+ } else if (angle < 0 && x < 0) {
+ angle = MATH_PI + angle;
+ }
+
+ if (angle > ANGLE_BEGIN || angle <= ANGLE_BEGIN - ANGLE_SPANNED) {
+ return false;
+ }
+
+ this.angle = angle;
+ return true;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ super.onTouchEvent(ev);
+
+ if (isEnabled()) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ updateThumbState(
+ isHittingThumbArea(ev.getX() - centerXY, centerXY - ev.getY()));
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float x = ev.getX() - centerXY;
+ final float y = centerXY - ev.getY();
+ if (!dragThumb && !updateThumbState(isHittingThumbArea(x, y))) {
+ // The thumb wasn't dragged and isn't being dragged, either.
+ break;
+ }
+
+ if (updateAngle(x, y)) {
+ int index = (int) ((ANGLE_BEGIN - angle) / radiantInterval);
+ if (updateColorIndex(index, true)) {
+ updateThumbPositionByColorIndex();
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ updateThumbState(false);
+ break;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if the user is hitting the correct thumb area.
+ */
+ private boolean isHittingThumbArea(float x, float y) {
+ final float radius = (float) Math.sqrt((x * x) + (y * y));
+ return (radius > innerRadius) && (radius < centerXY);
+ }
+
+
+ private boolean updateColorIndex(int index, boolean fromUser) {
+ if (index < 0 || index >= colorsDefined.length) {
+ return false;
+ }
+ if (colorIndex != index) {
+ colorIndex = index;
+
+ if (listener != null) {
+ listener.onColorChanged(colorsDefined[colorIndex], fromUser);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set the thumb position according to the selected color.
+ * The thumb will always be placed in the middle of the selected color.
+ */
+ private void updateThumbPositionByColorIndex() {
+ angle = ANGLE_BEGIN - (colorIndex + 0.5f) * radiantInterval;
+ invalidate();
+ }
+
+ private boolean updateThumbState(boolean dragThumb) {
+ if (this.dragThumb == dragThumb) {
+ // The state hasn't been changed; no need for updates.
+ return false;
+ }
+
+ this.dragThumb = dragThumb;
+ thumb.setState(dragThumb ? PRESSED_ENABLED_STATE_SET : ENABLED_STATE_SET);
+ invalidate();
+ return true;
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/CropAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/CropAction.java
new file mode 100644
index 0000000..5e6dd53
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/CropAction.java
@@ -0,0 +1,77 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.R;
+import com.android.photoeditor.filters.CropFilter;
+
+/**
+ * An action handling crop effect.
+ */
+public class CropAction extends FilterAction {
+
+ private static final float DEFAULT_CROP = 0.2f;
+ private CropFilter filter;
+
+ public CropAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools, R.string.crop_tooltip);
+ }
+
+ private RectF mapPhotoBounds(RectF bounds, RectF photoBounds) {
+ return new RectF((bounds.left - photoBounds.left) / photoBounds.width(),
+ (bounds.top - photoBounds.top) / photoBounds.height(),
+ (bounds.right - photoBounds.left) / photoBounds.width(),
+ (bounds.bottom - photoBounds.top) / photoBounds.height());
+ }
+
+ @Override
+ public void onBegin() {
+ filter = new CropFilter();
+
+ final RectF photoBounds = photoView.getPhotoDisplayBounds();
+ cropView.setPhotoBounds(new RectF(photoBounds));
+ cropView.setOnCropChangeListener(new CropView.OnCropChangeListener() {
+
+ @Override
+ public void onCropChanged(RectF bounds, boolean fromUser) {
+ if (fromUser) {
+ filter.setCropBounds(mapPhotoBounds(bounds, photoBounds));
+ notifyFilterChanged(filter, false);
+ }
+ }
+ });
+ RectF cropBounds = new RectF(photoBounds);
+ cropBounds.inset(photoBounds.width() * DEFAULT_CROP, photoBounds.height() * DEFAULT_CROP);
+ cropView.setCropBounds(cropBounds);
+ if (!cropView.fullPhotoCropped()) {
+ filter.setCropBounds(mapPhotoBounds(cropBounds, photoBounds));
+ notifyFilterChanged(filter, false);
+ }
+ cropView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ notifyFilterChanged(filter, true);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/CropView.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/CropView.java
new file mode 100644
index 0000000..6486c33
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/CropView.java
@@ -0,0 +1,330 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.RegionIterator;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.photoeditor.R;
+
+import java.util.Vector;
+
+/**
+ * A view that track touch motions and adjust crop bounds accordingly.
+ */
+class CropView extends View {
+
+ /**
+ * Listener of crop bounds.
+ */
+ public interface OnCropChangeListener {
+
+ void onCropChanged(RectF bounds, boolean fromUser);
+ }
+
+ private static final int TOUCH_AREA_NONE = 0;
+ private static final int TOUCH_AREA_LEFT = 1;
+ private static final int TOUCH_AREA_TOP = 2;
+ private static final int TOUCH_AREA_RIGHT = 4;
+ private static final int TOUCH_AREA_BOTTOM = 8;
+ private static final int TOUCH_AREA_INSIDE = 15;
+ private static final int TOUCH_AREA_OUTSIDE = 16;
+ private static final int TOUCH_AREA_TOP_LEFT = 3;
+ private static final int TOUCH_AREA_TOP_RIGHT = 6;
+ private static final int TOUCH_AREA_BOTTOM_LEFT = 9;
+ private static final int TOUCH_AREA_BOTTOM_RIGHT = 12;
+
+ private static final int BORDER_COLOR = 0xFF008AFF;
+ private static final int OUTER_COLOR = 0xA0000000;
+ private static final int INDICATION_COLOR = 0xFFCC9900;
+ private static final int TOUCH_AREA_SPAN = 20;
+ private static final int TOUCH_AREA_SPAN2 = TOUCH_AREA_SPAN * 2;
+ private static final float BORDER_WIDTH = 2.0f;
+
+ private final Paint outerAreaPaint;
+ private final Paint borderPaint;
+ private final Paint highlightPaint;
+
+ private final Drawable heightIndicator;
+ private final Drawable widthIndicator;
+ private final int indicatorSize;
+
+ private RectF cropBounds;
+ private RectF photoBounds;
+
+ private OnCropChangeListener listener;
+
+ private float lastX;
+ private float lastY;
+ private int currentTouchArea;
+
+ public CropView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ Resources resources = context.getResources();
+ heightIndicator = resources.getDrawable(R.drawable.crop_height_holo);
+ widthIndicator = resources.getDrawable(R.drawable.crop_width_holo);
+ indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
+
+ outerAreaPaint = new Paint();
+ outerAreaPaint.setStyle(Paint.Style.FILL);
+ outerAreaPaint.setColor(OUTER_COLOR);
+
+ borderPaint = new Paint();
+ borderPaint.setStyle(Paint.Style.STROKE);
+ borderPaint.setColor(BORDER_COLOR);
+ borderPaint.setStrokeWidth(BORDER_WIDTH);
+
+ highlightPaint = new Paint();
+ highlightPaint.setStyle(Paint.Style.STROKE);
+ highlightPaint.setColor(INDICATION_COLOR);
+ highlightPaint.setStrokeWidth(BORDER_WIDTH);
+
+ currentTouchArea = TOUCH_AREA_NONE;
+ }
+
+ public void setOnCropChangeListener(OnCropChangeListener listener) {
+ this.listener = listener;
+ }
+
+ private void notifyCropChange(boolean fromUser) {
+ if (listener != null) {
+ listener.onCropChanged(cropBounds, fromUser);
+ }
+ }
+
+ public void setCropBounds(RectF bounds) {
+ bounds.intersect(photoBounds);
+ cropBounds = bounds;
+ if (photoBounds.width() <= TOUCH_AREA_SPAN2) {
+ cropBounds.left = photoBounds.left;
+ cropBounds.right = photoBounds.right;
+ }
+ if (photoBounds.height() <= TOUCH_AREA_SPAN2) {
+ cropBounds.top = photoBounds.top;
+ cropBounds.bottom = photoBounds.bottom;
+ }
+ notifyCropChange(false);
+ invalidate();
+ }
+
+ /**
+ * Sets bounds to crop within.
+ */
+ public void setPhotoBounds(RectF bounds) {
+ photoBounds = bounds;
+ }
+
+ public boolean fullPhotoCropped() {
+ return cropBounds.contains(photoBounds);
+ }
+
+ private int detectTouchArea(float x, float y) {
+ RectF area = new RectF();
+ area.set(cropBounds);
+ area.inset(-TOUCH_AREA_SPAN, -TOUCH_AREA_SPAN);
+ if (!area.contains(x, y)) {
+ return TOUCH_AREA_OUTSIDE;
+ }
+
+ // left
+ area.set(cropBounds.left - TOUCH_AREA_SPAN, cropBounds.top + TOUCH_AREA_SPAN,
+ cropBounds.left + TOUCH_AREA_SPAN, cropBounds.bottom - TOUCH_AREA_SPAN);
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_LEFT;
+ }
+ // right
+ area.offset(cropBounds.width(), 0f);
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_RIGHT;
+ }
+ // top
+ area.set(cropBounds.left + TOUCH_AREA_SPAN, cropBounds.top - TOUCH_AREA_SPAN,
+ cropBounds.right - TOUCH_AREA_SPAN, cropBounds.top + TOUCH_AREA_SPAN);
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_TOP;
+ }
+ // bottom
+ area.offset(0f, cropBounds.height());
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_BOTTOM;
+ }
+ // top left
+ area.set(cropBounds.left - TOUCH_AREA_SPAN, cropBounds.top - TOUCH_AREA_SPAN,
+ cropBounds.left + TOUCH_AREA_SPAN, cropBounds.top + TOUCH_AREA_SPAN);
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_TOP_LEFT;
+ }
+ // top right
+ area.offset(cropBounds.width(), 0f);
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_TOP_RIGHT;
+ }
+ // bottom right
+ area.offset(0f, cropBounds.height());
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_BOTTOM_RIGHT;
+ }
+ // bottom left
+ area.offset(-cropBounds.width(), 0f);
+ if (area.contains(x, y)) {
+ return TOUCH_AREA_BOTTOM_LEFT;
+ }
+ return TOUCH_AREA_INSIDE;
+ }
+
+ private void performMove(float deltaX, float deltaY) {
+ if (currentTouchArea == TOUCH_AREA_INSIDE){ // moving the rect.
+ cropBounds.offset(deltaX, deltaY);
+ if (cropBounds.left < photoBounds.left) {
+ cropBounds.offset(photoBounds.left - cropBounds.left, 0f);
+ } else if (cropBounds.right > photoBounds.right) {
+ cropBounds.offset(photoBounds.right - cropBounds.right, 0f);
+ }
+ if (cropBounds.top < photoBounds.top) {
+ cropBounds.offset(0f, photoBounds.top - cropBounds.top);
+ } else if (cropBounds.bottom > photoBounds.bottom) {
+ cropBounds.offset(0f, photoBounds.bottom - cropBounds.bottom);
+ }
+ } else { // adjusting bounds.
+ if ((currentTouchArea & TOUCH_AREA_LEFT) != 0) {
+ cropBounds.left = Math.min(cropBounds.left + deltaX,
+ cropBounds.right - TOUCH_AREA_SPAN2);
+ }
+ if ((currentTouchArea & TOUCH_AREA_TOP) != 0) {
+ cropBounds.top = Math.min(cropBounds.top + deltaY,
+ cropBounds.bottom - TOUCH_AREA_SPAN2);
+ }
+ if ((currentTouchArea & TOUCH_AREA_RIGHT) != 0) {
+ cropBounds.right = Math.max(cropBounds.right + deltaX,
+ cropBounds.left + TOUCH_AREA_SPAN2);
+ }
+ if ((currentTouchArea & TOUCH_AREA_BOTTOM) != 0) {
+ cropBounds.bottom = Math.max(cropBounds.bottom + deltaY,
+ cropBounds.top + TOUCH_AREA_SPAN2);
+ }
+ cropBounds.intersect(photoBounds);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ if (!isEnabled()) {
+ return true;
+ }
+ float x = event.getX();
+ float y = event.getY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ currentTouchArea = detectTouchArea(x, y);
+ lastX = x;
+ lastY = y;
+ invalidate();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ performMove(x - lastX, y - lastY);
+
+ lastX = x;
+ lastY = y;
+ notifyCropChange(true);
+ invalidate();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ currentTouchArea = TOUCH_AREA_NONE;
+ invalidate();
+ break;
+ }
+ return true;
+ }
+
+ private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) {
+ int left = (int) centerX - indicatorSize / 2;
+ int top = (int) centerY - indicatorSize / 2;
+ int right = left + indicatorSize;
+ int bottom = top + indicatorSize;
+ indicator.setBounds(left, top, right, bottom);
+ indicator.draw(canvas);
+ }
+
+ private void drawIndicators(Canvas canvas) {
+ drawIndicator(canvas, heightIndicator, cropBounds.centerX(), cropBounds.top);
+ drawIndicator(canvas, heightIndicator, cropBounds.centerX(), cropBounds.bottom);
+ drawIndicator(canvas, widthIndicator, cropBounds.left, cropBounds.centerY());
+ drawIndicator(canvas, widthIndicator, cropBounds.right, cropBounds.centerY());
+ }
+
+ private void drawTouchHighlights(Canvas canvas) {
+ if ((currentTouchArea & TOUCH_AREA_TOP) != 0) {
+ canvas.drawLine(cropBounds.left, cropBounds.top, cropBounds.right, cropBounds.top,
+ highlightPaint);
+ }
+ if ((currentTouchArea & TOUCH_AREA_BOTTOM) != 0) {
+ canvas.drawLine(cropBounds.left, cropBounds.bottom, cropBounds.right,
+ cropBounds.bottom, highlightPaint);
+ }
+ if ((currentTouchArea & TOUCH_AREA_LEFT) != 0) {
+ canvas.drawLine(cropBounds.left, cropBounds.top, cropBounds.left, cropBounds.bottom,
+ highlightPaint);
+ }
+ if ((currentTouchArea & TOUCH_AREA_RIGHT) != 0) {
+ canvas.drawLine(cropBounds.right, cropBounds.top, cropBounds.right, cropBounds.bottom,
+ highlightPaint);
+ }
+ }
+
+ private void drawBounds(Canvas canvas) {
+ Rect r = new Rect();
+ photoBounds.roundOut(r);
+ Region drawRegion = new Region(r);
+ cropBounds.roundOut(r);
+ drawRegion.op(r, Region.Op.DIFFERENCE);
+ RegionIterator iter = new RegionIterator(drawRegion);
+ while (iter.next(r)) {
+ canvas.drawRect(r, outerAreaPaint);
+ }
+
+ canvas.drawRect(cropBounds, borderPaint);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ drawBounds(canvas);
+ if (currentTouchArea != TOUCH_AREA_NONE) {
+ drawTouchHighlights(canvas);
+ drawIndicators(canvas);
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/CrossProcessAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/CrossProcessAction.java
new file mode 100644
index 0000000..c4565ee
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/CrossProcessAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.CrossProcessFilter;
+
+/**
+ * An action handling cross-process effect.
+ */
+public class CrossProcessAction extends FilterAction {
+
+ public CrossProcessAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new CrossProcessFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/DocumentaryAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/DocumentaryAction.java
new file mode 100644
index 0000000..b8bd9c6
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/DocumentaryAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.DocumentaryFilter;
+
+/**
+ * An action handling the preset "Documentary" effect.
+ */
+public class DocumentaryAction extends FilterAction {
+
+ public DocumentaryAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new DocumentaryFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleAction.java
new file mode 100644
index 0000000..16cc37e
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleAction.java
@@ -0,0 +1,83 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.graphics.Path;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.R;
+import com.android.photoeditor.filters.DoodleFilter;
+
+/**
+ * An action handling doodle effect.
+ */
+public class DoodleAction extends FilterAction {
+
+ private static final int DEFAULT_COLOR_INDEX = 4;
+
+ private DoodleFilter filter;
+
+ public DoodleAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools, R.string.doodle_tooltip);
+ }
+
+ @Override
+ public void onBegin() {
+ filter = new DoodleFilter();
+
+ colorWheel.setOnColorChangeListener(new ColorWheel.OnColorChangeListener() {
+
+ @Override
+ public void onColorChanged(int color, boolean fromUser){
+ if (fromUser) {
+ doodleView.startPath(color);
+ filter.addPath(color);
+ }
+ }
+ });
+ colorWheel.setColorIndex(DEFAULT_COLOR_INDEX);
+ colorWheel.setVisibility(View.VISIBLE);
+
+ // Directly draw on doodle-view instead of waiting for top-filter output callback.
+ doodleView.setOnDoodleChangeListener(new DoodleView.OnDoodleChangeListener() {
+
+ private final Path transformPath = new Path();
+
+ @Override
+ public void onLastPathChanged(Path path) {
+ photoView.mapPhotoPath(path, transformPath);
+ filter.updateLastPath(transformPath);
+ notifyFilterChanged(filter, false);
+ }
+ });
+ doodleView.clear();
+ doodleView.clipBounds(photoView.getPhotoDisplayBounds());
+ doodleView.setVisibility(View.VISIBLE);
+
+ int color = colorWheel.getColor();
+ doodleView.startPath(color);
+
+ filter.addPath(color);
+ }
+
+ @Override
+ public void onEnd() {
+ notifyFilterChanged(filter, true);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleView.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleView.java
new file mode 100644
index 0000000..e975965
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/DoodleView.java
@@ -0,0 +1,138 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.Vector;
+
+/**
+ * A view that track touch motions as paths and paint them as doodles.
+ */
+class DoodleView extends View {
+
+ /**
+ * Listener of doodle paths.
+ */
+ public interface OnDoodleChangeListener {
+
+ void onLastPathChanged(Path path);
+ }
+
+ private final Vector<ColorPath> colorPaths = new Vector<ColorPath>();
+ private final Paint paint = ColorPath.createPaint();
+
+ private OnDoodleChangeListener listener;
+ private RectF clipBounds;
+ private float lastX;
+ private float lastY;
+
+ public DoodleView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setOnDoodleChangeListener(OnDoodleChangeListener listener) {
+ this.listener = listener;
+ }
+
+ public void startPath(int color) {
+ // Remove last empty path before adding a new path.
+ if (!colorPaths.isEmpty() && colorPaths.lastElement().path().isEmpty()) {
+ colorPaths.remove(colorPaths.size() - 1);
+ }
+ colorPaths.add(new ColorPath(color, new Path()));
+ colorPaths.lastElement().path().moveTo(lastX, lastY);
+ }
+
+ /**
+ * Clears clip bounds and paths drawn.
+ */
+ public void clear() {
+ colorPaths.clear();
+ clipBounds = null;
+ }
+
+ /**
+ * Clips bounds for paths being drawn.
+ */
+ public void clipBounds(RectF bounds) {
+ clipBounds = bounds;
+ }
+
+ private void pathUpdated(Path path) {
+ invalidate();
+ if (listener != null) {
+ listener.onLastPathChanged(path);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ if (isEnabled() && !colorPaths.isEmpty()) {
+ Path path = colorPaths.lastElement().path();
+ float x = event.getX();
+ float y = event.getY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ path.moveTo(x, y);
+ pathUpdated(path);
+ lastX = x;
+ lastY = y;
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ path.quadTo(lastX, lastY, (lastX + x) / 2, (lastY + y) / 2);
+ pathUpdated(path);
+ lastX = x;
+ lastY = y;
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // Line to last position with offset to draw at least dots for single clicks.
+ path.lineTo(lastX + 1, lastY + 1);
+ pathUpdated(path);
+ break;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.save();
+ if (clipBounds != null) {
+ canvas.clipRect(clipBounds);
+ }
+ for (ColorPath colorPath : colorPaths) {
+ colorPath.draw(canvas, paint);
+ }
+ canvas.restore();
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/DuotoneAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/DuotoneAction.java
new file mode 100644
index 0000000..80eaf0f
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/DuotoneAction.java
@@ -0,0 +1,48 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.DuotoneFilter;
+
+/**
+ * An action handling duo-tone effect.
+ */
+public class DuotoneAction extends FilterAction {
+
+ private static final int DEFAULT_FIRST_COLOR = 0x004488;
+ private static final int DEFAULT_SECOND_COLOR = 0xffff00;
+
+ public DuotoneAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ // TODO: Add several sets of duo-tone colors to select from.
+ DuotoneFilter filter = new DuotoneFilter();
+ filter.setDuotone(DEFAULT_FIRST_COLOR, DEFAULT_SECOND_COLOR);
+ notifyFilterChanged(filter, true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/FillLightAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/FillLightAction.java
new file mode 100644
index 0000000..60ddcd3
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/FillLightAction.java
@@ -0,0 +1,57 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.FillLightFilter;
+
+/**
+ * An action handling fill-light effect.
+ */
+public class FillLightAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0f;
+
+ public FillLightAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final FillLightFilter filter = new FillLightFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setBacklight(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/FilterAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/FilterAction.java
new file mode 100644
index 0000000..08c9fd2
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/FilterAction.java
@@ -0,0 +1,184 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.Photo;
+import com.android.photoeditor.PhotoOutputCallback;
+import com.android.photoeditor.PhotoView;
+import com.android.photoeditor.R;
+import com.android.photoeditor.SpinnerProgressDialog;
+import com.android.photoeditor.filters.Filter;
+
+/**
+ * An action binding UI controls and filter operation for editing photo.
+ */
+public abstract class FilterAction {
+
+ /**
+ * Listens to when this FilterAction has done editing and should be ended.
+ */
+ public interface FilterActionListener {
+
+ void onDone();
+ }
+
+ protected final FilterStack filterStack;
+ protected final PhotoView photoView;
+ protected final ScaleWheel scaleWheel;
+ protected final ColorWheel colorWheel;
+ protected final DoodleView doodleView;
+ protected final TouchView touchView;
+ protected final RotateView rotateView;
+ protected final CropView cropView;
+ private final ViewGroup tools;
+
+ private Toast tooltip;
+ private boolean pushedFilter;
+ private OutputCallback lastOutputCallback;
+ private FilterActionListener listener;
+
+ public FilterAction(FilterStack filterStack, ViewGroup tools) {
+ this.filterStack = filterStack;
+ this.tools = tools;
+
+ photoView = (PhotoView) tools.findViewById(R.id.photo_view);
+ scaleWheel = (ScaleWheel) tools.findViewById(R.id.scale_wheel);
+ colorWheel = (ColorWheel) tools.findViewById(R.id.color_wheel);
+ doodleView = (DoodleView) tools.findViewById(R.id.doodle_view);
+ touchView = (TouchView) tools.findViewById(R.id.touch_view);
+ rotateView = (RotateView) tools.findViewById(R.id.rotate_view);
+ cropView = (CropView) tools.findViewById(R.id.crop_view);
+ }
+
+ public FilterAction(FilterStack filterStack, ViewGroup tools, int tooltipId) {
+ this(filterStack, tools);
+
+ tooltip = Toast.makeText(tools.getContext(), tooltipId, Toast.LENGTH_SHORT);
+ }
+
+ protected void notifyFilterChanged(Filter filter, boolean output) {
+ if (!pushedFilter && filter.isValid()) {
+ filterStack.pushFilter(filter);
+ pushedFilter = true;
+ }
+ if (pushedFilter && output) {
+ // Notify the stack to output the changed top filter.
+ lastOutputCallback = new OutputCallback();
+ filterStack.topFilterChanged(lastOutputCallback);
+ }
+ }
+
+ public void begin(FilterActionListener listener) {
+ this.listener = listener;
+ if (tooltip != null) {
+ tooltip.show();
+ }
+ onBegin();
+ }
+
+ public void end() {
+ onEnd();
+
+ // Wait till last output callback is done before finishing.
+ if ((lastOutputCallback == null) || lastOutputCallback.done) {
+ finish();
+ } else {
+ final SpinnerProgressDialog progressDialog = SpinnerProgressDialog.show(tools);
+ lastOutputCallback.runnableOnDone = new Runnable() {
+
+ @Override
+ public void run() {
+ progressDialog.dismiss();
+ finish();
+ }
+ };
+ }
+ }
+
+ private void finish() {
+ // Close the tooltip if it's still showing.
+ if ((tooltip != null) && (tooltip.getView().getParent() != null)) {
+ tooltip.cancel();
+ }
+ if (scaleWheel.getVisibility() == View.VISIBLE) {
+ scaleWheel.setOnScaleChangeListener(null);
+ scaleWheel.setVisibility(View.INVISIBLE);
+ }
+ if (colorWheel.getVisibility() == View.VISIBLE) {
+ colorWheel.setOnColorChangeListener(null);
+ colorWheel.setVisibility(View.INVISIBLE);
+ }
+ if (doodleView.getVisibility() == View.VISIBLE) {
+ doodleView.setOnDoodleChangeListener(null);
+ doodleView.setVisibility(View.INVISIBLE);
+ }
+ if (touchView.getVisibility() == View.VISIBLE) {
+ touchView.setSingleTapListener(null);
+ touchView.setSwipeListener(null);
+ touchView.setVisibility(View.INVISIBLE);
+ }
+ if (rotateView.getVisibility() == View.VISIBLE) {
+ rotateView.setOnAngleChangeListener(null);
+ rotateView.setVisibility(View.INVISIBLE);
+ }
+ if (cropView.getVisibility() == View.VISIBLE) {
+ cropView.setOnCropChangeListener(null);
+ cropView.setVisibility(View.INVISIBLE);
+ }
+ photoView.clipPhoto(null);
+ // Notify the listener that this action is done finishing.
+ listener.onDone();
+ listener = null;
+ lastOutputCallback = null;
+ pushedFilter = false;
+ }
+
+ /**
+ * Called when the action is about to begin; subclasses should creates a specific filter and
+ * binds the filter to necessary UI controls here.
+ */
+ protected abstract void onBegin();
+
+ /**
+ * Called when the action is about to end; subclasses could do specific ending operations here.
+ */
+ protected abstract void onEnd();
+
+ /**
+ * Output callback for top filter changes.
+ */
+ private class OutputCallback implements PhotoOutputCallback {
+
+ private boolean done;
+ private Runnable runnableOnDone;
+
+ @Override
+ public void onReady(Photo photo) {
+ photoView.update(photo);
+ done = true;
+
+ if (runnableOnDone != null) {
+ runnableOnDone.run();
+ }
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/FisheyeAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/FisheyeAction.java
new file mode 100644
index 0000000..efd00e6
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/FisheyeAction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.FisheyeFilter;
+
+/**
+ * An action handling fisheye effect.
+ */
+public class FisheyeAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0.5f;
+
+ public FisheyeAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final FisheyeFilter filter = new FisheyeFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setScale(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+
+ filter.setScale(DEFAULT_SCALE);
+ notifyFilterChanged(filter, true);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/FlipAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/FlipAction.java
new file mode 100644
index 0000000..26402f7
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/FlipAction.java
@@ -0,0 +1,99 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.R;
+import com.android.photoeditor.animation.Rotate3DAnimation;
+import com.android.photoeditor.filters.FlipFilter;
+
+/**
+ * An action handling flip effect.
+ */
+public class FlipAction extends FilterAction {
+
+ private static final int ANIMATION_DURATION = 500;
+
+ private boolean flipHorizontal;
+ private boolean flipVertical;
+
+ public FlipAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools, R.string.flip_tooltip);
+ }
+
+ @Override
+ public void onBegin() {
+ final FlipFilter filter = new FlipFilter();
+
+ touchView.setSwipeListener(new TouchView.SwipeListener() {
+
+ @Override
+ public void onSwipeDown() {
+ setFlipAnimations(Rotate3DAnimation.Rotate.DOWN);
+ flipFilterVertically(filter);
+ }
+
+ @Override
+ public void onSwipeLeft() {
+ setFlipAnimations(Rotate3DAnimation.Rotate.LEFT);
+ flipFilterHorizontally(filter);
+ }
+
+ @Override
+ public void onSwipeRight() {
+ setFlipAnimations(Rotate3DAnimation.Rotate.RIGHT);
+ flipFilterHorizontally(filter);
+ }
+
+ @Override
+ public void onSwipeUp() {
+ setFlipAnimations(Rotate3DAnimation.Rotate.UP);
+ flipFilterVertically(filter);
+ }
+ });
+ touchView.setVisibility(View.VISIBLE);
+
+ flipHorizontal = false;
+ flipVertical = false;
+ setFlipAnimations(Rotate3DAnimation.Rotate.RIGHT);
+ flipFilterHorizontally(filter);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+
+ private void setFlipAnimations(Rotate3DAnimation.Rotate rotate) {
+ photoView.setTransitionAnimations(
+ Rotate3DAnimation.getFlipAnimations(rotate, ANIMATION_DURATION));
+ }
+
+ private void flipFilterHorizontally(final FlipFilter filter) {
+ flipHorizontal = !flipHorizontal;
+ filter.setFlip(flipHorizontal, flipVertical);
+ notifyFilterChanged(filter, true);
+ }
+
+ private void flipFilterVertically(final FlipFilter filter) {
+ flipVertical = !flipVertical;
+ filter.setFlip(flipHorizontal, flipVertical);
+ notifyFilterChanged(filter, true);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/GrainAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/GrainAction.java
new file mode 100644
index 0000000..a841d5d
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/GrainAction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.GrainFilter;
+
+/**
+ * An action handling the film-grain effect.
+ */
+public class GrainAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0.5f;
+
+ public GrainAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final GrainFilter filter = new GrainFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setScale(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+
+ filter.setScale(DEFAULT_SCALE);
+ notifyFilterChanged(filter, true);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/GrayscaleAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/GrayscaleAction.java
new file mode 100644
index 0000000..aa13f58
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/GrayscaleAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.GrayscaleFilter;
+
+/**
+ * An action handling grayscale effect.
+ */
+public class GrayscaleAction extends FilterAction {
+
+ public GrayscaleAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new GrayscaleFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/HighlightAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/HighlightAction.java
new file mode 100644
index 0000000..a5cc9d9
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/HighlightAction.java
@@ -0,0 +1,57 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.HighlightFilter;
+
+/**
+ * An action handling highlight effect.
+ */
+public class HighlightAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0f;
+
+ public HighlightAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final HighlightFilter filter = new HighlightFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setHighlight(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/LomoishAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/LomoishAction.java
new file mode 100644
index 0000000..0642023
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/LomoishAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.LomoishFilter;
+
+/**
+ * An action handling the preset "Lomo-ish" effect.
+ */
+public class LomoishAction extends FilterAction {
+
+ public LomoishAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new LomoishFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/NegativeAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/NegativeAction.java
new file mode 100644
index 0000000..421d5cc
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/NegativeAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.NegativeFilter;
+
+/**
+ * An action handling negative effect.
+ */
+public class NegativeAction extends FilterAction {
+
+ public NegativeAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new NegativeFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/PosterizeAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/PosterizeAction.java
new file mode 100644
index 0000000..e1c0528
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/PosterizeAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.PosterizeFilter;
+
+/**
+ * An action handling the "Posterize" effect.
+ */
+public class PosterizeAction extends FilterAction {
+
+ public PosterizeAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new PosterizeFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/RedEyeAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/RedEyeAction.java
new file mode 100644
index 0000000..9a36adb
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/RedEyeAction.java
@@ -0,0 +1,53 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.R;
+import com.android.photoeditor.filters.RedEyeFilter;
+
+/**
+ * An action handling red-eye removal.
+ */
+public class RedEyeAction extends FilterAction {
+
+ public RedEyeAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools, R.string.redeye_tooltip);
+ }
+
+ @Override
+ public void onBegin() {
+ final RedEyeFilter filter = new RedEyeFilter();
+
+ touchView.setSingleTapListener(new TouchView.SingleTapListener() {
+
+ @Override
+ public void onSingleTap(float x, float y) {
+ filter.addRedEyePosition(photoView.mapPhotoPoint(x, y));
+ notifyFilterChanged(filter, true);
+ }
+ });
+ touchView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/RotateAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/RotateAction.java
new file mode 100644
index 0000000..a04f9d3
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/RotateAction.java
@@ -0,0 +1,115 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.R;
+import com.android.photoeditor.RectUtils;
+import com.android.photoeditor.filters.RotateFilter;
+
+/**
+ * An action handling rotate effect.
+ */
+public class RotateAction extends FilterAction {
+
+ private static final float DEFAULT_ANGLE = 0.0f;
+ private static final float DEFAULT_ROTATE_SPAN = 360.0f;
+
+ private RotateFilter filter;
+ private float rotateDegrees;
+
+ public RotateAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools, R.string.rotate_tooltip);
+ }
+
+ @Override
+ public void onBegin() {
+ filter = new RotateFilter();
+ rotateDegrees = 0;
+
+ final Matrix matrix = new Matrix();
+ final RectF rotateBounds = new RectF();
+ final RectF photoBounds = photoView.getPhotoBounds();
+
+ // Directly transform photo-view instead of waiting for top-filter output callback.
+ rotateView.setOnAngleChangeListener(new RotateView.OnRotateChangeListener() {
+
+ @Override
+ public void onAngleChanged(float degrees, boolean fromUser){
+ if (fromUser) {
+ rotateDegrees = degrees;
+ filter.setAngle(degrees);
+ notifyFilterChanged(filter, false);
+ transformPhotoView(degrees);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch() {
+ // no-op
+ }
+
+ @Override
+ public void onStopTrackingTouch() {
+ if (roundFilterRotationDegrees()) {
+ notifyFilterChanged(filter, false);
+ transformPhotoView(rotateDegrees);
+ rotateView.setRotatedAngle(rotateDegrees);
+ }
+ }
+
+ private void transformPhotoView(float degrees) {
+ matrix.reset();
+ rotateBounds.set(photoBounds);
+ RectUtils.postRotateMatrix(degrees, rotateBounds, matrix);
+ float scale = RectUtils.getDisplayScale(rotateBounds, photoView);
+ matrix.postScale(scale, scale);
+ photoView.transformDisplay(matrix);
+ }
+ });
+ rotateView.setGridBounds(null);
+ rotateView.setRotatedAngle(DEFAULT_ANGLE);
+ rotateView.setRotateSpan(DEFAULT_ROTATE_SPAN);
+ rotateView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ // Round the current rotation degrees in case rotation tracking has not stopped yet.
+ roundFilterRotationDegrees();
+ notifyFilterChanged(filter, true);
+ }
+
+ /**
+ * Rounds filter rotation degrees to multiples of 90 degrees.
+ *
+ * @return true if the rotation degrees has been changed.
+ */
+ private boolean roundFilterRotationDegrees() {
+ if (rotateDegrees % 90 != 0) {
+ rotateDegrees = Math.round(rotateDegrees / 90) * 90;
+ filter.setAngle(rotateDegrees);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/RotateView.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/RotateView.java
new file mode 100644
index 0000000..b0cea16
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/RotateView.java
@@ -0,0 +1,222 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * View that shows grids and handles touch-events to adjust angle of rotation.
+ */
+class RotateView extends View {
+
+ /**
+ * Listens to rotate changes.
+ */
+ public interface OnRotateChangeListener {
+
+ void onAngleChanged(float degrees, boolean fromUser);
+
+ void onStartTrackingTouch();
+
+ void onStopTrackingTouch();
+ }
+
+ // All angles used are defined between PI and -PI.
+ private static final float MATH_PI = (float) Math.PI;
+ private static final float MATH_HALF_PI = MATH_PI / 2;
+ private static final float RADIAN_TO_DEGREE = 180f / MATH_PI;
+
+ private final Paint dashStrokePaint;
+ private final Path grids = new Path();
+ private final Path referenceLine = new Path();
+ private final RectF referenceLineBounds = new RectF();
+
+ private OnRotateChangeListener listener;
+ private int centerX;
+ private int centerY;
+ private float maxRotatedAngle;
+ private float minRotatedAngle;
+ private float currentRotatedAngle;
+ private float lastRotatedAngle;
+ private float touchStartAngle;
+
+ public RotateView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ dashStrokePaint = new Paint();
+ dashStrokePaint.setAntiAlias(true);
+ dashStrokePaint.setStyle(Paint.Style.STROKE);
+ dashStrokePaint.setPathEffect(new DashPathEffect(new float[] {15.0f, 5.0f}, 1.0f));
+ }
+
+ public void setRotatedAngle(float degrees) {
+ currentRotatedAngle = -degrees / RADIAN_TO_DEGREE;
+ notifyAngleChange(false);
+ }
+
+ /**
+ * Sets allowed degrees for rotation span before rotating the view.
+ */
+ public void setRotateSpan(float degrees) {
+ if (degrees >= 360f) {
+ maxRotatedAngle = Float.POSITIVE_INFINITY;
+ } else {
+ maxRotatedAngle = (degrees / RADIAN_TO_DEGREE) / 2;
+ }
+ minRotatedAngle = -maxRotatedAngle;
+ }
+
+ /**
+ * Sets grid bounds to be drawn or null to hide grids right before the view is visible.
+ */
+ public void setGridBounds(RectF bounds) {
+ grids.reset();
+ referenceLine.reset();
+ if (bounds != null) {
+ float delta = bounds.width() / 4.0f;
+ for (float x = bounds.left + delta; x < bounds.right; x += delta) {
+ grids.moveTo(x, bounds.top);
+ grids.lineTo(x, bounds.bottom);
+ }
+ delta = bounds.height() / 4.0f;
+ for (float y = bounds.top + delta; y < bounds.bottom; y += delta) {
+ grids.moveTo(bounds.left, y);
+ grids.lineTo(bounds.right, y);
+ }
+
+ // Make reference line long enough to cross the bounds diagonally after being rotated.
+ referenceLineBounds.set(bounds);
+ float radius = (float) Math.hypot(centerX, centerY);
+ delta = radius - centerX;
+ referenceLine.moveTo(-delta, centerY);
+ referenceLine.lineTo(getWidth() + delta, centerY);
+
+ delta = radius - centerY;
+ referenceLine.moveTo(centerX, -delta);
+ referenceLine.lineTo(centerX, getHeight() + delta);
+ }
+ invalidate();
+ }
+
+ public void setOnAngleChangeListener(OnRotateChangeListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ centerX = w / 2;
+ centerY = h / 2;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (!grids.isEmpty()) {
+ dashStrokePaint.setStrokeWidth(2f);
+ dashStrokePaint.setColor(0x99CCCCCC);
+ canvas.drawPath(grids, dashStrokePaint);
+ }
+
+ if (!referenceLine.isEmpty()) {
+ dashStrokePaint.setStrokeWidth(2f);
+ dashStrokePaint.setColor(0x99FFCC77);
+ canvas.save();
+ canvas.clipRect(referenceLineBounds);
+ canvas.rotate(-currentRotatedAngle * RADIAN_TO_DEGREE, centerX, centerY);
+ canvas.drawPath(referenceLine, dashStrokePaint);
+ canvas.restore();
+ }
+ }
+
+ private float calculateAngle(MotionEvent ev) {
+ float x = ev.getX() - centerX;
+ float y = centerY - ev.getY();
+
+ float angle;
+ if (x == 0) {
+ angle = (y >= 0) ? MATH_HALF_PI : -MATH_HALF_PI;
+ } else {
+ angle = (float) Math.atan(y / x);
+ }
+
+ if ((angle >= 0) && (x < 0)) {
+ angle = angle - MATH_PI;
+ } else if ((angle < 0) && (x < 0)) {
+ angle = MATH_PI + angle;
+ }
+ return angle;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ super.onTouchEvent(ev);
+
+ if (isEnabled()) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ lastRotatedAngle = currentRotatedAngle;
+ touchStartAngle = calculateAngle(ev);
+
+ if (listener != null) {
+ listener.onStartTrackingTouch();
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ float touchAngle = calculateAngle(ev);
+ float rotatedAngle = touchAngle - touchStartAngle + lastRotatedAngle;
+
+ if ((rotatedAngle > maxRotatedAngle) || (rotatedAngle < minRotatedAngle)) {
+ // Angles are out of range; restart rotating.
+ // TODO: Fix discontinuity around boundary.
+ lastRotatedAngle = currentRotatedAngle;
+ touchStartAngle = touchAngle;
+ } else {
+ currentRotatedAngle = rotatedAngle;
+ notifyAngleChange(true);
+ invalidate();
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (listener != null) {
+ listener.onStopTrackingTouch();
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ private void notifyAngleChange(boolean fromUser) {
+ if (listener != null) {
+ listener.onAngleChanged(-currentRotatedAngle * RADIAN_TO_DEGREE, fromUser);
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/SaturationAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/SaturationAction.java
new file mode 100644
index 0000000..aad4178
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/SaturationAction.java
@@ -0,0 +1,57 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.SaturationFilter;
+
+/**
+ * An action handling saturation effect.
+ */
+public class SaturationAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0.5f;
+
+ public SaturationAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final SaturationFilter filter = new SaturationFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setSaturation(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/ScaleWheel.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/ScaleWheel.java
new file mode 100644
index 0000000..fd25c87
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/ScaleWheel.java
@@ -0,0 +1,226 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+
+import com.android.photoeditor.R;
+
+/**
+ * Wheel that has a draggable thumb to set and get the normalized scale value from 0 to 1.
+ */
+class ScaleWheel extends View {
+
+ /**
+ * Listens to scale changes.
+ */
+ public interface OnScaleChangeListener {
+
+ void onProgressChanged(float progress, boolean fromUser);
+ }
+
+ private static final float MATH_PI = (float) Math.PI;
+ private static final float MATH_HALF_PI = MATH_PI / 2;
+
+ // Angles are defined between PI and -PI.
+ private static final float ANGLE_SPANNED = MATH_PI * 4 / 3;
+ private static final float ANGLE_BEGIN = ANGLE_SPANNED / 2.0f;
+
+ private static final float THUMB_RADIUS_RATIO = 0.363f;
+ private static final float INNER_RADIUS_RATIO = 0.24f;
+
+ private final Drawable thumb;
+ private final Drawable background;
+ private final int thumbSize;
+ private final Paint circlePaint;
+ private final int maxProgress;
+ private final float radiantInterval;
+ private int thumbRadius;
+ private int innerRadius;
+ private int centerXY;
+ private float angle;
+ private int progress;
+ private boolean dragThumb;
+ private OnScaleChangeListener listener;
+
+ public ScaleWheel(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ Resources resources = context.getResources();
+ thumbSize = (int) resources.getDimension(R.dimen.wheel_thumb_size);
+
+ // Set the maximum progress and compute the radiant interval between progress values.
+ maxProgress = 100;
+ radiantInterval = ANGLE_SPANNED / maxProgress;
+
+ thumb = resources.getDrawable(R.drawable.wheel_knot_selector);
+ background = resources.getDrawable(R.drawable.scale_wheel_background);
+ background.setAlpha(160);
+
+ circlePaint = new Paint();
+ circlePaint.setAntiAlias(true);
+ circlePaint.setColor(resources.getColor(R.color.scale_wheel_interior_color));
+ }
+
+ public void setProgress(float progress) {
+ if (updateProgress((int) (progress * maxProgress), false)) {
+ updateThumbPositionByProgress();
+ }
+ }
+
+ public void setOnScaleChangeListener(OnScaleChangeListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+
+ startAnimation(AnimationUtils.loadAnimation(getContext(),
+ (visibility == VISIBLE) ? R.anim.wheel_show : R.anim.wheel_hide));
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ int wheelSize = Math.min(w, h);
+ thumbRadius = (int) (wheelSize * THUMB_RADIUS_RATIO);
+ innerRadius = (int) (wheelSize * INNER_RADIUS_RATIO);
+
+ // The wheel would be centered at (centerXY, centerXY) and have outer-radius centerXY.
+ centerXY = wheelSize / 2;
+ updateThumbPositionByProgress();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ background.setBounds(0, 0, getWidth(), getHeight());
+ background.draw(canvas);
+
+ int thumbX = (int) (thumbRadius * Math.cos(angle) + centerXY);
+ int thumbY = (int) (centerXY - thumbRadius * Math.sin(angle));
+ int halfSize = thumbSize / 2;
+ thumb.setBounds(thumbX - halfSize, thumbY - halfSize, thumbX + halfSize, thumbY + halfSize);
+ thumb.draw(canvas);
+
+ float radius = (progress * innerRadius) / maxProgress;
+ canvas.drawCircle(centerXY, centerXY, radius, circlePaint);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ super.onTouchEvent(ev);
+
+ if (isEnabled()) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ updateThumbState(
+ isHittingThumbArea(ev.getX() - centerXY, centerXY - ev.getY()));
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ float x = ev.getX() - centerXY;
+ float y = centerXY - ev.getY();
+ if (!dragThumb && !updateThumbState(isHittingThumbArea(x, y))) {
+ // The thumb wasn't dragged and isn't being dragged, either.
+ break;
+ }
+
+ if (updateAngle(x, y)) {
+ int progress = (int) ((ANGLE_BEGIN - angle) / radiantInterval);
+ if (updateProgress(progress, true)) {
+ invalidate();
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ updateThumbState(false);
+ break;
+ }
+ }
+ return true;
+ }
+
+ private boolean isHittingThumbArea(float x, float y) {
+ double radius = Math.sqrt((x * x) + (y * y));
+ return (radius > innerRadius) && (radius < centerXY);
+ }
+
+ private boolean updateAngle(float x, float y) {
+ float angle;
+ if (x == 0) {
+ angle = (y >= 0) ? MATH_HALF_PI : -MATH_HALF_PI;
+ } else {
+ angle = (float) Math.atan(y / x);
+ }
+
+ if ((angle >= 0) && (x < 0)) {
+ angle = angle - MATH_PI;
+ } else if ((angle < 0) && (x < 0)) {
+ angle = MATH_PI + angle;
+ }
+
+ if ((angle > ANGLE_BEGIN) || (angle <= ANGLE_BEGIN - ANGLE_SPANNED)) {
+ return false;
+ }
+
+ this.angle = angle;
+ return true;
+ }
+
+ private boolean updateProgress(int progress, boolean fromUser) {
+ if ((this.progress != progress) && (progress >= 0) && (progress <= maxProgress)) {
+ this.progress = progress;
+
+ if (listener != null) {
+ listener.onProgressChanged((float) progress / maxProgress, fromUser);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void updateThumbPositionByProgress() {
+ angle = ANGLE_BEGIN - progress * radiantInterval;
+ invalidate();
+ }
+
+ private boolean updateThumbState(boolean dragThumb) {
+ if (this.dragThumb == dragThumb) {
+ // The state hasn't been changed; no need for updates.
+ return false;
+ }
+
+ this.dragThumb = dragThumb;
+ thumb.setState(dragThumb ? PRESSED_ENABLED_STATE_SET : ENABLED_STATE_SET);
+ invalidate();
+ return true;
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/SepiaAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/SepiaAction.java
new file mode 100644
index 0000000..26eea2e
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/SepiaAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.SepiaFilter;
+
+/**
+ * An action handling sepia effect.
+ */
+public class SepiaAction extends FilterAction {
+
+ public SepiaAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new SepiaFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/ShadowAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/ShadowAction.java
new file mode 100644
index 0000000..b159ba5
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/ShadowAction.java
@@ -0,0 +1,57 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.ShadowFilter;
+
+/**
+ * An action handling shadow effect.
+ */
+public class ShadowAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0f;
+
+ public ShadowAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final ShadowFilter filter = new ShadowFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setShadow(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/SharpenAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/SharpenAction.java
new file mode 100644
index 0000000..1c0542f
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/SharpenAction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.SharpenFilter;
+
+/**
+ * An action handling sharpen effect.
+ */
+public class SharpenAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0.5f;
+
+ public SharpenAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final SharpenFilter filter = new SharpenFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setSharpen(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+
+ filter.setSharpen(DEFAULT_SCALE);
+ notifyFilterChanged(filter, true);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/SoftFocusAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/SoftFocusAction.java
new file mode 100644
index 0000000..34dc201
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/SoftFocusAction.java
@@ -0,0 +1,57 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.SoftFocusFilter;
+
+/**
+ * An action handling soft focus effect.
+ */
+public class SoftFocusAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0f;
+
+ public SoftFocusAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final SoftFocusFilter filter = new SoftFocusFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setSoftFocus(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/StraightenAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/StraightenAction.java
new file mode 100644
index 0000000..0e84d9b
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/StraightenAction.java
@@ -0,0 +1,88 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.R;
+import com.android.photoeditor.RectUtils;
+import com.android.photoeditor.filters.StraightenFilter;
+
+/**
+ * An action handling straighten effect.
+ */
+public class StraightenAction extends FilterAction {
+
+ private static final float DEFAULT_ANGLE = 0.0f;
+ private static final float DEFAULT_ROTATE_SPAN = 60.0f;
+
+ private StraightenFilter filter;
+
+ public StraightenAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools, R.string.straighten_tooltip);
+ }
+
+ @Override
+ public void onBegin() {
+ filter = new StraightenFilter();
+
+ final Matrix matrix = new Matrix();
+ final RectF photoBounds = photoView.getPhotoBounds();
+ final float displayScale = RectUtils.getDisplayScale(photoBounds, photoView);
+
+ RectF displayBounds = photoView.getPhotoDisplayBounds();
+ photoView.clipPhoto(displayBounds);
+
+ // Directly transform photo-view instead of waiting for top-filter output callback.
+ rotateView.setOnAngleChangeListener(new RotateView.OnRotateChangeListener() {
+
+ @Override
+ public void onAngleChanged(float angle, boolean fromUser){
+ if (fromUser) {
+ filter.setAngle(angle);
+ notifyFilterChanged(filter, false);
+ RectUtils.getStraightenMatrix(photoBounds, angle, matrix);
+ matrix.postScale(displayScale, displayScale);
+ photoView.transformDisplay(matrix);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch() {
+ // no-op
+ }
+
+ @Override
+ public void onStopTrackingTouch() {
+ // no-op
+ }
+ });
+ rotateView.setGridBounds(displayBounds);
+ rotateView.setRotatedAngle(DEFAULT_ANGLE);
+ rotateView.setRotateSpan(DEFAULT_ROTATE_SPAN);
+ rotateView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onEnd() {
+ notifyFilterChanged(filter, true);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/TintAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/TintAction.java
new file mode 100644
index 0000000..23cca2b
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/TintAction.java
@@ -0,0 +1,61 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.TintFilter;
+
+/**
+ * An action handling tint effect.
+ */
+public class TintAction extends FilterAction {
+
+ private static final int DEFAULT_COLOR_INDEX = 13;
+
+ public TintAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final TintFilter filter = new TintFilter();
+
+ colorWheel.setOnColorChangeListener(new ColorWheel.OnColorChangeListener() {
+
+ @Override
+ public void onColorChanged(int color, boolean fromUser){
+ if (fromUser) {
+ filter.setTint(color);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ // Tint photo with the default color.
+ colorWheel.setColorIndex(DEFAULT_COLOR_INDEX);
+ colorWheel.setVisibility(View.VISIBLE);
+
+ filter.setTint(colorWheel.getColor());
+ notifyFilterChanged(filter, true);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/TouchView.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/TouchView.java
new file mode 100644
index 0000000..5c000fb
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/TouchView.java
@@ -0,0 +1,117 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * A view that detects user gestures and touch motions.
+ */
+class TouchView extends View {
+
+ /**
+ * Listener of swipes.
+ */
+ public interface SwipeListener {
+
+ void onSwipeLeft();
+
+ void onSwipeRight();
+
+ void onSwipeUp();
+
+ void onSwipeDown();
+ }
+
+ /**
+ * Listener of single tap confirmed (click).
+ */
+ public interface SingleTapListener {
+
+ void onSingleTap(float x, float y);
+ }
+
+ private final GestureDetector gestureDetector;
+
+ private SwipeListener swipeListener;
+ private SingleTapListener singleTapListener;
+
+ public TouchView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final int swipeThreshold = (int) (500 * getResources().getDisplayMetrics().density);
+ gestureDetector = new GestureDetector(
+ context, new GestureDetector.SimpleOnGestureListener() {
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // GestureDetector onTouchEvent returns true for fling events only when their
+ // preceding down events are consumed.
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ if (singleTapListener != null) {
+ singleTapListener.onSingleTap(e.getX(), e.getY());
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onFling(
+ MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {
+ if (swipeListener != null) {
+ float absX = Math.abs(velocityX);
+ float absY = Math.abs(velocityY);
+ float deltaX = me2.getX() - me1.getX();
+ float deltaY = me2.getY() - me1.getY();
+ int travelX = getWidth() / 4;
+ int travelY = getHeight() / 4;
+ if (velocityX > swipeThreshold && absY < absX && deltaX > travelX) {
+ swipeListener.onSwipeRight();
+ } else if (velocityX < -swipeThreshold && absY < absX && deltaX < -travelX) {
+ swipeListener.onSwipeLeft();
+ } else if (velocityY < -swipeThreshold && absX < absY && deltaY < -travelY) {
+ swipeListener.onSwipeUp();
+ } else if (velocityY > swipeThreshold && absX < absY / 2 && deltaY > travelY) {
+ swipeListener.onSwipeDown();
+ }
+ }
+ return true;
+ }
+ });
+ gestureDetector.setIsLongpressEnabled(false);
+ }
+
+ public void setSwipeListener(SwipeListener listener) {
+ swipeListener = listener;
+ }
+
+ public void setSingleTapListener(SingleTapListener listener) {
+ singleTapListener = listener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return isEnabled() && gestureDetector.onTouchEvent(event);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/VignetteAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/VignetteAction.java
new file mode 100644
index 0000000..6b54142
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/VignetteAction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.VignetteFilter;
+
+/**
+ * An action handling vignette effect.
+ */
+public class VignetteAction extends FilterAction {
+
+ private static final float DEFAULT_SCALE = 0.5f;
+
+ public VignetteAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ final VignetteFilter filter = new VignetteFilter();
+
+ scaleWheel.setOnScaleChangeListener(new ScaleWheel.OnScaleChangeListener() {
+
+ @Override
+ public void onProgressChanged(float progress, boolean fromUser) {
+ if (fromUser) {
+ filter.setScale(progress);
+ notifyFilterChanged(filter, true);
+ }
+ }
+ });
+ scaleWheel.setProgress(DEFAULT_SCALE);
+ scaleWheel.setVisibility(View.VISIBLE);
+
+ filter.setScale(DEFAULT_SCALE);
+ notifyFilterChanged(filter, true);
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/actions/WarmifyAction.java b/samples/PhotoEditor/src/com/android/photoeditor/actions/WarmifyAction.java
new file mode 100644
index 0000000..002e107
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/actions/WarmifyAction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.actions;
+
+import android.view.ViewGroup;
+
+import com.android.photoeditor.FilterStack;
+import com.android.photoeditor.filters.WarmifyFilter;
+
+/**
+ * An action handling warmify effect.
+ */
+public class WarmifyAction extends FilterAction {
+
+ public WarmifyAction(FilterStack filterStack, ViewGroup tools) {
+ super(filterStack, tools);
+ }
+
+ @Override
+ public void onBegin() {
+ notifyFilterChanged(new WarmifyFilter(), true);
+ end();
+ }
+
+ @Override
+ public void onEnd() {
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/animation/AnimationPair.java b/samples/PhotoEditor/src/com/android/photoeditor/animation/AnimationPair.java
new file mode 100644
index 0000000..153855c
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/animation/AnimationPair.java
@@ -0,0 +1,41 @@
+/*
+ * 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.photoeditor.animation;
+
+import android.view.animation.Animation;
+
+/**
+ * A pair of animations that will be played sequentially, e.g. fade-out and then fade-in.
+ */
+public class AnimationPair {
+
+ private final Animation first;
+ private final Animation second;
+
+ AnimationPair(Animation first, Animation second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ public Animation first() {
+ return first;
+ }
+
+ public Animation second() {
+ return second;
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/animation/FadeAnimation.java b/samples/PhotoEditor/src/com/android/photoeditor/animation/FadeAnimation.java
new file mode 100644
index 0000000..45a7c33
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/animation/FadeAnimation.java
@@ -0,0 +1,38 @@
+/*
+ * 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.photoeditor.animation;
+
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+
+/**
+ * Fading animation that either fades in or out the view by alpha animations.
+ */
+public class FadeAnimation {
+
+ /**
+ * Gets animations that fade out and then fade in the view.
+ */
+ public static AnimationPair getFadeOutInAnimations(int duration) {
+ Animation fadeOut = new AlphaAnimation(1.0f, 0.5f);
+ Animation fadeIn = new AlphaAnimation(0.5f, 1.0f);
+ fadeOut.setDuration(duration);
+ fadeIn.setDuration(duration);
+
+ return new AnimationPair(fadeOut, fadeIn);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/animation/Rotate3DAnimation.java b/samples/PhotoEditor/src/com/android/photoeditor/animation/Rotate3DAnimation.java
new file mode 100644
index 0000000..01e5a92
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/animation/Rotate3DAnimation.java
@@ -0,0 +1,144 @@
+/*
+ * 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.photoeditor.animation;
+
+import android.graphics.Camera;
+import android.graphics.Matrix;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Animation;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Transformation;
+
+/**
+ * Rotation animation that rotates a 2D view 90 degrees on either X or Y axis in 3D space. The
+ * rotation is performed around the view center point by its defined rotation direction and position
+ * it will start from or end to. The rotation duration can be specified as well as whether the
+ * rotation should be accelerated or decelerated in time.
+ */
+public class Rotate3DAnimation extends Animation {
+
+ public enum Rotate {
+ UP, DOWN, LEFT, RIGHT
+ }
+
+ private enum Angle {
+ FROM_DEGREES_0, TO_DEGREES_0
+ }
+
+ private static final float DEPTH_Z = 250.0f;
+ private static final float ROTATE_DEGREES = 90f;
+
+ private final Rotate rotate;
+ private final Angle angle;
+ private final int duration;
+ private final boolean accelerate;
+
+ private Camera camera;
+ private float centerX;
+ private float centerY;
+ private float fromDegrees;
+ private float toDegrees;
+
+ /**
+ * Gets animations that flip the view by 3D rotations.
+ */
+ public static AnimationPair getFlipAnimations(Rotate rotate, int duration) {
+ return new AnimationPair(
+ new Rotate3DAnimation(rotate, Angle.FROM_DEGREES_0, duration, true),
+ new Rotate3DAnimation(rotate, Angle.TO_DEGREES_0, duration, false));
+ }
+
+ private Rotate3DAnimation(Rotate rotate, Angle angle, int duration, boolean accelerate) {
+ this.rotate = rotate;
+ this.angle = angle;
+ this.duration = duration;
+ this.accelerate = accelerate;
+ }
+
+ @Override
+ public void initialize(int width, int height, int parentWidth, int parentHeight) {
+ super.initialize(width, height, parentWidth, parentHeight);
+ super.setDuration(duration);
+ super.setInterpolator(
+ accelerate ? new AccelerateInterpolator() : new DecelerateInterpolator());
+
+ camera = new Camera();
+ centerX = width / 2.0f;
+ centerY = height / 2.0f;
+
+ if (angle == Angle.FROM_DEGREES_0) {
+ fromDegrees = 0;
+
+ switch (rotate) {
+ case RIGHT:
+ case UP:
+ toDegrees = ROTATE_DEGREES;
+ break;
+
+ case LEFT:
+ case DOWN:
+ toDegrees = -ROTATE_DEGREES;
+ break;
+ }
+ } else if (angle == Angle.TO_DEGREES_0) {
+ toDegrees = 0;
+
+ switch (rotate) {
+ case RIGHT:
+ case UP:
+ fromDegrees = -ROTATE_DEGREES;
+ break;
+
+ case LEFT:
+ case DOWN:
+ fromDegrees = ROTATE_DEGREES;
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ camera.save();
+
+ if (angle == Angle.FROM_DEGREES_0) {
+ camera.translate(0.0f, 0.0f, DEPTH_Z * interpolatedTime);
+ } else if (angle == Angle.TO_DEGREES_0) {
+ camera.translate(0.0f, 0.0f, DEPTH_Z * (1.0f - interpolatedTime));
+ }
+
+ float degrees = fromDegrees + ((toDegrees - fromDegrees) * interpolatedTime);
+ switch (rotate) {
+ case RIGHT:
+ case LEFT:
+ camera.rotateY(degrees);
+ break;
+
+ case UP:
+ case DOWN:
+ camera.rotateX(degrees);
+ break;
+ }
+
+ final Matrix matrix = t.getMatrix();
+ camera.getMatrix(matrix);
+ camera.restore();
+
+ matrix.preTranslate(-centerX, -centerY);
+ matrix.postTranslate(centerX, centerY);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/AutoFixFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/AutoFixFilter.java
new file mode 100644
index 0000000..027f090
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/AutoFixFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Auto-fix filter applied to the image.
+ */
+public class AutoFixFilter extends Filter {
+
+ private float scale;
+
+ /**
+ * Sets the auto-fix level.
+ *
+ * @param scale ranges from 0 to 1.
+ */
+ public void setScale(float scale) {
+ this.scale = scale;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeHEQ(src.bitmap(), dst.bitmap(), scale);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/ColorTemperatureFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/ColorTemperatureFilter.java
new file mode 100644
index 0000000..4fb00af
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/ColorTemperatureFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Color temperature filter applied to the image.
+ */
+public class ColorTemperatureFilter extends Filter {
+
+ private float colorTemperature;
+
+ /**
+ * Sets the color temperature level.
+ *
+ * @param scale ranges from 0 to 1.
+ */
+ public void setColorTemperature(float scale) {
+ this.colorTemperature = (scale - 0.5f);
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeColorTemp(src.bitmap(), dst.bitmap(), colorTemperature);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/CropFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/CropFilter.java
new file mode 100644
index 0000000..5795bbb
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/CropFilter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.photoeditor.filters;
+
+import android.graphics.RectF;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Crop filter applied to the image.
+ */
+public class CropFilter extends Filter {
+
+ private RectF bounds;
+
+ /**
+ * The coordinates used here should range from 0 to 1.
+ */
+ public void setCropBounds(RectF bounds) {
+ this.bounds = bounds;
+ validate();
+ }
+
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeCopy(src.bitmap(), dst.bitmap());
+ dst.crop((int) (src.width() * bounds.left), (int) (src.height() * bounds.top),
+ (int) (src.width() * bounds.width()), (int) (src.height() * bounds.height()));
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/CrossProcessFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/CrossProcessFilter.java
new file mode 100644
index 0000000..943f2a8
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/CrossProcessFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Cross-process filter applied to the image.
+ */
+public class CrossProcessFilter extends Filter {
+
+ public CrossProcessFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeCrossProcess(src.bitmap(), dst.bitmap());
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/DocumentaryFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/DocumentaryFilter.java
new file mode 100644
index 0000000..0a0c097
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/DocumentaryFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Preset "Documentary" applied to the image.
+ */
+public class DocumentaryFilter extends Filter {
+
+ public DocumentaryFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeWhiteBlack(src.bitmap(), dst.bitmap(), 0.5f, 0f);
+ ImageUtils.nativeGrayscale(dst.bitmap(), dst.bitmap(), 1.0f);
+ ImageUtils.nativeVignetting(dst.bitmap(), dst.bitmap(), 0.83f);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/DoodleFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/DoodleFilter.java
new file mode 100644
index 0000000..365a737
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/DoodleFilter.java
@@ -0,0 +1,63 @@
+/*
+ * 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.photoeditor.filters;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+
+import com.android.photoeditor.actions.ColorPath;
+import com.android.photoeditor.Photo;
+
+import java.util.Vector;
+
+/**
+ * Doodle filter applied to the image.
+ */
+public class DoodleFilter extends Filter {
+
+ private final Vector<ColorPath> doodles = new Vector<ColorPath>();
+ private final Paint paint = ColorPath.createPaint();
+ private final Canvas canvas = new Canvas();
+
+ public void addPath(int color) {
+ // Remove last empty path before adding a new one.
+ if (!doodles.isEmpty() && doodles.lastElement().path().isEmpty()) {
+ doodles.remove(doodles.size() - 1);
+ }
+ doodles.add(new ColorPath(color, new Path()));
+ }
+
+ public synchronized void updateLastPath(Path path) {
+ if (!doodles.isEmpty()) {
+ doodles.lastElement().path().set(path);
+ validate();
+ }
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeCopy(src.bitmap(), dst.bitmap());
+
+ if (!doodles.isEmpty()) {
+ canvas.setBitmap(dst.bitmap());
+ for (ColorPath doodle : doodles) {
+ doodle.draw(canvas, paint);
+ }
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/DuotoneFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/DuotoneFilter.java
new file mode 100644
index 0000000..e56f2d5
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/DuotoneFilter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Duotone filter applied to the image.
+ */
+public class DuotoneFilter extends Filter {
+
+ private int firstColor;
+ private int secondColor;
+
+ public void setDuotone(int firstColor, int secondColor) {
+ this.firstColor = firstColor;
+ this.secondColor = secondColor;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeDuotone(src.bitmap(), dst.bitmap(), firstColor, secondColor);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/FillLightFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/FillLightFilter.java
new file mode 100644
index 0000000..547f082
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/FillLightFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Fill-light filter applied to the image.
+ */
+public class FillLightFilter extends Filter {
+
+ private float backlight;
+
+ /**
+ * Sets the backlight level.
+ *
+ * @param backlight ranges from 0 to 1.
+ */
+ public void setBacklight(float backlight) {
+ this.backlight = backlight;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeBacklight(src.bitmap(), dst.bitmap(), backlight);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/Filter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/Filter.java
new file mode 100644
index 0000000..11afd51
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/Filter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Image filter for photo editing.
+ */
+public abstract class Filter {
+
+ private boolean isValid;
+
+ protected void validate() {
+ isValid = true;
+ }
+
+ /**
+ * Some filters, e.g. lighting filters, are initially invalid until set up with parameters while
+ * others, e.g. sepia or flip filters, are initially valid without parameters.
+ */
+ public boolean isValid() {
+ return isValid;
+ }
+
+ /**
+ * Processes the source bitmap and matrix and output the destination bitmap and matrix.
+ *
+ * @param src source photo as the input.
+ * @param dst destination photo having the same dimension as source photo as the output.
+ */
+ public abstract void process(Photo src, Photo dst);
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/FisheyeFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/FisheyeFilter.java
new file mode 100644
index 0000000..f60918a
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/FisheyeFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Fisheye filter applied to the image.
+ */
+public class FisheyeFilter extends Filter {
+
+ private float scale;
+
+ /**
+ * Sets the fisheye distortion level.
+ *
+ * @param scale ranges from 0 to 1.
+ */
+ public void setScale(float scale) {
+ this.scale = scale;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeFisheye(src.bitmap(), dst.bitmap(), 0.5f, 0.5f, scale);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/FlipFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/FlipFilter.java
new file mode 100644
index 0000000..291afa2
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/FlipFilter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Flip filter applied to the image.
+ */
+public class FlipFilter extends Filter {
+
+ private boolean flipHorizontal;
+ private boolean flipVertical;
+
+ public void setFlip(boolean flipHorizontal, boolean flipVertical) {
+ this.flipHorizontal = flipHorizontal;
+ this.flipVertical = flipVertical;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ if (flipHorizontal && flipVertical) {
+ ImageUtils.nativeFlipBoth(src.bitmap(), dst.bitmap());
+ } else if (flipHorizontal) {
+ ImageUtils.nativeFlipHorizontal(src.bitmap(), dst.bitmap());
+ } else if (flipVertical){
+ ImageUtils.nativeFlipVertical(src.bitmap(), dst.bitmap());
+ } else {
+ ImageUtils.nativeCopy(src.bitmap(), dst.bitmap());
+ }
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/GrainFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/GrainFilter.java
new file mode 100644
index 0000000..8ee81d2
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/GrainFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Film grain filter applied to the image.
+ */
+public class GrainFilter extends Filter {
+
+ private float scale;
+
+ /**
+ * Set the grain noise level.
+ *
+ * @param scale ranges from 0 to 1.
+ */
+ public void setScale(float scale) {
+ this.scale = scale;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeGrain(src.bitmap(), dst.bitmap(), scale);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/GrayscaleFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/GrayscaleFilter.java
new file mode 100644
index 0000000..164aaa0
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/GrayscaleFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Grayscale filter applied to the image.
+ */
+public class GrayscaleFilter extends Filter {
+
+ public GrayscaleFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeGrayscale(src.bitmap(), dst.bitmap(), 1.0f);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/HighlightFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/HighlightFilter.java
new file mode 100644
index 0000000..22efbe1
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/HighlightFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Highlight filter applied to the image.
+ */
+public class HighlightFilter extends Filter {
+
+ private float white;
+
+ /**
+ * Sets the highlight level.
+ *
+ * @param highlight ranges from 0 to 1.
+ */
+ public void setHighlight(float highlight) {
+ white = 1f - highlight * 0.5f;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeWhiteBlack(src.bitmap(), dst.bitmap(), white, 0f);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/ImageUtils.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/ImageUtils.java
new file mode 100644
index 0000000..e37a30f
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/ImageUtils.java
@@ -0,0 +1,60 @@
+/*
+ * 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.photoeditor.filters;
+
+import android.graphics.Bitmap;
+import android.graphics.PointF;
+
+
+/**
+ * Image utilities that calls to JNI methods for image processing.
+ */
+public class ImageUtils {
+
+ static {
+ System.loadLibrary("jni_photoeditor");
+ }
+
+ public static native boolean init(byte[] pgm, int pgmLength);
+
+ public static native void nativeBacklight(Bitmap src, Bitmap dst, float backlight);
+ public static native void nativeBlur(Bitmap src, Bitmap dst, float scale);
+ public static native void nativeColorTemp(Bitmap src, Bitmap dst, float scale);
+ public static native void nativeCopy(Bitmap src, Bitmap dst);
+ public static native void nativeCrossProcess(Bitmap src, Bitmap dst);
+ public static native void nativeDuotone(Bitmap src, Bitmap dst, int firstColor,
+ int secondColor);
+ public static native void nativeFisheye(Bitmap src, Bitmap dst, float focusX,
+ float focusY, float scale);
+ public static native void nativeFlipBoth(Bitmap src, Bitmap dst);
+ public static native void nativeFlipHorizontal(Bitmap src, Bitmap dst);
+ public static native void nativeFlipVertical(Bitmap src, Bitmap dst);
+ public static native void nativeGrain(Bitmap src, Bitmap dst, float scale);
+ public static native void nativeGrayscale(Bitmap src, Bitmap dst, float scale);
+ public static native void nativeHEQ(Bitmap src, Bitmap dst, float scale);
+ public static native void nativeNegative(Bitmap src, Bitmap dst);
+ public static native void nativeQuantize(Bitmap src, Bitmap dst);
+ public static native void nativeRedEye(Bitmap src, Bitmap dst, PointF[] redeyes,
+ float radius, float intensity);
+ public static native void nativeSaturation(Bitmap src, Bitmap dst, float scale);
+ public static native void nativeSepia(Bitmap src, Bitmap dst);
+ public static native void nativeSharpen(Bitmap src, Bitmap dst, float scale);
+ public static native void nativeTint(Bitmap src, Bitmap dst, int tint);
+ public static native void nativeVignetting(Bitmap src, Bitmap dst, float range);
+ public static native void nativeWarmify(Bitmap src, Bitmap dst);
+ public static native void nativeWhiteBlack(Bitmap src, Bitmap dst, float white, float black);
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/LomoishFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/LomoishFilter.java
new file mode 100644
index 0000000..1028081
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/LomoishFilter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Preset "Lomo-ish" applied to the image.
+ */
+public class LomoishFilter extends Filter {
+
+ public LomoishFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeBlur(src.bitmap(), dst.bitmap(), 0.3f);
+ ImageUtils.nativeCrossProcess(dst.bitmap(), dst.bitmap());
+ ImageUtils.nativeWhiteBlack(dst.bitmap(), dst.bitmap(), 0.8f, 0.15f);
+ ImageUtils.nativeVignetting(dst.bitmap(), dst.bitmap(), 0.73f);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/NegativeFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/NegativeFilter.java
new file mode 100644
index 0000000..fda1862
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/NegativeFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Negative filter applied to the image.
+ */
+public class NegativeFilter extends Filter {
+
+ public NegativeFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeNegative(src.bitmap(), dst.bitmap());
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/PosterizeFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/PosterizeFilter.java
new file mode 100644
index 0000000..4001a90
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/PosterizeFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * "Posterize" filter applied to the image.
+ */
+public class PosterizeFilter extends Filter {
+
+ public PosterizeFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeQuantize(src.bitmap(), dst.bitmap());
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/RedEyeFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/RedEyeFilter.java
new file mode 100644
index 0000000..0965c61
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/RedEyeFilter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.photoeditor.filters;
+
+import android.graphics.PointF;
+
+import com.android.photoeditor.Photo;
+
+import java.util.Vector;
+
+/**
+ * Red-eye removal filter applied to the image.
+ */
+public class RedEyeFilter extends Filter {
+
+ private static final float RADIUS_RATIO = 0.06f;
+ private static final float MIN_RADIUS = 10.0f;
+ private static final float DEFAULT_RED_INTENSITY = 1.30f; // an empirical value
+
+ private final Vector<PointF> redeyePositions = new Vector<PointF>();
+
+ public void addRedEyePosition(PointF point) {
+ redeyePositions.add(point);
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ float radius = Math.max(MIN_RADIUS, RADIUS_RATIO * Math.min(src.width(), src.height()));
+
+ PointF[] a = new PointF[redeyePositions.size()];
+ ImageUtils.nativeRedEye(src.bitmap(), dst.bitmap(),
+ redeyePositions.toArray(a), radius, DEFAULT_RED_INTENSITY);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/RotateFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/RotateFilter.java
new file mode 100644
index 0000000..5152aca
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/RotateFilter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.photoeditor.filters;
+
+import android.graphics.Matrix;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Rotate filter applied to the image.
+ */
+public class RotateFilter extends Filter {
+
+ private final Matrix matrix = new Matrix();
+
+ public void setAngle(float degrees) {
+ matrix.setRotate(degrees);
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeCopy(src.bitmap(), dst.bitmap());
+ dst.transform(matrix);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/SaturationFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/SaturationFilter.java
new file mode 100644
index 0000000..6590670
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/SaturationFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Saturation filter applied to the image.
+ */
+public class SaturationFilter extends Filter {
+
+ private float saturation;
+
+ /**
+ * Sets the saturation level.
+ *
+ * @param saturation ranges from 0 to 1.
+ */
+ public void setSaturation(float saturation) {
+ this.saturation = (saturation - 0.5f) * 2;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeSaturation(src.bitmap(), dst.bitmap(), saturation);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/SepiaFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/SepiaFilter.java
new file mode 100644
index 0000000..696f578
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/SepiaFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Sepia filter applied to the image.
+ */
+public class SepiaFilter extends Filter {
+
+ public SepiaFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeSepia(src.bitmap(), dst.bitmap());
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/ShadowFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/ShadowFilter.java
new file mode 100644
index 0000000..5008e6a
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/ShadowFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Shadow filter applied to the image.
+ */
+public class ShadowFilter extends Filter {
+
+ private float black;
+
+ /**
+ * Sets the shadow blackness level.
+ *
+ * @param shadow ranges from 0 to 1.
+ */
+ public void setShadow(float shadow) {
+ black = shadow * 0.5f;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeWhiteBlack(src.bitmap(), dst.bitmap(), 1f, black);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/SharpenFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/SharpenFilter.java
new file mode 100644
index 0000000..7d3aa0d
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/SharpenFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Sharpen filter applied to the image.
+ */
+public class SharpenFilter extends Filter {
+
+ private float scale;
+
+ /**
+ * Sets the sharpen level.
+ *
+ * @param scale ranges from 0 to 1.
+ */
+ public void setSharpen(float scale) {
+ this.scale = scale;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeSharpen(src.bitmap(), dst.bitmap(), scale);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/SoftFocusFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/SoftFocusFilter.java
new file mode 100644
index 0000000..d2e41a5
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/SoftFocusFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Soft focus filter applied to the image.
+ */
+public class SoftFocusFilter extends Filter {
+
+ private float scale;
+
+ /**
+ * Sets the soft focus level.
+ *
+ * @param scale ranges from 0 to 1.
+ */
+ public void setSoftFocus(float scale) {
+ this.scale = scale;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeBlur(src.bitmap(), dst.bitmap(), scale);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/StraightenFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/StraightenFilter.java
new file mode 100644
index 0000000..8f265bd
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/StraightenFilter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.photoeditor.filters;
+
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+import com.android.photoeditor.Photo;
+import com.android.photoeditor.RectUtils;
+
+/**
+ * Straighten filter applied to the image.
+ */
+public class StraightenFilter extends Filter {
+
+ private final RectF bounds = new RectF();
+ private final Matrix matrix = new Matrix();
+ private final Canvas canvas = new Canvas();
+ private final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
+
+ private float angle;
+
+ public void setAngle(float degrees) {
+ this.angle = degrees;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ bounds.set(0, 0, src.width(), src.height());
+ RectUtils.getStraightenMatrix(bounds, angle, matrix);
+ matrix.mapRect(bounds);
+ matrix.postTranslate((src.width() - bounds.width()) / 2,
+ (src.height() - bounds.height()) / 2);
+ canvas.setBitmap(dst.bitmap());
+ canvas.drawBitmap(src.bitmap(), matrix, paint);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/TintFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/TintFilter.java
new file mode 100644
index 0000000..ef06fee
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/TintFilter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Tint filter applied to the image.
+ */
+public class TintFilter extends Filter {
+
+ private int tint;
+
+ public void setTint(int color) {
+ tint = color;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeTint(src.bitmap(), dst.bitmap(), tint);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/VignetteFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/VignetteFilter.java
new file mode 100644
index 0000000..3d5b566
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/VignetteFilter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Vignette filter applied to the image.
+ */
+public class VignetteFilter extends Filter {
+
+ private float range;
+
+ /**
+ * Sets the vignette range scale.
+ *
+ * @param scale ranges from 0 to 1.
+ */
+ public void setScale(float scale) {
+ // The 'range' is between 1.3 to 0.6. When scale is zero then range is 1.3
+ // which means no vignette at all because the luminousity difference is
+ // less than 1/256 and will cause nothing.
+ range = 1.30f - (float) Math.sqrt(scale) * 0.7f;
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeVignetting(src.bitmap(), dst.bitmap(), range);
+ }
+}
diff --git a/samples/PhotoEditor/src/com/android/photoeditor/filters/WarmifyFilter.java b/samples/PhotoEditor/src/com/android/photoeditor/filters/WarmifyFilter.java
new file mode 100644
index 0000000..dc745a0
--- /dev/null
+++ b/samples/PhotoEditor/src/com/android/photoeditor/filters/WarmifyFilter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.photoeditor.filters;
+
+import com.android.photoeditor.Photo;
+
+/**
+ * Warmify filter applied to the image.
+ */
+public class WarmifyFilter extends Filter {
+
+ public WarmifyFilter() {
+ validate();
+ }
+
+ @Override
+ public void process(Photo src, Photo dst) {
+ ImageUtils.nativeWarmify(src.bitmap(), dst.bitmap());
+ }
+}
diff --git a/samples/quake/res/drawable/app_quake.png b/samples/quake/res/drawable/app_quake.png
new file mode 100644
index 0000000..333b723
--- /dev/null
+++ b/samples/quake/res/drawable/app_quake.png
Binary files differ
diff --git a/samples/quake/res/layout/downloader.xml b/samples/quake/res/layout/downloader.xml
new file mode 100644
index 0000000..968c5f2
--- /dev/null
+++ b/samples/quake/res/layout/downloader.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2008 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/customText"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:padding="3dip"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView android:text="@string/download_activity_progress"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:padding="3dip"
+ android:gravity="right"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView android:id="@+id/progress"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:padding="3dip"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView android:text="@string/download_activity_time_remaining"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:padding="3dip"
+ android:gravity="right"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView android:id="@+id/time_remaining"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:padding="3dip"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </LinearLayout>
+
+ <Button android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="5dip"
+ android:text="@string/download_activity_cancel" />
+</LinearLayout>
diff --git a/samples/quake/res/layout/downloader_title.xml b/samples/quake/res/layout/downloader_title.xml
new file mode 100644
index 0000000..eae5d33
--- /dev/null
+++ b/samples/quake/res/layout/downloader_title.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2008 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/screen"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/windowTitleSize"
+ android:orientation="horizontal">
+
+ <TextView android:id="@+id/title_text"
+ android:gravity="center_vertical"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:paddingLeft="5dip"
+ style="?android:attr/windowTitleStyle"
+ android:singleLine="true"
+ android:layout_weight="1"
+ android:ellipsize="end" />
+
+ <LinearLayout android:id="@+id/loading_indicator"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content">
+
+ <TextView android:text="@string/download_activity_title"
+ android:gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingRight="5dip"
+ style="?android:attr/windowTitleStyle" />
+
+ <ProgressBar android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleSmallTitle"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="5dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/samples/quake/res/menu/menu.xml b/samples/quake/res/menu/menu.xml
new file mode 100644
index 0000000..4a0dfe3
--- /dev/null
+++ b/samples/quake/res/menu/menu.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/switch_mode"
+ android:title="@string/switch_mode" />
+</menu>
+
diff --git a/samples/quake/res/values/strings.xml b/samples/quake/res/values/strings.xml
new file mode 100644
index 0000000..3c9ee40
--- /dev/null
+++ b/samples/quake/res/values/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/Quake/res/strings.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+ them to be changed based on the locale and options. -->
+
+<resources>
+ <!-- Simple strings. -->
+ <string name="quake_activity">Quake</string>
+ <!-- Displayed while copying Quake data to storage. CHAR LIMIT=NONE -->
+ <string name="quake_customDownloadText">Please wait while the Quake data files are copied.</string>
+ <string name="download_activity_title">Downloading</string>
+ <string name="download_activity_cancel">Cancel Download</string>
+ <string name="download_activity_progress">Progress:</string>
+ <string name="download_activity_time_remaining">Time Remaining:</string>
+ <string name="download_activity_time_remaining_unknown">unknown</string>
+ <string name="download_activity_time_remaining_minutes">minutes</string>
+ <string name="download_activity_time_remaining_seconds">seconds</string>
+ <string name="download_activity_time_remaining_hours">hours</string>
+ <string name="download_activity_time_remaining_days">days</string>
+ <string name="download_activity_download_stopped">Download stopped</string>
+ <string name="download_activity_retry">Retry</string>
+ <string name="download_activity_quit">Quit</string>
+ <string name="switch_mode">Switch Mode</string>
+</resources>
+
diff --git a/toolchains/llvm/setup.mk b/toolchains/llvm/setup.mk
index e9832f7..5a3fbc5 100644
--- a/toolchains/llvm/setup.mk
+++ b/toolchains/llvm/setup.mk
@@ -11,7 +11,8 @@
TARGET_C_INCLUDES := $(GDK_PLATFORMS_ROOT)/android-portable/arch-llvm/usr/include
# Workaround before the required headers are in the above dir.
-TARGET_C_INCLUDES += $(NDK_ROOT)/platforms/android-9/arch-arm/usr/include/ \
+TARGET_C_INCLUDES += $(NDK_ROOT)/platforms/android-9/arch-arm/usr/include \
+ $(NDK_ROOT)/sources/cxx-stl/system/include \
$(NDK_ROOT)/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/lib/gcc/arm-linux-androideabi/4.4.3/include
TARGET_CC := $(OUT)/../../../host/linux-x86/bin/clang