From a9b27719b9f448879fb5ac821ee7ac1cf22b33ce Mon Sep 17 00:00:00 2001 From: Jorge Ruesga Date: Sun, 27 Oct 2013 13:46:12 +0100 Subject: Returns as a CyanogenMod project. Update author and Copyrights Signed-off-by: Jorge Ruesga --- .../wallpapers/photophase/AndroidHelper.java | 101 --- .../android/wallpapers/photophase/Colors.java | 58 -- .../wallpapers/photophase/EGLWallpaperService.java | 117 ---- .../android/wallpapers/photophase/FixedQueue.java | 207 ------ .../photophase/GLES20WallpaperService.java | 50 -- .../photophase/GLESSurfaceDispatcher.java | 64 -- .../photophase/GLESWallpaperService.java | 230 ------ .../photophase/MediaPictureDiscoverer.java | 307 -------- .../android/wallpapers/photophase/PhotoFrame.java | 296 -------- .../wallpapers/photophase/PhotoPhaseActivity.java | 171 ----- .../wallpapers/photophase/PhotoPhaseRenderer.java | 661 ------------------ .../wallpapers/photophase/PhotoPhaseWallpaper.java | 236 ------- .../photophase/PhotoPhaseWallpaperWorld.java | 419 ----------- .../wallpapers/photophase/StorageHelper.java | 82 --- .../wallpapers/photophase/TextureManager.java | 595 ---------------- .../wallpapers/photophase/TextureRequestor.java | 41 -- .../AlbumsFlip3dAnimationController.java | 164 ----- .../photophase/animations/Evaluators.java | 79 --- .../photophase/animations/Flip3dAnimation.java | 82 --- .../wallpapers/photophase/effects/BlurEffect.java | 60 -- .../wallpapers/photophase/effects/Effects.java | 338 --------- .../photophase/effects/EmbossEffect.java | 68 -- .../wallpapers/photophase/effects/GlowEffect.java | 69 -- .../photophase/effects/HalftoneEffect.java | 128 ---- .../photophase/effects/MirrorEffect.java | 63 -- .../wallpapers/photophase/effects/NullEffect.java | 61 -- .../photophase/effects/OutlineEffect.java | 74 -- .../photophase/effects/PhotoPhaseEffect.java | 268 ------- .../effects/PhotoPhaseEffectFactory.java | 124 ---- .../photophase/effects/PixelateEffect.java | 118 ---- .../photophase/effects/PopArtEffect.java | 64 -- .../photophase/effects/ScanlinesEffect.java | 59 -- .../android/wallpapers/photophase/model/Album.java | 110 --- .../wallpapers/photophase/model/Disposition.java | 118 ---- .../preferences/ChoosePicturesFragment.java | 496 ------------- .../preferences/DispositionFragment.java | 217 ------ .../preferences/GeneralPreferenceFragment.java | 160 ----- .../preferences/LandscapeDispositionFragment.java | 92 --- .../preferences/LayoutPreferenceFragment.java | 44 -- .../preferences/MediaPreferenceFragment.java | 131 ---- .../preferences/PhotoPhasePreferences.java | 85 --- .../preferences/PortraitDispositionFragment.java | 90 --- .../preferences/PreferencesProvider.java | 459 ------------ .../photophase/preferences/SeekBarPreference.java | 318 --------- .../preferences/SeekBarProgressPreference.java | 145 ---- .../photophase/preferences/TouchAction.java | 72 -- .../wallpapers/photophase/shapes/ColorShape.java | 150 ---- .../photophase/shapes/DrawableShape.java | 30 - .../wallpapers/photophase/shapes/OopsShape.java | 292 -------- .../photophase/tasks/AsyncPictureLoaderTask.java | 71 -- .../photophase/transitions/CubeTransition.java | 479 ------------- .../photophase/transitions/FadeTransition.java | 135 ---- .../photophase/transitions/FlipTransition.java | 257 ------- .../photophase/transitions/NullTransition.java | 171 ----- .../photophase/transitions/SwapTransition.java | 111 --- .../photophase/transitions/Transition.java | 207 ------ .../photophase/transitions/Transitions.java | 161 ----- .../transitions/TranslateTransition.java | 334 --------- .../photophase/transitions/WindowTransition.java | 376 ---------- .../wallpapers/photophase/utils/BitmapUtils.java | 147 ---- .../photophase/utils/DispositionUtil.java | 115 --- .../wallpapers/photophase/utils/GLESUtil.java | 531 -------------- .../wallpapers/photophase/utils/MERAlgorithm.java | 134 ---- .../android/wallpapers/photophase/utils/Utils.java | 83 --- .../wallpapers/photophase/widgets/AlbumInfo.java | 284 -------- .../photophase/widgets/AlbumPictures.java | 345 --------- .../wallpapers/photophase/widgets/CardLayout.java | 91 --- .../photophase/widgets/ColorPickerPreference.java | 253 ------- .../photophase/widgets/DispositionView.java | 768 --------------------- .../photophase/widgets/PicturesView.java | 164 ----- .../wallpapers/photophase/widgets/ResizeFrame.java | 301 -------- .../widgets/VerticalEndlessScroller.java | 114 --- .../wallpapers/photophase/AndroidHelper.java | 101 +++ .../cyanogenmod/wallpapers/photophase/Colors.java | 58 ++ .../wallpapers/photophase/EGLWallpaperService.java | 117 ++++ .../wallpapers/photophase/FixedQueue.java | 207 ++++++ .../photophase/GLES20WallpaperService.java | 50 ++ .../photophase/GLESSurfaceDispatcher.java | 64 ++ .../photophase/GLESWallpaperService.java | 230 ++++++ .../photophase/MediaPictureDiscoverer.java | 307 ++++++++ .../wallpapers/photophase/PhotoFrame.java | 296 ++++++++ .../wallpapers/photophase/PhotoPhaseActivity.java | 171 +++++ .../wallpapers/photophase/PhotoPhaseRenderer.java | 661 ++++++++++++++++++ .../wallpapers/photophase/PhotoPhaseWallpaper.java | 236 +++++++ .../photophase/PhotoPhaseWallpaperWorld.java | 419 +++++++++++ .../wallpapers/photophase/StorageHelper.java | 82 +++ .../wallpapers/photophase/TextureManager.java | 595 ++++++++++++++++ .../wallpapers/photophase/TextureRequestor.java | 41 ++ .../AlbumsFlip3dAnimationController.java | 164 +++++ .../photophase/animations/Evaluators.java | 79 +++ .../photophase/animations/Flip3dAnimation.java | 82 +++ .../wallpapers/photophase/effects/BlurEffect.java | 60 ++ .../wallpapers/photophase/effects/Effects.java | 338 +++++++++ .../photophase/effects/EmbossEffect.java | 68 ++ .../wallpapers/photophase/effects/GlowEffect.java | 69 ++ .../photophase/effects/HalftoneEffect.java | 128 ++++ .../photophase/effects/MirrorEffect.java | 63 ++ .../wallpapers/photophase/effects/NullEffect.java | 61 ++ .../photophase/effects/OutlineEffect.java | 74 ++ .../photophase/effects/PhotoPhaseEffect.java | 268 +++++++ .../effects/PhotoPhaseEffectFactory.java | 124 ++++ .../photophase/effects/PixelateEffect.java | 118 ++++ .../photophase/effects/PopArtEffect.java | 64 ++ .../photophase/effects/ScanlinesEffect.java | 59 ++ .../wallpapers/photophase/model/Album.java | 110 +++ .../wallpapers/photophase/model/Disposition.java | 118 ++++ .../preferences/ChoosePicturesFragment.java | 496 +++++++++++++ .../preferences/DispositionFragment.java | 217 ++++++ .../preferences/GeneralPreferenceFragment.java | 160 +++++ .../preferences/LandscapeDispositionFragment.java | 92 +++ .../preferences/LayoutPreferenceFragment.java | 44 ++ .../preferences/MediaPreferenceFragment.java | 131 ++++ .../preferences/PhotoPhasePreferences.java | 85 +++ .../preferences/PortraitDispositionFragment.java | 90 +++ .../preferences/PreferencesProvider.java | 459 ++++++++++++ .../photophase/preferences/SeekBarPreference.java | 318 +++++++++ .../preferences/SeekBarProgressPreference.java | 145 ++++ .../photophase/preferences/TouchAction.java | 72 ++ .../wallpapers/photophase/shapes/ColorShape.java | 150 ++++ .../photophase/shapes/DrawableShape.java | 30 + .../wallpapers/photophase/shapes/OopsShape.java | 292 ++++++++ .../photophase/tasks/AsyncPictureLoaderTask.java | 71 ++ .../photophase/transitions/CubeTransition.java | 479 +++++++++++++ .../photophase/transitions/FadeTransition.java | 135 ++++ .../photophase/transitions/FlipTransition.java | 257 +++++++ .../photophase/transitions/NullTransition.java | 171 +++++ .../photophase/transitions/SwapTransition.java | 111 +++ .../photophase/transitions/Transition.java | 207 ++++++ .../photophase/transitions/Transitions.java | 161 +++++ .../transitions/TranslateTransition.java | 334 +++++++++ .../photophase/transitions/WindowTransition.java | 376 ++++++++++ .../wallpapers/photophase/utils/BitmapUtils.java | 147 ++++ .../photophase/utils/DispositionUtil.java | 115 +++ .../wallpapers/photophase/utils/GLESUtil.java | 531 ++++++++++++++ .../wallpapers/photophase/utils/MERAlgorithm.java | 134 ++++ .../wallpapers/photophase/utils/Utils.java | 83 +++ .../wallpapers/photophase/widgets/AlbumInfo.java | 284 ++++++++ .../photophase/widgets/AlbumPictures.java | 345 +++++++++ .../wallpapers/photophase/widgets/CardLayout.java | 91 +++ .../photophase/widgets/ColorPickerPreference.java | 253 +++++++ .../photophase/widgets/DispositionView.java | 768 +++++++++++++++++++++ .../photophase/widgets/PicturesView.java | 164 +++++ .../wallpapers/photophase/widgets/ResizeFrame.java | 301 ++++++++ .../widgets/VerticalEndlessScroller.java | 114 +++ 144 files changed, 14095 insertions(+), 14095 deletions(-) delete mode 100644 src/com/ruesga/android/wallpapers/photophase/AndroidHelper.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/Colors.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/EGLWallpaperService.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/FixedQueue.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/GLES20WallpaperService.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/GLESSurfaceDispatcher.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/GLESWallpaperService.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/MediaPictureDiscoverer.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/PhotoFrame.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/PhotoPhaseActivity.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/PhotoPhaseRenderer.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaper.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaperWorld.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/StorageHelper.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/TextureManager.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/TextureRequestor.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/animations/Evaluators.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/animations/Flip3dAnimation.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/BlurEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/Effects.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/EmbossEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/GlowEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/HalftoneEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/MirrorEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/NullEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/OutlineEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/PixelateEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/PopArtEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/effects/ScanlinesEffect.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/model/Album.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/model/Disposition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/ChoosePicturesFragment.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/DispositionFragment.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/GeneralPreferenceFragment.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/LandscapeDispositionFragment.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/LayoutPreferenceFragment.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/MediaPreferenceFragment.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/PhotoPhasePreferences.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/PortraitDispositionFragment.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/PreferencesProvider.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarPreference.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarProgressPreference.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/preferences/TouchAction.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/shapes/ColorShape.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/shapes/DrawableShape.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/shapes/OopsShape.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/CubeTransition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/FadeTransition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/FlipTransition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/NullTransition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/SwapTransition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/Transition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/Transitions.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/TranslateTransition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/transitions/WindowTransition.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/utils/Utils.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/AlbumInfo.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/AlbumPictures.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/CardLayout.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/ColorPickerPreference.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/DispositionView.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/PicturesView.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/ResizeFrame.java delete mode 100644 src/com/ruesga/android/wallpapers/photophase/widgets/VerticalEndlessScroller.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/AndroidHelper.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/Colors.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/EGLWallpaperService.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/FixedQueue.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/GLES20WallpaperService.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/GLESSurfaceDispatcher.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/GLESWallpaperService.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/PhotoFrame.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseActivity.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaperWorld.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/StorageHelper.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/TextureManager.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/TextureRequestor.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimation.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/BlurEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/Effects.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/EmbossEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/GlowEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/HalftoneEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/MirrorEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/NullEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/OutlineEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/PixelateEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/PopArtEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/effects/ScanlinesEffect.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/model/Album.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/GeneralPreferenceFragment.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/LayoutPreferenceFragment.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/MediaPreferenceFragment.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarPreference.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarProgressPreference.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/preferences/TouchAction.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/shapes/ColorShape.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/shapes/DrawableShape.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/CubeTransition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/FadeTransition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/FlipTransition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/NullTransition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/SwapTransition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/Transition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/Transitions.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/TranslateTransition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/transitions/WindowTransition.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/utils/BitmapUtils.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/utils/GLESUtil.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/utils/Utils.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/ColorPickerPreference.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/ResizeFrame.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java (limited to 'src') diff --git a/src/com/ruesga/android/wallpapers/photophase/AndroidHelper.java b/src/com/ruesga/android/wallpapers/photophase/AndroidHelper.java deleted file mode 100644 index b7b7bfd..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/AndroidHelper.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.app.Activity; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.provider.Settings; -import android.util.DisplayMetrics; -import android.view.ViewConfiguration; - -/** - * A helper class with useful methods for deal with android. - */ -public final class AndroidHelper { - - /** - * Method that returns if the device is a tablet - * - * @param ctx The current context - * @return boolean If device is a table - */ - public static boolean isTablet(Context ctx) { - Configuration configuration = ctx.getResources().getConfiguration(); - return (configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) - >= Configuration.SCREENLAYOUT_SIZE_LARGE; - } - - /** - * Method that returns if an option menu has to be displayed - * - * @param ctx The current context - * @return boolean If an option menu has to be displayed - */ - public static boolean showOptionsMenu(Context ctx) { - // Show overflow button? - return !ViewConfiguration.get(ctx).hasPermanentMenuKey(); - } - - /** - * This method converts dp unit to equivalent device specific value in pixels. - * - * @param ctx The current context - * @param dp A value in dp (Device independent pixels) unit - * @return float A float value to represent Pixels equivalent to dp according to device - */ - public static float convertDpToPixel(Context ctx, float dp) { - Resources resources = ctx.getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - return dp * (metrics.densityDpi / 160f); - } - - /** - * This method converts device specific pixels to device independent pixels. - * - * @param ctx The current context - * @param px A value in px (pixels) unit - * @return A float value to represent dp equivalent to px value - */ - public static float convertPixelsToDp(Context ctx, float px) { - Resources resources = ctx.getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - return px / (metrics.densityDpi / 160f); - } - - /** - * Calculate the dimension of the status bar - * - * @param context The current context - * @return The height of the status bar - */ - public static int calculateStatusBarHeight(Context context) { - // CyanogenMod specific featured (DO NOT RELAY IN INTERNAL VARS) - boolean hiddenStatusBar = - Settings.System.getInt(context.getContentResolver(), "expanded_desktop_state", 0) == 1 && - Settings.System.getInt(context.getContentResolver(), "expanded_desktop_style", 0) == 2; - int result = 0; - if (!hiddenStatusBar && !(context instanceof Activity)) { - int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); - if (resourceId > 0) { - result = context.getResources().getDimensionPixelSize(resourceId); - } - } - return result; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/Colors.java b/src/com/ruesga/android/wallpapers/photophase/Colors.java deleted file mode 100644 index 94de590..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/Colors.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.content.Context; -import android.content.res.Resources; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; - -/** - * A class that defines some wallpaper GLColor colors. - */ -public class Colors { - - private static GLColor sBackground = new GLColor(0); - private static GLColor sOverlay = new GLColor(0); - - /** - * This method should be called on initialization for load the preferences color - */ - public static void register(Context ctx) { - Resources res = ctx.getResources(); - sBackground = Preferences.General.getBackgroundColor(); - sOverlay = new GLColor(res.getColor(R.color.wallpaper_overlay_color)); - } - - public static GLColor getBackground() { - return sBackground; - } - - public static void setBackground(GLColor background) { - Colors.sBackground = background; - } - - public static GLColor getOverlay() { - return sOverlay; - } - - public static void setOverlay(GLColor overlay) { - Colors.sOverlay = overlay; - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/EGLWallpaperService.java b/src/com/ruesga/android/wallpapers/photophase/EGLWallpaperService.java deleted file mode 100644 index 896a758..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/EGLWallpaperService.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.content.Context; -import android.opengl.GLSurfaceView; -import android.service.wallpaper.WallpaperService; -import android.view.SurfaceHolder; - -/** - * An abstract class for using a {@link GLSurfaceView} inside a {@link WallpaperService}. - */ -public abstract class EGLWallpaperService extends WallpaperService { - - /** - * A listener interface for the {@link GLESWallpaperService.GLESEngine} engine class. - */ - public interface EGLEngineListener { - // No methods - } - - /** - * An EGL implementation of {@link android.service.wallpaper.WallpaperService.Engine} that - * uses {@link GLSurfaceView}. - */ - public class EGLEngine extends Engine { - /** - * The internal {@link GLSurfaceView}. - */ - class WallpaperGLSurfaceView extends GLSurfaceView { - - /** - * @see GLSurfaceView - */ - WallpaperGLSurfaceView(Context context) { - super(context); - } - - /** - * {@inheritDoc} - */ - @Override - public SurfaceHolder getHolder() { - return getSurfaceHolder(); - } - - /** - * Should be called when the {@link GLSurfaceView} is not needed anymore. - */ - public void onDestroy() { - super.onDetachedFromWindow(); - } - } - - private WallpaperGLSurfaceView mGlSurfaceView; - - /** - * Method that sets the EGL engine listener - * - * @param listener The EGL engine listener - */ - public void setEGLEngineListener(EGLEngineListener listener) { - // No methods - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(SurfaceHolder surfaceHolder) { - super.onCreate(surfaceHolder); - mGlSurfaceView = createWallpaperGLSurfaceView(); - } - - /** - * Method that returns the internal {@link GLSurfaceView} - * - * @return GLSurfaceView The internal {@link GLSurfaceView}. - */ - protected GLSurfaceView getGlSurfaceView() { - return mGlSurfaceView; - } - - /** - * {@inheritDoc} - */ - @Override - public void onDestroy() { - super.onDestroy(); - mGlSurfaceView.onDestroy(); - } - - /** - * Override this method if a {@link GLSurfaceView} wrapper is needed, for example - * if you need implements some eve - * - * @return WallpaperGLSurfaceView The specialized EGL {@link GLSurfaceView}. - */ - public WallpaperGLSurfaceView createWallpaperGLSurfaceView() { - return new WallpaperGLSurfaceView(EGLWallpaperService.this); - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/FixedQueue.java b/src/com/ruesga/android/wallpapers/photophase/FixedQueue.java deleted file mode 100644 index b59d7ba..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/FixedQueue.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import java.util.ArrayList; -import java.util.List; - -/** - * A class that represent a FIFO queue with a fixed size. When the queue reach the maximum defined - * size then extract the next element from the queue. - * @param The type of object to hold. - */ -@SuppressWarnings("unchecked") -public class FixedQueue { - - /** - * An exception thrown when the queue hasn't more elements - */ - public static class EmptyQueueException extends Exception { - private static final long serialVersionUID = 1L; - } - - private final Object[] mQueue; - private final int mSize; - private int mHead; - private int mTail; - - /** - * Constructor of FixedQueue - * - * @param size The size of the queue. The limit of objects in queue. Beyond this limits - * the older objects are recycled. - */ - public FixedQueue(int size) { - super(); - this.mQueue = new Object[size]; - this.mSize = size; - this.mHead = 0; - this.mTail = 0; - } - - /** - * Method that inserts a new object to the queue. - * - * @param o The object to insert - * @return The object inserted (for concatenation purpose) - */ - public T insert(T o) { - synchronized (this.mQueue) { - if (o == null) throw new NullPointerException(); - if (this.mQueue[this.mHead] != null) { - try { - noSynchronizedRemove(); - } catch (Throwable ex) {/**NON BLOCK**/} - } - this.mQueue[this.mHead] = o; - this.mHead++; - if (this.mHead >= this.mSize) { - this.mHead = 0; - } - return o; - } - } - - /** - * Method that extract the first element in the queue - * - * @return The item extracted - * @throws EmptyQueueException If the queue hasn't element - */ - public T remove() throws EmptyQueueException { - synchronized (this.mQueue) { - return noSynchronizedRemove(); - } - } - - /** - * Method that extract all the items from the queue - * - * @return The items extracted - * @throws EmptyQueueException If the queue hasn't element - */ - public List removeAll() throws EmptyQueueException { - synchronized (this.mQueue) { - if (isEmpty()) throw new EmptyQueueException(); - List l = new ArrayList(); - while (!isEmpty()) { - l.add(noSynchronizedRemove()); - } - return l; - } - } - - /** - * Method that retrieves the first element in the queue. This method doesn't remove the item - * from queue. - * - * @return The item retrieved - * @throws EmptyQueueException If the queue hasn't element - */ - public T peek() throws EmptyQueueException { - synchronized (this.mQueue) { - T o = (T)this.mQueue[this.mTail]; - if (o == null) throw new EmptyQueueException(); - return o; - } - - } - - /** - * Method that retrieves all the items from the queue. This method doesn't remove any item - * from queue. - * - * @return The items retrieved - * @throws EmptyQueueException If the queue hasn't element - */ - public List peekAll() throws EmptyQueueException { - synchronized (this.mQueue) { - if (isEmpty()) throw new EmptyQueueException(); - List l = new ArrayList(); - int head = this.mHead; - int tail = this.mTail; - do { - l.add((T)this.mQueue[tail]); - tail++; - if (tail >= this.mSize) { - tail = 0; - } - } while (head != tail); - return l; - } - } - - /** - * Method that returns if the queue is empty - * - * @return boolean If the queue is empty - */ - public boolean isEmpty() { - synchronized (this.mQueue) { - return this.mQueue[this.mTail] == null; - } - } - - /** - * Method that returns the number of items in the queue - * - * @return int The number of items - */ - public int items() { - int cc = 0; - int head = this.mHead; - int tail = this.mTail; - do { - if (this.mQueue[tail] == null) { - return cc; - } - cc++; - tail++; - if (tail >= this.mSize) { - tail = 0; - } - } while (head != tail); - return cc; - } - - /** - * Method that remove one item without synchronization (for be called from - * synchronized method). - * - * @return The item extracted - * @throws EmptyQueueException If the queue hasn't element - */ - private T noSynchronizedRemove() throws EmptyQueueException { - T o = (T)this.mQueue[this.mTail]; - if (o == null) throw new EmptyQueueException(); - this.mQueue[this.mTail] = null; - this.mTail++; - if (this.mTail >= this.mSize) { - this.mTail = 0; - } - return o; - } - - /** - * Method that returns the size of this queue. - * - * @return The size of this queue - */ - public int size() { - return mSize; - } -} \ No newline at end of file diff --git a/src/com/ruesga/android/wallpapers/photophase/GLES20WallpaperService.java b/src/com/ruesga/android/wallpapers/photophase/GLES20WallpaperService.java deleted file mode 100644 index f872d8e..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/GLES20WallpaperService.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - - - - -/** - * An abstract implementation of {@link EGLWallpaperService} based on GLES. - */ -public abstract class GLES20WallpaperService extends GLESWallpaperService { - - /** - * {@inheritDoc} - */ - @Override - public Engine onCreateEngine() { - return new GLES20Engine(); - } - - /** - * A class that extends the {@link GLESWallpaperService.GLESEngine} to add support for - * GLES20. - */ - class GLES20Engine extends GLESWallpaperService.GLESEngine { - /** - * {@inheritDoc} - */ - @Override - void initialize() { - // Request an OpenGL ES 2.x compatible context. - getGlSurfaceView().setEGLContextClientVersion(2); - getGlSurfaceView().setEGLConfigChooser(false); - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/GLESSurfaceDispatcher.java b/src/com/ruesga/android/wallpapers/photophase/GLESSurfaceDispatcher.java deleted file mode 100644 index 96b889f..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/GLESSurfaceDispatcher.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.opengl.GLSurfaceView; - -/** - * A class responsible of dispatch GLES commands inside the main GLThread. - */ -public class GLESSurfaceDispatcher { - - private final GLSurfaceView mSurface; - - /** - * Constructor of GLESSurfaceDispatcher - * - * @param v The associated GLES surface view - */ - public GLESSurfaceDispatcher(GLSurfaceView v) { - super(); - mSurface = v; - } - - /** - * Method that dispatch a GLES commands inside the main GLThread. - * - * @param r The runnable that execute the GLES commands - */ - public void dispatch(Runnable r) { - this.mSurface.queueEvent(r); - } - - /** - * Method that set the render mode - * - * @param mode The GLES render mode - */ - public void setRenderMode(int mode) { - if (mSurface.getRenderMode() != mode) { - mSurface.setRenderMode(mode); - } - } - - /** - * Method that requests a render to the surface. - */ - public void requestRender() { - mSurface.requestRender(); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/GLESWallpaperService.java b/src/com/ruesga/android/wallpapers/photophase/GLESWallpaperService.java deleted file mode 100644 index f2c9627..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/GLESWallpaperService.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.opengl.GLSurfaceView; -import android.opengl.GLSurfaceView.Renderer; -import android.view.SurfaceHolder; - - -/** - * An abstract implementation of {@link EGLWallpaperService} based on GLES. - */ -public abstract class GLESWallpaperService extends EGLWallpaperService { - - /** - * A listener interface for the {@link GLESWallpaperService.GLESEngine} engine class. - */ - public interface GLESEngineListener extends EGLEngineListener { - /** - * Method invoked when the EGL surface is starting to initialize. - * - * @param view GLSurfaceView The EGL view - */ - void onInitializeEGLView(GLSurfaceView view); - - /** - * Method invoked when the EGL surface is recycled. - * - * @param view GLSurfaceView The EGL view - * @param renderer The renderer associated - */ - void onDestroyEGLView(GLSurfaceView view, Renderer renderer); - - /** - * Method invoked when the EGL surface was initialized. - * - * @param view GLSurfaceView The EGL view - */ - void onEGLViewInitialized(GLSurfaceView view); - - /** - * Method invoked when the EGL context is paused - * - * @param renderer The renderer associated - */ - void onPause(Renderer renderer); - - /** - * Method invoked when the EGL context is resumed - * - * @param renderer The renderer associated - */ - void onResume(Renderer renderer); - } - - /** - * {@inheritDoc} - */ - @Override - public Engine onCreateEngine() { - return new GLESEngine(); - } - - /** - * A class that extends the {@link EGLWallpaperService.EGLEngine} to add support for - * GLES. - */ - class GLESEngine extends EGLWallpaperService.EGLEngine { - - private GLESEngineListener mListener = null; - private WallpaperGLSurfaceView mWallpaperGLSurfaceView = null; - private Renderer mRenderer = null; - - private boolean mRendererHasBeenSet; - private boolean mPauseOnPreview; - - /** - * Method that sets the EGL engine listener - * - * @param listener The EGL engine listener - */ - public void setGLESEngineListener(GLESEngineListener listener) { - mListener = listener; - setEGLEngineListener(listener); - } - - /** - * Method that sets the {@link GLSurfaceView} to use. - * - * @param wallpaperGLSurfaceView A {@link GLSurfaceView} - */ - public void setWallpaperGLSurfaceView(WallpaperGLSurfaceView wallpaperGLSurfaceView) { - mWallpaperGLSurfaceView = wallpaperGLSurfaceView; - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(SurfaceHolder surfaceHolder) { - super.onCreate(surfaceHolder); - - // Notify initialization - if (mListener != null) { - mListener.onInitializeEGLView(getGlSurfaceView()); - } - - // Initialize the GLES context - initialize(); - - // Set the renderer to our user-defined renderer. - mRenderer = getNewRenderer(getGlSurfaceView()); - getGlSurfaceView().setRenderer(mRenderer); - mRendererHasBeenSet = true; - - // Notify that the EGL is initialized - if (mListener != null) { - mListener.onEGLViewInitialized(getGlSurfaceView()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onDestroy() { - super.onDestroy(); - - // Notify initialization - if (mListener != null) { - mListener.onDestroyEGLView(getGlSurfaceView(), mRenderer); - } - mRenderer = null; - } - - /** - * Method that initializes - */ - void initialize() { - // Request an OpenGL ES 1.x compatible context. - getGlSurfaceView().setEGLContextClientVersion(1); - } - - /** - * Returns the renderer used by the engine - * - * @return Renderer The renderer - */ - protected Renderer getRenderer() { - return mRenderer; - } - - /** - * {@inheritDoc} - */ - @Override - public WallpaperGLSurfaceView createWallpaperGLSurfaceView() { - // Check whether to use a proprietary GLSurfaceView reference or an internal one - if (mWallpaperGLSurfaceView != null) { - return mWallpaperGLSurfaceView; - } - return super.createWallpaperGLSurfaceView(); - } - - /** - * {@inheritDoc} - */ - @Override - public void onVisibilityChanged(boolean visible) { - super.onVisibilityChanged(visible); - if (mRendererHasBeenSet) { - if (visible) { - getGlSurfaceView().onResume(); - getGlSurfaceView().setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - mListener.onResume(mRenderer); - } else { - // Check that the user is not previewing the live wallpaper; if they are, then - // if they open up a settings dialog that appears over the preview, we don’t - // want to pause rendering - boolean preview = isPreview(); - if (!preview || (preview && mPauseOnPreview)) { - getGlSurfaceView().setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - getGlSurfaceView().onPause(); - mListener.onPause(mRenderer); - } - } - } - } - - /** - * Method that determines if the surface view should be paused on preview mode - * - * @return boolean whether the surface view should be paused on preview mode - */ - public boolean isPauseOnPreview() { - return mPauseOnPreview; - } - - /** - * Method that sets if the surface view should be paused on preview mode - * - * @param pauseOnPreview whether the surface view should be paused on preview mode - */ - public void setPauseOnPreview(boolean pauseOnPreview) { - this.mPauseOnPreview = pauseOnPreview; - } - } - - /** - * Method that return a new EGL renderer. - * - * @param view The view that will be associated to the renderer - * @return Renderer The new EGL renderer. - */ - public abstract Renderer getNewRenderer(GLSurfaceView view); -} diff --git a/src/com/ruesga/android/wallpapers/photophase/MediaPictureDiscoverer.java b/src/com/ruesga/android/wallpapers/photophase/MediaPictureDiscoverer.java deleted file mode 100644 index c2aa116..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/MediaPictureDiscoverer.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.AsyncTask.Status; -import android.provider.MediaStore; -import android.util.Log; - -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * A class that load asynchronously the paths of all media stored in the device. - * This class only seek at the specified paths - */ -public class MediaPictureDiscoverer { - - private static final String TAG = "MediaPictureDiscoverer"; - - private static final boolean DEBUG = false; - - /** - * An interface that is called when new data is ready. - */ - public interface OnMediaPictureDiscoveredListener { - /** - * Called when the starting to fetch data - * - * @param userRequest If the user requested this media discovery - */ - void onStartMediaDiscovered(boolean userRequest); - /** - * Called when the all the data is ready - * - * @param images All the images paths found - * @param userRequest If the user requested this media discovery - */ - void onEndMediaDiscovered(File[] images, boolean userRequest); - /** - * Called when the partial data is ready - * - * @param images All the images paths found - * @param userRequest If the user requested this media discovery - */ - void onPartialMediaDiscovered(File[] images, boolean userRequest); - } - - /** - * The asynchronous task for query the MediaStore - */ - private class AsyncDiscoverTask extends AsyncTask> { - - private final ContentResolver mFinalContentResolver; - private final OnMediaPictureDiscoveredListener mFinalCallback; - private final Set mFilter; - private final Set mLastAlbums; - private final Set mNewAlbums; - private final boolean mIsAutoSelectNewAlbums; - private final boolean mUserRequest; - - /** - * Constructor of AsyncDiscoverTask - * - * @param cr The {@link ContentResolver} - * @param cb The {@link OnMediaPictureDiscoveredListener} listener - * @param userRequest If the request was generated by the user - */ - public AsyncDiscoverTask( - ContentResolver cr, OnMediaPictureDiscoveredListener cb, boolean userRequest) { - super(); - mFinalContentResolver = cr; - mFinalCallback = cb; - mFilter = Preferences.Media.getSelectedMedia(); - mLastAlbums = Preferences.Media.getLastDiscorevedAlbums(); - mIsAutoSelectNewAlbums = Preferences.Media.isAutoSelectNewAlbums(); - mNewAlbums = new HashSet(); - mUserRequest = userRequest; - } - - /** - * {@inheritDoc} - */ - @Override - protected List doInBackground(Void...params) { - try { - // Start progress - publishProgress(new File[]{}); - - // The columns to read - final String[] projection = {MediaStore.MediaColumns.DATA}; - - // Query external content - List paths = - getPictures( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - projection, - null, - null); - if (DEBUG) { - int cc = paths.size(); - Log.v(TAG, "Pictures found (" + cc + "):"); - for (int i = 0; i < cc; i++) { - Log.v(TAG, "\t" + paths.get(i)); - } - } - return paths; - - } catch (Exception e) { - Log.e(TAG, "AsyncDiscoverTask failed.", e); - - // Return and empty list - return new ArrayList(); - } finally { - // Save the filter (could have new albums) - Preferences.Media.setSelectedMedia(mContext, mFilter); - Preferences.Media.setLastDiscorevedAlbums(mContext, mNewAlbums); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onProgressUpdate(File... values) { - if (mFinalCallback != null) { - if (values == null || values.length == 0) { - mFinalCallback.onStartMediaDiscovered(mUserRequest); - } else { - mFinalCallback.onPartialMediaDiscovered(values, mUserRequest); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPostExecute(List result) { - if (mFinalCallback != null) { - mFinalCallback.onEndMediaDiscovered(result.toArray(new File[result.size()]), mUserRequest); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onCancelled(List result) { - // Nothing found - if (mFinalCallback != null) { - // Overwrite the user request setting. If the task is cancelled then - // there is no notification to send to the user - mFinalCallback.onEndMediaDiscovered(new File[]{}, false); - } - } - - /** - * Method that return all the media store pictures for the content uri - * - * @param uri The content uri where to search - * @param projection The field data to return - * @param where A filter - * @param args The filter arguments - * @return List The pictures found - */ - private List getPictures( - Uri uri, String[] projection, String where, String[] args) { - long start = System.currentTimeMillis(); - List paths = new ArrayList(); - List partial = new ArrayList(); - Cursor c = mFinalContentResolver.query(uri, projection, where, args, null); - if (c != null) { - try { - int i = 0; - while (c.moveToNext()) { - // Only valid files (those i can read) - String p = c.getString(0); - if (p != null) { - File f = new File(p); - catalog(f); - - // Check if is a valid filter - if (matchFilter(f)) { - paths.add(f); - partial.add(f); - } - } - - // Publish partial data - if (i % 5 == 0 && partial.size() > 0) { - publishProgress(partial.toArray(new File[partial.size()])); - partial.clear(); - } - i++; - } - } finally { - try { - c.close(); - } catch (Exception e) { - // Ignore: handle exception - } - } - } - long end = System.currentTimeMillis(); - if (DEBUG) Log.v(TAG, "Media reloaded in " + (end - start) + " miliseconds"); - return paths; - } - - /** - * Method that checks if the picture match the preferences filter - * - * @param picture The picture to check - * @return boolean whether the picture match the filter - */ - private boolean matchFilter(File picture) { - return mFilter.contains(picture.getAbsolutePath()) || - mFilter.contains(picture.getParentFile().getAbsolutePath()); - } - - /** - * Method that catalog the file (set its album and determine if is a new album) - * - * @param f The file to catalog - */ - private void catalog(File f) { - File parent = f.getParentFile(); - String albumPath = parent.getAbsolutePath(); - - // Add to new albums - mNewAlbums.add(albumPath); - - // Is a new album? - if (!mLastAlbums.contains(albumPath)) { - // Is in the filter? - if (mIsAutoSelectNewAlbums && !mFilter.contains(albumPath)) { - // Add the album to the selected filter - mFilter.add(parent.getAbsolutePath()); - } - } - } - } - - /*package*/ final Context mContext; - private final OnMediaPictureDiscoveredListener mCallback; - - private AsyncDiscoverTask mTask; - - /** - * Constructor of MediaPictureDiscoverer. - * - * @param ctx The current context - * @param callback A callback to returns the data when it gets ready - */ - public MediaPictureDiscoverer(Context ctx, OnMediaPictureDiscoveredListener callback) { - super(); - mContext = ctx; - mCallback = callback; - } - - /** - * Method that request a new reload of the media store picture data. - * - * @param userRequest If the request was generated by the user - */ - public synchronized void discover(boolean userRequest) { - if (mTask != null && mTask.getStatus().compareTo(Status.FINISHED) != 0 && - !mTask.isCancelled()) { - mTask.cancel(true); - } - mTask = new AsyncDiscoverTask(mContext.getContentResolver(), mCallback, userRequest); - mTask.execute(); - } - - /** - * Method that destroy the references of this class - */ - public void recycle() { - if (mTask != null && !mTask.isCancelled()) { - mTask.cancel(true); - } - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/PhotoFrame.java b/src/com/ruesga/android/wallpapers/photophase/PhotoFrame.java deleted file mode 100644 index a2a1f6c..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/PhotoFrame.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.content.Context; -import android.graphics.RectF; -import android.opengl.GLES20; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - - -/** - * A GLES square geometry that represents one photo frame for show in the wallpaper. - */ -public class PhotoFrame implements TextureRequestor { - - /** - * @hide - */ - public static final int COORDS_PER_VERTER = 3; - - // The default texture coordinates (fit to frame) - private static final float[] DEFAULT_TEXTURE_COORDS = { - 0.0f, 1.0f, - 1.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f - }; - - - private final TextureManager mTextureManager; - - private final float[] mFrameVertex, mPhotoVertex; - private final float mFrameWidth, mFrameHeight; - private final float mPhotoWidth, mPhotoHeight; - - - private FloatBuffer mPositionBuffer; - private FloatBuffer mTextureBuffer; - - private GLESTextureInfo mTextureInfo; - - private final GLColor mBackgroundColor; - - private boolean mLoaded; - - private final Object mSync = new Object(); - - /** - * Constructor of PhotoFrame. - * - * @param ctx The current context - * @param textureManager The texture manager - * @param frameVertex A 4 dimension array with the coordinates per vertex plus padding - * @param photoVertex A 4 dimension array with the coordinates per vertex without padding - * @param color Background color - */ - public PhotoFrame(Context ctx, TextureManager textureManager, - float[] frameVertex, float[] photoVertex, GLColor color) { - super(); - mLoaded = false; - mBackgroundColor = color; - mTextureManager = textureManager; - - // Save dimensions - mFrameVertex = frameVertex; - mFrameWidth = frameVertex[6] - frameVertex[4]; - mFrameHeight = frameVertex[1] - frameVertex[5]; - mPhotoVertex = photoVertex; - mPhotoWidth = photoVertex[6] - photoVertex[4]; - mPhotoHeight = photoVertex[5] - photoVertex[1]; - - // Initialize vertex byte buffer for shape coordinates - ByteBuffer bb = ByteBuffer.allocateDirect(photoVertex.length * 4); // (# of coordinate values * 4 bytes per float) - bb.order(ByteOrder.nativeOrder()); - mPositionBuffer = bb.asFloatBuffer(); - mPositionBuffer.put(photoVertex); - mPositionBuffer.position(0); - - // Load the texture - mTextureInfo = null; - - // Request a new image for this frame - textureManager.request(this); - } - - /** - * {@inheritDoc} - */ - @Override - public RectF getRequestorDimensions() { - return new RectF(0, 0, mPhotoWidth, mPhotoHeight); - } - - /** - * {@inheritDoc} - */ - @Override - public void setTextureHandle(GLESTextureInfo ti) { - // If the picture is invalid request a new texture - if (ti == null || ti.handle <= 0) { - mTextureManager.request(this); - return; - } - - // Full frame picture - setTextureHandle(ti, DEFAULT_TEXTURE_COORDS); - mLoaded = true; - } - - /** - * Internal method that expose the texture coordinates to set - * - * @param ti The texture info - * @param textureCoords The texture coordinates - */ - private void setTextureHandle(GLESTextureInfo ti, final float[] textureCoords) { - // Recycle the previous handle - if (mTextureInfo != null) { - if (GLES20.glIsTexture(mTextureInfo.handle)) { - int[] textures = new int[]{mTextureInfo.handle}; - GLES20.glDeleteTextures(1, textures, 0); - GLESUtil.glesCheckError("glDeleteTextures"); - } - if (mTextureInfo.bitmap != null) { - mTextureInfo.bitmap.recycle(); - mTextureInfo.bitmap = null; - } - } - - // Initialize vertex byte buffer for shape coordinates - ByteBuffer bb = ByteBuffer.allocateDirect(textureCoords.length * 4); // (# of coordinate values * 4 bytes per float) - bb.order(ByteOrder.nativeOrder()); - synchronized (mSync) { - // Swap buffers - mTextureBuffer = bb.asFloatBuffer(); - mTextureBuffer.put(textureCoords); - mTextureBuffer.position(0); - } - mTextureInfo = ti; - } - - /** - * Method that returns the frame vertex - * - * @return float[] The frame vertex - */ - public float[] getFrameVertex() { - return mFrameVertex; - } - - /** - * Method that returns the frame width - * - * @return float The frame width - */ - public float getFrameWidth() { - return mFrameWidth; - } - - /** - * Method that returns the frame height - * - * @return float The frame height - */ - public float getFrameHeight() { - return mFrameHeight; - } - - /** - * Method that returns the photo vertex - * - * @return float[] The photo vertex - */ - public float[] getPhotoVertex() { - return mPhotoVertex; - } - - /** - * Method that returns the photo width - * - * @return float The photo width - */ - public float getPhotoWidth() { - return mPhotoWidth; - } - - /** - * Method that returns the photo height - * - * @return float The photo height - */ - public float getPhotoHeight() { - return mPhotoHeight; - } - - /** - * Method that returns the position vertex buffer - * - * @return FloatBuffer The position vertex buffer - */ - public FloatBuffer getPositionBuffer() { - return mPositionBuffer; - } - - /** - * Method that returns the texture buffer - * - * @return FloatBuffer The texture buffer - */ - public FloatBuffer getTextureBuffer() { - synchronized (mSync) { - return mTextureBuffer; - } - } - - /** - * Method that returns the texture handle - * - * @return int The texture handle - */ - public int getTextureHandle() { - if (mTextureInfo != null) { - return mTextureInfo.handle; - } - return -1; - } - - /** - * Method that returns the texture info - * - * @return GLESTextureInfo The texture info - */ - public GLESTextureInfo getTextureInfo() { - return mTextureInfo; - } - - /** - * Method that returns the background color of the frame - * - * @return GLColor The background color - */ - public GLColor getBackgroundColor() { - return mBackgroundColor; - } - - /** - * Method that returns if the frame is loaded (has its picture loaded) - * - * @return boolean If the frame is loaded (has its picture loaded) - */ - public boolean isLoaded() { - return mLoaded; - } - - /** - * Request a recycle of the references of the object - */ - public void recycle() { - if (mTextureInfo != null && mTextureInfo.handle != 0) { - int[] textures = new int[]{mTextureInfo.handle}; - GLES20.glDeleteTextures(1, textures, 0); - GLESUtil.glesCheckError("glDeleteTextures"); - } - mTextureInfo = null; - - if (mPositionBuffer != null) { - mPositionBuffer.clear(); - } - if (mTextureBuffer != null) { - mTextureBuffer.clear(); - } - mPositionBuffer = null; - mTextureBuffer = null; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseActivity.java b/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseActivity.java deleted file mode 100644 index 052bbcb..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseActivity.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.app.Activity; -import android.content.Intent; -import android.opengl.GLSurfaceView; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.Window; -import android.view.WindowManager; - -import com.ruesga.android.wallpapers.photophase.preferences.PhotoPhasePreferences; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider; - -/** - * A testing activity to simulate the PhotoPhase Live Wallpaper inside an GLES activity. - */ -public class PhotoPhaseActivity extends Activity implements OnTouchListener { - - private static final String TAG = "PhotoPhaseActivity"; - - private static final boolean DEBUG = false; - - private GLSurfaceView mGLSurfaceView; - private PhotoPhaseRenderer mRenderer; - - /** - * {@inheritDoc} - */ - @Override - protected void onCreate(Bundle savedInstanceState) { - if (DEBUG) Log.d(TAG, "onCreate"); - super.onCreate(savedInstanceState); - - boolean preserveEglCtx = getResources().getBoolean(R.bool.config_preserve_egl_context); - - // Instance the application - PreferencesProvider.reload(this); - Colors.register(this); - - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - - // Configure the EGL context - mGLSurfaceView = new GLSurfaceView(getApplicationContext()); - mGLSurfaceView.setEGLContextClientVersion(2); - mGLSurfaceView.setEGLConfigChooser(false); - mRenderer = new PhotoPhaseRenderer(this, new GLESSurfaceDispatcher(mGLSurfaceView)); - mGLSurfaceView.setRenderer(mRenderer); - mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - mGLSurfaceView.setPreserveEGLContextOnPause(preserveEglCtx); - mGLSurfaceView.setOnTouchListener(this); - setContentView(mGLSurfaceView); - - mRenderer.onCreate(); - } - - /** - * {@inheritDoc} - */ - @Override - public void onDestroy() { - if (DEBUG) Log.d(TAG, "onDestroy"); - super.onDestroy(); - mRenderer.onDestroy(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onResume() { - super.onResume(); - if (DEBUG) Log.d(TAG, "onResume"); - mGLSurfaceView.onResume(); - mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - if (mRenderer != null) { - mRenderer.onResume(); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPause() { - super.onPause(); - if (DEBUG) Log.d(TAG, "onPause"); - mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - mRenderer.onPause(); - mGLSurfaceView.onPause(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.main, menu); - return super.onCreateOptionsMenu(menu); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.mnu_settings: - Intent settings = new Intent(this, PhotoPhasePreferences.class); - startActivity(settings); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onTouch(View v, MotionEvent event) { - int action = event.getAction(); - float x = event.getX(); - float y = event.getY(); - switch (action) { - case MotionEvent.ACTION_UP: - mRenderer.onTouch(x, y); - return true; - - default: - break; - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void onLowMemory() { - super.onLowMemory(); - Log.i(TAG, "onLowMemory"); - // Pause the wallpaper and destroy the cached textures - mRenderer.onPause(); - mRenderer.onLowMemory(); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseRenderer.java b/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseRenderer.java deleted file mode 100644 index 0df0ace..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseRenderer.java +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Configuration; -import android.content.res.Resources.NotFoundException; -import android.graphics.PointF; -import android.graphics.Rect; -import android.media.effect.EffectContext; -import android.net.Uri; -import android.opengl.GLES20; -import android.opengl.GLException; -import android.opengl.GLSurfaceView; -import android.opengl.Matrix; -import android.os.Handler; -import android.util.Log; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; -import com.ruesga.android.wallpapers.photophase.utils.Utils; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.preferences.TouchAction; -import com.ruesga.android.wallpapers.photophase.shapes.ColorShape; -import com.ruesga.android.wallpapers.photophase.shapes.OopsShape; -import com.ruesga.android.wallpapers.photophase.transitions.Transition; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -/** - * The EGL renderer of PhotoPhase Live Wallpaper. - */ -public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { - - private static final String TAG = "PhotoPhaseRenderer"; - - private static final boolean DEBUG = false; - - private final long mInstance; - private static long sInstances; - - /*package*/ final Context mContext; - /*package*/ EffectContext mEffectContext; - private final Handler mHandler; - /*package*/ final GLESSurfaceDispatcher mDispatcher; - /*package*/ TextureManager mTextureManager; - - /*package*/ PhotoPhaseWallpaperWorld mWorld; - /*package*/ ColorShape mOverlay; - /*package*/ OopsShape mOopsShape; - - /*package*/ long mLastRunningTransition; - - private long mLastTouchTime; - private static final long TOUCH_BARRIER_TIME = 1000L; - - /*package*/ int mWidth = -1; - /*package*/ int mHeight = -1; - private int mStatusBarHeight = 0; - /*package*/ int mMeasuredHeight = -1; - - private final float[] mMVPMatrix = new float[16]; - private final float[] mProjMatrix = new float[16]; - private final float[] mVMatrix = new float[16]; - - private final Object mDrawing = new Object(); - - /*package*/ final Object mMediaSync = new Object(); - private PendingIntent mMediaScanIntent; - - private final BroadcastReceiver mSettingsChangedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - // Check what flags are been requested - boolean recreateWorld = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, false); - boolean redraw = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, false); - boolean emptyTextureQueue = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_EMPTY_TEXTURE_QUEUE, false); - boolean mediaReload = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, false); - boolean mediaIntervalChanged = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_INTERVAL_CHANGED, false); - if (emptyTextureQueue) { - if (mTextureManager != null) { - mTextureManager.emptyTextureQueue(true); - } - } - if (mediaReload) { - synchronized (mMediaSync) { - if (mTextureManager != null) { - boolean userReloadRequest = - intent.getBooleanExtra( - PreferencesProvider.EXTRA_ACTION_MEDIA_USER_RELOAD_REQUEST, false); - mTextureManager.reloadMedia(userReloadRequest); - scheduleOrCancelMediaScan(); - } - } - } - if (mediaIntervalChanged) { - scheduleOrCancelMediaScan(); - } - if (recreateWorld && mWorld != null) { - // Recreate the wallpaper world - try { - mWorld.recreateWorld(mWidth, mMeasuredHeight); - } catch (GLException e) { - Log.e(TAG, "Cannot recreate the wallpaper world.", e); - } - } - if (redraw) { - mDispatcher.requestRender(); - } - } - }; - - private final Runnable mTransitionThread = new Runnable() { - /** - * {@inheritDoc} - */ - @Override - public void run() { - // Run in GLES's thread - mDispatcher.dispatch(new Runnable() { - @Override - public void run() { - try { - // Select a new transition - mWorld.selectRandomTransition(); - mLastRunningTransition = System.currentTimeMillis(); - - // Now force continuously render while transition is applied - mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - } catch (Throwable ex) { - Log.e(TAG, "Something was wrong selecting the transition", ex); - } - } - }); - } - }; - - /** - * Constructor of PhotoPhaseRenderer - * - * @param ctx The current context - * @param dispatcher The GLES dispatcher - */ - public PhotoPhaseRenderer(Context ctx, GLESSurfaceDispatcher dispatcher) { - super(); - mContext = ctx; - mHandler = new Handler(); - mDispatcher = dispatcher; - mInstance = sInstances; - sInstances++; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (mInstance ^ (mInstance >>> 32)); - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PhotoPhaseRenderer other = (PhotoPhaseRenderer) obj; - if (mInstance != other.mInstance) - return false; - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "PhotoPhaseRenderer [instance: " + mInstance + "]"; - } - - /** - * Method called when renderer is created - */ - public void onCreate() { - if (DEBUG) Log.d(TAG, "onCreate [" + mInstance + "]"); - // Register a receiver to listen for media reload request - IntentFilter filter = new IntentFilter(); - filter.addAction(PreferencesProvider.ACTION_SETTINGS_CHANGED); - mContext.registerReceiver(mSettingsChangedReceiver, filter); - - // Check whether the media scan is active - int interval = Preferences.Media.getRefreshFrecuency(); - if (interval != Preferences.Media.MEDIA_RELOAD_DISABLED) { - // Schedule a media scan - scheduleMediaScan(interval); - } - } - - /** - * Method called when renderer is destroyed - */ - public void onDestroy() { - if (DEBUG) Log.d(TAG, "onDestroy [" + mInstance + "]"); - // Register a receiver to listen for media reload request - mContext.unregisterReceiver(mSettingsChangedReceiver); - recycle(); - if (mEffectContext != null) { - mEffectContext.release(); - } - mEffectContext = null; - mWidth = -1; - mHeight = -1; - mMeasuredHeight = -1; - } - - /** - * Method called when system runs under low memory - */ - public void onLowMemory() { - mTextureManager.emptyTextureQueue(false); - } - - /** - * Method called when the renderer should be paused - */ - public void onPause() { - if (DEBUG) Log.d(TAG, "onPause [" + mInstance + "]"); - mHandler.removeCallbacks(mTransitionThread); - if (mTextureManager != null) { - mTextureManager.setPause(true); - } - } - - /** - * Method called when the renderer should be resumed - */ - public void onResume() { - if (DEBUG) Log.d(TAG, "onResume [" + mInstance + "]"); - if (mTextureManager != null) { - mTextureManager.setPause(false); - } - } - - /** - * Method called when the renderer should process a touch event over the screen - * - * @param x The x coordinate - * @param y The y coordinate - */ - public void onTouch(float x , float y) { - if (mWorld != null) { - // Do user action - TouchAction touchAction = Preferences.General.getTouchAction(); - if (touchAction.compareTo(TouchAction.NONE) == 0) { - //Ignore - } else { - // Avoid to handle multiple touchs - long touchTime = System.currentTimeMillis(); - long diff = touchTime - mLastTouchTime; - mLastTouchTime = touchTime; - if (diff < TOUCH_BARRIER_TIME) { - return; - } - - // Retrieve the photo frame for its coordinates - final PhotoFrame frame = mWorld.getFrameFromCoordinates(new PointF(x, y)); - if (frame == null) { - Log.w(TAG, "No frame from coordenates"); - return; - } - - // Apply the action - if (touchAction.compareTo(TouchAction.TRANSITION) == 0) { - try { - // Select the frame with a transition - // Run in GLES's thread - mDispatcher.dispatch(new Runnable() { - @Override - public void run() { - // Select a new transition - deselectCurrentTransition(); - mWorld.selectTransition(frame); - mLastRunningTransition = System.currentTimeMillis(); - - // Now force continuously render while transition is applied - mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - } - }); - - } catch (NotFoundException ex) { - Log.e(TAG, "The frame not exists " + frame.getTextureInfo().path, ex); - } - - } else if (touchAction.compareTo(TouchAction.OPEN) == 0) { - // Open the image - try { - Uri uri = getUriFromFrame(frame); - if (uri != null) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.setDataAndType(uri, "image/*"); - mContext.startActivity(intent); - } - } catch (ActivityNotFoundException ex) { - Log.e(TAG, "Open activity not found for " + frame.getTextureInfo().path, ex); - } - - } else if (touchAction.compareTo(TouchAction.SHARE) == 0) { - // Send the image - try { - Uri uri = getUriFromFrame(frame); - if (uri != null) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.setType("image/*"); - intent.putExtra(Intent.EXTRA_STREAM, uri); - mContext.startActivity(intent); - } - } catch (ActivityNotFoundException ex) { - Log.e(TAG, "Send activity not found for " + frame.getTextureInfo().path, ex); - } - } - } - } - } - - /** - * Method that returns an Uri reference from a photo frame - * - * @param frame The photo frame - * @return Uri The image uri - */ - private static Uri getUriFromFrame(final PhotoFrame frame) { - // Sanity checks - GLESTextureInfo info = frame.getTextureInfo(); - if (info == null) { - Log.e(TAG, "The frame has not a valid reference right now." + - "Touch action is not available."); - return null; - } - if (info.path == null || !info.path.isFile()) { - Log.e(TAG, "The image do not exists. Touch action is not available."); - return null; - } - - // Return the uri from the path - return Uri.fromFile(frame.getTextureInfo().path); - } - - /** - * Method that deselect the current transition - */ - /*package*/ synchronized void deselectCurrentTransition() { - mHandler.removeCallbacks(mTransitionThread); - mWorld.deselectTransition(mMVPMatrix); - mLastRunningTransition = 0; - } - - /*package*/ void scheduleOrCancelMediaScan() { - int interval = Preferences.Media.getRefreshFrecuency(); - if (interval != Preferences.Media.MEDIA_RELOAD_DISABLED) { - scheduleMediaScan(interval); - } else { - cancelMediaScan(); - } - } - - /** - * Method that schedules a new media scan - * - * @param interval The new interval - */ - private void scheduleMediaScan(int interval) { - AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); - - Intent i = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); - i.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); - mMediaScanIntent = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); - - long milliseconds = Preferences.Media.getRefreshFrecuency() * 1000L; - am.set(AlarmManager.RTC, System.currentTimeMillis() + milliseconds, mMediaScanIntent); - } - - /** - * Method that cancels a pending media scan - */ - private void cancelMediaScan() { - if (mMediaScanIntent != null) { - AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); - am.cancel(mMediaScanIntent); - mMediaScanIntent = null; - } - } - - /** - * Method that destroy all the internal references - */ - private void recycle() { - if (DEBUG) Log.d(TAG, "recycle [" + mInstance + "]"); - synchronized (mDrawing) { - // Remove any pending handle - if (mHandler != null && mTransitionThread != null) { - mHandler.removeCallbacks(mTransitionThread); - } - - // Delete the world - if (mWorld != null) mWorld.recycle(); - if (mTextureManager != null) mTextureManager.recycle(); - if (mOverlay != null) mOverlay.recycle(); - if (mOopsShape != null) mOopsShape.recycle(); - mWorld = null; - mTextureManager = null; - mOverlay = null; - mOopsShape = null; - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { - if (DEBUG) Log.d(TAG, "onSurfaceCreated [" + mInstance + "]"); - - mWidth = -1; - mHeight = -1; - mMeasuredHeight = -1; - mStatusBarHeight = 0; - - // We have a 2d (fake) scenario, disable all unnecessary tests. Deep are - // necessary for some 3d effects - GLES20.glDisable(GL10.GL_DITHER); - GLESUtil.glesCheckError("glDisable"); - GLES20.glDisable(GL10.GL_CULL_FACE); - GLESUtil.glesCheckError("glDisable"); - GLES20.glEnable(GL10.GL_DEPTH_TEST); - GLESUtil.glesCheckError("glEnable"); - GLES20.glDepthMask(false); - GLESUtil.glesCheckError("glDepthMask"); - GLES20.glDepthFunc(GLES20.GL_LEQUAL); - GLESUtil.glesCheckError("glDepthFunc"); - - // Create an effect context - if (mEffectContext != null) { - mEffectContext.release(); - } - mEffectContext = EffectContext.createWithCurrentGlContext(); - - // Create the texture manager and recycle the old one - if (mTextureManager == null) { - // Precalculate the window size for the TextureManager. In onSurfaceChanged - // the best fixed size will be set. The disposition size is simple for a better - // performance of the internal arrays - final Configuration conf = mContext.getResources().getConfiguration(); - int orientation = mContext.getResources().getConfiguration().orientation; - int w = (int) AndroidHelper.convertDpToPixel(mContext, conf.screenWidthDp); - int h = (int) AndroidHelper.convertDpToPixel(mContext, conf.screenHeightDp); - Rect dimensions = new Rect(0, 0, w, h); - int cc = (orientation == Configuration.ORIENTATION_PORTRAIT) - ? Preferences.Layout.getPortraitDisposition().size() - : Preferences.Layout.getLandscapeDisposition().size(); - - // Recycle the current texture manager and create a new one - recycle(); - mTextureManager = new TextureManager( - mContext, mHandler, mEffectContext, mDispatcher, cc, dimensions); - } else { - mTextureManager.updateEffectContext(mEffectContext); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onSurfaceChanged(GL10 glUnused, int width, int height) { - if (DEBUG) Log.d(TAG, "onSurfaceChanged [" + mInstance + "," + width + "x" + height + "]"); - - // Check if the size was changed - if (mWidth == width && mHeight == height) { - return; - } - - // Save the width and height to avoid recreate the world - mWidth = width; - mHeight = height; - mStatusBarHeight = AndroidHelper.calculateStatusBarHeight(mContext); - mMeasuredHeight = mHeight + mStatusBarHeight; - - // Calculate a better fixed size for the pictures - Rect dimensions = Utils.isTablet(mContext) - ? new Rect(0, 0, width / 2, height / 2) - : new Rect(0, 0, width / 4, height / 4); - Rect screenDimensions = new Rect(0, mStatusBarHeight, width, height); - mTextureManager.setDimensions(dimensions); - mTextureManager.setScreenDimesions(screenDimensions); - mTextureManager.setPause(false); - - // Create the wallpaper (destroy the previous) - if (mWorld != null) { - mWorld.recycle(); - } - mWorld = new PhotoPhaseWallpaperWorld(mContext, mTextureManager); - - // Create the overlay shape - final float[] vertex = { - -1.0f, -1.0f, - 1.0f, -1.0f, - -1.0f, 1.0f, - 1.0f, 1.0f - }; - mOverlay = new ColorShape(mContext, vertex, Colors.getOverlay()); - - // Create the Oops shape - mOopsShape = new OopsShape(mContext, R.string.no_pictures_oops_msg); - - // Set the viewport and the fustrum - GLES20.glViewport(0, -mStatusBarHeight, mWidth, mHeight); - GLESUtil.glesCheckError("glViewport"); - Matrix.frustumM(mProjMatrix, 0, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 2.0f); - - // Recreate the wallpaper world - try { - mWorld.recreateWorld(width, mMeasuredHeight); - } catch (GLException e) { - Log.e(TAG, "Cannot recreate the wallpaper world.", e); - } - - // Force an immediate redraw of the screen (draw thread could be in dirty mode only) - deselectCurrentTransition(); - mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - } - - /** - * {@inheritDoc} - */ - @Override - public void onDrawFrame(GL10 glUnused) { - synchronized (mDrawing) { - // Set the projection, view and model - GLES20.glViewport(0, -mStatusBarHeight, mWidth, mHeight); - Matrix.setLookAtM(mVMatrix, 0, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); - Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0); - - if (mTextureManager != null) { - if (mTextureManager.getStatus() == 1 && mTextureManager.isEmpty()) { - // Advise the user and stop - drawOops(); - mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - - } else { - // Draw the background - drawBackground(); - - if (mWorld != null) { - // Now draw the world (all the photo frames with effects) - mWorld.draw(mMVPMatrix); - - // Check if we have some pending transition or transition has - // exceed its timeout - if (Preferences.General.Transitions.getTransitionInterval() > 0) { - if (!mWorld.hasRunningTransition() || firedTransitionTimeout()) { - mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - - // Now start a delayed thread to generate the next effect - deselectCurrentTransition(); - mLastRunningTransition = 0; - mHandler.postDelayed(mTransitionThread, - Preferences.General.Transitions.getTransitionInterval()); - } - } else { - // Just display the initial frames and never make transitions - if (!mWorld.hasRunningTransition() || firedTransitionTimeout()) { - mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - } - } - - // Draw the overlay - drawOverlay(); - } - } - } - - } - } - - /** - * Check whether the transition has exceed the timeout - * - * @return boolean if the transition has exceed the timeout - */ - private boolean firedTransitionTimeout() { - long now = System.currentTimeMillis(); - long diff = now - mLastRunningTransition; - return mLastRunningTransition != 0 && diff > Transition.MAX_TRANSTION_TIME; - } - - /** - * Method that draws the background of the wallpaper - */ - private static void drawBackground() { - GLColor bg = Colors.getBackground(); - GLES20.glClearColor(bg.r, bg.g, bg.b, bg.a); - GLESUtil.glesCheckError("glClearColor"); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); - GLESUtil.glesCheckError("glClear"); - } - - /** - * Method that draws the overlay of the wallpaper - */ - private void drawOverlay() { - if (mOverlay != null) { - mOverlay.setAlpha(Preferences.General.getWallpaperDim() / 100.0f); - mOverlay.draw(mMVPMatrix); - } - } - - /** - * Method that draws the oops message - */ - private void drawOops() { - if (mOopsShape != null) { - mOopsShape.draw(mMVPMatrix); - } - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaper.java b/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaper.java deleted file mode 100644 index f2ceeed..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaper.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.app.ActivityManager; -import android.app.WallpaperManager; -import android.content.Context; -import android.opengl.GLSurfaceView; -import android.opengl.GLSurfaceView.Renderer; -import android.os.Bundle; -import android.os.Handler; -import android.util.Log; -import android.view.ViewConfiguration; - -import com.ruesga.android.wallpapers.photophase.GLESWallpaperService.GLESEngineListener; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider; - -import java.util.ArrayList; -import java.util.List; - - -/** - * The PhotoPhase Live Wallpaper service. - */ -public class PhotoPhaseWallpaper - extends GLES20WallpaperService implements GLESEngineListener { - - private static final String TAG = "PhotoPhaseWallpaper"; - - private static final boolean DEBUG = false; - - private List mRenderers; - private PhotoPhaseWallpaperEngine mEngine; - - private boolean mPreserveEGLContext; - - // List of the current top activities. Tap should be ignored when this acitivities are - // in the foreground - static final String[] TOP_ACTIVITIES = {"com.android.internal.app.ChooserActivity"}; - - /** - * {@inheritDoc} - */ - @Override - public void onCreate() { - if (DEBUG) Log.d(TAG, "onCreate"); - super.onCreate(); - - // Load the configuration - mPreserveEGLContext = getResources().getBoolean(R.bool.config_preserve_egl_context); - mRenderers = new ArrayList(); - - // Instance the application - PreferencesProvider.reload(this); - Colors.register(this); - } - - /** - * {@inheritDoc} - */ - @Override - public void onDestroy() { - if (DEBUG) Log.d(TAG, "onDestroy"); - super.onDestroy(); - for (PhotoPhaseRenderer renderer : mRenderers) { - renderer.onDestroy(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Engine onCreateEngine() { - mEngine = new PhotoPhaseWallpaperEngine(this); - return mEngine; - } - - /** - * A wallpaper engine implementation using GLES. - */ - class PhotoPhaseWallpaperEngine extends GLES20WallpaperService.GLES20Engine { - - private final Handler mHandler; - /*package*/ final ActivityManager mActivityManager; - - /** - * Constructor of PhotoPhaseWallpaperEngine - * - * @param wallpaper The wallpaper service reference - */ - PhotoPhaseWallpaperEngine(PhotoPhaseWallpaper wallpaper) { - super(); - mHandler = new Handler(); - mActivityManager = (ActivityManager)getApplication().getSystemService(ACTIVITY_SERVICE); - setOffsetNotificationsEnabled(false); - setTouchEventsEnabled(false); - setGLESEngineListener(wallpaper); - setWallpaperGLSurfaceView(new PhotoPhaseWallpaperGLSurfaceView(wallpaper)); - setPauseOnPreview(true); - } - - /** - * Out custom GLSurfaceView class to let us access all events stuff. - */ - class PhotoPhaseWallpaperGLSurfaceView extends WallpaperGLSurfaceView { - /** - * The constructor of PhotoPhaseWallpaperGLSurfaceView. - * - * @param context The current context - */ - public PhotoPhaseWallpaperGLSurfaceView(Context context) { - super(context); - } - } - - /** - * {@inheritDoc} - */ - @Override - public Bundle onCommand(final String action, final int x, final int y, final int z, - final Bundle extras, final boolean resultRequested) { - if (action.compareTo(WallpaperManager.COMMAND_TAP) == 0) { - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - // Only if the wallpaper is visible after a long press and - // not in preview mode - if (isVisible() && !isPreview()) { - List taskInfo = - mActivityManager.getRunningTasks(1); - String topActivity = taskInfo.get(0).topActivity.getClassName(); - for (String activity : TOP_ACTIVITIES) { - if (activity.compareTo(topActivity) == 0) { - // Ignore tap event - return; - } - } - - // Pass the x and y position to the renderer - ((PhotoPhaseRenderer)getRenderer()).onTouch(x, y); - } - } - }, ViewConfiguration.getLongPressTimeout() + 100L); - } - return super.onCommand(action, x, y, z, extras, resultRequested); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onLowMemory() { - super.onLowMemory(); - Log.i(TAG, "onLowMemory"); - for (PhotoPhaseRenderer renderer : mRenderers) { - // Pause the wallpaper and destroy the cached textures - renderer.onPause(); - renderer.onLowMemory(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onInitializeEGLView(GLSurfaceView view) { - if (DEBUG) Log.d(TAG, "onInitializeEGLView"); - } - - /** - * {@inheritDoc} - */ - @Override - public void onDestroyEGLView(GLSurfaceView view, Renderer renderer) { - if (DEBUG) Log.d(TAG, "onDestroyEGLView" + renderer); - mRenderers.remove(renderer); - ((PhotoPhaseRenderer)renderer).onPause(); - ((PhotoPhaseRenderer)renderer).onDestroy(); - } - - /** - * {@inheritDoc} - */ - @Override - public void onEGLViewInitialized(GLSurfaceView view) { - view.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); - view.setPreserveEGLContextOnPause(mPreserveEGLContext); - } - - /** - * {@inheritDoc} - */ - @Override - public void onPause(Renderer renderer) { - if (DEBUG) Log.d(TAG, "onPause: " + renderer); - ((PhotoPhaseRenderer)renderer).onPause(); - } - - /** - * {@inheritDoc} - */ - @Override - public void onResume(Renderer renderer) { - if (DEBUG) Log.d(TAG, "onResume: " + renderer); - ((PhotoPhaseRenderer)renderer).onResume(); - } - - /** - * {@inheritDoc} - */ - @Override - public Renderer getNewRenderer(GLSurfaceView view) { - if (DEBUG) Log.d(TAG, "getNewRenderer()"); - PhotoPhaseRenderer renderer = new PhotoPhaseRenderer(this, new GLESSurfaceDispatcher(view)); - renderer.onCreate(); - mRenderers.add(renderer); - if (DEBUG) Log.d(TAG, "renderer" + renderer); - return renderer; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaperWorld.java b/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaperWorld.java deleted file mode 100644 index e44e250..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/PhotoPhaseWallpaperWorld.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.PointF; -import android.graphics.RectF; -import android.util.Log; - -import com.ruesga.android.wallpapers.photophase.model.Disposition; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.transitions.Transition; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; -import com.ruesga.android.wallpapers.photophase.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -/** - * A class that represents the wallpapers with all its photo frames. - */ -public class PhotoPhaseWallpaperWorld { - - private static final String TAG = "PhotoPhaseWallpaperWorld"; - - private static final boolean DEBUG = false; - - // The frame padding - private static final int PHOTO_FRAME_PADDING = 2; - - private final Context mContext; - private final TextureManager mTextureManager; - - private List mPhotoFrames; - private List mTransitions; - private final List mUnusedTransitions; - - private List mTransitionsQueue; - private List mUsedTransitionsQueue; - private int mCurrent; - - private int mWidth; - private int mHeight; - - private boolean mRecycled; - - /** - * Constructor PhotoPhaseWallpaperWorld - * - * @param ctx The current context - * @param textureManager The texture manager - */ - public PhotoPhaseWallpaperWorld( - Context ctx, TextureManager textureManager) { - super(); - mContext = ctx; - mTextureManager = textureManager; - mCurrent = -1; - mUnusedTransitions = new ArrayList(); - mRecycled = false; - } - - /** - * Method that returns an unused transition for the type of transition - * - * @param type The type of transition - * @return Transition The unused transition - */ - private Transition getUnusedTransition(TRANSITIONS type) { - for (Transition transition : mUnusedTransitions) { - if (transition.getType().compareTo(type) == 0) { - mUnusedTransitions.remove(transition); - return transition; - } - } - return null; - } - - /** - * Method that returns or creates a transition for the type of transition - * - * @param type The type of transition - * @param frame The frame which the effect will be applied to - * @return Transition The unused transition - */ - private Transition getOrCreateTransition(TRANSITIONS type, PhotoFrame frame) { - Transition transition = getUnusedTransition(type); - if (transition == null) { - transition = Transitions.createTransition(mContext, mTextureManager, type, frame); - } - transition.reset(); - return transition; - } - - /** - * Method that ensures the transitions queue - */ - private void ensureTransitionsQueue() { - if (mTransitionsQueue.isEmpty()) { - mTransitionsQueue.addAll(mUsedTransitionsQueue); - mUsedTransitionsQueue.clear(); - } - } - - /** - * Method that selects a transition and assign it to a random photo frame. - */ - public void selectRandomTransition() { - // Ensure queue - ensureTransitionsQueue(); - - // Get a random frame to which apply the transition - int item = Utils.getNextRandom(0, mTransitionsQueue.size() - 1); - int pos = mTransitionsQueue.remove(item).intValue(); - mUsedTransitionsQueue.add(Integer.valueOf(pos)); - PhotoFrame frame = mPhotoFrames.get(pos); - - // Select the transition - selectTransition(frame, pos); - } - - /** - * Method that selects a transition and assign it to the photo frame. - * - * @param frame The photo frame to select - */ - public void selectTransition(PhotoFrame frame) { - // Ensure queue - ensureTransitionsQueue(); - - // Get a random frame to which apply the transition - int pos = mPhotoFrames.indexOf(frame); - if (pos == -1) { - return; - } - mTransitionsQueue.remove(Integer.valueOf(pos)); - mUsedTransitionsQueue.add(Integer.valueOf(pos)); - - // Select the transition - selectTransition(frame, pos); - } - - /** - * Method that selects a transition and assign it to a photo frame. - * - * @param frame The frame to select - * @param pos The position - */ - private void selectTransition(PhotoFrame frame, int pos) { - // Create or use a transition - Transition transition = null; - boolean isSelectable = false; - while (transition == null || !isSelectable) { - boolean isRandom = Preferences.General.Transitions.getTransitionTypes().length > 1; - TRANSITIONS type = Transitions.getNextTypeOfTransition(frame); - transition = getOrCreateTransition(type, frame); - isSelectable = transition.isSelectable(frame); - if (!isSelectable) { - mUnusedTransitions.add(transition); - if (!isRandom) { - // If is not possible to select a valid transition then select a swap - // transition (this one doesn't relies on any selection) - transition = getOrCreateTransition(TRANSITIONS.SWAP, frame); - isSelectable = true; - } - } - } - mTransitions.set(pos, transition); - transition.select(frame); - mCurrent = pos; - } - - /** - * Method that deselect the current transition. - * - * @param matrix The model-view-projection matrix - */ - public void deselectTransition(float[] matrix) { - if (mCurrent != -1 && mCurrent < mTransitions.size()) { - // Retrieve the finally target - Transition currentTransition = mTransitions.get(mCurrent); - PhotoFrame currentTarget = currentTransition.getTarget(); - PhotoFrame finalTarget = currentTransition.getTransitionTarget(); - mUnusedTransitions.add(currentTransition); - - if (finalTarget != null) { - Transition transition = getOrCreateTransition(TRANSITIONS.NO_TRANSITION, finalTarget); - mTransitions.set(mCurrent, transition); - - currentTarget.recycle(); - mPhotoFrames.set(mCurrent, finalTarget); - transition.select(finalTarget); - - // Draw the transition once - transition.apply(matrix); - } - mCurrent = -1; - } - } - - /** - * Method that removes all internal references. - */ - public void recycle() { - // Destroy the previous world - if (mTransitions != null) { - int cc = mTransitions.size() - 1; - for (int i = cc; i >= 0; i--) { - Transition transition = mTransitions.get(i); - transition.recycle(); - mTransitions.remove(i); - } - } - mCurrent = -1; - if (mUnusedTransitions != null) { - int cc = mUnusedTransitions.size() - 1; - for (int i = cc; i >= 0; i--) { - Transition transition = mUnusedTransitions.get(i); - transition.recycle(); - mUnusedTransitions.remove(i); - } - } - if (mTransitionsQueue != null) { - mTransitionsQueue.clear(); - } - if (mUsedTransitionsQueue != null) { - mUsedTransitionsQueue.clear(); - } - mRecycled = true; - } - - /** - * Method that returns if there are any transition running in the world. - * - * @return boolean If there are any transition running in the world - */ - public boolean hasRunningTransition() { - if (mTransitions != null) { - for (Transition transition : mTransitions) { - if (transition.isRunning()) { - return true; - } - } - } - return false; - } - - /** - * Method that creates and fills the world with {@link PhotoFrame} objects. - * - * @param w The new width dimension - * @param h The new height dimension - */ - public synchronized void recreateWorld(int w, int h) { - if (DEBUG) Log.d(TAG, "Recreating the world. New surface: " + w + "x" + h); - - // Destroy the previous world - if (mRecycled) { - recycle(); - mRecycled = false; - } - - // Save the new dimensions of the wallpaper - mWidth = w; - mHeight = h; - - // Calculate the new world - int orientation = mContext.getResources().getConfiguration().orientation; - boolean portrait = orientation == Configuration.ORIENTATION_PORTRAIT; - int cols = portrait ? Preferences.Layout.getCols() : Preferences.Layout.getRows(); - int rows = portrait ? Preferences.Layout.getRows() : Preferences.Layout.getCols(); - float cellw = 2.0f / cols; - float cellh = 2.0f / rows; - List dispositions = portrait - ? Preferences.Layout.getPortraitDisposition() - : Preferences.Layout.getLandscapeDisposition(); - if (DEBUG) Log.d(TAG, - "Dispositions: " + dispositions.size() + " | " + String.valueOf(dispositions)); - mPhotoFrames = new ArrayList(dispositions.size()); - mTransitions = new ArrayList(dispositions.size()); - mTransitionsQueue = new ArrayList(dispositions.size()); - mUsedTransitionsQueue = new ArrayList(dispositions.size()); - int i = 0; - for (Disposition disposition : dispositions) { - // Create the photo frame - float[] frameVertices = getVerticesFromDisposition(disposition, cellw, cellh); - float[] photoVertices = getFramePadding(frameVertices, portrait ? w : h, portrait ? h : w); - PhotoFrame frame = - new PhotoFrame( - mContext, - mTextureManager, - frameVertices, - photoVertices, - Colors.getBackground()); - mPhotoFrames.add(frame); - - // Assign a null transition to the photo frame - Transition transition = getOrCreateTransition(TRANSITIONS.NO_TRANSITION, frame); - transition.select(frame); - mTransitions.add(transition); - - mTransitionsQueue.add(Integer.valueOf(i)); - i++; - } - } - - /** - * Method that returns a photo frame from a coordinates in screen - * - * @param coordinates The coordinates - * @return The photo frame reference or null if none found - */ - public PhotoFrame getFrameFromCoordinates(PointF coordinates) { - // Translate pixels coordinates to GLES coordinates - float tx = ((coordinates.x * 2) / mWidth) - 1; - float ty = (((coordinates.y * 2) / mHeight) - 1) * -1; - - // Locate the frame - for (PhotoFrame frame : mPhotoFrames) { - RectF vertex = Utils.rectFromVertex(frame.getPhotoVertex()); - if (vertex.left < tx && vertex.right > tx && vertex.top > ty && vertex.bottom < ty) { - return frame; - } - } - return null; - } - - /** - * Method that draws all the photo frames. - * - * @param matrix The model-view-projection matrix - */ - public void draw(float[] matrix) { - // Apply every transition - if (mTransitions != null) { - // First draw the non-running transitions; then the active ones - for (Transition transition : mTransitions) { - if (!transition.isRunning()) { - transition.apply(matrix); - } - } - for (Transition transition : mTransitions) { - if (transition.isRunning()) { - transition.apply(matrix); - } - } - } - } - - /** - * Method that returns a coordinates per vertex array from a disposition - * - * @param disposition The source disposition - * @param cellw The cell width based on the surface - * @param cellh The cell height based on the surface - * @return float[] The coordinates per vertex array - */ - private static float[] getVerticesFromDisposition( - Disposition disposition, float cellw, float cellh) { - return new float[] - { - // bottom left - -1.0f + (disposition.x * cellw), - 1.0f - ((disposition.y * cellh) + (disposition.h * cellh)), - - // bottom right - -1.0f + ((disposition.x * cellw) + (disposition.w * cellw)), - 1.0f - ((disposition.y * cellh) + (disposition.h * cellh)), - - // top left - -1.0f + (disposition.x * cellw), - 1.0f - (disposition.y * cellh), - - // top right - -1.0f + ((disposition.x * cellw) + (disposition.w * cellw)), - 1.0f - (disposition.y * cellh) - }; - } - - /** - * Method that applies a padding to the frame - * - * @param texCoords The source coordinates - * @param screenWidth The screen width - * @param screenHeight The screen height - * @return float[] The new coordinates - */ - private static float[] getFramePadding(float[] coords, int screenWidth, int screenHeight) { - float[] paddingCoords = new float[coords.length]; - System.arraycopy(coords, 0, paddingCoords, 0, coords.length); - final float pxw = (1 / (float)screenWidth) * PHOTO_FRAME_PADDING; - final float pxh = (1 / (float)screenHeight) * PHOTO_FRAME_PADDING; - paddingCoords[0] += pxw; - paddingCoords[1] += pxh; - paddingCoords[2] -= pxw; - paddingCoords[3] += pxh; - paddingCoords[4] += pxw; - paddingCoords[5] -= pxh; - paddingCoords[6] -= pxw; - paddingCoords[7] -= pxh; - return paddingCoords; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/StorageHelper.java b/src/com/ruesga/android/wallpapers/photophase/StorageHelper.java deleted file mode 100644 index f8b95a3..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/StorageHelper.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.util.Log; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** - * A helper class to deal with android storage - */ -public final class StorageHelper { - - private static final String TAG = "StorageHelper"; - - private static final String EXTERNAL_REGEXP = "(?i).*vold.*(fuse|vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*"; - - /** - * Method that returns all the external mounts - * - * @return List All the external mounts - */ - public static List getExternalMounts() { - final List out = new ArrayList(); - - // Execute the mount command to list mounts - final StringBuilder sb = new StringBuilder(); - try { - final Process process = - new ProcessBuilder().command("mount") - .redirectErrorStream(true).start(); - process.waitFor(); - final InputStream is = process.getInputStream(); - byte[] buffer = new byte[1024]; - int read = 0; - while ((read = is.read(buffer, 0, 1024)) != -1) { - sb.append(new String(buffer, 0, read)); - } - is.close(); - } catch (IOException ioex) { - Log.e(TAG, "Failed to list external mounts", ioex); - } catch (InterruptedException iex) { - Log.e(TAG, "Failed to list external mounts", iex); - } - - // Parse the output - final String[] lines = sb.toString().split("\n"); - for (String line : lines) { - if (!line.toLowerCase(Locale.US).contains("asec")) { - if (line.matches(EXTERNAL_REGEXP)) { - String[] parts = line.split(" "); - for (String part : parts) { - if (part.startsWith("/")) { - if (!part.toLowerCase(Locale.US).contains("vold")) { - out.add(part); - } - } - } - } - } - } - return out; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/TextureManager.java b/src/com/ruesga/android/wallpapers/photophase/TextureManager.java deleted file mode 100644 index 45c14ca..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/TextureManager.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.RectF; -import android.media.ThumbnailUtils; -import android.media.effect.Effect; -import android.media.effect.EffectContext; -import android.opengl.GLES20; -import android.os.Handler; -import android.util.Log; -import android.widget.Toast; - -import com.ruesga.android.wallpapers.photophase.FixedQueue.EmptyQueueException; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.Utils; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; -import com.ruesga.android.wallpapers.photophase.MediaPictureDiscoverer.OnMediaPictureDiscoveredListener; -import com.ruesga.android.wallpapers.photophase.effects.Effects; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A class that manages the acquisition of new textures. - */ -public class TextureManager implements OnMediaPictureDiscoveredListener { - - private static final String TAG = "TextureManager"; - - private static final int QUEUE_SIZE = 3; - - final Context mContext; - final Handler mHandler; - Effects mEffects; - final Object mSync; - final List mPendingRequests; - final FixedQueue mQueue = new FixedQueue(QUEUE_SIZE); - BackgroundPictureLoaderThread mBackgroundTask; - /*protected*/ final MediaPictureDiscoverer mPictureDiscoverer; - - /*package*/ Rect mScreenDimensions; - /*package*/ Rect mDimensions; - - final GLESSurfaceDispatcher mDispatcher; - - // The status of the texture manager: - // 0 - Loading - // 1 - Loaded - // 2 - Error - private byte mStatus; - - /** - * A private runnable that will run in the GLThread - */ - /*package*/ class PictureDispatcher implements Runnable { - File mImage; - GLESTextureInfo ti = null; - final Object mWait = new Object(); - - /** - * {@inheritDoc} - */ - @Override - public void run() { - try { - Effect effect = null; - synchronized (mEffects) { - effect = mEffects.getNextEffect(); - } - - // Load and bind to the GLES context. The effect is applied when the image - // is associated to the destination target (only if aspect ratio will be applied) - if (!Preferences.General.isFixAspectRatio()) { - ti = GLESUtil.loadTexture( - mImage, mDimensions, effect, mDimensions, false); - } else { - ti = GLESUtil.loadTexture(mImage, mDimensions, null, null, false); - ti.effect = effect; - } - - synchronized (mSync) { - // Notify the new images to all pending frames - if (mPendingRequests.size() > 0) { - // Invalid textures are also reported, so requestor can handle it - TextureRequestor requestor = mPendingRequests.remove(0); - fixAspectRatio(requestor, ti); - requestor.setTextureHandle(ti); - - // Clean up memory - if (ti.bitmap != null) { - ti.bitmap.recycle(); - ti.bitmap = null; - } - - } else { - // Add to the queue (only valid textures) - if (ti.handle > 0) { - mQueue.insert(ti); - } - } - } - - } catch (Throwable e) { - Log.e(TAG, "Something was wrong loading the texture: " + - mImage.getAbsolutePath(), e); - - } finally { - // Notify that we have a new image - synchronized (mWait) { - mWait.notify(); - } - } - } - } - - /** - * Constructor of TextureManager - * - * @param ctx The current context - * @param effectCtx The current effect context - * @param dispatcher The GLES dispatcher - * @param requestors The number of requestors - * @param screenDimensions The screen dimensions - */ - public TextureManager(final Context ctx, final Handler handler, final EffectContext effectCtx, - GLESSurfaceDispatcher dispatcher, int requestors, Rect screenDimensions) { - super(); - mContext = ctx; - mHandler = handler; - mEffects = new Effects(effectCtx); - mDispatcher = dispatcher; - mScreenDimensions = screenDimensions; - mDimensions = screenDimensions; // For now, use the screen dimensions as the preferred dimensions for bitmaps - mSync = new Object(); - mPendingRequests = new ArrayList(requestors); - mPictureDiscoverer = new MediaPictureDiscoverer(mContext, this); - - // Run the media discovery thread - mBackgroundTask = new BackgroundPictureLoaderThread(); - mBackgroundTask.mTaskPaused = false; - reloadMedia(false); - } - - /** - * Method that update the effect context if the EGL context change - * - * @param effectCtx The new effect context - */ - protected void updateEffectContext(final EffectContext effectCtx) { - synchronized (mEffects) { - if (mEffects != null) { - mEffects.release(); - mEffects = null; - } - mEffects = new Effects(effectCtx); - } - emptyTextureQueue(true); - } - - /** - * Method that allow to change the preferred dimensions of the bitmaps loaded - * - * @param dimensions The new dimensions - */ - public void setDimensions(Rect dimensions) { - mDimensions = dimensions; - } - - /** - * Method that allow to change the screen dimensions - * - * @param dimensions The new dimensions - */ - public void setScreenDimesions(Rect dimensions) { - mScreenDimensions = dimensions; - } - - /** - * Method that returns if the texture manager is paused - * - * @return boolean whether the texture manager is paused - */ - public boolean isPaused() { - return mBackgroundTask != null && mBackgroundTask.mTaskPaused; - } - - /** - * Method that pauses the internal threads - * - * @param pause If the thread is paused (true) or resumed (false) - */ - public synchronized void setPause(boolean pause) { - synchronized (mBackgroundTask.mLoadSync) { - mBackgroundTask.mTaskPaused = pause; - if (!mBackgroundTask.mTaskPaused) { - mBackgroundTask.mLoadSync.notify(); - } - } - } - - /** - * Method that reload the references of media pictures - * - * @param userRequest If the request was generated by the user - */ - void reloadMedia(final boolean userRequest) { - Log.d(TAG, "Reload media picture data"); - // Discovery new media - // GLThread doesn't run in the UI thread and AsyncThread can't create a - // valid handler in ICS (it's fixed in JB+) so we force to run the async - // thread in a valid UI thread - mHandler.post(new Runnable() { - @Override - public void run() { - mPictureDiscoverer.discover(userRequest); - } - }); - } - - /** - * Method that request a new picture for the {@link TextureRequestor} - * - * @param requestor The requestor of the texture - */ - public void request(TextureRequestor requestor) { - synchronized (mSync) { - try { - GLESTextureInfo ti = mQueue.remove(); - fixAspectRatio(requestor, ti); - requestor.setTextureHandle(ti); - - // Clean up memory - if (ti.bitmap != null) { - ti.bitmap.recycle(); - ti.bitmap = null; - } - - } catch (EmptyQueueException eqex) { - // Add to queue of pending request to be notified when - // we have a new bitmap in the queue - mPendingRequests.add(requestor); - } - } - - synchronized (mBackgroundTask.mLoadSync) { - mBackgroundTask.mLoadSync.notify(); - } - } - - /** - * Method that removes all the textures from the queue - * - * @param reload Forces a reload of the queue - */ - public void emptyTextureQueue(boolean reload) { - synchronized (mSync) { - // Recycle the textures - try { - List all = mQueue.removeAll(); - for (GLESTextureInfo info : all) { - if (GLES20.glIsTexture(info.handle)) { - int[] textures = new int[] {info.handle}; - GLES20.glDeleteTextures(1, textures, 0); - GLESUtil.glesCheckError("glDeleteTextures"); - } - // Return the bitmap - info.bitmap.recycle(); - info.bitmap = null; - } - } catch (EmptyQueueException eqex) { - // Ignore - } - - // Remove all pictures in the queue - try { - mQueue.removeAll(); - } catch (EmptyQueueException ex) { - // Ignore - } - - // Reload the queue - if (reload) { - synchronized (mBackgroundTask.mLoadSync) { - mBackgroundTask.resetAvailableImages(); - mBackgroundTask.mLoadSync.notify(); - } - } - } - } - - /** - * Method that cancels a request did it previously. - * - * @param requestor The requestor of the texture - */ - public void cancelRequest(TextureRequestor requestor) { - synchronized (mSync) { - if (mPendingRequests.contains(requestor)) { - mPendingRequests.remove(requestor); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onStartMediaDiscovered(boolean userRequest) { - // No images but thread should start here to received partial data - this.mStatus = 0; // Loading - if (mBackgroundTask != null) { - mBackgroundTask.setAvailableImages(new File[]{}); - if (!mBackgroundTask.mRun) { - mBackgroundTask.start(); - } else { - synchronized (mBackgroundTask.mLoadSync) { - mBackgroundTask.mLoadSync.notify(); - } - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onPartialMediaDiscovered(File[] images, boolean userRequest) { - if (mBackgroundTask != null) { - mBackgroundTask.setPartialAvailableImages(images); - } - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("boxing") - public void onEndMediaDiscovered(File[] images, boolean userRequest) { - // Now we have the paths of the images to use. Notify to the thread to - // load pictures in background - if (mBackgroundTask != null) { - mBackgroundTask.setAvailableImages(images); - synchronized (mBackgroundTask.mLoadSync) { - mBackgroundTask.mLoadSync.notify(); - } - this.mStatus = 1; // Loaded - - // Audit - int found = images == null ? 0 : images.length; - Log.d(TAG, "Media picture data reloaded: " + found + " images found."); - if (userRequest) { - CharSequence msg = - String.format(mContext.getResources().getQuantityText( - R.plurals.msg_media_reload_complete, found).toString(), found); - Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); - } - } else { - this.mStatus = 2; // Error - } - } - - /** - * Method that destroy the references of this class - */ - public void recycle() { - // Destroy the media discovery task - mPictureDiscoverer.recycle(); - mEffects.release(); - - // Destroy the background task - if (mBackgroundTask != null) { - mBackgroundTask.mRun = false; - try { - synchronized (mBackgroundTask.mLoadSync) { - mBackgroundTask.interrupt(); - } - } catch (Exception e) { - // Ignore - } - } - mBackgroundTask = null; - } - - - /** - * Returns the status of the texture manager - * - * @return byte The status - */ - public byte getStatus() { - return mStatus; - } - - /** - * Returns if the texture manager is empty - * - * @return boolean If the texture manager is empty - */ - public boolean isEmpty() { - return mBackgroundTask != null && mBackgroundTask.mEmpty; - } - - /** - * Method that fix the aspect ratio of a image to fit the destination target - * - * @param request The requestor target - * @param ti The original texture information - * @param effect The effect to apply to the destination picture - */ - /*package*/ void fixAspectRatio(TextureRequestor requestor, GLESTextureInfo ti) { - // Check if we have to apply any correction to the image - if (Preferences.General.isFixAspectRatio()) { - // Transform requestor dimensions to screen dimensions - RectF dimens = requestor.getRequestorDimensions(); - Rect pixels = new Rect( - 0, - 0, - (int)(mScreenDimensions.width() * dimens.width() / 2), - (int)(mScreenDimensions.height() * dimens.height() / 2)); - - // Create a thumbnail of the image - Bitmap thumb = ThumbnailUtils.extractThumbnail( - ti.bitmap, - pixels.width(), - pixels.height(), - ThumbnailUtils.OPTIONS_RECYCLE_INPUT); - GLESTextureInfo dst = GLESUtil.loadTexture(thumb, ti.effect, pixels); - - // Destroy references - int[] textures = new int[]{ti.handle}; - GLES20.glDeleteTextures(1, textures, 0); - GLESUtil.glesCheckError("glDeleteTextures"); - if (ti.bitmap != null) { - ti.bitmap.recycle(); - ti.bitmap = null; - } - - // Swap references - ti.bitmap = dst.bitmap; - ti.handle = dst.handle; - ti.effect = null; - } - } - - /** - * An internal thread to load pictures in background - */ - private class BackgroundPictureLoaderThread extends Thread { - - final Object mLoadSync = new Object(); - boolean mRun; - boolean mTaskPaused; - - /*package*/ boolean mEmpty; - private final List mNewImages; - private final List mUsedImages; - - /** - * Constructor of BackgroundPictureLoaderThread. - */ - public BackgroundPictureLoaderThread() { - super(); - mNewImages = new ArrayList(); - mUsedImages = new ArrayList(); - } - - /** - * Method that sets the current available images. - * - * @param images The current images - */ - public void setAvailableImages(File[] images) { - synchronized (mLoadSync) { - mNewImages.clear(); - mNewImages.addAll(Arrays.asList(images)); - - // Retain used images - int count = mUsedImages.size() - 1; - for (int i = count; i >= 0; i--) { - File image = mUsedImages.get(i); - if (!mNewImages.contains(image)) { - mUsedImages.remove(image); - } else { - mNewImages.remove(image); - } - } - - mEmpty = images.length == 0; - } - } - - /** - * Method that adds some available images. - * - * @param images The current images - */ - public void setPartialAvailableImages(File[] images) { - synchronized (mLoadSync) { - mNewImages.addAll(Arrays.asList(images)); - mEmpty = images.length == 0; - } - } - - /** - * Method that reset the current available images queue. - */ - public void resetAvailableImages() { - synchronized (mLoadSync) { - mNewImages.addAll(mUsedImages); - mUsedImages.clear(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void run() { - mRun = true; - while (mRun) { - // Check if we need to load more images - while (!mTaskPaused && TextureManager.this.mQueue.items() < TextureManager.this.mQueue.size()) { - File image = null; - synchronized (mLoadSync) { - // Swap arrays if needed - if (mNewImages.size() == 0) { - mNewImages.addAll(mUsedImages); - mUsedImages.clear(); - } - if (mNewImages.size() == 0) { - if (!mEmpty) { - reloadMedia(false); - } - break; - } - - // Extract a random image - int low = 0; - int high = mNewImages.size() - 1; - image = mNewImages.remove(Utils.getNextRandom(low, high)); - } - - // Run commands in the GLThread - if (!mRun) break; - PictureDispatcher pd = new PictureDispatcher(); - pd.mImage = image; - mDispatcher.dispatch(pd); - - // Wait until the texture is loaded - try { - synchronized (pd.mWait) { - pd.mWait.wait(); - } - } catch (Exception e) { - // Ignore - } - - // Add to used images - mUsedImages.add(image); - } - - // Wait for new request - synchronized (mLoadSync) { - try { - mLoadSync.wait(); - } catch (Exception e) { - // Ignore - } - } - } - } - - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/TextureRequestor.java b/src/com/ruesga/android/wallpapers/photophase/TextureRequestor.java deleted file mode 100644 index 7836dd1..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/TextureRequestor.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase; - -import android.graphics.RectF; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; - -/** - * An interface that defines an object as able to request textures. - */ -public interface TextureRequestor { - - /** - * Method that set the texture handle requested. - * - * @param ti The texture information - */ - void setTextureHandle(GLESTextureInfo ti); - - /** - * Method that returns the dimension of the requestor - * - * @return RectF The dimensions of the requestor - */ - RectF getRequestorDimensions(); -} diff --git a/src/com/ruesga/android/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java b/src/com/ruesga/android/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java deleted file mode 100644 index 0188d84..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.animations; - -import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; - -import com.ruesga.android.wallpapers.photophase.model.Album; -import com.ruesga.android.wallpapers.photophase.widgets.AlbumInfo; -import com.ruesga.android.wallpapers.photophase.widgets.AlbumPictures; -import com.ruesga.android.wallpapers.photophase.widgets.AlbumPictures.CallbacksListener; - -/** - * A class that manages a flip 3d effect of an album - */ -public class AlbumsFlip3dAnimationController { - - private static final int DURATION = 200; - - View mFront; - View mBack; - boolean mFrontFace; - - /** - * Constructor of AlbumsFlip3dAnimationController - * - * @param front The front view - * @param back The back view - */ - public AlbumsFlip3dAnimationController(AlbumInfo front, AlbumPictures back) { - super(); - mFront = front; - mBack = back; - mBack.setVisibility(View.GONE); - mFrontFace = true; - } - - /** - * Method that register the controller - */ - public void register() { - getFrontView().setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - getBackView().setVisibility(View.INVISIBLE); - applyAnimation(false); - } - }); - ((AlbumPictures)getBackView()).addCallBackListener(new CallbacksListener() { - @Override - public void onBackButtonClick(View v) { - getBackView().setVisibility(View.INVISIBLE); - applyAnimation(true); - } - - @Override - public void onSelectionChanged(Album album) { - // Ignore - } - }); - } - - /** - * Method that unregister the controller - */ - public void unregister() { - getFrontView().setOnClickListener(null); - getBackView().setOnClickListener(null); - } - - /** - * Method that reset the controller - */ - public void reset() { - if (!mFrontFace) { - applyAnimation(true); - } - } - - /** - * Method that applies the animation over the views - * - * @param inverse Applies the inverse animation - */ - /*package*/ void applyAnimation(boolean inverse) { - applyTransformation(getFrontView(), 0, 90 * (inverse ? -1 : 1), true); - } - - /*package*/ void applyTransformation(final View v, float start, float end, final boolean step1) { - final float centerX = v.getWidth() / 2.0f; - final float centerY = v.getHeight() / 2.0f; - - final Flip3dAnimation anim = new Flip3dAnimation(start, end, centerX, centerY); - anim.setDuration(DURATION); - anim.setFillAfter(true); - anim.setInterpolator(new AccelerateInterpolator()); - - anim.setAnimationListener(new AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - if (!step1) { - getBackView().setVisibility(View.VISIBLE); - } - getFrontView().setOnClickListener(null); - getBackView().setOnClickListener(null); - } - - @Override - public void onAnimationRepeat(Animation animation) { - // Ignore - } - - @Override - public void onAnimationEnd(Animation animation) { - getFrontView().setAnimation(null); - getBackView().setAnimation(null); - if (step1) { - getFrontView().setVisibility(View.INVISIBLE); - applyTransformation(getBackView(), -90 * (!mFrontFace ? -1 : 1), 0, false); - } else { - mFrontFace = !mFrontFace; - getBackView().setVisibility(View.GONE); - if (mFrontFace) { - getFrontView().setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - getBackView().setVisibility(View.INVISIBLE); - applyAnimation(false); - } - }); - } else { - ((AlbumPictures)getFrontView()).onShow(); - } - } - } - }); - v.startAnimation(anim); - } - - /*package*/ View getFrontView() { - return mFrontFace ? mFront : mBack; - } - - /*package*/ View getBackView() { - return !mFrontFace ? mFront : mBack; - } -} - diff --git a/src/com/ruesga/android/wallpapers/photophase/animations/Evaluators.java b/src/com/ruesga/android/wallpapers/photophase/animations/Evaluators.java deleted file mode 100644 index b78f265..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/animations/Evaluators.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.animations; - -import android.animation.IntEvaluator; -import android.view.View; -import android.view.ViewGroup; - -/** - * A class with helpful evaluators - */ -public class Evaluators { - - /** - * A width evaluator - */ - public static class WidthEvaluator extends IntEvaluator { - private View mView; - - /** - * Constructor of WidthEvaluator - * - * @param v The view - */ - public WidthEvaluator(View v) { - super(); - mView = v; - } - - @Override - public Integer evaluate(float fraction, Integer startValue, Integer endValue) { - Integer num = super.evaluate(fraction, startValue, endValue); - ViewGroup.LayoutParams params = mView.getLayoutParams(); - params.width = num.intValue(); - mView.setLayoutParams(params); - return num; - } - } - - /** - * A height evaluator - */ - public static class HeightEvaluator extends IntEvaluator { - private View mView; - - /** - * Constructor of HeightEvaluator - * - * @param v The view - */ - public HeightEvaluator(View v) { - super(); - mView = v; - } - - @Override - public Integer evaluate(float fraction, Integer startValue, Integer endValue) { - Integer num = super.evaluate(fraction, startValue, endValue); - ViewGroup.LayoutParams params = mView.getLayoutParams(); - params.height = num.intValue(); - mView.setLayoutParams(params); - return num; - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/animations/Flip3dAnimation.java b/src/com/ruesga/android/wallpapers/photophase/animations/Flip3dAnimation.java deleted file mode 100644 index 786f1ef..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/animations/Flip3dAnimation.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.animations; - -import android.graphics.Camera; -import android.graphics.Matrix; -import android.view.animation.Animation; -import android.view.animation.Transformation; - -/** - * A 3d flit animation - */ -public class Flip3dAnimation extends Animation { - - private final float mFromDegrees; - private final float mToDegrees; - private final float mCenterX; - private final float mCenterY; - private Camera mCamera; - - /** - * Constructor of Flip3dAnimation - * - * @param fromDegrees From origin degrees - * @param toDegrees To destination degrees - * @param centerX The center horizontal position - * @param centerY The center vertical position - */ - public Flip3dAnimation(float fromDegrees, float toDegrees, float centerX, float centerY) { - mFromDegrees = fromDegrees; - mToDegrees = toDegrees; - mCenterX = centerX; - mCenterY = centerY; - } - - /** - * {@inheritDoc} - */ - @Override - public void initialize(int width, int height, int parentWidth, int parentHeight) { - super.initialize(width, height, parentWidth, parentHeight); - mCamera = new Camera(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - final float fromDegrees = mFromDegrees; - float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); - - final float centerX = mCenterX; - final float centerY = mCenterY; - final Camera camera = mCamera; - - final Matrix matrix = t.getMatrix(); - - camera.save(); - camera.rotateY(degrees); - camera.getMatrix(matrix); - camera.restore(); - - matrix.preTranslate(-centerX, -centerY); - matrix.postTranslate(centerX, centerY); - } - -} - diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/BlurEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/BlurEffect.java deleted file mode 100644 index 857fdc6..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/BlurEffect.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * A blur effect
- * - * - *
Parameter nameMeaningValid values
- */ -public class BlurEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "void main(void)\n" + - "{\n" + - " float step = 0.02;\n" + - " vec3 c1 = texture2D(tex_sampler, vec2(v_texcoord.s - step, v_texcoord.t - step)).bgr;\n" + - " vec3 c2 = texture2D(tex_sampler, vec2(v_texcoord.s + step, v_texcoord.t + step)).bgr;\n" + - " vec3 c3 = texture2D(tex_sampler, vec2(v_texcoord.s - step, v_texcoord.t + step)).bgr;\n" + - " vec3 c4 = texture2D(tex_sampler, vec2(v_texcoord.s + step, v_texcoord.t - step)).bgr;\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = (c1 + c2 + c3 + c4) / 4.0;\n" + - "}"; - - /** - * Constructor of BlurEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public BlurEffect(EffectContext ctx, String name) { - super(ctx, BlurEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/Effects.java b/src/com/ruesga/android/wallpapers/photophase/effects/Effects.java deleted file mode 100644 index 796cc2d..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/Effects.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.effects; - -import android.graphics.Color; -import android.media.effect.Effect; -import android.media.effect.EffectContext; -import android.media.effect.EffectFactory; - -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.utils.Utils; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A class that manages all the supported effects - */ -public class Effects { - - /** - * Enumeration of the supported effects - */ - public enum EFFECTS { - /** - * @see PhotoPhaseEffectFactory#EFFECT_NULL - */ - NO_EFFECT, - /** - * @see EffectFactory#EFFECT_AUTOFIX - */ - AUTOFIX, - /** - * @see PhotoPhaseEffectFactory#EFFECT_BLUR - */ - BLUR, - /** - * @see EffectFactory#EFFECT_CROSSPROCESS - */ - CROSSPROCESS, - /** - * @see EffectFactory#EFFECT_DOCUMENTARY - */ - DOCUMENTARY, - /** - * @see EffectFactory#EFFECT_DUOTONE - */ - DUOTONE, - /** - * @see PhotoPhaseEffectFactory#EFFECT_EMBOSS - */ - EMBOSS, - /** - * @see EffectFactory#EFFECT_FISHEYE - */ - FISHEYE, - /** - * @see PhotoPhaseEffectFactory#EFFECT_GLOW - */ - GLOW, - /** - * @see EffectFactory#EFFECT_GRAIN - */ - GRAIN, - /** - * @see EffectFactory#EFFECT_GRAYSCALE - */ - GRAYSCALE, - /** - * @see PhotoPhaseEffectFactory#EFFECT_HALFTONE - */ - HALFTONE, - /** - * @see EffectFactory#EFFECT_LOMOISH - */ - LOMOISH, - /** - * @see PhotoPhaseEffectFactory#EFFECT_MIRROR - */ - MIRROR, - /** - * @see EffectFactory#EFFECT_NEGATIVE - */ - NEGATIVE, - /** - * @see PhotoPhaseEffectFactory#EFFECT_OUTLINE - */ - OUTLINE, - /** - * @see PhotoPhaseEffectFactory#EFFECT_PIXELATE - */ - PIXELATE, - /** - * @see PhotoPhaseEffectFactory#EFFECT_POPART - */ - POPART, - /** - * @see EffectFactory#EFFECT_POSTERIZE - */ - POSTERIZE, - /** - * @see EffectFactory#EFFECT_SATURATE - */ - SATURATE, - /** - * @see PhotoPhaseEffectFactory#EFFECT_SCANLINES - */ - SCANLINES, - /** - * @see EffectFactory#EFFECT_SEPIA - */ - SEPIA, - /** - * @see EffectFactory#EFFECT_TEMPERATURE - */ - TEMPERATURE, - /** - * @see EffectFactory#EFFECT_TINT - */ - TINT, - /** - * @see EffectFactory#EFFECT_VIGNETTE - */ - VIGNETTE; - - /** - * Method that returns the effect from its ordinal position - * - * @param ordinal The ordinal position - * @return EFFECTS The effect or null if wasn't found - */ - public static EFFECTS fromOrdinal(int ordinal) { - for (EFFECTS effect : EFFECTS.values()) { - if (effect.ordinal() == ordinal) { - return effect; - } - } - return null; - } - } - - private final Map mCachedEffects; - private final EffectContext mEffectContext; - - /** - * Constructor of Effects - * - * @param effectContext The current effect context - */ - public Effects(EffectContext effectContext) { - super(); - mCachedEffects = new HashMap(); - mEffectContext = effectContext; - } - - /** - * Method that that release the cached data - */ - public void release() { - if (mCachedEffects != null) { - for (Effect effect : mCachedEffects.values()) { - effect.release(); - } - mCachedEffects.clear(); - } - } - - /** - * Method that return the next effect to use with the picture. - * - * @return Effect The next effect to use or null if no need to apply any effect - */ - @SuppressWarnings("boxing") - public Effect getNextEffect() { - // Get a new instance of a effect factory - EffectFactory effectFactory = mEffectContext.getFactory(); - Effect effect = null; - - // Get an effect based on the user preference - List effects = Arrays.asList(Preferences.General.Effects.getEffectTypes()); - EFFECTS nextEffect = null; - if (effects.size() > 0) { - int low = 0; - int high = effects.size() - 1; - int pos = Utils.getNextRandom(low, high); - nextEffect = effects.get(pos); - } - if (nextEffect == null) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_NULL)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_NULL); - mCachedEffects.put(nextEffect, effect); - } - return effect; - } - - // Has a cached effect? - if (mCachedEffects.containsKey(nextEffect)) { - return mCachedEffects.get(nextEffect); - } - - // Select the effect if is available - if (nextEffect.compareTo(EFFECTS.AUTOFIX) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_AUTOFIX)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_AUTOFIX); - effect.setParameter("scale", 0.5f); - } - } else if (nextEffect.compareTo(EFFECTS.BLUR) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_BLUR)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_BLUR); - } - } else if (nextEffect.compareTo(EFFECTS.CROSSPROCESS) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_CROSSPROCESS)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_CROSSPROCESS); - } - } else if (nextEffect.compareTo(EFFECTS.DOCUMENTARY) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_DOCUMENTARY)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_DOCUMENTARY); - } - } else if (nextEffect.compareTo(EFFECTS.DUOTONE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_DUOTONE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); - effect.setParameter("first_color", Color.parseColor("#FF8CACFF")); - effect.setParameter("second_color", Color.WHITE); - } - } else if (nextEffect.compareTo(EFFECTS.EMBOSS) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_EMBOSS)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_EMBOSS); - } - } else if (nextEffect.compareTo(EFFECTS.FISHEYE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_FISHEYE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_FISHEYE); - effect.setParameter("scale", 1.0f); - } - } else if (nextEffect.compareTo(EFFECTS.GLOW) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_GLOW)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_GLOW); - } - } else if (nextEffect.compareTo(EFFECTS.GRAIN) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_GRAIN)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAIN); - effect.setParameter("strength", 1.0f); - } - } else if (nextEffect.compareTo(EFFECTS.GRAYSCALE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_GRAYSCALE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAYSCALE); - } - } else if (nextEffect.compareTo(EFFECTS.HALFTONE) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_HALFTONE)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_HALFTONE); - effect.setParameter("strength", 8.0f); - } - } else if (nextEffect.compareTo(EFFECTS.MIRROR) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_MIRROR)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_MIRROR); - } - } else if (nextEffect.compareTo(EFFECTS.LOMOISH) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_LOMOISH)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_LOMOISH); - } - } else if (nextEffect.compareTo(EFFECTS.NEGATIVE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_NEGATIVE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_NEGATIVE); - } - } else if (nextEffect.compareTo(EFFECTS.OUTLINE) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_OUTLINE)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_OUTLINE); - } - } else if (nextEffect.compareTo(EFFECTS.PIXELATE) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_PIXELATE)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_PIXELATE); - effect.setParameter("strength", 8.0f); - } - } else if (nextEffect.compareTo(EFFECTS.POPART) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_POPART)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_POPART); - } - } else if (nextEffect.compareTo(EFFECTS.POSTERIZE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_POSTERIZE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_POSTERIZE); - } - } else if (nextEffect.compareTo(EFFECTS.SATURATE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_SATURATE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_SATURATE); - effect.setParameter("scale", .5f); - } - } else if (nextEffect.compareTo(EFFECTS.SCANLINES) == 0) { - if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_SCANLINES)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_SCANLINES); - } - } else if (nextEffect.compareTo(EFFECTS.SEPIA) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_SEPIA)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_SEPIA); - } - } else if (nextEffect.compareTo(EFFECTS.TEMPERATURE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_TEMPERATURE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_TEMPERATURE); - effect.setParameter("scale", .9f); - } - } else if (nextEffect.compareTo(EFFECTS.TINT) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_TINT)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_TINT); - } - } else if (nextEffect.compareTo(EFFECTS.VIGNETTE) == 0) { - if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_VIGNETTE)) { - effect = effectFactory.createEffect(EffectFactory.EFFECT_VIGNETTE); - effect.setParameter("scale", .5f); - } - } - - // Instead of not to apply any effect, just use one null effect to follow the same - // effect model. This allow to use the same height when Effect.apply is applied for all - // the frames - if (effect == null && EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_NULL)) { - effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_NULL); - nextEffect = EFFECTS.NO_EFFECT; - } - - // Cache the effects - mCachedEffects.put(nextEffect, effect); - return effect; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/EmbossEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/EmbossEffect.java deleted file mode 100644 index b67458b..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/EmbossEffect.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * An emboss effect
- * - * - *
Parameter nameMeaningValid values
- */ -public class EmbossEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "const float step_w = 0.0015625;\n" + - "const float step_h = 0.0027778;\n" + - "void main(void)\n" + - "{\n" + - " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + - " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + - " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + - " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + - " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + - " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + - " vec3 rr = -4.0 * t1 - 4.0 * t2 - 4.0 * t4 + 12.0 * t5;\n" + - " float y = (rr.r + rr.g + rr.b) / 3.0;\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = vec3(y, y, y) + 0.3;\n" + - "}"; - - /** - * Constructor of EmbossEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public EmbossEffect(EffectContext ctx, String name) { - super(ctx, EmbossEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/GlowEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/GlowEffect.java deleted file mode 100644 index 25f1143..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/GlowEffect.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * A glow effect
- * - * - *
Parameter nameMeaningValid values
- */ -public class GlowEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "const float step_w = 0.0015625;\n" + - "const float step_h = 0.0027778;\n" + - "void main(void)\n" + - "{\n" + - " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + - " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + - " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + - " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + - " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + - " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + - " vec3 xx= t1 + 2.0*t2 + t3 - t7 - 2.0*t8 - t9;\n" + - " vec3 yy = t1 - t3 + 2.0*t4 - 2.0*t6 + t7 - t9;\n" + - " vec3 rr = sqrt(xx * xx + yy * yy);\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = rr * 2.0 * t5;\n" + - "}"; - - /** - * Constructor of GlowEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public GlowEffect(EffectContext ctx, String name) { - super(ctx, GlowEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/HalftoneEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/HalftoneEffect.java deleted file mode 100644 index 7f7bb4c..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/HalftoneEffect.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; -import android.opengl.GLES20; -import android.util.Log; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; - -/** - * A halftone effect
- * - * - * - * - * - * - * - *
Parameter nameMeaningValid values
strengthThe halftone strength.Positive float (>0). Higher numbers produce smallest points.
- */ -public class HalftoneEffect extends PhotoPhaseEffect { - - private static final String TAG = "HalftoneEffect"; - - private static final String STRENGTH_PARAMETER = "strength"; - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "const float step_w = 0.0015625;\n" + - "const float step_h = 0.0027778;\n" + - "uniform float strength;\n" + - "void main(void)\n" + - "{\n" + - " float offx = floor(v_texcoord.s / (strength * step_w));\n" + - " float offy = floor(v_texcoord.t / (strength * step_h));\n" + - " vec3 res = texture2D(tex_sampler, vec2(offx * strength * step_w , offy * strength * step_h)).bgr;\n" + - " vec2 prc = fract(v_texcoord.st / vec2(strength * step_w, strength * step_h));\n" + - " vec2 pw = pow(abs(prc - 0.5), vec2(2.0));\n" + - " float rs = pow(0.45, 2.0);\n" + - " float gr = smoothstep(rs - 0.1, rs + 0.1, pw.x + pw.y);\n" + - " float y = (res.r + res.g + res.b) / 3.0; \n" + - " vec3 ra = res / y;\n" + - " float ls = 0.3;\n" + - " float lb = ceil(y / ls);\n" + - " float lf = ls * lb + 0.3;\n" + - " res = lf * res;\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = mix(res, vec3(0.1, 0.1, 0.1), gr);\n" + - "}"; - - private float mStrength = 16.0f; - private int mStepsHandle; - - /** - * Constructor of HalftoneEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public HalftoneEffect(EffectContext ctx, String name) { - super(ctx, HalftoneEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - - /** - * {@inheritDoc} - */ - @Override - void init(String vertexShader, String fragmentShader) { - super.init(vertexShader, fragmentShader); - - // Parameters - mStepsHandle = GLES20.glGetUniformLocation(mProgram, "strength"); - GLESUtil.glesCheckError("glGetUniformLocation"); - } - - /** - * {@inheritDoc} - */ - @Override - void applyParameters() { - // Set parameters - GLES20.glUniform1f(mStepsHandle, mStrength); - GLESUtil.glesCheckError("glUniform1f"); - } - - /** - * {@inheritDoc} - */ - @Override - public void setParameter(String parameterKey, Object value) { - if (parameterKey.compareTo(STRENGTH_PARAMETER) == 0) { - try { - float strength = Float.parseFloat(value.toString()); - if (strength <= 0) { - Log.w(TAG, "strength parameter must be >= 0"); - return; - } - mStrength = strength; - } catch (NumberFormatException ex) { - // Ignore - } - } - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/MirrorEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/MirrorEffect.java deleted file mode 100644 index d9fef06..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/MirrorEffect.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * A mirror effect
- * - * - *
Parameter nameMeaningValid values
- */ -public class MirrorEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "void main(void)\n" + - "{\n" + - " vec2 off = vec2(0.0, 0.0);\n" + - " if (v_texcoord.t > 0.5) {\n" + - " off.t = 1.0 - v_texcoord.t;\n" + - " off.s = v_texcoord.s;\n" + - " } else {\n" + - " off = v_texcoord;\n" + - " }\n" + - " vec3 color = texture2D(tex_sampler, vec2(off)).bgr;\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = color;\n" + - "}"; - - /** - * Constructor of MirrorEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public MirrorEffect(EffectContext ctx, String name) { - super(ctx, MirrorEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/NullEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/NullEffect.java deleted file mode 100644 index 8bc6c42..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/NullEffect.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * This effect only copies the source texture to the destination texture.
- * - * - *
Parameter nameMeaningValid values
- */ -public class NullEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "void main(void)\n" + - "{\n" + - " gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" + - "}"; - - /** - * Constructor of NullEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public NullEffect(EffectContext ctx, String name) { - super(ctx, NullEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - - /** - * {@inheritDoc} - */ - @Override - void apply(int inputTexId) { - // Nothing to draw - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/OutlineEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/OutlineEffect.java deleted file mode 100644 index 2652b89..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/OutlineEffect.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * An outline effect (highlight edges)
- * - * - *
Parameter nameMeaningValid values
- */ -public class OutlineEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "const float step_w = 0.0015625;\n" + - "const float step_h = 0.0027778;\n" + - "void main(void)\n" + - "{\n" + - " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + - " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + - " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + - " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + - " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + - " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + - " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + - " vec3 xx= t1 + 2.0*t2 + t3 - t7 - 2.0*t8 - t9;\n" + - " vec3 yy = t1 - t3 + 2.0*t4 - 2.0*t6 + t7 - t9;\n" + - " vec3 rr = sqrt(xx * xx + yy * yy);\n" + - " float y = (rr.r + rr.g + rr.b) / 3.0;\n" + - " if (y > 0.2)\n" + - " rr = vec3(0.0, 0.0, 0.0);\n" + - " else\n" + - " rr = vec3(1.0, 1.0, 1.0);\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = rr;\n" + - "}"; - - /** - * Constructor of OutlineEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public OutlineEffect(EffectContext ctx, String name) { - super(ctx, OutlineEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffect.java deleted file mode 100644 index 3d2c0fd..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffect.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.Effect; -import android.media.effect.EffectContext; -import android.media.effect.EffectFactory; -import android.opengl.GLES20; -import android.opengl.GLUtils; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -/** - * An abstract class definition for all the PhotoPhase custom effects - */ -public abstract class PhotoPhaseEffect extends Effect { - - private static final int FLOAT_SIZE_BYTES = 4; - - private static final String MCA_IDENTITY_EFFECT = "IdentityEffect"; - - static final String VERTEX_SHADER = - "attribute vec4 a_position;\n" + - "attribute vec2 a_texcoord;\n" + - "varying vec2 v_texcoord;\n" + - "void main() {\n" + - " gl_Position = vec4(a_position.xy, 0.0, 1.0);\n" + - " gl_Position = sign(gl_Position);\n" + - " v_texcoord = a_texcoord;\n" + - "}\n"; - - private static final float[] TEX_VERTICES = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; - private static final float[] POS_VERTICES = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; - - private final int GL_STATE_FBO = 0; - private final int GL_STATE_PROGRAM = 1; - private final int GL_STATE_ARRAYBUFFER = 2; - private final int GL_STATE_COUNT = 3; - - private int[] mOldState = new int[GL_STATE_COUNT]; - - private final EffectContext mEffectContext; - private final String mName; - - private Effect mIdentityEffect; - - int mProgram; - int mTexSamplerHandle; - int mTexCoordHandle; - int mPosCoordHandle; - - FloatBuffer mTexVertices; - FloatBuffer mPosVertices; - - /** - * An abstract constructor of Effect to follow the rules - * defined by {@link EffectFactory}. - * - * @param ctx The effect context - * @param name The effect name - */ - public PhotoPhaseEffect(EffectContext ctx, String name) { - super(); - mEffectContext = ctx; - mName = name; - - // Stand on MCA identity effect for the initialization work - EffectFactory effectFactory = mEffectContext.getFactory(); - mIdentityEffect = effectFactory.createEffect(MCA_IDENTITY_EFFECT); - } - - /** - * Method that initializes the effect - */ - void init(String vertexShader, String fragmentShader) { - // Create program - mProgram = GLESUtil.createProgram(vertexShader, fragmentShader); - - // Bind attributes and uniforms - mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram, "tex_sampler"); - GLESUtil.glesCheckError("glGetUniformLocation"); - mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord"); - GLESUtil.glesCheckError("glGetAttribLocation"); - mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position"); - GLESUtil.glesCheckError("glGetAttribLocation"); - - // Setup coordinate buffers - mTexVertices = ByteBuffer.allocateDirect( - TEX_VERTICES.length * FLOAT_SIZE_BYTES) - .order(ByteOrder.nativeOrder()).asFloatBuffer(); - mTexVertices.put(TEX_VERTICES).position(0); - mPosVertices = ByteBuffer.allocateDirect( - POS_VERTICES.length * FLOAT_SIZE_BYTES) - .order(ByteOrder.nativeOrder()).asFloatBuffer(); - mPosVertices.put(POS_VERTICES).position(0); - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return mName; - } - - /** - * Method that returns the effect context - * - * @return EffectContext The effect context - */ - public EffectContext getEffectContext() { - return mEffectContext; - } - - /** - * {@inheritDoc} - */ - @Override - public final synchronized void apply(int inputTexId, int width, int height, int outputTexId) { - // Save the GLES state - saveGLState(); - - try { - // Create a framebuffer object and call the effect apply method to draw the effect - int[] fb = new int[1]; - GLES20.glGenFramebuffers(1, fb, 0); - GLESUtil.glesCheckError("glGenFramebuffers"); - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[0]); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Render on the whole framebuffer - GLES20.glViewport(0, 0, width, height); - GLESUtil.glesCheckError("glViewport"); - - // Create a new output texture (Use the MCA identity to clone the input to the output) - mIdentityEffect.apply(inputTexId, width, height, outputTexId); - - // Create the framebuffer - GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20. GL_TEXTURE_2D, outputTexId, 0); - GLESUtil.glesCheckError("glFramebufferTexture2D"); - - // Check if the buffer was built successfully - if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { - // Something when wrong. Throw an exception - GLESUtil.glesCheckError("glCheckFramebufferStatus"); - int error = GLES20.glGetError(); - throw new android.opengl.GLException(error, GLUtils.getEGLErrorString(error)); - } - - // Apply the effect - apply(inputTexId); - - } finally { - // Restore the GLES state - restoreGLState(); - } - - } - - /** - * {@inheritDoc} - */ - @Override - public void setParameter(String parameterKey, Object value) { - // Ignore - } - - /** - * {@inheritDoc} - */ - @Override - public void release() { - if (GLES20.glIsProgram(mProgram)) { - GLES20.glDeleteProgram(mProgram); - GLESUtil.glesCheckError("glDeleteProgram"); - } - mTexVertices = null; - mPosVertices = null; - } - - /** - * Method that applies the effect. - * - * @param inputTexId The input texture - */ - void apply(int inputTexId) { - // Use our shader program - GLES20.glUseProgram(mProgram); - GLESUtil.glesCheckError("glUseProgram"); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Set the vertex attributes - GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTexVertices); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTexCoordHandle); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mPosVertices); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPosCoordHandle); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Set parameters - applyParameters(); - - // Set the input texture - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, inputTexId); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTexSamplerHandle, 0); - GLESUtil.glesCheckError("glUniform1i"); - - // Draw - GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLESUtil.glesCheckError("glClearColor"); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - GLESUtil.glesCheckError("glClear"); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawArrays"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mTexCoordHandle); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mPosCoordHandle); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Method that applies the parameters of the effect. - */ - void applyParameters() { - // Do nothing - } - - - private final void saveGLState() { - GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, mOldState, GL_STATE_FBO); - GLES20.glGetIntegerv(GLES20.GL_CURRENT_PROGRAM, mOldState, GL_STATE_PROGRAM); - GLES20.glGetIntegerv(GLES20.GL_ARRAY_BUFFER_BINDING, mOldState, GL_STATE_ARRAYBUFFER); - } - - private final void restoreGLState() { - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mOldState[GL_STATE_FBO]); - GLES20.glUseProgram(mOldState[GL_STATE_PROGRAM]); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mOldState[GL_STATE_ARRAYBUFFER]); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java b/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java deleted file mode 100644 index 0e3a027..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.effects; - -/** - * A class that defines the own PhotoPhase's effects implementation. This class follows the - * rules of the MCA aosp library. - */ -public class PhotoPhaseEffectFactory { - - /** - *

Applies a blur effect to the image.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_BLUR = "com.ruesga.android.wallpapers.photophase.effects.BlurEffect"; - - /** - *

Applies an emboss effect to the image.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_EMBOSS = "com.ruesga.android.wallpapers.photophase.effects.EmbossEffect"; - - /** - *

Applies a glow effect to the image.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_GLOW = "com.ruesga.android.wallpapers.photophase.effects.GlowEffect"; - - /** - *

Applies a halftone effect to the image.

- *

Available parameters:

- * - * - * - * - * - * - * - *
Parameter nameMeaningValid values
strengthThe halftone steps multiplier.Positive float (>0). Higher numbers produce smallest points
- */ - public static final String EFFECT_HALFTONE = "com.ruesga.android.wallpapers.photophase.effects.HalftoneEffect"; - - /** - *

Applies a mirror effect to the image.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_MIRROR = "com.ruesga.android.wallpapers.photophase.effects.MirrorEffect"; - - /** - *

Doesn't apply any effect.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_NULL = "com.ruesga.android.wallpapers.photophase.effects.NullEffect"; - - /** - *

Applies an outline effect to the image.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_OUTLINE = "com.ruesga.android.wallpapers.photophase.effects.OutlineEffect"; - - /** - *

Applies a pixelate effect to the image.

- *

Available parameters:

- * - * - * - * - * - * - * - *
Parameter nameMeaningValid values
strengthThe pixelate steps multiplier.Positive float (>0). Higher numbers produce more pixelation.
- */ - public static final String EFFECT_PIXELATE = "com.ruesga.android.wallpapers.photophase.effects.PixelateEffect"; - - /** - *

Applies a pop art (Warhol) effect to the image.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_POPART = "com.ruesga.android.wallpapers.photophase.effects.PopArtEffect"; - - /** - *

Applies a TV scan line effect to the image.

- *

Available parameters:

- * - * - *
Parameter nameMeaningValid values
- */ - public static final String EFFECT_SCANLINES = "com.ruesga.android.wallpapers.photophase.effects.ScanlinesEffect"; -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PixelateEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/PixelateEffect.java deleted file mode 100644 index 1f7ea70..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/PixelateEffect.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; -import android.opengl.GLES20; -import android.util.Log; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; - -/** - * A pixelate effect
- * - * - * - * - * - * - * - *
Parameter nameMeaningValid values
strengthThe pixelate strength.Positive float (>0). Higher numbers produce more pixelation.
- */ -public class PixelateEffect extends PhotoPhaseEffect { - - private static final String TAG = "PixelateEffect"; - - private static final String STRENGTH_PARAMETER = "strength"; - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "const float step_w = 0.0015625;\n" + - "const float step_h = 0.0027778;\n" + - "uniform float strength;\n" + - "void main(void)\n" + - "{\n" + - " float offx = floor(v_texcoord.s / (strength * step_w));\n" + - " float offy = floor(v_texcoord.t / (strength * step_h));\n" + - " vec3 res = texture2D(tex_sampler, vec2(offx * strength * step_w , offy * strength * step_h)).bgr;\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = res;\n" + - "}"; - - private float mStrength = 8.0f; - private int mStepsHandle; - - /** - * Constructor of PixelateEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public PixelateEffect(EffectContext ctx, String name) { - super(ctx, PixelateEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - - /** - * {@inheritDoc} - */ - @Override - void init(String vertexShader, String fragmentShader) { - super.init(vertexShader, fragmentShader); - - // Parameters - mStepsHandle = GLES20.glGetUniformLocation(mProgram, "strength"); - GLESUtil.glesCheckError("glGetUniformLocation"); - } - - /** - * {@inheritDoc} - */ - @Override - void applyParameters() { - // Set parameters - GLES20.glUniform1f(mStepsHandle, mStrength); - GLESUtil.glesCheckError("glUniform1f"); - } - - /** - * {@inheritDoc} - */ - @Override - public void setParameter(String parameterKey, Object value) { - if (parameterKey.compareTo(STRENGTH_PARAMETER) == 0) { - try { - float strength = Float.parseFloat(value.toString()); - if (strength <= 0) { - Log.w(TAG, "strength parameter must be >= 0"); - return; - } - mStrength = strength; - } catch (NumberFormatException ex) { - // Ignore - } - } - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PopArtEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/PopArtEffect.java deleted file mode 100644 index cda5fb0..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/PopArtEffect.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of kodemongki: -// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * A pop art (Warhol) effect
- * - * - *
Parameter nameMeaningValid values
- */ -public class PopArtEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "varying vec2 v_texcoord;\n" + - "void main(void)\n" + - "{\n" + - " vec3 col = texture2D(tex_sampler, v_texcoord).bgr;\n" + - " float y = 0.3 *col.r + 0.59 * col.g + 0.11 * col.b;\n" + - " y = y < 0.3 ? 0.0 : (y < 0.6 ? 0.5 : 1.0);\n" + - " if (y == 0.5)\n" + - " col = vec3(0.8, 0.0, 0.0);\n" + - " else if (y == 1.0)\n" + - " col = vec3(0.9, 0.9, 0.0);\n" + - " else\n" + - " col = vec3(0.0, 0.0, 0.0);\n" + - " gl_FragColor.a = 1.0;\n" + - " gl_FragColor.rgb = col;\n" + - "}"; - - /** - * Constructor of PopArtEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public PopArtEffect(EffectContext ctx, String name) { - super(ctx, PopArtEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/ScanlinesEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/ScanlinesEffect.java deleted file mode 100644 index 8343ed2..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/effects/ScanlinesEffect.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * - * 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. - */ -// -// Based on the shaders of Max Maischein of App-VideoMixer: -// http://cpansearch.perl.org/src/CORION/App-VideoMixer-0.02/filters/scanlines.glsl -// - -package com.ruesga.android.wallpapers.photophase.effects; - -import android.media.effect.EffectContext; - -/** - * A TV scanline effect
- * - * - *
Parameter nameMeaningValid values
- */ -public class ScanlinesEffect extends PhotoPhaseEffect { - - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "uniform sampler2D tex_sampler;\n" + - "uniform float offset;\n" + - "float frequency = 83.0;\n" + - "varying vec2 v_texcoord;\n" + - "void main(void)\n" + - "{\n" + - " float global_pos = (v_texcoord.y + offset) * frequency;\n" + - " float wave_pos = cos((fract(global_pos) - 0.5)*3.14);\n" + - " vec4 pel = texture2D(tex_sampler, v_texcoord);\n" + - " gl_FragColor = mix(vec4(0,0,0,0), pel, wave_pos);\n" + - "}"; - - /** - * Constructor of ScanlinesEffect. - * - * @param ctx The effect context - * @param name The effect name - */ - public ScanlinesEffect(EffectContext ctx, String name) { - super(ctx, ScanlinesEffect.class.getName()); - init(VERTEX_SHADER, FRAGMENT_SHADER); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/model/Album.java b/src/com/ruesga/android/wallpapers/photophase/model/Album.java deleted file mode 100644 index e7a015c..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/model/Album.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.model; - -import android.graphics.drawable.Drawable; - -import java.util.ArrayList; -import java.util.List; - -/** - * A class that represents an album - */ -public class Album implements Comparable, Cloneable { - - private Drawable mIcon; - private String mPath; - private String mName; - private String mDate; - private boolean mSelected; - private List mItems; - private List mSelectedItems; - - public Drawable getIcon() { - return mIcon; - } - - public void setIcon(Drawable icon) { - this.mIcon = icon; - } - - public String getPath() { - return mPath; - } - - public void setPath(String path) { - this.mPath = path; - } - - public String getName() { - return mName; - } - - public void setName(String name) { - this.mName = name; - } - - public String getDate() { - return mDate; - } - - public void setDate(String date) { - this.mDate = date; - } - - public boolean isSelected() { - return mSelected; - } - - public void setSelected(boolean selected) { - this.mSelected = selected; - } - - public List getItems() { - return mItems; - } - - public void setItems(List items) { - this.mItems = items; - } - - public List getSelectedItems() { - return mSelectedItems; - } - - public void setSelectedItems(List selectedItems) { - this.mSelectedItems = selectedItems; - } - - @Override - public int compareTo(Album another) { - return mPath.compareTo(another.mPath); - } - - @Override - public Object clone() { - Album album = new Album(); - album.mIcon = mIcon; - album.mPath = mPath; - album.mName = mName; - album.mDate = mDate; - album.mItems = new ArrayList(mItems); - album.mSelectedItems = new ArrayList(mSelectedItems); - album.mSelected = mSelected; - return album; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/model/Disposition.java b/src/com/ruesga/android/wallpapers/photophase/model/Disposition.java deleted file mode 100644 index 2a1b6b2..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/model/Disposition.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.model; - -import com.ruesga.android.wallpapers.photophase.PhotoFrame; - -/** - * A class that holds a {@link PhotoFrame} disposition. - */ -public class Disposition implements Comparable { - /** - * Column - */ - public int x; - /** - * Row - */ - public int y; - /** - * Columns width - */ - public int w; - /** - * Rows height - */ - public int h; - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + h; - result = prime * result + w; - result = prime * result + x; - result = prime * result + y; - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Disposition other = (Disposition) obj; - if (h != other.h) - return false; - if (w != other.w) - return false; - if (x != other.x) - return false; - if (y != other.y) - return false; - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "Disposition [x=" + x + ", y=" + y + ", w=" + w + ", h=" + h + "]"; - } - - /** - * {@inheritDoc} - */ - @Override - public int compareTo(Disposition another) { - if (x == another.x && y == another.y && w == another.w && h == another.h) { - return 0; - } - if (x < another.x) { - return -1; - } - if (x > another.x) { - return 1; - } - if (y < another.y) { - return -1; - } - if (y > another.y) { - return 1; - } - if (w < another.w) { - return -1; - } - if (w > another.w) { - return 1; - } - if (h < another.h) { - return -1; - } - return 1; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/ChoosePicturesFragment.java b/src/com/ruesga/android/wallpapers/photophase/preferences/ChoosePicturesFragment.java deleted file mode 100644 index dad1982..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/ChoosePicturesFragment.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.Cursor; -import android.os.AsyncTask; -import android.os.AsyncTask.Status; -import android.os.Bundle; -import android.preference.PreferenceFragment; -import android.provider.MediaStore; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.animations.AlbumsFlip3dAnimationController; -import com.ruesga.android.wallpapers.photophase.model.Album; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.widgets.AlbumInfo; -import com.ruesga.android.wallpapers.photophase.widgets.AlbumPictures; -import com.ruesga.android.wallpapers.photophase.widgets.CardLayout; -import com.ruesga.android.wallpapers.photophase.widgets.VerticalEndlessScroller; -import com.ruesga.android.wallpapers.photophase.widgets.VerticalEndlessScroller.OnEndScrollListener; - -import java.io.File; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -/** - * A fragment class for select the picture that will be displayed on the wallpaper - */ -public class ChoosePicturesFragment extends PreferenceFragment implements OnEndScrollListener { - - private static final String TAG = "ChoosePicturesFragment"; - - private static final boolean DEBUG = false; - - private static final int AMOUNT_OF_ADDED_STEPS = 5; - - private final AsyncTask mAlbumsLoaderTask = new AsyncTask() { - /** - * {@inheritDoc} - */ - @Override - protected Void doInBackground(Void... params) { - // Query all the external content and classify the pictures in albums and load the cards - DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); - Album album = null; - unregister(); - Cursor c = mContentResolver.query( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - new String[]{ MediaStore.MediaColumns.DATA }, - null, - null, - MediaStore.MediaColumns.DATA); - if (c != null) { - try { - long start = System.currentTimeMillis(); - if (DEBUG) Log.v(TAG, "Media library:"); - while (c.moveToNext()) { - // Only valid files (those i can read) - String p = c.getString(0); - if (DEBUG) Log.v(TAG, "\t" + p); - if (p != null) { - File f = new File(p); - if (f.isFile() && f.canRead()) { - File path = f.getParentFile(); - String name = path.getName(); - if (album == null || album.getPath().compareTo(path.getAbsolutePath()) != 0) { - if (album != null) { - mAlbums.add(album); - mOriginalAlbums.add((Album)album.clone()); - } - album = new Album(); - album.setPath(path.getAbsolutePath()); - album.setName(name); - album.setDate(df.format(new Date(path.lastModified()))); - album.setSelected(isSelectedItem(album.getPath())); - album.setItems(new ArrayList()); - album.setSelectedItems(new ArrayList()); - } - album.getItems().add(f.getAbsolutePath()); - if (isSelectedItem(f.getAbsolutePath())) { - album.getSelectedItems().add(f.getAbsolutePath()); - } - } - } - } - - // Add the last album - if (album != null) { - mAlbums.add(album); - mOriginalAlbums.add((Album)album.clone()); - } - long end = System.currentTimeMillis(); - if (DEBUG) Log.v(TAG, "Library loaded in " + (end - start) + " miliseconds"); - - } finally { - c.close(); - } - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPostExecute(Void result) { - Resources res = getActivity().getResources(); - int size = (int)(res.getDimension(R.dimen.album_size) + - res.getDimension(R.dimen.small_margin)); - mScroller.setEndPadding(size * AMOUNT_OF_ADDED_STEPS); - int height = mScroller.getMeasuredHeight(); - int steps = (height / size) + AMOUNT_OF_ADDED_STEPS; - - // Create the views an force a redraw the items - mAlbumViews = new ArrayList(mAlbums.size()); - for (Album item : mAlbums) { - mAlbumViews.add(createAlbumView(item)); - } - doEndScroll(steps, true); - - // We not need Hardware acceleration anymore (no more animations) - // Disable drawing cache - mScroller.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - mScroller.setDrawingCacheEnabled(false); - mScroller.setSmoothScrollingEnabled(true); - } - }; - - /*package*/ ContentResolver mContentResolver; - - /*package*/ List mAlbums; - /*package*/ List mAlbumViews; - /*package*/ List mOriginalAlbums; - /*package*/ List mAnimationControllers; - - /*package*/ Set mSelectedAlbums; - private Set mOriginalSelectedAlbums; - - /*package*/ VerticalEndlessScroller mScroller; - private CardLayout mAlbumsPanel; - - /*package*/ boolean mSelectionChanged; - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mContentResolver = getActivity().getContentResolver(); - - // Create an empty album - mAlbums = new ArrayList(); - mOriginalAlbums = new ArrayList(); - mAnimationControllers = new ArrayList(); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); - - // Load the albums user selection - mOriginalSelectedAlbums = Preferences.Media.getSelectedMedia(); - mSelectedAlbums = new HashSet(mOriginalSelectedAlbums); - mSelectionChanged = false; - - setHasOptionsMenu(true); - } - - /** - * {@inheritDoc} - */ - @Override - public void onDestroy() { - super.onDestroy(); - if (mAlbumsLoaderTask.getStatus().compareTo(Status.PENDING) == 0) { - mAlbumsLoaderTask.cancel(true); - } - unbindDrawables(mAlbumsPanel); - unregister(); - - // Notify that the settings was changed - Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); - if (mSelectionChanged) { - intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); - intent.putExtra(PreferencesProvider.EXTRA_FLAG_EMPTY_TEXTURE_QUEUE, Boolean.TRUE); - intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); - } - getActivity().sendBroadcast(intent); - } - - /*package*/ void unregister() { - mAlbums.clear(); - mOriginalAlbums.clear(); - mAnimationControllers.clear(); - } - - /** - * Method that unbind all the drawables for a view - * - * @param view The root view - */ - private void unbindDrawables(View view) { - if (view.getBackground() != null) { - view.getBackground().setCallback(null); - } - if (view instanceof ViewGroup) { - for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { - unbindDrawables(((ViewGroup) view).getChildAt(i)); - } - ((ViewGroup) view).removeAllViews(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - mScroller = - (VerticalEndlessScroller)inflater.inflate( - R.layout.choose_picture_fragment, container, false); - mScroller.setCallback(this); - mAlbumsPanel = (CardLayout)mScroller.findViewById(R.id.albums_panel); - - // Force Hardware acceleration - if (!mScroller.isHardwareAccelerated()) { - mScroller.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - if (!mAlbumsPanel.isHardwareAccelerated()) { - mAlbumsPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - - // Load the albums - mAlbumsLoaderTask.execute(); - - return mScroller; - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.albums, menu); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.mnu_ok: - getActivity().finish(); - return true; - case R.id.mnu_restore: - restoreData(); - return true; - case R.id.mnu_invert_all: - invertAll(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - /** - * Method that restores the albums to its original state - */ - private void restoreData() { - // Restore and the albums the selection - mSelectedAlbums = new HashSet(mOriginalSelectedAlbums); - mAlbums.clear(); - for (Album album : mOriginalAlbums) { - mAlbums.add((Album)album.clone()); - } - - // Update all the views - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - updateAll(); - } - - /** - * Method that inverts the selection of all the albums - */ - private void invertAll() { - // Restore and the albums the selection - mSelectedAlbums = new HashSet(); - for (Album album : mAlbums) { - album.setSelected(!album.isSelected()); - album.setSelectedItems(new ArrayList()); - if (album.isSelected()) { - mSelectedAlbums.add(album.getPath()); - } else { - mSelectedAlbums.addAll(album.getSelectedItems()); - } - } - - // Update all the views - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - updateAll(); - } - - /** - * Method that updates the current state of all the albums - */ - private void updateAll() { - // Update every view (albums and views should have the same size) - int count = mAlbumsPanel.getChildCount(); - for (int i = 0; i < count; i++) { - Album album = mAlbums.get(i); - View v = mAlbumsPanel.getChildAt(i); - AlbumInfo albumInfo = (AlbumInfo)v.findViewById(R.id.album_info); - AlbumPictures albumPictures = (AlbumPictures)v.findViewById(R.id.album_pictures); - albumInfo.updateView(album); - albumPictures.updateView(album, true); - } - - // Restore the preference - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - mSelectionChanged = true; - - // Restore all the animations states - for (AlbumsFlip3dAnimationController controller : mAnimationControllers) { - controller.reset(); - } - } - - /** - * Method that creates a new album to the card layout - * - * @param album The album to create - * @return View The view create - */ - View createAlbumView(final Album album) { - LayoutInflater li = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - final View albumView = li.inflate(R.layout.album, mAlbumsPanel, false); - final AlbumInfo albumInfo = (AlbumInfo)albumView.findViewById(R.id.album_info); - final AlbumPictures albumPictures = (AlbumPictures)albumView.findViewById(R.id.album_pictures); - - // Load the album info - albumInfo.post(new Runnable() { - @Override - public void run() { - albumInfo.updateView(album); - } - }); - if (album.isSelected()) { - albumInfo.setSelected(true); - } - albumInfo.addCallBackListener(new AlbumInfo.CallbacksListener() { - @Override - public void onAlbumSelected(Album ref) { - // Remove all pictures of the album and add the album reference - removeAlbumItems(ref); - mSelectedAlbums.add(ref.getPath()); - ref.setSelected(true); - albumPictures.updateView(ref, true); - - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - mSelectionChanged = true; - } - - @Override - public void onAlbumDeselected(Album ref) { - // Remove all pictures of the album - removeAlbumItems(ref); - ref.setSelected(false); - albumPictures.updateView(ref, true); - - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - mSelectionChanged = true; - } - - - }); - - // Load the album picture data - albumPictures.updateView(album, false); - albumPictures.addCallBackListener(new AlbumPictures.CallbacksListener() { - @Override - public void onBackButtonClick(View v) { - // Ignored - } - - @Override - public void onSelectionChanged(Album ref) { - // Remove, add, and persist the selection - removeAlbumItems(ref); - mSelectedAlbums.addAll(ref.getSelectedItems()); - ref.setSelected(false); - albumInfo.updateView(ref); - - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - mSelectionChanged = true; - } - }); - - // Register the animation controller - AlbumsFlip3dAnimationController controller = new AlbumsFlip3dAnimationController(albumInfo, albumPictures); - controller.register(); - mAnimationControllers.add(controller); - - return albumView; - } - - /** - * Method that checks if an item is selected - * - * @param item The item - * @return boolean if an item is selected - */ - /*package*/ boolean isSelectedItem(String item) { - Iterator it = mSelectedAlbums.iterator(); - while (it.hasNext()) { - String albumPath = it.next(); - if (item.compareTo(albumPath) == 0) { - return true; - } - } - return false; - } - - /** - * Method that removes the reference to all the items and itself - * - * @param ref The album - */ - /*package*/ void removeAlbumItems(Album ref) { - Iterator it = mSelectedAlbums.iterator(); - while (it.hasNext()) { - String item = it.next(); - String parent = new File(item).getParent(); - if (parent.compareTo(ref.getPath()) == 0) { - it.remove(); - } else if (item.compareTo(ref.getPath()) == 0) { - it.remove(); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onEndScroll() { - doEndScroll(AMOUNT_OF_ADDED_STEPS, false); - } - - /** - * Method that performs a scroll creating new items - * - * @param amount The amount of items to create - * @param animate If the add should be animated - */ - /*package*/ synchronized void doEndScroll(int amount, boolean animate) { - for (int i = 0; i < amount; i++) { - //Add to the panel of cards - if (mAlbumViews == null || mAlbumViews.isEmpty()) { - break; - } - mAlbumsPanel.addCard(mAlbumViews.remove(0), animate); - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/DispositionFragment.java b/src/com/ruesga/android/wallpapers/photophase/preferences/DispositionFragment.java deleted file mode 100644 index 5ce902e..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/DispositionFragment.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.preference.PreferenceFragment; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.model.Disposition; -import com.ruesga.android.wallpapers.photophase.widgets.DispositionView; -import com.ruesga.android.wallpapers.photophase.widgets.DispositionView.OnFrameSelectedListener; -import com.ruesga.android.wallpapers.photophase.widgets.ResizeFrame; - -import java.util.List; - -/** - * An abstract fragment class that allow to choose the layout disposition of the wallpaper. - */ -public abstract class DispositionFragment - extends PreferenceFragment implements OnFrameSelectedListener { - - private Runnable mRedraw = new Runnable() { - @Override - public void run() { - if (getActivity() == null) return; - try { - mDispositionView.setDispositions(getUserDispositions(), getCols(), getRows()); - } catch (Exception ex) { - // Ignored - } - } - }; - - /*package*/ DispositionView mDispositionView; - - private MenuItem mDeleteMenu; - - /** - * Constructor of DispositionFragment - */ - public DispositionFragment() { - super(); - } - - /** - * Method that returns the current user preference for the disposition - * - * @return List The current user preference dispositions - */ - public abstract List getUserDispositions(); - - /** - * Method that returns the default preference for the disposition - * - * @return List The default preference dispositions - */ - public abstract List getDefaultDispositions(); - - /** - * Method that request to save the dispositions - * - * @param dispositions The dispositions to save - */ - public abstract void saveDispositions(List dispositions); - - /** - * Method that returns the number of rows to use - * - * @return int The number of rows - */ - public abstract int getRows(); - - /** - * Method that returns the number of cols to use - * - * @return int The number of cols - */ - public abstract int getCols(); - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); - - setHasOptionsMenu(true); - } - - /** - * {@inheritDoc} - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - ViewGroup v = (ViewGroup)inflater.inflate(R.layout.choose_disposition_fragment, container, false); - mDispositionView = (DispositionView)v.findViewById(R.id.disposition_view); - mDispositionView.setResizeFrame((ResizeFrame)v.findViewById(R.id.resize_frame)); - mDispositionView.setOnFrameSelectedListener(this); - mDispositionView.post(mRedraw); - return v; - } - - /** - * {@inheritDoc} - */ - @Override - public void onDestroyView() { - super.onDestroyView(); - if (mDispositionView != null) { - mDispositionView.removeCallbacks(mRedraw); - if (mDispositionView.isChanged()) { - saveDispositions(mDispositionView.getDispositions()); - } - } - - // Notify that the settings was changed - Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); - if (mDispositionView.isChanged()) { - intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); - intent.putExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, Boolean.TRUE); - } - getActivity().sendBroadcast(intent); - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.dispositions, menu); - mDeleteMenu = menu.findItem(R.id.mnu_delete); - if (mDeleteMenu != null) { - mDeleteMenu.setVisible(false); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.mnu_ok: - getActivity().finish(); - return true; - case R.id.mnu_restore: - restoreData(); - return true; - case R.id.mnu_delete: - deleteFrame(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - /** - * Method that restores the disposition view to the default state - */ - private void restoreData() { - mDispositionView.setDispositions(getUserDispositions(), getCols(), getRows()); - } - - /** - * Method that restores the disposition view to the default state - */ - private void deleteFrame() { - mDispositionView.deleteCurrentFrame(); - } - - /** - * {@inheritDoc} - */ - @Override - public void onFrameSelectedListener(View v) { - if (mDeleteMenu != null) { - mDeleteMenu.setVisible(true); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onFrameUnselectedListener() { - if (mDeleteMenu != null) { - mDeleteMenu.setVisible(false); - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/GeneralPreferenceFragment.java b/src/com/ruesga/android/wallpapers/photophase/preferences/GeneralPreferenceFragment.java deleted file mode 100644 index 02cf741..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/GeneralPreferenceFragment.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.MultiSelectListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceFragment; -import android.util.Log; - -import com.ruesga.android.wallpapers.photophase.Colors; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor; -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.preferences.SeekBarProgressPreference.OnDisplayProgress; -import com.ruesga.android.wallpapers.photophase.widgets.ColorPickerPreference; - -/** - * A fragment class with all the general settings - */ -public class GeneralPreferenceFragment extends PreferenceFragment { - - private static final String TAG = "GeneralPreferenceFragment"; - - private static final boolean DEBUG = false; - - private SeekBarProgressPreference mWallpaperDim; - private ColorPickerPreference mBackgroundColor; - private ListPreference mTouchActions; - private CheckBoxPreference mFixAspectRatio; - private MultiSelectListPreference mTransitionsTypes; - private SeekBarProgressPreference mTransitionsInterval; - private MultiSelectListPreference mEffectsTypes; - - boolean mRedrawFlag; - boolean mEmptyTextureQueueFlag; - - private final OnPreferenceChangeListener mOnChangeListener = new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) { - String key = preference.getKey(); - if (DEBUG) Log.d(TAG, "Preference changed: " + key + "=" + newValue); - if (key.compareTo("ui_wallpaper_dim") == 0) { - mRedrawFlag = true; - } else if (key.compareTo("ui_background_color") == 0) { - mRedrawFlag = true; - int color = ((Integer)newValue).intValue(); - Colors.setBackground(new GLColor(color)); - } else if (key.compareTo("ui_fix_aspect_ratio") == 0) { - mRedrawFlag = true; - } else if (key.compareTo("ui_transition_types") == 0) { - mRedrawFlag = true; - } else if (key.compareTo("ui_transition_interval") == 0) { - mRedrawFlag = true; - } else if (key.compareTo("ui_effect_types") == 0) { - mRedrawFlag = true; - mEmptyTextureQueueFlag = true; - } - return true; - } - }; - - /** - * {@inheritDoc} - */ - @Override - public void onDestroy() { - super.onDestroy(); - - // Reload the settings - PreferencesProvider.reload(getActivity()); - - // Notify that the settings was changed - Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); - if (mRedrawFlag) { - intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); - } - if (mEmptyTextureQueueFlag) { - intent.putExtra(PreferencesProvider.EXTRA_FLAG_EMPTY_TEXTURE_QUEUE, Boolean.TRUE); - } - getActivity().sendBroadcast(intent); - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); - - final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - final Resources res = getActivity().getResources(); - - // Add the preferences - addPreferencesFromResource(R.xml.preferences_general); - - mWallpaperDim = (SeekBarProgressPreference)findPreference("ui_wallpaper_dim"); - mWallpaperDim.setFormat(getString(R.string.pref_general_settings_wallpaper_dim_format)); - mWallpaperDim.setOnPreferenceChangeListener(mOnChangeListener); - - mBackgroundColor = (ColorPickerPreference)findPreference("ui_background_color"); - mBackgroundColor.setOnPreferenceChangeListener(mOnChangeListener); - - mTouchActions = (ListPreference)findPreference("ui_touch_action"); - mTouchActions.setOnPreferenceChangeListener(mOnChangeListener); - - mFixAspectRatio = (CheckBoxPreference)findPreference("ui_fix_aspect_ratio"); - mFixAspectRatio.setOnPreferenceChangeListener(mOnChangeListener); - - mTransitionsTypes = (MultiSelectListPreference)findPreference("ui_transition_types"); - mTransitionsTypes.setOnPreferenceChangeListener(mOnChangeListener); - - final int[] transitionsIntervals = res.getIntArray(R.array.transitions_intervals_values); - mTransitionsInterval = (SeekBarProgressPreference)findPreference("ui_transition_interval"); - mTransitionsInterval.setFormat(getString(R.string.pref_general_transitions_interval_format)); - mTransitionsInterval.setMax(transitionsIntervals.length - 1); - int transitionInterval = prefs.getInt("ui_transition_interval", - Preferences.General.Transitions.DEFAULT_TRANSITION_INTERVAL_INDEX); - if (transitionInterval > (transitionsIntervals.length - 1)) { - mTransitionsInterval.setProgress( - Preferences.General.Transitions.DEFAULT_TRANSITION_INTERVAL_INDEX); - } - mTransitionsInterval.setOnDisplayProgress(new OnDisplayProgress() { - @Override - public String onDisplayProgress(int progress) { - return String.valueOf(transitionsIntervals[progress] / 1000); - } - }); - mTransitionsInterval.setOnPreferenceChangeListener(mOnChangeListener); - - mEffectsTypes = (MultiSelectListPreference)findPreference("ui_effect_types"); - mEffectsTypes.setOnPreferenceChangeListener(mOnChangeListener); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/LandscapeDispositionFragment.java b/src/com/ruesga/android/wallpapers/photophase/preferences/LandscapeDispositionFragment.java deleted file mode 100644 index 5dd2abc..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/LandscapeDispositionFragment.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.pm.ActivityInfo; -import android.os.Bundle; - -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.model.Disposition; -import com.ruesga.android.wallpapers.photophase.utils.DispositionUtil; - -import java.util.List; - -/** - * A fragment class that allow to choose the layout disposition of the wallpaper for landscape - * screen. - */ -public class LandscapeDispositionFragment extends DispositionFragment { - - /** - * Constructor of LandscapeDispositionFragment - */ - public LandscapeDispositionFragment() { - super(); - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } - - /** - * {@inheritDoc} - */ - @Override - public List getUserDispositions() { - return Preferences.Layout.getLandscapeDisposition(); - } - - /** - * {@inheritDoc} - */ - @Override - public List getDefaultDispositions() { - return DispositionUtil.toDispositions( - Preferences.Layout.DEFAULT_LANDSCAPE_DISPOSITION); - } - - /** - * {@inheritDoc} - */ - @Override - public void saveDispositions(List dispositions) { - Preferences.Layout.setLandscapeDisposition(getActivity(), dispositions); - } - - /** - * {@inheritDoc} - */ - @Override - public int getRows() { - // inverted - return Preferences.Layout.getCols(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getCols() { - // inverted - return Preferences.Layout.getRows(); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/LayoutPreferenceFragment.java b/src/com/ruesga/android/wallpapers/photophase/preferences/LayoutPreferenceFragment.java deleted file mode 100644 index e7a78e1..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/LayoutPreferenceFragment.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.Context; -import android.os.Bundle; -import android.preference.PreferenceFragment; - -import com.ruesga.android.wallpapers.photophase.R; - -/** - * A fragment class with the layout disposition - */ -public class LayoutPreferenceFragment extends PreferenceFragment { - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); - - // Add the preferences - addPreferencesFromResource(R.xml.preferences_layout); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/MediaPreferenceFragment.java b/src/com/ruesga/android/wallpapers/photophase/preferences/MediaPreferenceFragment.java deleted file mode 100644 index b15a771..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/MediaPreferenceFragment.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceFragment; -import android.util.Log; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; - -/** - * A fragment class with all the media settings - */ -public class MediaPreferenceFragment extends PreferenceFragment { - - private static final String TAG = "MediaPreferenceFragment"; - - private static final boolean DEBUG = false; - - ListPreference mRefreshInterval; - Preference mRefreshNow; - - boolean mMediaIntevalChangedFlag; - - private final OnPreferenceChangeListener mOnChangeListener = new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(final Preference preference, Object newValue) { - String key = preference.getKey(); - if (DEBUG) Log.d(TAG, "Preference changed: " + key + "=" + newValue); - if (key.compareTo("ui_media_refresh_interval") == 0) { - setRefreshIntervalSummary(Integer.valueOf(String.valueOf(newValue)).intValue()); - mMediaIntevalChangedFlag = true; - } - return true; - } - }; - - /** - * {@inheritDoc} - */ - @Override - public void onDestroy() { - super.onDestroy(); - - // Reload the settings - PreferencesProvider.reload(getActivity()); - - // Notify that the settings was changed - Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); - if (mMediaIntevalChangedFlag) { - intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_INTERVAL_CHANGED, Boolean.TRUE); - } - getActivity().sendBroadcast(intent); - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Change the preference manager - getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); - - // Add the preferences - addPreferencesFromResource(R.xml.preferences_media); - - mRefreshInterval = (ListPreference)findPreference("ui_media_refresh_interval"); - setRefreshIntervalSummary(Preferences.Media.getRefreshFrecuency()); - mRefreshInterval.setOnPreferenceChangeListener(mOnChangeListener); - - mRefreshNow = findPreference("ui_media_refresh_now"); - mRefreshNow.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - // Request a refresh of the media data - Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); - intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); - intent.putExtra(PreferencesProvider.EXTRA_ACTION_MEDIA_USER_RELOAD_REQUEST, Boolean.TRUE); - getActivity().sendBroadcast(intent); - return true; - } - }); - } - - /** - * Method that set the refresh interval summary - * - * @param interval The interval value - */ - void setRefreshIntervalSummary(int interval) { - String v = String.valueOf(interval); - String[] labels = getResources().getStringArray(R.array.refresh_intervals_labels); - String[] values = getResources().getStringArray(R.array.refresh_intervals_values); - int cc = values.length; - for (int i = 0; i < cc; i++) { - if (values[i].compareTo(String.valueOf(v)) == 0) { - v = labels[i]; - break; - } - } - String summary = - (interval == 0) - ? getString(R.string.pref_media_settings_refresh_interval_disable) - : getString(R.string.pref_media_settings_refresh_interval_summary, v); - mRefreshInterval.setSummary(summary); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/PhotoPhasePreferences.java b/src/com/ruesga/android/wallpapers/photophase/preferences/PhotoPhasePreferences.java deleted file mode 100644 index 8fbc2d2..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/PhotoPhasePreferences.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.app.ActionBar; -import android.os.Bundle; -import android.preference.PreferenceActivity; -import android.view.MenuItem; - -import com.ruesga.android.wallpapers.photophase.R; - -import java.util.List; - -/** - * The PhotoPhase Live Wallpaper preferences. - */ -public class PhotoPhasePreferences extends PreferenceActivity { - - /** - * {@inheritDoc} - */ - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - //Initialize action bars - initTitleActionBar(); - } - - /** - * Method that initializes the titlebar of the activity. - */ - private void initTitleActionBar() { - //Configure the action bar options - getActionBar().setDisplayOptions( - ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE); - getActionBar().setDisplayHomeAsUpEnabled(true); - } - - /** - * {@inheritDoc} - */ - @Override - public void onBuildHeaders(List
target) { - loadHeadersFromResource(R.xml.preferences_headers, target); - - // Retrieve the about header - Header aboutHeader = target.get(target.size() - 1); - try { - String appver = - this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName; - aboutHeader.summary = getString(R.string.pref_about_summary, appver); - } catch (Exception e) { - aboutHeader.summary = getString(R.string.pref_about_summary, ""); //$NON-NLS-1$ - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/PortraitDispositionFragment.java b/src/com/ruesga/android/wallpapers/photophase/preferences/PortraitDispositionFragment.java deleted file mode 100644 index 7b55c69..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/PortraitDispositionFragment.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.pm.ActivityInfo; -import android.os.Bundle; - -import com.ruesga.android.wallpapers.photophase.model.Disposition; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.utils.DispositionUtil; - -import java.util.List; - -/** - * A fragment class that allow to choose the layout disposition of the wallpaper for portrait - * screen. - */ -public class PortraitDispositionFragment extends DispositionFragment { - - /** - * Constructor of PortraitDispositionFragment - */ - public PortraitDispositionFragment() { - super(); - } - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } - - /** - * {@inheritDoc} - */ - @Override - public List getUserDispositions() { - return Preferences.Layout.getPortraitDisposition(); - } - - /** - * {@inheritDoc} - */ - @Override - public List getDefaultDispositions() { - return DispositionUtil.toDispositions( - Preferences.Layout.DEFAULT_PORTRAIT_DISPOSITION); - } - - /** - * {@inheritDoc} - */ - @Override - public void saveDispositions(List dispositions) { - Preferences.Layout.setPortraitDisposition(getActivity(), dispositions); - } - - /** - * {@inheritDoc} - */ - @Override - public int getRows() { - return Preferences.Layout.getRows(); - } - - /** - * {@inheritDoc} - */ - @Override - public int getCols() { - return Preferences.Layout.getCols(); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/PreferencesProvider.java b/src/com/ruesga/android/wallpapers/photophase/preferences/PreferencesProvider.java deleted file mode 100644 index f134516..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/PreferencesProvider.java +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.res.Resources; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor; -import com.ruesga.android.wallpapers.photophase.effects.Effects.EFFECTS; -import com.ruesga.android.wallpapers.photophase.model.Disposition; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; -import com.ruesga.android.wallpapers.photophase.utils.DispositionUtil; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * A class that holds all the preferences of the wallpaper - */ -@SuppressWarnings("boxing") -public final class PreferencesProvider { - - /** - * Internal broadcast action to communicate that some setting was changed - * @see #EXTRA_FLAG_REDRAW - * {@hide} - */ - public static final String ACTION_SETTINGS_CHANGED = "com.ruesga.android.wallpapers.photophase.actions.SETTINGS_CHANGED"; - - /** - * An extra setting that indicates that the changed setting request a whole recreation of the wallpaper world - * {@hide} - */ - public static final String EXTRA_FLAG_RECREATE_WORLD = "flag_recreate_world"; - - /** - * An extra setting that indicates that the changed setting request a redraw of the wallpaper - * {@hide} - */ - public static final String EXTRA_FLAG_REDRAW = "flag_redraw"; - - /** - * An extra setting that indicates that the changed setting request to empty the texture queue - * {@hide} - */ - public static final String EXTRA_FLAG_EMPTY_TEXTURE_QUEUE = "flag_empty_texture_queue"; - - /** - * An extra setting that indicates that the changed setting request a reload of the media data - * {@hide} - */ - public static final String EXTRA_FLAG_MEDIA_RELOAD = "flag_media_reload"; - - /** - * An extra setting that indicates that the changed setting notifies that the media - * interval was changed - * {@hide} - */ - public static final String EXTRA_FLAG_MEDIA_INTERVAL_CHANGED = "flag_media_interval_changed"; - - /** - * An extra setting that indicates that the media reload becomes from a user - * - * @see #EXTRA_FLAG_MEDIA_RELOAD - * {@hide} - */ - public static final String EXTRA_ACTION_MEDIA_USER_RELOAD_REQUEST = "action_media_user_reload_req"; - - /** - * The shared preferences file - */ - public static final String PREFERENCES_FILE = "com.ruesga.android.wallpapers.photophase"; - - private static Map mPreferences = new HashMap(); - - /** - * @hide - */ - /*package*/ static int[] TRANSITIONS_INTERVALS; - - /** - * Method that loads the all the preferences of the application - * - * @param context The current context - */ - public static void reload(Context context) { - SharedPreferences preferences = - context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); - mPreferences = preferences.getAll(); - - final Resources res = context.getResources(); - TRANSITIONS_INTERVALS = res.getIntArray(R.array.transitions_intervals_values); - } - - /** - * Method that returns a integer property value. - * - * @param key The preference key - * @param def The default value - * @return int The integer property value - */ - static int getInt(String key, int def) { - return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Integer ? - (Integer) mPreferences.get(key) : def; - } - - /** - * Method that returns a long property value. - * - * @param key The preference key - * @param def The default value - * @return long The long property value - */ - static long getLong(String key, long def) { - return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Long ? - (Long) mPreferences.get(key) : def; - } - - /** - * Method that returns a boolean property value. - * - * @param key The preference key - * @param def The default value - * @return boolean The boolean property value - */ - static boolean getBoolean(String key, boolean def) { - return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Boolean ? - (Boolean) mPreferences.get(key) : def; - } - - /** - * Method that returns a string property value. - * - * @param key The preference key - * @param def The default value - * @return String The string property value - */ - static String getString(String key, String def) { - return mPreferences.containsKey(key) && mPreferences.get(key) instanceof String ? - (String) mPreferences.get(key) : def; - } - - /** - * Method that returns a string set property value. - * - * @param key The preference key - * @param def The default value - * @return Set The string property value - */ - @SuppressWarnings("unchecked") - static Set getStringSet(String key, Set def) { - return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Set ? - (Set) mPreferences.get(key) : def; - } - - /** - * A class for access to the preferences of the application - */ - public static class Preferences { - /** - * General preferences - */ - public static class General { - private static final GLColor DEFAULT_BACKGROUND_COLOR = new GLColor("#ff202020"); - - /** - * Method that returns the wallpaper dimmed value. - * - * @return float If the wallpaper dimmed value (0-black, 100-black) - */ - public static float getWallpaperDim() { - return getInt("ui_wallpaper_dim", 0); - } - - /** - * Method that returns the background color - * - * @return GLColor The background color - */ - public static GLColor getBackgroundColor() { - int color = getInt("ui_background_color", 0); - if (color == 0) { - return DEFAULT_BACKGROUND_COLOR; - } - return new GLColor(color); - } - - /** - * Return the current user preference of the action to do when a frame is tap. - * - * @return TouchAction The action (default NONE) - */ - public static TouchAction getTouchAction() { - return TouchAction.fromValue(Integer.valueOf(getString("ui_touch_action", "0"))); - } - - /** - * Return the current user preference about fix or not fix the aspect ratio - * of the image by cropping the image. - * - * @return boolean Indicates if the image should be cropped - */ - public static boolean isFixAspectRatio() { - return getBoolean("ui_fix_aspect_ratio", true); - } - - /** - * Transitions preferences - */ - public static class Transitions { - /** - * The default transition interval - */ - public static final int DEFAULT_TRANSITION_INTERVAL_INDEX = 2; - - /** - * Return the current user preference about the transition to apply to - * the pictures of the wallpaper. - * - * @return TRANSITIONS[] The transition to apply to the wallpaper's pictures - */ - public static TRANSITIONS[] getTransitionTypes() { - Set set = getStringSet("ui_transition_types", new HashSet()); - if (set.isEmpty()) { - // Return all the transitions if no one is selected - return TRANSITIONS.getValidTranstions(); - } - String[] values = set.toArray(new String[set.size()]); - int count = values.length; - TRANSITIONS[] transitions = new TRANSITIONS[count]; - for (int i = 0; i < count; i++) { - transitions[i] = TRANSITIONS.fromOrdinal(Integer.valueOf(values[i])); - } - return transitions; - } - - /** - * Method that returns how often the transitions are triggered. - * - * @return int The milliseconds in which the next transition will be triggered - */ - public static int getTransitionInterval() { - int interval = getInt("ui_transition_interval", - DEFAULT_TRANSITION_INTERVAL_INDEX); - return TRANSITIONS_INTERVALS[interval]; - } - } - - /** - * Effects preferences - */ - public static class Effects { - /** - * Return the current user preference about the effect to apply to - * the pictures of the wallpaper. - * - * @return EFFECTS[] The effects to apply to the wallpaper's pictures - */ - public static EFFECTS[] getEffectTypes() { - Set defaults = new HashSet(); - defaults.add(String.valueOf(EFFECTS.NO_EFFECT.ordinal())); - Set set = getStringSet("ui_effect_types", defaults); - String[] values = set.toArray(new String[set.size()]); - int count = values.length; - EFFECTS[] effects = new EFFECTS[count]; - for (int i = 0; i < count; i++) { - effects[i] = EFFECTS.fromOrdinal(Integer.valueOf(values[i])); - } - return effects; - } - } - } - - /** - * Media preferences - */ - public static class Media { - /** - * Constant that indicates that the media reload is disabled - */ - public static final int MEDIA_RELOAD_DISABLED = 0; - - /** - * Method that returns the frequency with which the media is updated. - * - * @return int The interval in seconds between updates. 0 means that updates are disabled - */ - public static int getRefreshFrecuency() { - return Integer.valueOf(getString("ui_media_refresh_interval", String.valueOf(MEDIA_RELOAD_DISABLED))); - } - - /** - * Method that returns if the app must be select new albums when they are discovered. - * - * @return boolean If the app must be select new albums when they are discovered. - */ - public static boolean isAutoSelectNewAlbums() { - return getBoolean("ui_media_auto_select_new", Boolean.TRUE); - } - - // Internal settings (non-UI) - /** - * Method that returns the list of albums and pictures to be displayed - * - * @return Set The list of albums and pictures to be displayed - */ - public static Set getSelectedMedia() { - return getStringSet("media_selected_media", new HashSet()); - } - - /** - * Method that returns the list of albums and pictures to be displayed - * - * @param context The current context - * @param selection The new list of albums and pictures to be displayed - */ - public static synchronized void setSelectedMedia(Context context, Set selection) { - SharedPreferences preferences = - context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); - Editor editor = preferences.edit(); - editor.putStringSet("media_selected_media", selection); - editor.commit(); - reload(context); - } - - /** - * Method that returns the list of the name of the albums seen by the - * last media discovery scan. - * - * @return Set The list of albums and pictures to be displayed - */ - public static Set getLastDiscorevedAlbums() { - return getStringSet("media_last_disvored_albums", new HashSet()); - } - - /** - * Method that sets the list of the name of the albums seen by the - * last media discovery scan. - * - * @param context The current context - * @param albums The albums seen by the last media discovery scan - */ - public static synchronized void setLastDiscorevedAlbums(Context context, Set albums) { - SharedPreferences preferences = - context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); - Editor editor = preferences.edit(); - editor.putStringSet("media_last_disvored_albums", albums); - editor.commit(); - reload(context); - } - } - - /** - * Layout preferences - */ - public static class Layout { - - private static final int DEFAULT_COLS = 4; - private static final int DEFAULT_ROWS = 7; - public static final String DEFAULT_PORTRAIT_DISPOSITION = "0x0:2x1|0x2:1x3|0x4:3x6|2x2:3x3|3x0:3x0|3x1:3x1"; - public static final String DEFAULT_LANDSCAPE_DISPOSITION = "0x0:2x3|3x0:5x1|3x2:4x3|5x2:6x3|6x0:6x0|6x1:6x1"; - - /** - * Method that returns the rows of the wallpaper. - * - * @return int The rows of the wallpaper - */ - public static int getRows() { - return getInt("ui_layout_rows", DEFAULT_ROWS); - } - - /** - * Method that returns the columns of the wallpaper. - * - * @return int The columns of the wallpaper - */ - public static int getCols() { - return getInt("ui_layout_cols", DEFAULT_COLS); - } - - /** - * Returns the disposition of the photo frames in the wallpaper on portrait screen. The - * setting is stored as 0x0:1x2|2x2:3x4|..., which it means (position x=0, y=0, - * 1 cells width, 2 cells height, ...). - * - * @return List The photo frames dispositions - */ - public static List getPortraitDisposition() { - return DispositionUtil.toDispositions( - getString("ui_layout_portrait_disposition", DEFAULT_PORTRAIT_DISPOSITION)); - } - - /** - * Sets the disposition of the photo frames in the wallpaper on landscape screen. - * - * @param context The current context - * @param dispositions The photo frames dispositions - */ - public static void setPortraitDisposition(Context context, List dispositions) { - SharedPreferences preferences = - context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); - Editor editor = preferences.edit(); - editor.putString("ui_layout_portrait_disposition", - DispositionUtil.fromDispositions(dispositions)); - editor.commit(); - reload(context); - } - - /** - * Returns the disposition of the photo frames in the wallpaper on landscape screen. The - * setting is stored as 0x0:1x2|2x2:3x4|..., which it means (position x=0, y=0, - * 1 cells width, 2 cells height, ...). - * - * @return List The photo frames dispositions - */ - public static List getLandscapeDisposition() { - return DispositionUtil.toDispositions( - getString("ui_layout_landscape_disposition", DEFAULT_LANDSCAPE_DISPOSITION)); - } - - /** - * Sets the disposition of the photo frames in the wallpaper on landscape screen. - * - * @param context The current context - * @param dispositions The photo frames dispositions - */ - public static void setLandscapeDisposition(Context context, List dispositions) { - SharedPreferences preferences = - context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); - Editor editor = preferences.edit(); - editor.putString("ui_layout_landscape_disposition", - DispositionUtil.fromDispositions(dispositions)); - editor.commit(); - reload(context); - } - } - - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarPreference.java b/src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarPreference.java deleted file mode 100644 index 362f54e..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarPreference.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.Context; -import android.content.res.TypedArray; -import android.os.Parcel; -import android.os.Parcelable; -import android.preference.Preference; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.View; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; - -import com.ruesga.android.wallpapers.photophase.R; - -/** - * A preference with a seekbar widget - */ -public class SeekBarPreference extends Preference implements OnSeekBarChangeListener { - - private int mProgress; - private int mMax; - private boolean mTrackingTouch; - - /** - * Constructor of SeekBarPreference - * - * @param context The current context - * @param attrs The attributes of the view - * @param defStyle The resource with the style - */ - public SeekBarPreference( - Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setMax(100); - setLayoutResource(R.layout.preference_widget_seekbar); - } - - /** - * Constructor of SeekBarPreference - * - * @param context The current context - * @param attrs The attributes of the view - */ - public SeekBarPreference(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - /** - * Constructor of SeekBarPreference - * - * @param context The current context - */ - public SeekBarPreference(Context context) { - this(context, null); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onBindView(View view) { - super.onBindView(view); - SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar); - seekBar.setOnSeekBarChangeListener(this); - seekBar.setMax(mMax); - seekBar.setProgress(mProgress); - seekBar.setEnabled(isEnabled()); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("boxing") - protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { - setProgress(restoreValue ? getPersistedInt(mProgress) : (Integer) defaultValue); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("boxing") - protected Object onGetDefaultValue(TypedArray a, int index) { - return a.getInt(index, 0); - } - - /** - * Allows a Preference to intercept key events without having focus. - * For example, SeekBarPreference uses this to intercept +/- to adjust - * the progress. - * - * @param v The view - * @param keyCode The key code - * @param event The key event - * @return True if the Preference handled the key. Returns false by default. - */ - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() != KeyEvent.ACTION_UP) { - if (keyCode == KeyEvent.KEYCODE_PLUS - || keyCode == KeyEvent.KEYCODE_EQUALS) { - setProgress(getProgress() + 1); - return true; - } - if (keyCode == KeyEvent.KEYCODE_MINUS) { - setProgress(getProgress() - 1); - return true; - } - } - return false; - } - - /** - * Method that set the maximum progress - * - * @param max The maximum progress - */ - public void setMax(int max) { - if (max != mMax) { - mMax = max; - notifyChanged(); - } - } - - /** - * Method that set the actual progress - * - * @param progress The actual progress - */ - public void setProgress(int progress) { - setProgress(progress, true); - } - - /** - * Method that set the actual progress - * - * @param progress The actual progress - * @param notifyChanged Whether notify if the progress was changed - */ - protected void setProgress(int progress, boolean notifyChanged) { - int p = progress; - if (p > mMax) { - p = mMax; - } - if (p < 0) { - p = 0; - } - if (p != mProgress) { - mProgress = p; - persistInt(p); - if (notifyChanged) { - notifyChanged(); - } - } - } - - /** - * Method that returns the current progress - * - * @return int The current progress - */ - public int getProgress() { - return mProgress; - } - - /** - * Persist the seekBar's progress value if callChangeListener - * - * returns boolean True, otherwise set the seekBar's progress to the stored value - */ - @SuppressWarnings("boxing") - void syncProgress(SeekBar seekBar) { - int progress = seekBar.getProgress(); - if (progress != mProgress) { - if (callChangeListener(progress)) { - setProgress(progress, false); - } else { - seekBar.setProgress(mProgress); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser && !mTrackingTouch) { - syncProgress(seekBar); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - mTrackingTouch = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - mTrackingTouch = false; - if (seekBar.getProgress() != mProgress) { - syncProgress(seekBar); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected Parcelable onSaveInstanceState() { - /* - * Suppose a client uses this preference type without persisting. We - * must save the instance state so it is able to, for example, survive - * orientation changes. - */ - - final Parcelable superState = super.onSaveInstanceState(); - if (isPersistent()) { - // No need to save instance state since it's persistent - return superState; - } - - // Save the instance state - final SavedState myState = new SavedState(superState); - myState.progress = mProgress; - myState.max = mMax; - return myState; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onRestoreInstanceState(Parcelable state) { - if (!state.getClass().equals(SavedState.class)) { - // Didn't save state for us in onSaveInstanceState - super.onRestoreInstanceState(state); - return; - } - - // Restore the instance state - SavedState myState = (SavedState) state; - super.onRestoreInstanceState(myState.getSuperState()); - mProgress = myState.progress; - mMax = myState.max; - notifyChanged(); - } - - /** - * SavedState, a subclass of {@link BaseSavedState}, will store the state - * of MyPreference, a subclass of Preference. - *

- * It is important to always call through to super methods. - */ - private static class SavedState extends BaseSavedState { - int progress; - int max; - - public SavedState(Parcel source) { - super(source); - - // Restore the click counter - progress = source.readInt(); - max = source.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - - // Save the click counter - dest.writeInt(progress); - dest.writeInt(max); - } - - public SavedState(Parcelable superState) { - super(superState); - } - - @SuppressWarnings({"unused", "hiding"}) - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarProgressPreference.java b/src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarProgressPreference.java deleted file mode 100644 index d48c788..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/SeekBarProgressPreference.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.TextView; - -import com.ruesga.android.wallpapers.photophase.R; - -/** - * A preference with a seekbar widget that display the progress - */ -public class SeekBarProgressPreference extends SeekBarPreference { - - /** - * Interface to intercept the progress value to display on screen - */ - public interface OnDisplayProgress { - /** - * Method invoked when a progress value is going to display on screen - * - * @param progress The real progress - * @return The progress to display - */ - String onDisplayProgress(int progress); - } - - private String mFormat; - private OnDisplayProgress mOnDisplayProgress; - - TextView mTextView; - - /** - * Constructor of SeekBarProgressPreference - * - * @param context The current context - * @param attrs The attributes of the view - * @param defStyle The resource with the style - */ - public SeekBarProgressPreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Constructor of SeekBarProgressPreference - * - * @param context The current context - * @param attrs The attributes of the view - */ - public SeekBarProgressPreference(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of SeekBarProgressPreference - * - * @param context The current context - */ - public SeekBarProgressPreference(Context context) { - super(context); - init(); - } - - /** - * Method that initializes the preference - */ - void init() { - mFormat = "%s"; - mOnDisplayProgress = null; - setWidgetLayoutResource(R.layout.preference_widget_seekbar_progress); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onBindView(View view) { - super.onBindView(view); - mTextView = (TextView) view.findViewById(R.id.text); - setText(); - } - - /** - * Method that set the actual progress - * - * @param progress The actual progress - * @param notifyChanged Whether notify if the progress was changed - */ - @Override - protected void setProgress(int progress, boolean notifyChanged) { - super.setProgress(progress, notifyChanged); - setText(); - } - - /** - * Method that displays the progress value - */ - private void setText() { - if (mTextView != null) { - String value = String.valueOf(getProgress()); - if (mOnDisplayProgress != null) { - value = mOnDisplayProgress.onDisplayProgress(getProgress()); - } - mTextView.setText(String.format(mFormat, value)); - } - } - - /** - * Method that sets the callback to intercept the progress value before it will be - * displayed on screen. - * - * @param onDisplayProgress The callback - */ - public void setOnDisplayProgress(OnDisplayProgress onDisplayProgress) { - this.mOnDisplayProgress = onDisplayProgress; - } - - /** - * Method that set the format of the progress - * - * @param format The format of the string progress - */ - public void setFormat(String format) { - mFormat = format; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/preferences/TouchAction.java b/src/com/ruesga/android/wallpapers/photophase/preferences/TouchAction.java deleted file mode 100644 index bfbb2b2..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/preferences/TouchAction.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.preferences; - -/** - * An enumeration with all the touch actions supported - */ -public enum TouchAction { - /** - * No action - */ - NONE(0), - /** - * Force transition of the frame - */ - TRANSITION(1), - /** - * Open the picture of the frame - */ - OPEN(2), - /** - * Share/send the picture of the frame - */ - SHARE(3); - - private final int mValue; - - /** - * Constructor of TouchAction - * - * @param id The unique identifier - */ - private TouchAction(int value) { - mValue = value; - } - - /** - * Method that returns the value - * - * @return int The value - */ - public int getValue() { - return mValue; - } - - /** - * Method that gets the reference of a TouchAction from its value - * - * @param value The value - * @return TouchAction The reference - */ - public static final TouchAction fromValue(int value) { - if (value == TRANSITION.mValue) return TRANSITION; - if (value == OPEN.mValue) return OPEN; - if (value == SHARE.mValue) return SHARE; - return NONE; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/shapes/ColorShape.java b/src/com/ruesga/android/wallpapers/photophase/shapes/ColorShape.java deleted file mode 100644 index c771ba5..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/shapes/ColorShape.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.shapes; - -import android.content.Context; -import android.opengl.GLES20; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor; -import com.ruesga.android.wallpapers.photophase.R; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -/** - * A shape plus color. - */ -public class ColorShape implements DrawableShape { - - private int mProgramHandler; - private int mPositionHandler; - private int mColorHandler; - private int mMatrixHandler; - private FloatBuffer mVertexBuffer; - - private final GLColor mColor; - - /** - * Constructor of ColorShape. - * - * @param ctx The current context - * @param vertex The vertext data - * @param color The color - */ - public ColorShape(Context ctx, float[] vertex, GLColor color) { - super(); - mColor = color; - - mProgramHandler = GLESUtil.createProgram( - ctx.getResources(), - R.raw.color_vertex_shader, - R.raw.color_fragment_shader); - mPositionHandler = GLES20.glGetAttribLocation(mProgramHandler, "aPosition"); - GLESUtil.glesCheckError("glGetAttribLocation"); - mColorHandler = GLES20.glGetAttribLocation(mProgramHandler, "aColor"); - GLESUtil.glesCheckError("glGetAttribLocation"); - mMatrixHandler = GLES20.glGetUniformLocation(mProgramHandler, "uMVPMatrix"); - GLESUtil.glesCheckError("glGetUniformLocation"); - - // Initialize vertex byte buffer for shape coordinates - ByteBuffer bb = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) - bb.order(ByteOrder.nativeOrder()); - mVertexBuffer = bb.asFloatBuffer(); - mVertexBuffer.put(vertex); - mVertexBuffer.position(0); - } - - /** - * {@inheritDoc} - */ - @Override - public void draw(float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Enable properties - if (mColor.a != 1.0f) { - GLES20.glEnable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glEnable"); - GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_ALPHA); - GLESUtil.glesCheckError("glBlendFunc"); - } - - // Set the program and its attributes - GLES20.glUseProgram(mProgramHandler); - GLESUtil.glesCheckError("glUseProgram"); - - // Position - mVertexBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandler, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandler); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Color - GLES20.glVertexAttrib4f(mColorHandler, mColor.r, mColor.g, mColor.b, mColor.a); - GLESUtil.glesCheckError("glVertexAttrib4f"); - - // Apply the projection and view transformation - GLES20.glUniformMatrix4fv(mMatrixHandler, 1, false, matrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Draw the photo frame - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandler); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mColorHandler); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - - // Disable properties - if (mColor.a != 1.0f) { - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - } - } - - /** - * Method that sets the alpha color of the shape - * - * @param value The new alpha color of the shape - */ - public void setAlpha(float value) { - mColor.a = value; - } - - /** - * Method that destroy all the internal references - */ - public void recycle() { - if (GLES20.glIsProgram(mProgramHandler)) { - GLES20.glDeleteProgram(mProgramHandler); - GLESUtil.glesCheckError("glDeleteProgram"); - } - mProgramHandler = 0; - mPositionHandler = 0; - mColorHandler = 0; - mMatrixHandler = 0; - mVertexBuffer.clear(); - mVertexBuffer = null; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/shapes/DrawableShape.java b/src/com/ruesga/android/wallpapers/photophase/shapes/DrawableShape.java deleted file mode 100644 index 8ca1521..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/shapes/DrawableShape.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.shapes; - -/** - * An interface that defines an object as a drawable shape. - */ -public interface DrawableShape { - - /** - * Method that request redraw of the shape. - * - * @param matrix The model-view-projection matrix - */ - void draw(float[] matrix); -} diff --git a/src/com/ruesga/android/wallpapers/photophase/shapes/OopsShape.java b/src/com/ruesga/android/wallpapers/photophase/shapes/OopsShape.java deleted file mode 100644 index c765db8..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/shapes/OopsShape.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.shapes; - -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.opengl.GLES20; - -import com.ruesga.android.wallpapers.photophase.Colors; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor; -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; -import com.ruesga.android.wallpapers.photophase.R; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -/** - * A shape to draw an oops message - */ -public class OopsShape implements DrawableShape { - - private static final int VERTEX_SHADER = R.raw.default_vertex_shader; - private static final int FRAGMENT_SHADER = R.raw.default_fragment_shader; - - // The texture coordinates - private static final float[] TEXTURE_COORDS = { - 0.0f, 1.0f, - 1.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f - }; - - // The vertex position coordinates - private static final float[] VERTEX_COORDS_PORTRAIT = { - -0.75f, -0.5f, - 0.75f, -0.5f, - -0.75f, 0.5f, - 0.75f, 0.5f - }; - private static final float[] VERTEX_COORDS_LANDSCAPE = { - -0.5f, -0.75f, - 0.5f, -0.75f, - -0.5f, 0.75f, - 0.5f, 0.75f - }; - - private FloatBuffer mPositionBuffer; - private FloatBuffer mTextureBuffer; - - private int[] mProgramHandlers; - private int[] mTextureHandlers; - private int[] mPositionHandlers; - private int[] mTextureCoordHandlers; - private int[] mMVPMatrixHandlers; - - private String mMessage; - - private GLESTextureInfo mOopsImageTexture; - private GLESTextureInfo mOopsTextTexture; - - /** - * Constructor of OopsShape - * - * @param ctx The current context - * @param resourceMessageId The resource identifier with the message - */ - public OopsShape(Context ctx, int resourceMessageId) { - super(); - - int orientation = ctx.getResources().getConfiguration().orientation; - float[] vertex = VERTEX_COORDS_PORTRAIT; - if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - vertex = VERTEX_COORDS_LANDSCAPE; - } - - // Load the buffers - ByteBuffer bb1 = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) - bb1.order(ByteOrder.nativeOrder()); - mPositionBuffer = bb1.asFloatBuffer(); - mPositionBuffer.put(vertex); - mPositionBuffer.position(0); - // - - ByteBuffer bb2 = ByteBuffer.allocateDirect(TEXTURE_COORDS.length * 4); // (# of coordinate values * 4 bytes per float) - bb2.order(ByteOrder.nativeOrder()); - mTextureBuffer = bb2.asFloatBuffer(); - mTextureBuffer.put(TEXTURE_COORDS); - mTextureBuffer.position(0); - - // Initialize the structures - mProgramHandlers = new int[2]; - mTextureHandlers = new int[2]; - mPositionHandlers = new int[2]; - mTextureCoordHandlers = new int[2]; - mMVPMatrixHandlers = new int[2]; - - // Create all the params - for (int i = 0; i < 2; i++) { - mProgramHandlers[i] = - GLESUtil.createProgram( - ctx.getResources(), VERTEX_SHADER, FRAGMENT_SHADER); - mTextureHandlers[i] = - GLES20.glGetAttribLocation(mProgramHandlers[i], "sTexture"); - mPositionHandlers[i] = - GLES20.glGetAttribLocation(mProgramHandlers[i], "aPosition"); - GLESUtil.glesCheckError("glGetAttribLocation"); - mTextureCoordHandlers[i] = - GLES20.glGetAttribLocation(mProgramHandlers[i], "aTextureCoord"); - GLESUtil.glesCheckError("glGetAttribLocation"); - mMVPMatrixHandlers[i] = - GLES20.glGetUniformLocation(mProgramHandlers[i], "uMVPMatrix"); - GLESUtil.glesCheckError("glGetUniformLocation"); - } - - // Get the localized message - mMessage = ctx.getString(resourceMessageId); - - // Load the textures - mOopsImageTexture = GLESUtil.loadTexture(ctx, R.drawable.bg_oops, null, null, false); - Bitmap textBitmap = text2Bitmap(ctx, mMessage); - mOopsTextTexture = GLESUtil.loadTexture(textBitmap, null, null); - - // Recycle - mOopsImageTexture.bitmap.recycle(); - mOopsImageTexture.bitmap = null; - textBitmap.recycle(); - textBitmap = null; - mOopsTextTexture.bitmap.recycle(); - mOopsTextTexture.bitmap = null; - } - - /** - * {@inheritDoc} - */ - @Override - public void draw(float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Clear background - GLColor bg = Colors.getBackground(); - GLES20.glClearColor(bg.r, bg.g, bg.b, bg.a); - GLESUtil.glesCheckError("glClearColor"); - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); - GLESUtil.glesCheckError("glClear"); - - // Enable blend - GLES20.glEnable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glEnable"); - GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_COLOR); - GLESUtil.glesCheckError("glBlendFunc"); - - // Draw the textures - drawTexture(matrix, 0, mOopsImageTexture.handle); - drawTexture(matrix, 1, mOopsTextTexture.handle); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - } - - /** - * Method that draws a texture - * - * @param matrix The model-view-projection matrix - * @param index The index of the texture - * @param texture The texture handler - */ - private void drawTexture(float[] matrix, int index, int texture) { - // Use our shader program - GLES20.glUseProgram(mProgramHandlers[index]); - GLESUtil.glesCheckError("glUseProgram()"); - - // Apply the projection and view transformation - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[index], 1, false, matrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Texture - GLES20.glVertexAttribPointer(mTextureCoordHandlers[index], 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[index]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - GLES20.glVertexAttribPointer(mPositionHandlers[index], 2, GLES20.GL_FLOAT, false, 0, mPositionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[index]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Set the input textures - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[index], 0); - GLESUtil.glesCheckError("glUniform1i"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[index]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[index]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Method that requests to remove the internal references and resources. - */ - public void recycle() { - // Remove textures - if (mOopsImageTexture != null && mOopsImageTexture.handle != 0) { - int[] textures = new int[]{mOopsImageTexture.handle}; - GLES20.glDeleteTextures(1, textures, 0); - GLESUtil.glesCheckError("glDeleteTextures"); - } - mOopsImageTexture = null; - if (mOopsTextTexture != null && mOopsTextTexture.handle != 0) { - int[] textures = new int[]{mOopsTextTexture.handle}; - GLES20.glDeleteTextures(1, textures, 0); - GLESUtil.glesCheckError("glDeleteTextures"); - } - mOopsTextTexture = null; - - // Remove buffers - if (mPositionBuffer != null) { - mPositionBuffer.clear(); - } - if (mTextureBuffer != null) { - mTextureBuffer.clear(); - } - mPositionBuffer = null; - mTextureBuffer = null; - - for (int i = 0; i < 2; i++) { - if (GLES20.glIsProgram(mProgramHandlers[i])) { - GLES20.glDeleteProgram(mProgramHandlers[i]); - GLESUtil.glesCheckError("glDeleteProgram(" + i + ")"); - } - mProgramHandlers[i] = 0; - mTextureHandlers[i] = 0; - mPositionHandlers[i] = 0; - mTextureCoordHandlers[i] = 0; - mMVPMatrixHandlers[i] = 0; - } - } - - /** - * Method that converts a text to a bitmap - * - * @param ctx The current context - * @param text The text to draw to the bitmap - * @return Bitmap The bitmap with the text - */ - public Bitmap text2Bitmap(Context ctx, String text) { - Paint paint = new Paint(); - Typeface font = Typeface.createFromAsset(ctx.getAssets(), "fonts/Roboto-Bold.ttf"); - paint.setTypeface(font); - paint.setColor(Color.WHITE); - paint.setTextSize(24.0f); - paint.setAntiAlias(true); - paint.setTextAlign(Paint.Align.CENTER); - Bitmap src = mOopsImageTexture.bitmap; - Bitmap image = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(image); - canvas.drawText(text, src.getWidth()/2, src.getHeight() - (src.getHeight() * 0.33f), paint); - return image; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java b/src/com/ruesga/android/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java deleted file mode 100644 index ad76b2d..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.tasks; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; -import android.widget.ImageView; - -import com.ruesga.android.wallpapers.photophase.utils.BitmapUtils; - -import java.io.File; - -/** - * A class for load images associated to a ImageView in background. - */ -public class AsyncPictureLoaderTask extends AsyncTask { - - private final Context mContext; - private final ImageView mView; - - /** - * Constructor of AsyncPictureLoaderTask - * - * @param context The current context - * @param v The associated view - */ - public AsyncPictureLoaderTask(Context context, ImageView v) { - super(); - mContext = context; - mView = v; - } - - /** - * {@inheritDoc} - */ - @Override - protected Drawable doInBackground(File... params) { - int width = mView.getMeasuredWidth(); - int height = mView.getMeasuredHeight(); - Bitmap bitmap = BitmapUtils.decodeBitmap(params[0], width, height); - if (bitmap != null) { - return new BitmapDrawable(mContext.getResources(), bitmap); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPostExecute(Drawable result) { - mView.setImageDrawable(result); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/CubeTransition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/CubeTransition.java deleted file mode 100644 index 6acbf6b..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/CubeTransition.java +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLES20; -import android.opengl.GLException; -import android.opengl.Matrix; -import android.os.SystemClock; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.Utils; -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A transition that applies a cube effect transition to the picture. - */ -public class CubeTransition extends Transition { - - /** - * The enumeration of all possibles window movements - */ - public enum WINDOW_MODES { - /** - * Open the picture from left to right - */ - LEFT_TO_RIGHT, - /** - * Open the picture from right to left - */ - RIGHT_TO_LEFT - } - - private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; - private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; - - private static final float TRANSITION_TIME = 1000.0f; - - private static final float SCALE_AMOUNT = 0.2f; - - private WINDOW_MODES mMode; - - private boolean mRunning; - private long mTime; - - private FloatBuffer mPositionBuffer; - private float[] mTranslationMatrix; - private float[] mVertex; - - private float mAmount; - - /** - * Constructor of CubeTransition - * - * @param ctx The current context - * @param tm The texture manager - */ - public CubeTransition(Context ctx, TextureManager tm) { - super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); - - // Initialized - mTranslationMatrix = new float[16]; - reset(); - } - - /** - * {@inheritDoc} - */ - @Override - public TRANSITIONS getType() { - return TRANSITIONS.WINDOW; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasTransitionTarget() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunning() { - return mRunning; - } - - /** - * {@inheritDoc} - */ - @Override - public void select(PhotoFrame target) { - super.select(target); - mAmount = getAmount(); - - // Create the interal buffer - float[] vertex = target.getFrameVertex(); - if (mPositionBuffer == null) { - ByteBuffer bb = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) - bb.order(ByteOrder.nativeOrder()); - mPositionBuffer = bb.asFloatBuffer(); - } - if (mVertex == null) { - mVertex = new float[vertex.length]; - } - - // Random mode - List modes = - new ArrayList( - Arrays.asList(WINDOW_MODES.values())); - int low = 0; - int high = modes.size() - 1; - mMode = modes.get(Utils.getNextRandom(low, high)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectable(PhotoFrame frame) { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public void reset() { - mTime = -1; - mRunning = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void apply(float[] matrix) throws GLException { - // Check internal vars - if (mTarget == null || - mTarget.getPositionBuffer() == null || - mTarget.getTextureBuffer() == null) { - return; - } - if (mTransitionTarget == null || - mTransitionTarget.getPositionBuffer() == null || - mTransitionTarget.getTextureBuffer() == null) { - return; - } - - // Set the time the first time - if (mTime == -1) { - mTime = SystemClock.uptimeMillis(); - } - - // Calculate the delta time - final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; - - // Apply the transition - if (delta < 1) { - applyDstTransition(delta, matrix); - applySrcTransition(delta, matrix); - } else { - applyFinalTransition(matrix); - } - - // Transition ending - if (delta == 1) { - mRunning = false; - } - } - - /** - * Apply the source transition - * - * @param delta The delta time - * @param matrix The model-view-projection matrix - */ - private void applySrcTransition(float delta, float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Set the program - useProgram(0); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Set the input texture - int textureHandle = mTarget.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[0], 0); - GLESUtil.glesCheckError("glBindTexture"); - - // Texture - FloatBuffer textureBuffer = mTarget.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - setInternalVertex(); - float interpolation = delta > 0.5f - ? 1 - delta - : delta; - float w = Math.abs(mVertex[6] - mVertex[4]); - switch (mMode) { - case RIGHT_TO_LEFT: - mVertex[1] -= interpolation * mAmount; - mVertex[5] += interpolation * mAmount; - mVertex[4] += w * delta; - mVertex[0] = mVertex[4]; - break; - case LEFT_TO_RIGHT: - mVertex[3] -= interpolation * mAmount; - mVertex[7] += interpolation * mAmount; - mVertex[6] -= w * delta; - mVertex[2] = mVertex[6]; - break; - default: - break; - } - mPositionBuffer.position(0); - mPositionBuffer.put(mVertex); - mPositionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, mPositionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Calculate the delta angle and the translation and rotate parameters - float angle = 0.0f; - float translateX = 0.0f; - float rotateY = 0.0f; - switch (mMode) { - case RIGHT_TO_LEFT: - angle = delta * 90; - rotateY = -1.0f; - translateX = mVertex[2] * -1; - break; - case LEFT_TO_RIGHT: - angle = delta * -90; - rotateY = -1.0f; - translateX = mVertex[0] * -1; - break; - - default: - break; - } - - // Apply the projection and view transformation - Matrix.setIdentityM(matrix, 0); - Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, 0.0f, 0.0f); - Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, 0.0f, rotateY, 0.0f); - Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, 0.0f, 0.0f); - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, mTranslationMatrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Apply the destination transition - * - * @param delta The delta time - * @param matrix The model-view-projection matrix - */ - private void applyDstTransition(float delta, float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Set the program - useProgram(1); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Set the input texture - int textureHandle = mTransitionTarget.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[1], 0); - GLESUtil.glesCheckError("glBindTexture"); - - // Texture - FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - setInternalVertex(); - float interpolation = delta > 0.5f - ? 1 - delta - : delta; - float w = Math.abs(mVertex[6] - mVertex[4]); - switch (mMode) { - case LEFT_TO_RIGHT: - mVertex[1] -= interpolation * mAmount; - mVertex[5] += interpolation * mAmount; - mVertex[4] += w * (1 - delta); - mVertex[0] = mVertex[4]; - break; - case RIGHT_TO_LEFT: - mVertex[3] -= interpolation * mAmount; - mVertex[7] += interpolation * mAmount; - mVertex[6] -= w * (1 - delta); - mVertex[2] = mVertex[6]; - break; - default: - break; - } - mPositionBuffer.position(0); - mPositionBuffer.put(mVertex); - mPositionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, mPositionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Calculate the delta angle and the translation and rotate parameters - float angle = 0.0f; - float translateX = 0.0f; - float rotateY = 0.0f; - switch (mMode) { - case LEFT_TO_RIGHT: - angle = 90 - (delta * 90); - rotateY = -1.0f; - translateX = mVertex[2] * -1; - break; - case RIGHT_TO_LEFT: - angle = -90 + (delta * 90); - rotateY = -1.0f; - translateX = mVertex[0] * -1; - break; - - default: - break; - } - - // Apply the projection and view transformation - Matrix.setIdentityM(matrix, 0); - Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, 0.0f, 0.0f); - Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, 0.0f, rotateY, 0.0f); - Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, 0.0f, 0.0f); - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, mTranslationMatrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Apply the destination transition (just draw the image) - * - * @param matrix The model-view-projection matrix - */ - private void applyFinalTransition(float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Use our shader program - useProgram(1); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Apply the projection and view transformation - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, matrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Texture - FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - FloatBuffer positionBuffer = mTransitionTarget.getPositionBuffer(); - positionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Set the input texture - int textureHandle = mTransitionTarget.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[1], 0); - GLESUtil.glesCheckError("glUniform1i"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Method that prepares the internal vertex array - */ - private void setInternalVertex() { - float[] originalVertex = mTarget.getFrameVertex(); - System.arraycopy(originalVertex, 0, mVertex, 0, originalVertex.length); - } - - /** - * Return the scale amount to apply to the transition - * - * @return float The scale amount - */ - private float getAmount() { - return ((mTarget.getFrameWidth() * SCALE_AMOUNT) / 2); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/FadeTransition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/FadeTransition.java deleted file mode 100644 index 061c90e..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/FadeTransition.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLException; -import android.os.SystemClock; - -import com.ruesga.android.wallpapers.photophase.Colors; -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.shapes.ColorShape; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -/** - * A transition that applies a fade transition to the picture. - */ -public class FadeTransition extends NullTransition { - - private static final float TRANSITION_TIME = 600.0f; - - private boolean mRunning; - private long mTime; - - ColorShape mOverlay; - - /** - * Constructor of FadeTransition - * - * @param ctx The current context - * @param tm The texture manager - */ - public FadeTransition(Context ctx, TextureManager tm) { - super(ctx, tm); - } - - /** - * {@inheritDoc} - */ - @Override - public TRANSITIONS getType() { - return TRANSITIONS.FADE; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasTransitionTarget() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunning() { - return mRunning; - } - - /** - * {@inheritDoc} - */ - @Override - public void reset() { - super.reset(); - mTime = -1; - mRunning = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void select(PhotoFrame target) { - super.select(target); - mOverlay = new ColorShape(mContext, target.getFrameVertex(), Colors.getBackground()); - mOverlay.setAlpha(0); - } - - /** - * {@inheritDoc} - */ - @Override - public void apply(float[] matrix) throws GLException { - // Check internal vars - if (mTarget == null || - mTarget.getPositionBuffer() == null || - mTarget.getTextureBuffer() == null) { - return; - } - if (mTransitionTarget == null || - mTransitionTarget.getPositionBuffer() == null || - mTransitionTarget.getTextureBuffer() == null) { - return; - } - - // Set the time the first time - if (mTime == -1) { - mTime = SystemClock.uptimeMillis(); - } - - final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; - if (delta <= 0.5) { - // Draw the src target - draw(mTarget, matrix); - mOverlay.setAlpha(delta * 2.0f); - } else { - // Draw the dst target - draw(mTransitionTarget, matrix); - mOverlay.setAlpha((1 - delta) * 2.0f); - } - mOverlay.draw(matrix); - - // Transition ended - if (delta == 1) { - mRunning = false; - } - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/FlipTransition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/FlipTransition.java deleted file mode 100644 index f70fe95..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/FlipTransition.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLES20; -import android.opengl.GLException; -import android.opengl.Matrix; -import android.os.SystemClock; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.Utils; -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -import java.nio.FloatBuffer; - -/** - * A transition that applies a translation transition to the picture. - */ -public class FlipTransition extends Transition { - - /** - * The enumeration of all possibles translations movements - */ - public enum FLIP_MODES { - /** - * Flip the picture horizontally - */ - HORIZONTAL, - /** - * Flip the picture vertically - */ - VERTICAL - } - - private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; - private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; - - private static final float TRANSITION_TIME = 600.0f; - - private FLIP_MODES mMode; - - private float[] mTranslationMatrix; - - private boolean mRunning; - private long mTime; - - /** - * Constructor of FlipTransition - * - * @param ctx The current context - * @param tm The texture manager - */ - public FlipTransition(Context ctx, TextureManager tm) { - super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); - - // Initialized - mTranslationMatrix = new float[16]; - reset(); - } - - /** - * {@inheritDoc} - */ - @Override - public TRANSITIONS getType() { - return TRANSITIONS.FLIP; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasTransitionTarget() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunning() { - return mRunning; - } - - /** - * {@inheritDoc} - */ - @Override - public void select(PhotoFrame target) { - super.select(target); - - // Random mode - FLIP_MODES[] modes = FLIP_MODES.values(); - int low = 0; - int high = modes.length - 1; - mMode = modes[Utils.getNextRandom(low, high)]; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectable(PhotoFrame frame) { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public void reset() { - mTime = -1; - mRunning = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void apply(float[] matrix) throws GLException { - // Check internal vars - if (mTarget == null || - mTarget.getPositionBuffer() == null || - mTarget.getTextureBuffer() == null) { - return; - } - if (mTransitionTarget == null || - mTransitionTarget.getPositionBuffer() == null || - mTransitionTarget.getTextureBuffer() == null) { - return; - } - - // Set the time the first time - if (mTime == -1) { - mTime = SystemClock.uptimeMillis(); - } - - // Calculate the delta time - final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; - - // Apply the transition - applyTransition(delta, matrix, delta <= 0.5 ? mTarget : mTransitionTarget); - - // Transition ending - if (delta == 1) { - mRunning = false; - } - } - - /** - * Apply the transition - * - * @param delta The delta time - * @param matrix The model-view-projection matrix - * @param target The photo frame target - */ - private void applyTransition(float delta, float[] matrix, PhotoFrame target) { - // Retrieve the index of the structures - int index = delta <= 0.5f ? 0 : 1; - - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Set the program - useProgram(index); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Set the input texture - int textureHandle = target.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[index], 0); - GLESUtil.glesCheckError("glBindTexture"); - - // Texture - FloatBuffer textureBuffer = target.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[index], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[index]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - FloatBuffer positionBuffer = target.getPositionBuffer(); - positionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[index], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[index]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Calculate the delta angle and the translation and rotate parameters - float angle = (delta * 90) / 0.5f; - if (index == 1) { - angle = 90 - ((delta - 0.5f) * 90) / 0.5f; - } - float translateX = 0.0f; - float translateY = 0.0f; - float rotateX = 0.0f; - float rotateY = 0.0f; - switch (mMode) { - case HORIZONTAL: - rotateY = -1.0f; - translateX = (mTarget.getFrameVertex()[2] - ((mTarget.getFrameVertex()[2] - mTarget.getFrameVertex()[0]) / 2)) * -1; - break; - case VERTICAL: - rotateX = -1.0f; - translateY = (mTarget.getFrameVertex()[5] - ((mTarget.getFrameVertex()[5] - mTarget.getFrameVertex()[1]) / 2)) * -1; - break; - - default: - break; - } - - // Apply the projection and view transformation - Matrix.setIdentityM(matrix, 0); - Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, -translateY, 0.0f); - Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, rotateX, rotateY, 0.0f); - Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, translateY, 0.0f); - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[index], 1, false, mTranslationMatrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[index]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[index]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/NullTransition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/NullTransition.java deleted file mode 100644 index 6a28f2f..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/NullTransition.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLES20; -import android.opengl.GLException; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -import java.nio.FloatBuffer; -/** - * A special transition that does nothing other than draw the {@link PhotoFrame} - * on the screen continually. No transition is done. - */ -public class NullTransition extends Transition { - - private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader}; - private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader}; - - /** - * Constructor of NullTransition - * - * @param ctx The current context - * @param tm The texture manager - */ - public NullTransition(Context ctx, TextureManager tm) { - super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); - } - - /** - * {@inheritDoc} - */ - @Override - public void select(PhotoFrame target) { - super.select(target); - } - - /** - * {@inheritDoc} - */ - @Override - public TRANSITIONS getType() { - return TRANSITIONS.NO_TRANSITION; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasTransitionTarget() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunning() { - return mTarget == null || !mTarget.isLoaded(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectable(PhotoFrame frame) { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public void reset() { - // Nothing to do - } - - /** - * {@inheritDoc} - */ - @Override - public void apply(float[] matrix) throws GLException { - // Check internal vars - if (mTarget == null || - mTarget.getPositionBuffer() == null || - mTarget.getTextureBuffer() == null) { - return; - } - - // Draw the current target - draw(mTarget, matrix); - } - - /** - * Method that draws the picture texture - * - * @param target The target to draw - * @param matrix The model-view-projection matrix - */ - protected void draw(PhotoFrame target, float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Use our shader program - useProgram(0); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Apply the projection and view transformation - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, matrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Texture - FloatBuffer textureBuffer = target.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - FloatBuffer positionBuffer = target.getPositionBuffer(); - positionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Set the input texture - int textureHandle = target.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[0], 0); - GLESUtil.glesCheckError("glUniform1i"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/SwapTransition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/SwapTransition.java deleted file mode 100644 index 7a40eeb..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/SwapTransition.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLException; -import android.os.SystemClock; - -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -/** - * A simple transition that swap an image after the transition time is ended. - */ -public class SwapTransition extends NullTransition { - - private static final float TRANSITION_TIME = 250.0f; - - private boolean mRunning; - private long mTime; - - /** - * Constructor of SwapTransition - * - * @param ctx The current context - * @param tm The texture manager - */ - public SwapTransition(Context ctx, TextureManager tm) { - super(ctx, tm); - } - - /** - * {@inheritDoc} - */ - @Override - public TRANSITIONS getType() { - return TRANSITIONS.SWAP; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasTransitionTarget() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunning() { - return mRunning; - } - - /** - * {@inheritDoc} - */ - @Override - public void reset() { - super.reset(); - mTime = -1; - mRunning = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void apply(float[] matrix) throws GLException { - // Check internal vars - if (mTarget == null || - mTarget.getPositionBuffer() == null || - mTarget.getTextureBuffer() == null) { - return; - } - if (mTransitionTarget == null || - mTransitionTarget.getPositionBuffer() == null || - mTransitionTarget.getTextureBuffer() == null) { - return; - } - - // Set the time the first time - if (mTime == -1) { - mTime = SystemClock.uptimeMillis(); - } - - // Calculate the delta time - final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; - - // Apply the transition - boolean ended = delta == 1; - draw(ended ? mTransitionTarget : mTarget, matrix); - mRunning = !ended; - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/Transition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/Transition.java deleted file mode 100644 index db468ef..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/Transition.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLES20; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -/** - * The base class of all transitions that can be applied to the {@link PhotoFrame} classes. - */ -public abstract class Transition { - - public static final long MAX_TRANSTION_TIME = 1500L; - - protected final Context mContext; - private final TextureManager mTextureManager; - - protected int[] mProgramHandlers; - protected int[] mTextureHandlers; - protected int[] mPositionHandlers; - protected int[] mTextureCoordHandlers; - protected int[] mMVPMatrixHandlers; - - protected PhotoFrame mTarget; - protected PhotoFrame mTransitionTarget; - - private final int[] mVertexShader; - private final int[] mFragmentShader; - - /** - * Constructor of Transition - * - * @param ctx The current context - * @param tm The current texture manager - * @param vertexShader The vertex shaders of the programs - * @param fragmentShader The fragment shaders of the programs - */ - public Transition(Context ctx, TextureManager tm, int[] vertexShader, int[] fragmentShader) { - super(); - mContext = ctx; - mTextureManager = tm; - mVertexShader = vertexShader; - mFragmentShader = fragmentShader; - - // Compile every program - assert mVertexShader.length != mFragmentShader.length; - int cc = mVertexShader.length; - mProgramHandlers = new int[cc]; - mTextureHandlers = new int[cc]; - mPositionHandlers = new int[cc]; - mTextureCoordHandlers = new int[cc]; - mMVPMatrixHandlers = new int[cc]; - for (int i = 0; i < cc; i++) { - createProgram(i); - } - } - - /** - * Method that requests to apply this transition. - * - * @param target The target photo frame - */ - public void select(PhotoFrame target) { - mTarget = target; - if (hasTransitionTarget()) { - // Load the transition frame and request a picture for it - mTransitionTarget = - new PhotoFrame( - mContext, - mTextureManager, - mTarget.getFrameVertex(), - mTarget.getPhotoVertex(), - mTarget.getBackgroundColor()); - } - } - - /** - * Method that returns the target of the transition. - * - * @return PhotoFrame The target of the transition - */ - public PhotoFrame getTarget() { - return mTarget; - } - - /** - * Method that returns the transition target of the transition. - * - * @return PhotoFrame The transition target of the transition - */ - public PhotoFrame getTransitionTarget() { - return mTransitionTarget; - } - - /** - * Method that returns if the transition is selectable for the passed frame. - * - * @param frame The frame which the transition should be applied to - * @return boolean If the transition is selectable for the passed frame - */ - public abstract boolean isSelectable(PhotoFrame frame); - - /** - * Method that resets the current status of the transition. - */ - public abstract void reset(); - - /** - * Method that returns the type of transition. - * - * @return TRANSITIONS The type of transition - */ - public abstract TRANSITIONS getType(); - - /** - * Method that requests to apply this transition. - * - * @param matrix The model-view-projection matrix - */ - public abstract void apply(float[] matrix); - - /** - * Method that returns if the transition is being transition. - * - * @return boolean If the transition is being transition. - */ - public abstract boolean isRunning(); - - /** - * Method that return if the transition has a secondary target - * - * @return boolean If the transition has a secondary target - */ - public abstract boolean hasTransitionTarget(); - - /** - * Method that creates the program - */ - protected void createProgram(int index) { - mProgramHandlers[index] = - GLESUtil.createProgram( - mContext.getResources(), mVertexShader[index], mFragmentShader[index]); - mTextureHandlers[index] = - GLES20.glGetAttribLocation(mProgramHandlers[index], "sTexture"); - mPositionHandlers[index] = - GLES20.glGetAttribLocation(mProgramHandlers[index], "aPosition"); - GLESUtil.glesCheckError("glGetAttribLocation"); - mTextureCoordHandlers[index] = - GLES20.glGetAttribLocation(mProgramHandlers[index], "aTextureCoord"); - GLESUtil.glesCheckError("glGetAttribLocation"); - mMVPMatrixHandlers[index] = - GLES20.glGetUniformLocation(mProgramHandlers[index], "uMVPMatrix"); - GLESUtil.glesCheckError("glGetUniformLocation"); - } - - /** - * Method that set the program to use - * - * @param index The index of the program to use - */ - protected void useProgram(int index) { - if (!GLES20.glIsProgram(mProgramHandlers[index])) { - createProgram(index); - } - GLES20.glUseProgram(mProgramHandlers[index]); - GLESUtil.glesCheckError("glUseProgram()"); - } - - /** - * Method that requests to the transition to remove its internal references and resources. - */ - public void recycle() { - int cc = mProgramHandlers.length; - for (int i = 0; i < cc; i++) { - if (GLES20.glIsProgram(mProgramHandlers[i])) { - GLES20.glDeleteProgram(mProgramHandlers[i]); - GLESUtil.glesCheckError("glDeleteProgram"); - } - mProgramHandlers[i] = -1; - mTextureHandlers[i] = -1; - mPositionHandlers[i] = -1; - mTextureCoordHandlers[i] = -1; - mMVPMatrixHandlers[i] = -1; - } - mTransitionTarget = null; - mTarget = null; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/Transitions.java b/src/com/ruesga/android/wallpapers/photophase/transitions/Transitions.java deleted file mode 100644 index 964893a..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/Transitions.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; - -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import com.ruesga.android.wallpapers.photophase.utils.Utils; - -import java.util.Arrays; -import java.util.List; - - -/** - * A class that manages all the supported transitions - */ -public class Transitions { - - /** - * Enumeration of the supported transitions - */ - public enum TRANSITIONS { - /** - * @see NullTransition - */ - NO_TRANSITION, - /** - * @see CubeTransition - */ - CUBE, - /** - * @see FadeTransition - */ - FADE, - /** - * @see FlipTransition - */ - FLIP, - /** - * @see SwapTransition - */ - SWAP, - /** - * @see TranslateTransition - */ - TRANSLATION, - /** - * @see WindowTransition - */ - WINDOW; - - /** - * Method that returns the transition from its ordinal position - * - * @param ordinal The ordinal position - * @return TRANSITIONS The transition or null if wasn't found - */ - public static TRANSITIONS fromOrdinal(int ordinal) { - for (TRANSITIONS transition : TRANSITIONS.values()) { - if (transition.ordinal() == ordinal) { - return transition; - } - } - return null; - } - - /** - * Method that the returns an array with all the valid transitions (NO_TRANSITION is not - * a valid one). - * - * @return TRANSITIONS[] The valid transitions - */ - public static TRANSITIONS[] getValidTranstions() { - TRANSITIONS[] src = TRANSITIONS.values(); - TRANSITIONS[] dst = new TRANSITIONS[src.length-1]; - System.arraycopy(src, 1, dst, 0, src.length-1); - return dst; - } - } - - /** - * Method that return the next type of transition to apply the picture. - * - * @param frame The frame which the translation will be applied to - * @return TRANSITIONS The next type of transition to apply - */ - public static TRANSITIONS getNextTypeOfTransition(PhotoFrame frame) { - // Get a transition based on the user preference - List transitions = - Arrays.asList(Preferences.General.Transitions.getTransitionTypes()); - TRANSITIONS nextTransition = null; - if (transitions.size() > 0) { - int low = 0; - int high = transitions.size() - 1; - int pos = Utils.getNextRandom(low, high); - nextTransition = transitions.get(pos); - } - if (nextTransition == null) { - return TRANSITIONS.NO_TRANSITION; - } - - // Select the transition if is available - if (nextTransition.compareTo(TRANSITIONS.SWAP) == 0) { - return TRANSITIONS.SWAP; - } else if (nextTransition.compareTo(TRANSITIONS.FADE) == 0) { - return TRANSITIONS.FADE; - } else if (nextTransition.compareTo(TRANSITIONS.TRANSLATION) == 0) { - return TRANSITIONS.TRANSLATION; - } else if (nextTransition.compareTo(TRANSITIONS.FLIP) == 0) { - return TRANSITIONS.FLIP; - } else if (nextTransition.compareTo(TRANSITIONS.WINDOW) == 0) { - return TRANSITIONS.WINDOW; - } else if (nextTransition.compareTo(TRANSITIONS.CUBE) == 0) { - return TRANSITIONS.CUBE; - } - return TRANSITIONS.NO_TRANSITION; - } - - /** - * Method that creates a new transition. - * - * @param ctx The current context - * @param tm The texture manager - * @param type The type of transition - * @param frame The frame which the translation will be applied to - * @return Transition The next transition to apply - */ - public static Transition createTransition( - Context ctx, TextureManager tm, TRANSITIONS type, PhotoFrame frame) { - if (type.compareTo(TRANSITIONS.SWAP) == 0) { - return new SwapTransition(ctx, tm); - } else if (type.compareTo(TRANSITIONS.FADE) == 0) { - return new FadeTransition(ctx, tm); - } else if (type.compareTo(TRANSITIONS.TRANSLATION) == 0) { - return new TranslateTransition(ctx, tm); - } else if (type.compareTo(TRANSITIONS.FLIP) == 0) { - return new FlipTransition(ctx, tm); - } else if (type.compareTo(TRANSITIONS.WINDOW) == 0) { - return new WindowTransition(ctx, tm); - } else if (type.compareTo(TRANSITIONS.CUBE) == 0) { - return new CubeTransition(ctx, tm); - } - return new NullTransition(ctx, tm); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/TranslateTransition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/TranslateTransition.java deleted file mode 100644 index 3e3471e..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/TranslateTransition.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLES20; -import android.opengl.GLException; -import android.opengl.Matrix; -import android.os.SystemClock; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.Utils; -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A transition that applies a translation transition to the picture. - */ -public class TranslateTransition extends Transition { - - /** - * The enumeration of all possibles translations movements - */ - public enum TRANSLATE_MODES { - /** - * Translate the picture from left to right - */ - LEFT_TO_RIGHT, - /** - * Translate the picture from right to left - */ - RIGHT_TO_LEFT, - /** - * Translate the picture from up to down - */ - UP_TO_DOWN, - /** - * Translate the picture from down to up - */ - DOWN_TO_UP - } - - private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; - private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; - - private static final float TRANSITION_TIME = 600.0f; - - private TRANSLATE_MODES mMode; - - private boolean mRunning; - private long mTime; - - /** - * Constructor of TranslateTransition - * - * @param ctx The current context - * @param tm The texture manager - */ - public TranslateTransition(Context ctx, TextureManager tm) { - super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); - - // Initialized - reset(); - } - - /** - * {@inheritDoc} - */ - @Override - public TRANSITIONS getType() { - return TRANSITIONS.TRANSLATION; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasTransitionTarget() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunning() { - return mRunning; - } - - /** - * {@inheritDoc} - */ - @Override - public void select(PhotoFrame target) { - super.select(target); - - // Discard all non-supported modes - List modes = - new ArrayList( - Arrays.asList(TRANSLATE_MODES.values())); - float[] vertex = target.getFrameVertex(); - if (vertex[4] != -1.0f) { - modes.remove(TRANSLATE_MODES.RIGHT_TO_LEFT); - } - if (vertex[6] != 1.0f) { - modes.remove(TRANSLATE_MODES.LEFT_TO_RIGHT); - } - if (vertex[5] != 1.0f) { - modes.remove(TRANSLATE_MODES.DOWN_TO_UP); - } - if (vertex[1] != -1.0f) { - modes.remove(TRANSLATE_MODES.UP_TO_DOWN); - } - - // Random mode - int low = 0; - int high = modes.size() - 1; - mMode = modes.get(Utils.getNextRandom(low, high)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectable(PhotoFrame frame) { - float[] vertex = frame.getFrameVertex(); - if (vertex[4] == -1.0f || vertex[6] == 1.0f || - vertex[5] == 1.0f || vertex[1] == -1.0f) { - return true; - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void reset() { - mTime = -1; - mRunning = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void apply(float[] matrix) throws GLException { - // Check internal vars - if (mTarget == null || - mTarget.getPositionBuffer() == null || - mTarget.getTextureBuffer() == null) { - return; - } - if (mTransitionTarget == null || - mTransitionTarget.getPositionBuffer() == null || - mTransitionTarget.getTextureBuffer() == null) { - return; - } - - // Set the time the first time - if (mTime == -1) { - mTime = SystemClock.uptimeMillis(); - } - - // Calculate the delta time - final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; - - // Apply the transition - applyTransitionToDst(delta, matrix); - if (delta < 1) { - applyTransitionToSrc(delta, matrix); - } - - // Transition ending - if (delta == 1) { - mRunning = false; - } - } - - /** - * Apply the transition to the source frame - * - * @param delta The delta time - * @param matrix The model-view-projection matrix - */ - private void applyTransitionToSrc(float delta, float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Set the program - useProgram(0); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Set the input texture - int textureHandle = mTarget.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[0], 0); - GLESUtil.glesCheckError("glBindTexture"); - - // Texture - FloatBuffer textureBuffer = mTarget.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - FloatBuffer positionBuffer = mTarget.getPositionBuffer(); - positionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Calculate the delta distance - float distance = - (mMode.compareTo(TRANSLATE_MODES.LEFT_TO_RIGHT) == 0 || mMode.compareTo(TRANSLATE_MODES.RIGHT_TO_LEFT) == 0) - ? mTarget.getFrameWidth() - : mTarget.getFrameHeight(); - if (mMode.compareTo(TRANSLATE_MODES.RIGHT_TO_LEFT) == 0 || mMode.compareTo(TRANSLATE_MODES.DOWN_TO_UP) == 0) { - distance *= -1; - } - distance *= delta; - boolean vertical = (mMode.compareTo(TRANSLATE_MODES.UP_TO_DOWN) == 0 || mMode.compareTo(TRANSLATE_MODES.DOWN_TO_UP) == 0); - - // Apply the projection and view transformation - float[] translationMatrix = new float[16]; - if (vertical) { - Matrix.translateM(translationMatrix, 0, matrix, 0, 0.0f, distance, 0.0f); - } else { - Matrix.translateM(translationMatrix, 0, matrix, 0, distance, 0.0f, 0.0f); - } - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, translationMatrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Apply the transition to the destination frame - * - * @param delta The delta time - * @param matrix The model-view-projection matrix - */ - private void applyTransitionToDst(float delta, float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Set the program - useProgram(1); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Set the input texture - int textureHandle = mTransitionTarget.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[1], 0); - GLESUtil.glesCheckError("glUniform1i"); - - // Texture - FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - FloatBuffer positionBuffer = mTransitionTarget.getPositionBuffer(); - positionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Apply the projection and view transformation - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, matrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/transitions/WindowTransition.java b/src/com/ruesga/android/wallpapers/photophase/transitions/WindowTransition.java deleted file mode 100644 index 255e829..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/transitions/WindowTransition.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.transitions; - -import android.content.Context; -import android.opengl.GLES20; -import android.opengl.GLException; -import android.opengl.Matrix; -import android.os.SystemClock; -import android.view.animation.AccelerateInterpolator; - -import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; -import com.ruesga.android.wallpapers.photophase.utils.Utils; -import com.ruesga.android.wallpapers.photophase.PhotoFrame; -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.TextureManager; -import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * A transition that applies a window effect transition to the picture. - */ -public class WindowTransition extends Transition { - - /** - * The enumeration of all possibles window movements - */ - public enum WINDOW_MODES { - /** - * Open the picture from left to right - */ - LEFT_TO_RIGHT, - /** - * Open the picture from right to left - */ - RIGHT_TO_LEFT - } - - private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; - private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; - - private static final float TRANSITION_TIME = 800.0f; - - private static final float SCALE_AMOUNT = 0.2f; - - private WINDOW_MODES mMode; - - private float[] mTranslationMatrix; - - private boolean mRunning; - private long mTime; - - private AccelerateInterpolator mInterpolation; - private float mAmount; - - /** - * Constructor of WindowTransition - * - * @param ctx The current context - * @param tm The texture manager - */ - public WindowTransition(Context ctx, TextureManager tm) { - super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); - - // Initialized - mTranslationMatrix = new float[16]; - reset(); - } - - /** - * {@inheritDoc} - */ - @Override - public TRANSITIONS getType() { - return TRANSITIONS.WINDOW; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasTransitionTarget() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRunning() { - return mRunning; - } - - /** - * {@inheritDoc} - */ - @Override - public void select(PhotoFrame target) { - super.select(target); - mInterpolation = new AccelerateInterpolator(); - mAmount = getAmount(); - - // Discard all non-supported modes - List modes = - new ArrayList( - Arrays.asList(WINDOW_MODES.values())); - float[] vertex = target.getFrameVertex(); - if (vertex[4] != -1.0f) { - modes.remove(WINDOW_MODES.RIGHT_TO_LEFT); - } - if (vertex[6] != 1.0f) { - modes.remove(WINDOW_MODES.LEFT_TO_RIGHT); - } - - // Random mode - int low = 0; - int high = modes.size() - 1; - mMode = modes.get(Utils.getNextRandom(low, high)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isSelectable(PhotoFrame frame) { - float[] vertex = frame.getFrameVertex(); - if (vertex[4] == -1.0f || vertex[6] == 1.0f) { - return true; - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public void reset() { - mTime = -1; - mRunning = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void apply(float[] matrix) throws GLException { - // Check internal vars - if (mTarget == null || - mTarget.getPositionBuffer() == null || - mTarget.getTextureBuffer() == null) { - return; - } - if (mTransitionTarget == null || - mTransitionTarget.getPositionBuffer() == null || - mTransitionTarget.getTextureBuffer() == null) { - return; - } - - // Set the time the first time - if (mTime == -1) { - mTime = SystemClock.uptimeMillis(); - } - - // Calculate the delta time - final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; - - // Apply the transition - applyDstTransition(matrix); - if (delta < 1) { - applySrcTransition(delta, matrix); - } - - // Transition ending - if (delta == 1) { - mRunning = false; - } - } - - /** - * Apply the source transition - * - * @param delta The delta time - * @param matrix The model-view-projection matrix - * @param target The photo frame target - */ - private void applySrcTransition(float delta, float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Set the program - useProgram(0); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Set the input texture - int textureHandle = mTarget.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[0], 0); - GLESUtil.glesCheckError("glBindTexture"); - - // Texture - FloatBuffer textureBuffer = mTarget.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - float[] vertex = cloneVertex(); - float interpolation = mInterpolation.getInterpolation(delta); - switch (mMode) { - case LEFT_TO_RIGHT: - vertex[1] -= interpolation * mAmount; - vertex[5] += interpolation * mAmount; - break; - case RIGHT_TO_LEFT: - vertex[3] -= interpolation * mAmount; - vertex[7] += interpolation * mAmount; - break; - default: - break; - } - ByteBuffer bb = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) - bb.order(ByteOrder.nativeOrder()); - FloatBuffer positionBuffer = bb.asFloatBuffer(); - positionBuffer.put(vertex); - positionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Calculate the delta angle and the translation and rotate parameters - float angle = 0.0f; - float translateX = 0.0f; - float rotateY = 0.0f; - switch (mMode) { - case LEFT_TO_RIGHT: - angle = delta * 90; - rotateY = -1.0f; - translateX = mTarget.getFrameVertex()[2] * -1; - break; - case RIGHT_TO_LEFT: - angle = delta * -90; - rotateY = -1.0f; - translateX = mTarget.getFrameVertex()[0] * -1; - break; - - default: - break; - } - - // Apply the projection and view transformation - Matrix.setIdentityM(matrix, 0); - Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, 0.0f, 0.0f); - Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, 0.0f, rotateY, 0.0f); - Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, 0.0f, 0.0f); - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, mTranslationMatrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Apply the destination transition (just draw the image) - * - * @param matrix The model-view-projection matrix - */ - private void applyDstTransition(float[] matrix) { - // Bind default FBO - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLESUtil.glesCheckError("glBindFramebuffer"); - - // Use our shader program - useProgram(1); - - // Disable blending - GLES20.glDisable(GLES20.GL_BLEND); - GLESUtil.glesCheckError("glDisable"); - - // Apply the projection and view transformation - GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, matrix, 0); - GLESUtil.glesCheckError("glUniformMatrix4fv"); - - // Texture - FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); - textureBuffer.position(0); - GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Position - FloatBuffer positionBuffer = mTransitionTarget.getPositionBuffer(); - positionBuffer.position(0); - GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); - GLESUtil.glesCheckError("glVertexAttribPointer"); - GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glEnableVertexAttribArray"); - - // Set the input texture - int textureHandle = mTransitionTarget.getTextureHandle(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLESUtil.glesCheckError("glActiveTexture"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); - GLESUtil.glesCheckError("glBindTexture"); - GLES20.glUniform1i(mTextureHandlers[1], 0); - GLESUtil.glesCheckError("glUniform1i"); - - // Draw - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLESUtil.glesCheckError("glDrawElements"); - - // Disable attributes - GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); - GLESUtil.glesCheckError("glDisableVertexAttribArray"); - } - - /** - * Method that copy the vertex array - * - * @return The copy of the vertex - */ - private float[] cloneVertex() { - float[] originalVertex = mTarget.getFrameVertex(); - float[] vertex = new float[originalVertex.length]; - System.arraycopy(originalVertex, 0, vertex, 0, originalVertex.length); - return vertex; - } - - /** - * Return the scale amount to apply to the transition - * - * @return float The scale amount - */ - private float getAmount() { - return ((mTarget.getFrameWidth() * SCALE_AMOUNT) / 2); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java b/src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java deleted file mode 100644 index 4fb7257..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.utils; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapFactory.Options; -import android.graphics.Matrix; -import android.media.ExifInterface; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -/** - * A helper class for deal with Bitmaps - */ -public class BitmapUtils { - - /** - * Method that decodes a bitmap - * - * @param bitmap The bitmap buffer to decode - * @return Bitmap The decoded bitmap - */ - public static Bitmap decodeBitmap(InputStream bitmap) { - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferQualityOverSpeed = false; - options.inPreferredConfig = Bitmap.Config.RGB_565; - return BitmapFactory.decodeStream(bitmap, null, options); - } - - /** - * Method that decodes a bitmap - * - * @param file The bitmap file to decode - * @param reqWidth The request width - * @param reqHeight The request height - * @return Bitmap The decoded bitmap - */ - public static Bitmap decodeBitmap(File file, int reqWidth, int reqHeight) { - // First decode with inJustDecodeBounds=true to check dimensions - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(file.getAbsolutePath(), options); - - // Calculate inSampleSize (use 1024 as maximum size, the minimum supported - // by all the gles20 devices) - options.inSampleSize = calculateBitmapRatio( - options, - Math.min(reqWidth, 1024), - Math.min(reqHeight, 1024)); - - // Decode the bitmap with inSampleSize set - options.inJustDecodeBounds = false; - options.inPreferQualityOverSpeed = false; - options.inPurgeable = true; - options.inInputShareable = true; - options.inDither = true; - Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options); - if (bitmap == null) { - return null; - } - - // Test if the bitmap has exif format, and decode properly - Bitmap out = decodeExifBitmap(file, bitmap); - if (!out.equals(bitmap)) { - bitmap.recycle(); - bitmap = null; - } - return out; - } - - /** - * Method that decodes an Exif bitmap - * - * @param file The file to decode - * @param src The bitmap reference - * @return Bitmap The decoded bitmap - */ - private static Bitmap decodeExifBitmap(File file, Bitmap src) { - try { - // Try to load the bitmap as a bitmap file - ExifInterface exif = new ExifInterface(file.getAbsolutePath()); - int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); - if (orientation == 0) { - return src; - } - Matrix matrix = new Matrix(); - if (orientation == 6) { - matrix.postRotate(90); - } else if (orientation == 3) { - matrix.postRotate(180); - } else if (orientation == 8) { - matrix.postRotate(270); - } - // Rotate the bitmap - return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); - } catch (IOException e) { - // Ignore - } - return src; - } - - /** - * Method that calculate the bitmap size prior to decode - * - * @param options The bitmap factory options - * @param reqWidth The request width - * @param reqHeight The request height - * @return int The picture ratio - */ - private static int calculateBitmapRatio(Options options, int reqWidth, int reqHeight) { - // Raw height and width of image - final int height = options.outHeight; - final int width = options.outWidth; - int inSampleSize = 1; - - if (height > reqHeight || width > reqWidth) { - // Calculate ratios of height and width to requested height and width - final int heightRatio = Math.round((float) height / (float) reqHeight); - final int widthRatio = Math.round((float) width / (float) reqWidth); - - // Choose the smallest ratio as inSampleSize value, this will guarantee - // a final image with both dimensions larger than or equal to the - // requested height and width. - inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; - } - - return inSampleSize; - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java b/src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java deleted file mode 100644 index 9d6b383..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.utils; - -import android.graphics.Rect; - -import com.ruesga.android.wallpapers.photophase.model.Disposition; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A helper class with disposition utils - */ -public final class DispositionUtil { - - /** - * Method that converts a disposition string to a disposition reference - * - * @param value The value to convert - * @return List The dispositions reference - */ - public static List toDispositions(String value) { - String[] v = value.split("\\|"); - List dispositions = new ArrayList(v.length); - for (String s : v) { - String[] s1 = s.split(":"); - String[] s2 = s1[0].split("x"); - String[] s3 = s1[1].split("x"); - Disposition disposition = new Disposition(); - disposition.x = Integer.parseInt(s2[0]); - disposition.y = Integer.parseInt(s2[1]); - disposition.w = Integer.parseInt(s3[0]) - disposition.x + 1; - disposition.h = Integer.parseInt(s3[1]) - disposition.y + 1; - dispositions.add(disposition); - } - Collections.sort(dispositions); - return dispositions; - } - - /** - * Method that converts a disposition reference to a disposition string - * - * @param dispositions The value to convert - * @return String The dispositions string - */ - public static String fromDispositions(List dispositions) { - Collections.sort(dispositions); - StringBuilder sb = new StringBuilder(); - int count = dispositions.size(); - for (int i = 0; i < count; i++) { - Disposition disposition = dispositions.get(i); - sb.append(disposition.x) - .append("x") - .append(disposition.y) - .append(":") - .append(disposition.x + disposition.w - 1) - .append("x") - .append(disposition.y + disposition.h - 1); - if (i < (count - 1)) { - sb.append("|"); - } - } - return sb.toString(); - } - - /** - * Method that transform the disposition to a byte matrix - * - * @param dispositions The - * @return byte[][] The boolean matrix of the disposition - */ - public static byte[][] toMatrix(List dispositions, int cols, int rows) { - byte[][] matrix = new byte[rows][cols]; - for (Disposition disposition : dispositions) { - int count = disposition.y + disposition.h; - for (int row = disposition.y; row < count; row++) { - int count2 = disposition.x + disposition.w; - for (int col = disposition.x; col < count2; col++) { - matrix[row][col] = 1; - } - } - } - return matrix; - } - - /** - * Method that returns a disposition from a {@link Rect} reference - * - * @return Disposition The disposition - */ - public static Disposition fromRect(Rect r) { - Disposition disposition = new Disposition(); - disposition.x = r.left; - disposition.y = r.top; - disposition.w = r.width(); - disposition.h = r.height(); - return disposition; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java b/src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java deleted file mode 100644 index 51b7b74..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.utils; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Rect; -import android.media.effect.Effect; -import android.opengl.GLES20; -import android.opengl.GLUtils; -import android.util.Log; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; - - -/** - * A helper class with some useful methods for deal with GLES. - */ -public final class GLESUtil { - - private static final String TAG = "GLESUtil"; - - private static final boolean DEBUG = false; - - private static final Object sSync = new Object(); - - /** - * A helper class to deal with OpenGL float colors. - */ - public static class GLColor { - - private static final float MAX_COLOR = 255.0f; - - /** - * Red - */ - public float r; - /** - * Green - */ - public float g; - /** - * Blue - */ - public float b; - /** - * Alpha - */ - public float a; - - /** - * Constructor of GLColor from ARGB - * - * @param a Alpha - * @param r Red - * @param g Green - * @param b Alpha - */ - public GLColor(int a, int r, int g, int b) { - this.a = a / MAX_COLOR; - this.r = r / MAX_COLOR; - this.g = g / MAX_COLOR; - this.b = b / MAX_COLOR; - } - - /** - * Constructor of GLColor from ARGB. - * - * @param argb An #AARRGGBB string - */ - public GLColor(String argb) { - int color = Color.parseColor(argb); - this.a = Color.alpha(color) / MAX_COLOR; - this.r = Color.red(color) / MAX_COLOR; - this.g = Color.green(color) / MAX_COLOR; - this.b = Color.blue(color) / MAX_COLOR; - } - - /** - * Constructor of GLColor from ARGB. - * - * @param argb An #AARRGGBB number - */ - public GLColor(int argb) { - this.a = Color.alpha(argb) / MAX_COLOR; - this.r = Color.red(argb) / MAX_COLOR; - this.g = Color.green(argb) / MAX_COLOR; - this.b = Color.blue(argb) / MAX_COLOR; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Float.floatToIntBits(a); - result = prime * result + Float.floatToIntBits(b); - result = prime * result + Float.floatToIntBits(g); - result = prime * result + Float.floatToIntBits(r); - return result; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - GLColor other = (GLColor) obj; - if (Float.floatToIntBits(a) != Float.floatToIntBits(other.a)) - return false; - if (Float.floatToIntBits(b) != Float.floatToIntBits(other.b)) - return false; - if (Float.floatToIntBits(g) != Float.floatToIntBits(other.g)) - return false; - if (Float.floatToIntBits(r) != Float.floatToIntBits(other.r)) - return false; - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "#"+Integer.toHexString(Color.argb((int)a, (int)r, (int)g, (int)b)); - } - } - - /** - * Class that holds some information about a GLES texture - */ - public static class GLESTextureInfo { - /** - * Handle of the texture - */ - public int handle = 0; - /** - * The bitmap reference - */ - public Bitmap bitmap; - /** - * The path to the texture - */ - public File path; - /** - * The effect to apply - */ - public Effect effect; - } - - /** - * Method that load a vertex shader and returns its handler identifier. - * - * @param src The source shader - * @return int The handler identifier of the shader - */ - public static int loadVertexShader(String src) { - return loadShader(src, GLES20.GL_VERTEX_SHADER); - } - - /** - * Method that load a fragment shader and returns its handler identifier. - * - * @param src The source shader - * @return int The handler identifier of the shader - */ - public static int loadFragmentShader(String src) { - return loadShader(src, GLES20.GL_FRAGMENT_SHADER); - } - - /** - * Method that load a shader and returns its handler identifier. - * - * @param src The source shader - * @param type The type of shader - * @return int The handler identifier of the shader - */ - public static int loadShader(String src, int type) { - int[] compiled = new int[1]; - // Create, load and compile the shader - int shader = GLES20.glCreateShader(type); - GLESUtil.glesCheckError("glCreateShader"); - if (shader <= 0) { - Log.e(TAG, "Cannot create a shader"); - return 0; - } - GLES20.glShaderSource(shader, src); - GLESUtil.glesCheckError("glShaderSource"); - GLES20.glCompileShader(shader); - GLESUtil.glesCheckError("glesCheckError"); - GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); - GLESUtil.glesCheckError("glesCheckError"); - if (compiled[0] <= 0) { - String msg = "Shader compilation error trace:\n" + GLES20.glGetShaderInfoLog(shader); - Log.e(TAG, msg); - return 0; - } - return shader; - } - - /** - * Method that create a new program from its shaders (vertex and fragment) - * - * @param res A resources reference - * @param vertexShaderId The vertex shader glsl resource - * @param fragmentShaderId The fragment shader glsl resource - * @return int The handler identifier of the program - */ - public static int createProgram(Resources res, int vertexShaderId, int fragmentShaderId) { - return createProgram( - readResource(res, vertexShaderId), - readResource(res, fragmentShaderId)); - } - - /** - * Method that create a new program from its shaders (vertex and fragment) - * - * @param vertexShaderSrc The vertex shader - * @param fragmentShaderSrc The fragment shader - * @return int The handler identifier of the program. - */ - public static int createProgram(String vertexShaderSrc, String fragmentShaderSrc) { - int vshader = 0; - int fshader = 0; - int progid = 0; - int[] link = new int[1]; - - try { - // Check that we have valid shaders - if (vertexShaderSrc == null || fragmentShaderSrc == null) { - return 0; - } - - // Load the vertex and fragment shaders - vshader = loadVertexShader(vertexShaderSrc); - fshader = loadFragmentShader(fragmentShaderSrc); - - // Create the programa ref - progid = GLES20.glCreateProgram(); - GLESUtil.glesCheckError("glCreateProgram"); - if (progid <= 0) { - String msg = "Cannot create a program"; - Log.e(TAG, msg); - return 0; - } - - // Attach the shaders - GLES20.glAttachShader(progid, vshader); - GLESUtil.glesCheckError("glAttachShader"); - GLES20.glAttachShader(progid, fshader); - GLESUtil.glesCheckError("glAttachShader"); - - // Link the program - GLES20.glLinkProgram(progid); - GLESUtil.glesCheckError("glLinkProgram"); - - GLES20.glGetProgramiv(progid, GLES20.GL_LINK_STATUS, link, 0); - GLESUtil.glesCheckError("glGetProgramiv"); - if (link[0] <= 0) { - String msg = "Program compilation error trace:\n" + GLES20.glGetProgramInfoLog(progid); - Log.e(TAG, msg); - return 0; - } - - // Return the program - return progid; - - } finally { - // Delete the shaders - if (vshader != 0) { - GLES20.glDeleteShader(vshader); - GLESUtil.glesCheckError("glDeleteShader"); - } - if (fshader != 0) { - GLES20.glDeleteShader(fshader); - GLESUtil.glesCheckError("glDeleteShader"); - } - } - } - - /** - * Method that loads a texture from a file. - * - * @param file The image file - * @param dimensions The desired dimensions - * @param effect The effect to apply to the image or null if no effect is needed - * @param dimen The new dimensions - * @param recycle If the bitmap should be recycled - * @return GLESTextureInfo The texture info - */ - public static GLESTextureInfo loadTexture( - File file, Rect dimensions, Effect effect, Rect dimen, boolean recycle) { - Bitmap bitmap = null; - try { - // Decode and associate the bitmap (invert the desired dimensions) - bitmap = BitmapUtils.decodeBitmap(file, dimensions.height(), dimensions.width()); - if (bitmap == null) { - Log.e(TAG, "Failed to decode the file bitmap"); - return new GLESTextureInfo(); - } - - if (DEBUG) Log.d(TAG, "image: " + file.getAbsolutePath()); - GLESTextureInfo ti = loadTexture(bitmap, effect, dimen); - ti.path = file; - return ti; - - } catch (Exception e) { - String msg = "Failed to generate a valid texture from file: " + file.getAbsolutePath(); - Log.e(TAG, msg, e); - return new GLESTextureInfo(); - - } finally { - // Recycle the bitmap - if (bitmap != null && recycle) { - bitmap.recycle(); - bitmap = null; - } - } - } - - /** - * Method that loads a texture from a resource context. - * - * @param ctx The current context - * @param resourceId The resource identifier - * @param effect The effect to apply to the image or null if no effect is needed - * @param dimen The new dimensions - * @param recycle If the bitmap should be recycled - * @return GLESTextureInfo The texture info - */ - public static GLESTextureInfo loadTexture( - Context ctx, int resourceId, Effect effect, Rect dimen, boolean recycle) { - Bitmap bitmap = null; - InputStream raw = null; - try { - // Decode and associate the bitmap - raw = ctx.getResources().openRawResource(resourceId); - bitmap = BitmapUtils.decodeBitmap(raw); - if (bitmap == null) { - String msg = "Failed to decode the resource bitmap"; - Log.e(TAG, msg); - return new GLESTextureInfo(); - } - - if (DEBUG) Log.d(TAG, "resourceId: " + resourceId); - GLESTextureInfo ti = loadTexture(bitmap, effect, dimen); - return ti; - - } catch (Exception e) { - String msg = "Failed to generate a valid texture from resource: " + resourceId; - Log.e(TAG, msg, e); - return new GLESTextureInfo(); - - } finally { - // Close the buffer - try { - if (raw != null) { - raw.close(); - } - } catch (IOException e) { - // Ignore. - } - // Recycle the bitmap - if (bitmap != null && recycle) { - bitmap.recycle(); - bitmap = null; - } - } - } - - /** - * Method that loads texture from a bitmap reference. - * - * @param bitmap The bitmap reference - * @param effect The effect to apply to the image or null if no effect is needed - * @param dimen The new dimensions - * @return GLESTextureInfo The texture info - */ - public static GLESTextureInfo loadTexture(Bitmap bitmap, Effect effect, Rect dimen) { - // Check that we have a valid image name reference - if (bitmap == null) { - return new GLESTextureInfo(); - } - - int num = effect == null ? 1 : 2; - - int[] textureHandles = new int[num]; - GLES20.glGenTextures(num, textureHandles, 0); - GLESUtil.glesCheckError("glGenTextures"); - if (textureHandles[0] <= 0 || (effect != null && textureHandles[1] <= 0)) { - Log.e(TAG, "Failed to generate a valid texture"); - return new GLESTextureInfo(); - } - - // Bind the texture to the name - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[0]); - GLESUtil.glesCheckError("glBindTexture"); - - // Set the texture properties - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); - GLESUtil.glesCheckError("glTexParameteri"); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); - GLESUtil.glesCheckError("glTexParameteri"); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLESUtil.glesCheckError("glTexParameteri"); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - GLESUtil.glesCheckError("glTexParameteri"); - - // Load the texture - GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); - if (!GLES20.glIsTexture(textureHandles[0])) { - Log.e(TAG, "Failed to load a valid texture"); - return new GLESTextureInfo(); - } - - // Has a effect? - int handle = textureHandles[0]; - if (effect != null) { - // Apply the effect (we need a thread-safe call here) - synchronized (sSync) { - // No more than 1024 (the minimum supported by all the gles20 devices) - int w = Math.min(dimen.width(), 1024); - int h = Math.min(dimen.width(), 1024); - effect.apply(textureHandles[0], w, h, textureHandles[1]); - } - handle = textureHandles[1]; - - // Delete the unused texture - int[] textures = {textureHandles[0]}; - GLES20.glDeleteTextures(1, textures, 0); - GLESUtil.glesCheckError("glDeleteTextures"); - } - - // Return the texture handle identifier and the associated info - GLESTextureInfo ti = new GLESTextureInfo(); - ti.handle = handle; - ti.bitmap = bitmap; - ti.path = null; - return ti; - } - - /** - * Method that checks if an GLES error is present - * - * @param func The GLES function to check - * @return boolean If there was an error - */ - public static boolean glesCheckError(String func) { - int error = GLES20.glGetError(); - if (error != 0) { - Log.e(TAG, "GLES20 Error (" + glesGetErrorModule() + ") (" + func + "): " + - GLUtils.getEGLErrorString(error)); - return true; - } - return false; - } - - /** - * Method that returns the line and module that generates the current error - * - * @return String The line and module - */ - private static String glesGetErrorModule() { - try { - return String.valueOf(Thread.currentThread().getStackTrace()[4]); - } catch (IndexOutOfBoundsException ioobEx) { - // Ignore - } - return ""; - } - - /** - * Method that read a resource. - * - * @param res The resources reference - * @param resId The resource identifier - * @return String The shader source - * @throws IOException If an error occurs while loading the resource - */ - private static String readResource(Resources res, int resId) { - Reader reader = new InputStreamReader(res.openRawResource(resId)); - try { - final int BUFFER = 1024; - char[] data = new char[BUFFER]; - int read = 0; - StringBuilder sb = new StringBuilder(); - while ((read = reader.read(data, 0, BUFFER)) != -1) { - sb.append(data, 0, read); - } - return sb.toString(); - } catch (Exception e) { - Log.e(TAG, "Failed to read the resource " + resId); - return null; - } finally { - try { - reader.close(); - } catch (Exception ex) { - // Ignore - } - } - } - -} diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java b/src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java deleted file mode 100644 index 43701e1..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.utils; - -import android.graphics.Rect; -import java.util.Stack; - -/** - * The maximal empty rectangle algorithm that allows to find the rectangle with the maximal - * area that could be create in empty areas (in this case 0 in a byte matrix) - */ -// -// Based on the source discussed at http://discuss.leetcode.com/questions/260/maximal-rectangle -// -public final class MERAlgorithm { - - /** - * Method that returns the maximal empty rectangle (MER) for a matrix of bytes (1/0) - * - * @param matrix The matrix - * @return Rect The maximal empty rectangle - */ - public static Rect getMaximalEmptyRectangle(byte[][] matrix) { - // Check matrix - int rows = matrix.length; - if (rows == 0) return null; - int cols = matrix[0].length; - - // Convert to histogram - int[][] histogram = toHistogram(matrix); - - // Find the maximal area of every histogram - Rect maxRect = new Rect(); - for (int i = 0; i < rows; ++i) { - Rect rect = maximalRectangle(histogram[i], i); - if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { - maxRect = rect; - } - } - return ensureBounds(maxRect, cols, rows); - } - - /** - * Method that ensure the bounds of the max rectangle - * - * @param rect The rectangle to check - * @param cols The number of cols - * @param rows The number of rows - * @return Rect The rectangle checked - */ - private static Rect ensureBounds(Rect rect, int cols, int rows) { - if (rect.right - rect.left >= cols) rect.right = cols; - if (rect.bottom - rect.top >= rows) rect.bottom = rows; - return rect; - } - - /** - * Method that returns the maximal rectangle for an histogram of areas - * - * @return Rect The maximal rectangle histogram/area - */ - @SuppressWarnings("boxing") - private static Rect maximalRectangle(int[] histogram, int row) { - Stack stack = new Stack(); - int length = histogram.length; - Rect maxRect = new Rect(); - int i = 0; - while (i < length) { - if (stack.isEmpty() || histogram[i] >= histogram[stack.peek()]) { - stack.push(i++); - } else { - Rect rect = new Rect(); - rect.left = stack.pop(); - rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1)); - rect.top = row - histogram[rect.left] + 1; - rect.bottom = rect.top + histogram[rect.left]; - if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { - maxRect = rect; - } - } - } - while (!stack.isEmpty()) { - Rect rect = new Rect(); - rect.left = stack.pop(); - rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1)); - rect.top = row - histogram[rect.left] + 1; - rect.bottom = rect.top + histogram[rect.left]; - if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { - maxRect = rect; - } - } - return maxRect; - } - - /** - * Method that converts the empty areas to a histogram - * - * @param matrix The matrix where to find the MER - * return int[][] The histogram of empty areas - */ - private static int[][] toHistogram(byte[][] matrix) { - int rows = matrix.length; - int cols = matrix[0].length; - int[][] histogram = new int[rows][cols]; - for (int h=0; h < cols; h++) { - if (matrix[0][h] == 0) { - histogram[0][h] = 1; - } - } - for (int w=1; w < rows; w++) { - for (int h=0; h < cols; h++) { - if (matrix[w][h] == 1) { - continue; - } - histogram[w][h] = histogram[w-1][h] + 1; - } - } - return histogram; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/Utils.java b/src/com/ruesga/android/wallpapers/photophase/utils/Utils.java deleted file mode 100644 index 296b96a..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/utils/Utils.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.utils; - -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.RectF; -import android.util.DisplayMetrics; - -import java.util.Random; - -/** - * A helper class with utilities - */ -public class Utils { - - private static Random sRandom = new Random(); - - /** - * This method converts dp unit to equivalent device specific value in pixels. - * - * @param ctx The current context - * @param dp A value in dp (Device independent pixels) unit - * @return float A float value to represent Pixels equivalent to dp according to device - */ - public static float convertDpToPixel(Context ctx, float dp) { - Resources resources = ctx.getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - return dp * (metrics.densityDpi / 160f); - } - - /** - * Used to determine if the device is a tablet or not - * - * @param context The {@link Context} to use. - * @return True if the device is a tablet, false otherwise. - */ - public static final boolean isTablet(final Context context) { - final int layout = context.getResources().getConfiguration().screenLayout; - return (layout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; - } - - /** - * Method that converts a rect from a vertex data - * - * @param vertex The vertex array - * @return RectF The rect data - */ - public static RectF rectFromVertex(float[] vertex) { - RectF rect = new RectF(); - rect.left = vertex[0]; - rect.top = vertex[7]; - rect.right = vertex[6]; - rect.bottom = vertex[1]; - return rect; - } - - /** - * Method that returns a random number between two numbers. - * - * @param low The low number - * @param high The high number - * @return int The random number - */ - public static int getNextRandom(int low, int high) { - return low + (int)(sRandom.nextDouble() * ((high - low) + 1)); - } -} \ No newline at end of file diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/AlbumInfo.java b/src/com/ruesga/android/wallpapers/photophase/widgets/AlbumInfo.java deleted file mode 100644 index e930521..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/AlbumInfo.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import android.content.Context; -import android.content.res.Resources; -import android.os.AsyncTask.Status; -import android.util.AttributeSet; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.model.Album; -import com.ruesga.android.wallpapers.photophase.tasks.AsyncPictureLoaderTask; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * A view that contains the info about an album - */ -public class AlbumInfo extends RelativeLayout - implements OnClickListener, OnMenuItemClickListener { - - /** - * A convenient listener for receive events of the AlbumPictures class - * - */ - public interface CallbacksListener { - /** - * Invoked when an album was selected - * - * @param album The album - */ - void onAlbumSelected(Album album); - - /** - * Invoked when an album was deselected - * - * @param album The album - */ - void onAlbumDeselected(Album album); - } - - private List mCallbacks; - - /*package*/ Album mAlbum; - - /*package*/ AsyncPictureLoaderTask mTask; - - /*package*/ ImageView mIcon; - private TextView mSelectedItems; - private TextView mName; - private TextView mItems; - private View mOverflowButton; - - /** - * Constructor of AlbumInfo. - * - * @param context The current context - */ - public AlbumInfo(Context context) { - super(context); - init(); - } - - /** - * Constructor of AlbumInfo. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public AlbumInfo(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of AlbumInfo. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - * @param defStyle The default style to apply to this view. If 0, no style - * will be applied (beyond what is included in the theme). This may - * either be an attribute resource, whose value will be retrieved - * from the current theme, or an explicit style resource. - */ - public AlbumInfo(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Method that initializes the internal references - */ - private void init() { - mCallbacks = new ArrayList(); - } - - /** - * Method that adds the class that will be listen for events of this class - * - * @param callback The callback class - */ - public void addCallBackListener(CallbacksListener callback) { - this.mCallbacks.add(callback); - } - - /** - * Method that removes the class from the current callbacks - * - * @param callback The callback class - */ - public void removeCallBackListener(CallbacksListener callback) { - this.mCallbacks.remove(callback); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - mIcon = (ImageView)findViewById(R.id.album_thumbnail); - mSelectedItems = (TextView)findViewById(R.id.album_selected_items); - mName = (TextView)findViewById(R.id.album_name); - mItems = (TextView)findViewById(R.id.album_items); - mOverflowButton = findViewById(R.id.overflow); - mOverflowButton.setOnClickListener(this); - - updateView(mAlbum); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - // Cancel pending tasks - if (mTask != null && mTask.getStatus().compareTo(Status.PENDING) == 0) { - mTask.cancel(true); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onClick(View v) { - if (v.equals(mOverflowButton)) { - PopupMenu popup = new PopupMenu(getContext(), v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.album_actions, popup.getMenu()); - onPreparePopupMenu(popup.getMenu()); - popup.setOnMenuItemClickListener(this); - popup.show(); - return; - } - } - - /** - * Method called prior to show the popup menu - * - * @param popup The popup menu - */ - public void onPreparePopupMenu(Menu popup) { - if (isSelected()) { - popup.findItem(R.id.mnu_select_album).setVisible(false); - } else { - popup.findItem(R.id.mnu_deselect_album).setVisible(false); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.mnu_select_album: - doSelection(true); - break; - - case R.id.mnu_deselect_album: - doSelection(false); - break; - - default: - return false; - } - return true; - } - - /** - * Method that select/deselect the album - * - * @param selected whether the album is selected - */ - public void doSelection(boolean selected) { - setSelected(selected); - mAlbum.setSelected(selected); - mAlbum.setSelectedItems(new ArrayList()); - updateView(mAlbum); - notifySelectionChanged(); - } - - /** - * Method that notifies to all the registered callbacks that the selection - * was changed - */ - private void notifySelectionChanged() { - for (CallbacksListener callback : mCallbacks) { - if (mAlbum.isSelected()) { - callback.onAlbumSelected(mAlbum); - } else { - callback.onAlbumDeselected(mAlbum); - } - } - } - - /** - * Method that updates the view - * - * @param album The album data - */ - @SuppressWarnings("boxing") - public void updateView(Album album) { - mAlbum = album; - - if (mAlbum != null && mIcon != null) { - Resources res = getContext().getResources(); - - int selectedItems = mAlbum.getSelectedItems().size(); - String count = String.valueOf(selectedItems); - if (selectedItems > 99) { - count += "+"; - } - mSelectedItems.setText(count); - mSelectedItems.setVisibility(mAlbum.isSelected() ? View.INVISIBLE : View.VISIBLE); - mName.setText(mAlbum.getName()); - int items = mAlbum.getItems().size(); - mItems.setText(String.format(res.getQuantityText( - R.plurals.album_number_of_pictures, items).toString(), items)); - setSelected(album.isSelected()); - - if (mTask == null) { - post(new Runnable() { - @Override - public void run() { - // Show as icon, the first picture - mTask = new AsyncPictureLoaderTask(getContext(), mIcon); - mTask.execute(new File(mAlbum.getItems().get(0))); - } - }); - } - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/AlbumPictures.java b/src/com/ruesga/android/wallpapers/photophase/widgets/AlbumPictures.java deleted file mode 100644 index 3a9866c..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/AlbumPictures.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import android.content.Context; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.LinearLayout; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.model.Album; - -import java.util.ArrayList; -import java.util.List; - -/** - * A view that contains the pictures of an album - */ -public class AlbumPictures extends RelativeLayout - implements OnClickListener, OnMenuItemClickListener { - - private static final int SELECTION_SELECT_ALL = 1; - private static final int SELECTION_DESELECT_ALL = 2; - private static final int SELECTION_INVERT = 3; - - /** - * A convenient listener for receive events of the AlbumPictures class - * - */ - public interface CallbacksListener { - /** - * Invoked when the user pressed the back button - */ - void onBackButtonClick(View v); - - /** - * Invoked when the selection was changed - * - * @param album The album - */ - void onSelectionChanged(Album album); - } - - private List mCallbacks; - - private Handler mHandler; - - /*package*/ PicturesView mScroller; - /*package*/ LinearLayout mHolder; - private View mBackButton; - private View mOverflowButton; - - private boolean mInitialized; - - /*package*/ Album mAlbum; - - /** - * Constructor of AlbumPictures. - * - * @param context The current context - */ - public AlbumPictures(Context context) { - super(context); - init(); - } - - /** - * Constructor of AlbumPictures. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public AlbumPictures(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of AlbumPictures. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - * @param defStyle The default style to apply to this view. If 0, no style - * will be applied (beyond what is included in the theme). This may - * either be an attribute resource, whose value will be retrieved - * from the current theme, or an explicit style resource. - */ - public AlbumPictures(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Method that initializes the internal references - */ - private void init() { - mCallbacks = new ArrayList(); - mHandler = new Handler(); - mInitialized = false; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mScroller = (PicturesView)findViewById(R.id.album_pictures_scroller); - mHolder = (LinearLayout)findViewById(R.id.album_pictures_holder); - mBackButton = findViewById(R.id.back); - mBackButton.setOnClickListener(this); - mOverflowButton = findViewById(R.id.overflow); - mOverflowButton.setOnClickListener(this); - TextView title = (TextView)findViewById(R.id.album_pictures_title); - title.setText(mAlbum.getName()); - - updateView(mAlbum, false); - } - - /** - * Method that adds the class that will be listen for events of this class - * - * @param callback The callback class - */ - public void addCallBackListener(CallbacksListener callback) { - this.mCallbacks.add(callback); - } - - /** - * Method that removes the class from the current callbacks - * - * @param callback The callback class - */ - public void removeCallBackListener(CallbacksListener callback) { - this.mCallbacks.remove(callback); - } - - /** - * Method that set the data of the view - * - * @param album The album data - * @param recreate If the view should be recreated - */ - public void updateView(Album album, boolean recreate) { - mAlbum = album; - recreateView(false); - } - - /** - * Method that recreates the the view - * - * @param propagateShow If should propagate the show event - */ - private void recreateView(final boolean propagateShow) { - if (mHolder != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - int pictures = mHolder.getChildCount(); - if (pictures != mAlbum.getItems().size()) { - // Recreate the pictures - final LayoutInflater inflater = (LayoutInflater) getContext(). - getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mScroller.cancelTasks(); - mHolder.removeAllViews(); - for (final String picture : mAlbum.getItems()) { - View v = createPicture(inflater, picture, isPictureSelected(picture)); - mHolder.addView(v); - } - } else { - int i = 0; - for (final String picture : mAlbum.getItems()) { - View v = mHolder.getChildAt(i); - v.setSelected(isPictureSelected(picture)); - i++; - } - } - if (propagateShow) { - mScroller.onShow(); - } - } - }); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onClick(View v) { - // Check which is the view pressed - if (v.equals(mBackButton)) { - for (CallbacksListener callback : mCallbacks) { - callback.onBackButtonClick(v); - } - return; - } - if (v.equals(mOverflowButton)) { - PopupMenu popup = new PopupMenu(getContext(), v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.pictures_actions, popup.getMenu()); - popup.setOnMenuItemClickListener(this); - popup.show(); - return; - } - - // A picture view - v.setSelected(!v.isSelected()); - notifySelectionChanged(); - } - - /** - * Method that notifies to all the registered callbacks that the selection - * was changed - */ - private void notifySelectionChanged() { - List selection = new ArrayList(); - int count = mHolder.getChildCount(); - for (int i = 0; i < count; i++) { - View v = mHolder.getChildAt(i); - if (v.isSelected()) { - selection.add((String)v.getTag()); - } - } - mAlbum.setSelectedItems(selection); - mAlbum.setSelected(false); - - for (CallbacksListener callback : mCallbacks) { - callback.onSelectionChanged(mAlbum); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.mnu_select_all: - doSelection(SELECTION_SELECT_ALL); - break; - - case R.id.mnu_deselect_all: - doSelection(SELECTION_DESELECT_ALL); - break; - - case R.id.mnu_invert_selection: - doSelection(SELECTION_INVERT); - break; - - default: - return false; - } - return true; - } - - /** - * Operate over the selection of the pictures of this album. - * - * @param action Takes the next values: - *

    - *
  • SELECTION_SELECT_ALL: select all
  • - *
  • SELECTION_DESELECT_ALL: deselect all
  • - *
  • SELECTION_INVERT: invert selection
  • - *
- */ - private void doSelection(int action) { - int count = mHolder.getChildCount(); - for (int i = 0; i < count; i++) { - View v = mHolder.getChildAt(i); - - boolean selected = true; - if (action == SELECTION_DESELECT_ALL) { - selected = false; - } else if (action == SELECTION_INVERT) { - selected = !v.isSelected(); - } - v.setSelected(selected); - } - notifySelectionChanged(); - } - - /** - * Method invoked when the view is displayed - */ - public void onShow() { - if (!mInitialized) { - mInitialized = true; - recreateView(true); - } - } - - /** - * Method that creates a new picture view - * - * @param inflater The inflater of the parent view - * @param picture The path of the picture - * @param selected If the picture is selected - */ - /*package*/ View createPicture(LayoutInflater inflater, String picture, boolean selected) { - final View v = inflater.inflate(R.layout.picture_item, mHolder, false); - v.setTag(picture); - v.setSelected(selected); - v.setOnClickListener(this); - return v; - } - - /** - * Method that check if a picture is selected - * - * @param picture The picture to check - * @return boolean whether the picture is selected - */ - /*package*/ boolean isPictureSelected(String picture) { - for (String item : mAlbum.getSelectedItems()) { - if (item.compareTo(picture) == 0) { - return true; - } - } - return false; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/CardLayout.java b/src/com/ruesga/android/wallpapers/photophase/widgets/CardLayout.java deleted file mode 100644 index 9d7c524..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/CardLayout.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.animation.AnimationUtils; -import android.widget.LinearLayout; - -import com.ruesga.android.wallpapers.photophase.R; - -/** - * A "Google Now Card Layout" like layout - */ -public class CardLayout extends LinearLayout { - - boolean inverted = false; - - /** - * Constructor of CardLayout. - * - * @param context The current context - */ - public CardLayout(Context context) { - super(context); - } - - /** - * Constructor of CardLayout. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public CardLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Constructor of CardLayout. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - * @param defStyle The default style to apply to this view. If 0, no style - * will be applied (beyond what is included in the theme). This may - * either be an attribute resource, whose value will be retrieved - * from the current theme, or an explicit style resource. - */ - public CardLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * Add a new card to the layout - * - * @param card The card view to add - * @param animate If the add should be animated - */ - public void addCard(final View card, final boolean animate) { - post(new Runnable() { - @Override - public void run() { - addView(card); - if (animate) { - if (inverted) { - card.startAnimation(AnimationUtils.loadAnimation( - getContext(), R.anim.cards_animation_up_right)); - } else { - card.startAnimation(AnimationUtils.loadAnimation( - getContext(), R.anim.cards_animation_up_left)); - } - inverted = !inverted; - } - } - }); - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/ColorPickerPreference.java b/src/com/ruesga/android/wallpapers/photophase/widgets/ColorPickerPreference.java deleted file mode 100644 index 6db4b44..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/ColorPickerPreference.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import afzkl.development.mColorPicker.views.ColorDialogView; -import afzkl.development.mColorPicker.views.ColorPanelView; - -import android.app.AlertDialog.Builder; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.SharedPreferences; -import android.content.res.TypedArray; -import android.os.Parcel; -import android.os.Parcelable; -import android.preference.DialogPreference; -import android.preference.Preference; -import android.util.AttributeSet; -import android.view.View; - -import com.ruesga.android.wallpapers.photophase.R; - -/** - * A {@link Preference} that allow to select/pick a color in a new window dialog. - */ -public class ColorPickerPreference extends DialogPreference { - - private ColorPanelView mColorPicker; - private int mColor; - - private ColorDialogView mColorDlg; - - /** - * Constructor of ColorPickerPreference - * - * @param context The current context - */ - public ColorPickerPreference(Context context) { - this(context, null); - } - - /** - * Constructor of ColorPickerPreference - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the preference. - */ - public ColorPickerPreference(Context context, AttributeSet attrs) { - super(context, attrs); - setWidgetLayoutResource(R.layout.color_picker_pref_item); - } - - /** - * Returns the color of the picker. - * - * @return The color of the picker. - */ - public int getColor() { - return this.mColor; - } - - /** - * Sets the color of the picker and saves it to the {@link SharedPreferences}. - * - * @param color The new color. - */ - public void setColor(int color) { - // Always persist/notify the first time; don't assume the field's default of false. - final boolean changed = this.mColor != color; - if (changed) { - this.mColor = color; - // when called from onSetInitialValue the view is still not set - if (this.mColorPicker != null) { - this.mColorPicker.setColor(color); - } - persistInt(color); - if (changed) { - notifyDependencyChange(shouldDisableDependents()); - notifyChanged(); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - protected Object onGetDefaultValue(TypedArray a, int index) { - return Integer.valueOf(a.getColor(index, 0)); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { - setColor(restoreValue ? getPersistedInt(0) : ((Integer)defaultValue).intValue()); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPrepareDialogBuilder(Builder builder) { - super.onPrepareDialogBuilder(builder); - - // Configure the dialog - this.mColorDlg = new ColorDialogView(getContext()); - this.mColorDlg.setColor(this.mColor); - this.mColorDlg.showAlphaSlider(true); - this.mColorDlg.setAlphaSliderText( - getContext().getString(R.string.color_picker_alpha_slider_text)); - this.mColorDlg.setCurrentColorText( - getContext().getString(R.string.color_picker_current_text)); - this.mColorDlg.setNewColorText( - getContext().getString(R.string.color_picker_new_text)); - this.mColorDlg.setColorLabelText( - getContext().getString(R.string.color_picker_color)); - builder.setView(this.mColorDlg); - - // The color is selected by the user and confirmed by clicking ok - builder.setPositiveButton(android.R.string.ok, new OnClickListener() { - @Override - @SuppressWarnings("synthetic-access") - public void onClick(DialogInterface dialog, int which) { - int color = ColorPickerPreference.this.mColorDlg.getColor(); - if (callChangeListener(Integer.valueOf(color))) { - setColor(color); - } - dialog.dismiss(); - } - }); - } - - - /** - * {@inheritDoc} - */ - @Override - protected void onBindView(View view) { - super.onBindView(view); - View v = view.findViewById(R.id.color_picker); - if (v != null && v instanceof ColorPanelView) { - this.mColorPicker = (ColorPanelView)v; - this.mColorPicker.setColor(this.mColor); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected Parcelable onSaveInstanceState() { - final Parcelable superState = super.onSaveInstanceState(); - if (isPersistent()) { - // No need to save instance state since it's persistent - return superState; - } - - final SavedState myState = new SavedState(superState); - myState.color = getColor(); - return myState; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onRestoreInstanceState(Parcelable state) { - if (state == null || !state.getClass().equals(SavedState.class)) { - // Didn't save state for us in onSaveInstanceState - super.onRestoreInstanceState(state); - return; - } - - SavedState myState = (SavedState) state; - super.onRestoreInstanceState(myState.getSuperState()); - setColor(myState.color); - } - - /** - * A class for managing the instance state of a {@link ColorPickerPreference}. - */ - static class SavedState extends BaseSavedState { - int color; - - /** - * Constructor of SavedState - * - * @param source The source - */ - public SavedState(Parcel source) { - super(source); - this.color = source.readInt(); - } - - /** - * Constructor of SavedState - * - * @param superState The parcelable state - */ - public SavedState(Parcelable superState) { - super(superState); - } - - /** - * {@inheritDoc} - */ - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(this.color); - } - - /** - * A class that generates instances of the SavedState class from a Parcel. - */ - @SuppressWarnings("hiding") - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - - /** - * {@inheritDoc} - */ - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - /** - * {@inheritDoc} - */ - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/DispositionView.java b/src/com/ruesga/android/wallpapers/photophase/widgets/DispositionView.java deleted file mode 100644 index 674482d..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/DispositionView.java +++ /dev/null @@ -1,768 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Rect; -import android.os.Vibrator; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.view.View.OnLongClickListener; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.Toast; -import android.widget.ImageView.ScaleType; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.animations.Evaluators; -import com.ruesga.android.wallpapers.photophase.model.Disposition; -import com.ruesga.android.wallpapers.photophase.utils.DispositionUtil; -import com.ruesga.android.wallpapers.photophase.utils.MERAlgorithm; -import com.ruesga.android.wallpapers.photophase.widgets.ResizeFrame.OnResizeListener; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A class that allow to select the frames disposition visually - */ -public class DispositionView extends RelativeLayout implements OnLongClickListener, OnResizeListener { - - /** - * An interface to communicate the selection/unselection of a frame - */ - public interface OnFrameSelectedListener { - /** - * Invoked when a frame is selected - * - * @param v The frame view selected - */ - void onFrameSelectedListener(View v); - /** - * Invoked when a frame is unselected - */ - void onFrameUnselectedListener(); - } - - private boolean mChanged; - private List mDispositions; - private int mCols; - private int mRows; - - /*package*/ View mTarget; - private ResizeFrame mResizeFrame; - private int mInternalPadding; - private Rect mOldResizeFrameLocation; - - private OnFrameSelectedListener mOnFrameSelectedListener; - - private Vibrator mVibrator; - - /** - * Constructor of DispositionView. - * - * @param context The current context - */ - public DispositionView(Context context) { - super(context); - init(); - } - - /** - * Constructor of DispositionView. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public DispositionView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of DispositionView. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - * @param defStyle The default style to apply to this view. If 0, no style - * will be applied (beyond what is included in the theme). This may - * either be an attribute resource, whose value will be retrieved - * from the current theme, or an explicit style resource. - */ - public DispositionView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Initialize the view - */ - private void init() { - mVibrator = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE); - mInternalPadding = (int)getResources().getDimension(R.dimen.disposition_frame_padding); - } - - /** - * Method that returns the dispositions drawn on this view - * - * @return List The dispositions drawn - */ - public List getDispositions() { - return mDispositions; - } - - /** - * Method that sets the disposition to draw on this view - * - * @param dispositions The dispositions to draw - * @param cols The number of cols - * @param rows The number of rows - */ - public void setDispositions( - List dispositions, int cols, int rows) { - mDispositions = dispositions; - mCols = cols; - mRows = rows; - - // Remove all the current views and add the new ones - recreateDispositions(true); - mResizeFrame.setVisibility(View.GONE); - mChanged = false; - } - - /** - * Method that sets the resize frame view - * - * @param resizeFrame The resize frame view - */ - public void setResizeFrame(ResizeFrame resizeFrame) { - mResizeFrame = resizeFrame; - mResizeFrame.setOnResizeListener(this); - } - - /** - * Method that set the listener for listen frame selection/unselection events - * - * @param onFrameSelectedListener The callback - */ - public void setOnFrameSelectedListener(OnFrameSelectedListener onFrameSelectedListener) { - this.mOnFrameSelectedListener = onFrameSelectedListener; - } - - /** - * Method that returns if the view was changed - * - * @return boolean true if the view was changed - */ - public boolean isChanged() { - return mChanged; - } - - /** - * Method that recreates all the dispositions - * - * @param animate If the recreate should be done with an animation - */ - private void recreateDispositions(boolean animate) { - // Remove all the current views and add the new ones - removeAllViews(); - for (Disposition disposition : mDispositions) { - createFrame(getLocationFromDisposition(disposition), animate); - } - mOldResizeFrameLocation = null; - mTarget = null; - if (mOnFrameSelectedListener != null) { - mOnFrameSelectedListener.onFrameUnselectedListener(); - } - } - - /** - * Method that request the deletion of the current selected frame - */ - @SuppressWarnings("boxing") - public void deleteCurrentFrame() { - if (mTarget == null) return; - - final Disposition targetDisposition = resizerToDisposition(); - - // Get valid dispositions to move - final List adjacents = findAdjacentsDispositions(targetDisposition); - if (adjacents == null) { - // Nothing to do - Toast.makeText(getContext(), - R.string.pref_disposition_unable_delete_advise, Toast.LENGTH_SHORT).show(); - return; - } - - // Hide resizer - mResizeFrame.setVisibility(View.GONE); - - // Animate adjacents views - List animators = new ArrayList(); - animators.add(ObjectAnimator.ofFloat(mTarget, "scaleX", 1.0f, 0.0f)); - animators.add(ObjectAnimator.ofFloat(mTarget, "scaleY", 1.0f, 0.0f)); - - Disposition first = null; - for (Disposition adjacent : adjacents) { - // Extract the view and remove from dispositions - View v = findViewFromRect(getLocationFromDisposition(adjacent)); - mDispositions.remove(adjacent); - - // Clone first disposition - if (first == null) { - first = new Disposition(); - first.x = adjacent.x; - first.y = adjacent.y; - first.w = adjacent.w; - first.h = adjacent.h; - } - - // Add animators and fix the adjacent - if (v != null) { - if (first.x < targetDisposition.x) { - // From Left to Right - int width = mTarget.getWidth() + mInternalPadding; - animators.add(ValueAnimator.ofObject( - new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width)); - - // Update the adjacent - adjacent.w += targetDisposition.w; - mDispositions.add(adjacent); - - } else if (first.x > targetDisposition.x) { - // From Right to Left - int width = mTarget.getWidth() + mInternalPadding; - animators.add(ValueAnimator.ofObject( - new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width)); - animators.add(ObjectAnimator.ofFloat(v, "x", v.getX(), mTarget.getX())); - - // Update the adjacent - adjacent.x = targetDisposition.x; - adjacent.w += targetDisposition.w; - mDispositions.add(adjacent); - - } else if (first.y < targetDisposition.y) { - // From Top to Bottom - int height = mTarget.getHeight() + mInternalPadding; - animators.add(ValueAnimator.ofObject( - new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height)); - - // Update the adjacent - adjacent.h += targetDisposition.h; - mDispositions.add(adjacent); - - } else if (first.y > targetDisposition.y) { - // From Bottom to Top - int height = mTarget.getHeight() + mInternalPadding; - animators.add(ValueAnimator.ofObject( - new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height)); - animators.add(ObjectAnimator.ofFloat(v, "y", v.getY(), mTarget.getY())); - - // Update the adjacent - adjacent.y = targetDisposition.y; - adjacent.h += targetDisposition.h; - mDispositions.add(adjacent); - } - } - } - if (animators.size() > 0) { - AnimatorSet animSet = new AnimatorSet(); - animSet.playTogether(animators); - animSet.setDuration(getResources().getInteger(R.integer.disposition_hide_anim)); - animSet.setInterpolator(new AccelerateInterpolator()); - animSet.addListener(new AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - // Ignore - } - @Override - public void onAnimationRepeat(Animator animation) { - // Ignore - } - @Override - public void onAnimationEnd(Animator animation) { - finishDeleteAnimation(targetDisposition); - } - - @Override - public void onAnimationCancel(Animator animation) { - finishDeleteAnimation(targetDisposition); - } - }); - animSet.start(); - } - } - - /** - * Method that finalizes the delete animation - * - * @param target The disposition target - */ - /*package*/ void finishDeleteAnimation(Disposition target) { - removeView(mTarget); - mDispositions.remove(target); - Collections.sort(mDispositions); - mChanged = true; - - // Clean status - mOldResizeFrameLocation = null; - mTarget = null; - if (mOnFrameSelectedListener != null) { - mOnFrameSelectedListener.onFrameUnselectedListener(); - } - } - - /** - * Method that create a new frame to be drawn in the specified location - * - * @param r The location relative to the parent layout - * @return v The new view - */ - private View createFrame(Rect r, boolean animate) { - int padding = (int)getResources().getDimension(R.dimen.disposition_frame_padding); - final ImageView v = new ImageView(getContext()); - v.setImageResource(R.drawable.ic_camera); - v.setScaleType(ScaleType.CENTER); - v.setBackgroundColor(getResources().getColor(R.color.disposition_frame_bg_color)); - RelativeLayout.LayoutParams params = - new RelativeLayout.LayoutParams(r.width() - padding, r.height() - padding); - v.setX(r.left + padding); - v.setY(r.top + padding); - v.setOnLongClickListener(this); - if (animate) { - v.setVisibility(View.INVISIBLE); - } - addView(v, params); - - // Animate the view - if (animate) { - post(new Runnable() { - @Override - public void run() { - Animation anim = AnimationUtils.loadAnimation( - getContext(), R.anim.display_with_bounce); - anim.setFillBefore(true); - anim.setFillAfter(true); - v.startAnimation(anim); - } - }); - } - - return v; - } - - /** - * Method that returns the location of the frame from its disposition - * - * @param disposition The source disposition - * @return Rect The location on parent view - */ - private Rect getLocationFromDisposition(Disposition disposition) { - int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); - int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); - int cw = w / mCols; - int ch = h / mRows; - - Rect location = new Rect(); - location.left = disposition.x * cw; - location.top = disposition.y * ch; - location.right = location.left + disposition.w * cw; - location.bottom = location.top + disposition.h * ch; - return location; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onLongClick(View v) { - if (!selectTarget(v)) return false; - mVibrator.vibrate(300); - return true; - } - - @Override - public void onStartResize(int mode) { - mOldResizeFrameLocation = new Rect( - mResizeFrame.getLeft(), - mResizeFrame.getTop(), - mResizeFrame.getRight(), - mResizeFrame.getBottom()); - } - - @Override - public void onResize(int mode, int delta) { - if (mTarget == null) return; - - int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); - int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); - int minWidth = (w / mCols) + (w / mCols) / 2; - int minHeight = (h / mRows) + (h / mRows) / 2; - - FrameLayout.LayoutParams params = - (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); - switch (mode) { - case Gravity.LEFT: - float newpos = mResizeFrame.getX() + delta; - if ((delta < 0 && newpos < (getPaddingLeft() * -1)) || - (delta > 0 && newpos > (mResizeFrame.getX() + params.width - minWidth))) { - return; - } - mResizeFrame.setX(newpos); - params.width -= delta; - break; - case Gravity.RIGHT: - if ((delta < 0 && ((params.width + delta) < minWidth)) || - (delta > 0 && (mResizeFrame.getX() + delta + params.width) > (getPaddingLeft() + getMeasuredWidth()))) { - return; - } - params.width += delta; - break; - case Gravity.TOP: - newpos = mResizeFrame.getY() + delta; - if ((delta < 0 && newpos < (getPaddingTop() * -1)) || - (delta > 0 && newpos > (mResizeFrame.getY() + params.height - minHeight))) { - return; - } - mResizeFrame.setY(newpos); - params.height -= delta; - break; - case Gravity.BOTTOM: - if ((delta < 0 && ((params.height + delta) < minHeight)) || - (delta > 0 && (mResizeFrame.getY() + delta + params.height) > (getPaddingTop() + getMeasuredHeight()))) { - return; - } - params.height += delta; - break; - - default: - break; - } - mResizeFrame.setLayoutParams(params); - } - - /** - * {@inheritDoc} - */ - @Override - public void onEndResize(final int mode) { - if (mTarget == null) return; - - // Compute the removed dispositions - computeRemovedDispositions(mode); - recreateDispositions(false); - computeNewDispositions(mode); - - // Finish resize (select the target and create the new dispositions) - post(new Runnable() { - @Override - public void run() { - // Select the target - View v = findTargetFromResizeFrame(); - if (v != null) { - selectTarget(v); - } - } - }); - } - - /** - * {@inheritDoc} - */ - @Override - public void onCancel() { - if (mOldResizeFrameLocation != null) { - mTarget.setLeft(mOldResizeFrameLocation.left); - mTarget.setRight(mOldResizeFrameLocation.right); - mTarget.setTop(mOldResizeFrameLocation.top); - mTarget.setBottom(mOldResizeFrameLocation.bottom); - } - mOldResizeFrameLocation = null; - mTarget = null; - if (mOnFrameSelectedListener != null) { - mOnFrameSelectedListener.onFrameUnselectedListener(); - } - } - - /** - * Method that returns the target view for the current resize frame - * - * @return The target view - */ - /*package*/ View findTargetFromResizeFrame() { - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View v = getChildAt(i); - if (v.getX() < (mResizeFrame.getX() + (mResizeFrame.getWidth() / 2)) && - (v.getX() + v.getWidth()) > (mResizeFrame.getX() + (mResizeFrame.getWidth() / 2)) && - v.getY() < (mResizeFrame.getY() + (mResizeFrame.getHeight() / 2)) && - (v.getY() + v.getHeight()) > (mResizeFrame.getY() + (mResizeFrame.getHeight() / 2))) { - return v; - } - } - return null; - } - - /** - * Method that returns the view under the rect - * - * @return The view - */ - private View findViewFromRect(Rect r) { - int count = getChildCount(); - for (int i = 0; i < count; i++) { - View v = getChildAt(i); - if (v.getX() < (r.left + (r.width() / 2)) && - (v.getX() + v.getWidth()) > (r.left + (r.width() / 2)) && - v.getY() < (r.top + (r.height() / 2)) && - (v.getY() + v.getHeight()) > (r.top + (r.height() / 2))) { - return v; - } - } - return null; - } - - /** - * Method that select a view as the target of to resize - * - * @param v The target view - */ - /*package*/ boolean selectTarget(View v) { - //Do not do long click if we do not have a target - if (mTarget != null && v.equals(mTarget)) return false; - - // Show the resize frame view just in place of the current clicked view - mResizeFrame.hide(); - FrameLayout.LayoutParams frameParams = - (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); - int padding = mInternalPadding + mResizeFrame.getNeededPadding(); - frameParams.width = v.getWidth() + (padding * 2); - frameParams.height = v.getHeight() + (padding * 2); - mResizeFrame.setX(v.getX() - padding); - mResizeFrame.setY(v.getY() - padding); - mResizeFrame.show(); - - // Save the new view - mTarget = v; - if (mOnFrameSelectedListener != null) { - mOnFrameSelectedListener.onFrameSelectedListener(v); - } - return true; - } - - /** - * Computes the removed layout disposition based on the actual resize frame - * - * @param mode The resize mode - */ - private void computeRemovedDispositions(int mode) { - // Transform the resizer to a dispositions object - Disposition resizer = resizerToDisposition(); - - // Delete all overlapped - int count = mDispositions.size(); - for (int i = count - 1; i >= 0; i--) { - Disposition disposition = mDispositions.get(i); - if (!isVisible(disposition) || isOverlapped(resizer, disposition)) { - mDispositions.remove(disposition); - } - } - - // Add the new disposition - mDispositions.add(resizer); - Collections.sort(mDispositions); - - mChanged = true; - } - - /** - * Computes the new layout disposition based on the actual resize frame - * - * @param mode The resize mode - */ - private void computeNewDispositions(int mode) { - // Fill the empty areas - do { - byte[][] dispositionMatrix = DispositionUtil.toMatrix(mDispositions, mCols, mRows); - Rect rect = MERAlgorithm.getMaximalEmptyRectangle(dispositionMatrix); - if (rect.width() == 0 && rect.height() == 0) { - // No more empty areas - break; - } - Disposition disposition = DispositionUtil.fromRect(rect); - createFrame(getLocationFromDisposition(disposition), true); - mDispositions.add(disposition); - } while (true); - - // Now the view was changed and should be reported - Collections.sort(mDispositions); - mChanged = true; - } - - /** - * Method that converts the resize frame to a dispostion reference - * - * @return Disposition The disposition reference - */ - private Disposition resizerToDisposition() { - int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); - int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); - int cw = w / mCols; - int ch = h / mRows; - - //Remove overlapped areas - Disposition resizer = new Disposition(); - resizer.x = Math.round(mResizeFrame.getX() / cw); - resizer.y = Math.round(mResizeFrame.getY() / ch); - resizer.w = Math.round(mResizeFrame.getWidth() / cw); - resizer.h = Math.round(mResizeFrame.getHeight() / ch); - - // Fix disposition (limits) - resizer.x = Math.max(resizer.x, 0); - resizer.y = Math.max(resizer.y, 0); - resizer.w = Math.min(resizer.w, mCols - resizer.x); - resizer.h = Math.min(resizer.h, mRows - resizer.y); - - return resizer; - } - - /** - * Method that returns all dispositions that matched exactly (in one side) with - * the argument disposition. - * - * @param disposition The disposition to check - */ - private List findAdjacentsDispositions(Disposition disposition) { - if (mDispositions.size() <= 1) return null; - - // Check left size - if (disposition.x != 0) { - List dispositions = new ArrayList(); - for (Disposition d : mDispositions) { - if (d.compareTo(disposition) != 0) { - if ((d.x + d.w) == disposition.x && - (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) { - dispositions.add(d); - } - } - } - // Check if the sum of all the dispositions matches the disposition - int sum = 0; - for (Disposition d : dispositions) { - sum += d.h; - } - if (sum == disposition.h) { - return dispositions; - } - } - // Check top size - if (disposition.y != 0) { - List dispositions = new ArrayList(); - for (Disposition d : mDispositions) { - if (d.compareTo(disposition) != 0) { - if ((d.y + d.h) == disposition.y && - (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) { - dispositions.add(d); - } - } - } - // Check if the sum of all the dispositions matches the disposition - int sum = 0; - for (Disposition d : dispositions) { - sum += d.w; - } - if (sum == disposition.w) { - return dispositions; - } - } - // Check right size - if ((disposition.x + disposition.w) != mCols) { - List dispositions = new ArrayList(); - for (Disposition d : mDispositions) { - if (d.compareTo(disposition) != 0) { - if ((d.x) == (disposition.x + disposition.w) && - (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) { - dispositions.add(d); - } - } - } - // Check if the sum of all the dispositions matches the disposition - int sum = 0; - for (Disposition d : dispositions) { - sum += d.h; - } - if (sum == disposition.h) { - return dispositions; - } - } - // Check bottom size - if ((disposition.y + disposition.h) != mRows) { - List dispositions = new ArrayList(); - for (Disposition d : mDispositions) { - if (d.compareTo(disposition) != 0) { - if ((d.y) == (disposition.y + disposition.h) && - (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) { - dispositions.add(d); - } - } - } - // Check if the sum of all the dispositions matches the disposition - int sum = 0; - for (Disposition d : dispositions) { - sum += d.w; - } - if (sum == disposition.w) { - return dispositions; - } - } - return null; - } - - /** - * Method that checks if a dispositions overlaps another other disposition - * - * @param d1 One disposition - * @param d2 Another disposition - * @return boolean true if d1 overlaps d2 - */ - private static boolean isOverlapped(Disposition d1, Disposition d2) { - Rect r1 = new Rect(d1.x, d1.y, d1.x + d1.w, d1.y + d1.h); - Rect r2 = new Rect(d2.x, d2.y, d2.x + d2.w, d2.y + d2.h); - return r1.intersect(r2); - } - - /** - * Method that checks if a dispositions is visible - * - * @param d The disposition to check - * @return boolean true if d is visible - */ - private static boolean isVisible(Disposition d) { - return d.w > 0 && d.h > 0; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/PicturesView.java b/src/com/ruesga/android/wallpapers/photophase/widgets/PicturesView.java deleted file mode 100644 index 172bed4..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/PicturesView.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import android.content.Context; -import android.graphics.Rect; -import android.os.AsyncTask.Status; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.ViewGroup; -import android.widget.HorizontalScrollView; -import android.widget.ImageView; - -import com.ruesga.android.wallpapers.photophase.R; -import com.ruesga.android.wallpapers.photophase.tasks.AsyncPictureLoaderTask; - -import java.io.File; -import java.util.HashMap; -import java.util.Iterator; - -/** - * A view that contains all the pictures of an album - */ -public class PicturesView extends HorizontalScrollView { - - private HashMap mTasks; - private Handler mHandler; - - /** - * Constructor of PicturesView. - * - * @param context The current context - */ - public PicturesView(Context context) { - super(context); - init(); - } - - /** - * Constructor of PicturesView. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public PicturesView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of PicturesView. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - * @param defStyle The default style to apply to this view. If 0, no style - * will be applied (beyond what is included in the theme). This may - * either be an attribute resource, whose value will be retrieved - * from the current theme, or an explicit style resource. - */ - public PicturesView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Method that initializes the structures of this class - */ - private void init() { - mTasks = new HashMap(); - mHandler = new Handler(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - cancelTasks(); - } - - /** - * Method that removes all tasks - */ - public void cancelTasks() { - // Cancel all the pending task - Iterator it = mTasks.values().iterator(); - while (it.hasNext()) { - AsyncPictureLoaderTask task = it.next(); - if (task.getStatus().compareTo(Status.PENDING) == 0) { - task.cancel(true); - } - } - mTasks.clear(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - // Estimated velocity (in some moment we must obtain some scrolling with an estimated - // velocity below of 3) - int velocity = Math.abs(l - oldl); - if (velocity <= 3) { - mHandler.post(new Runnable() { - @Override - public void run() { - requestLoadOfPendingPictures(); - } - }); - } - } - - /** - * Method invoked when the view is displayed - */ - public void onShow() { - mHandler.post(new Runnable() { - @Override - public void run() { - requestLoadOfPendingPictures(); - } - }); - } - - /** - * Method that load in background all visible and pending pictures - */ - /*package*/ void requestLoadOfPendingPictures() { - // Get the visible rect - Rect r = new Rect(); - getHitRect(r); - - // Get all the image views - ViewGroup vg = (ViewGroup)getChildAt(0); - int count = vg.getChildCount(); - for (int i = 0; i < count; i++) { - ViewGroup picView = (ViewGroup)vg.getChildAt(i); - File image = new File((String)picView.getTag()); - if (picView.getLocalVisibleRect(r) && !mTasks.containsKey(image)) { - ImageView iv = (ImageView)picView.findViewById(R.id.picture_thumbnail); - AsyncPictureLoaderTask task = new AsyncPictureLoaderTask(getContext(), iv); - task.execute(image); - mTasks.put(image, task); - } - } - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/ResizeFrame.java b/src/com/ruesga/android/wallpapers/photophase/widgets/ResizeFrame.java deleted file mode 100644 index d59db31..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/ResizeFrame.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import android.content.Context; -import android.graphics.BitmapFactory; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.RelativeLayout; - -import com.ruesga.android.wallpapers.photophase.R; - -/** - * The hold view to resize a frame. A square with 4 handles in every border - * to drag and resize a view - */ -public class ResizeFrame extends RelativeLayout { - - /** - * An interface to communicate resize event states - */ - public interface OnResizeListener { - /** - * Called when the resize is going to start - * - * @param mode The resize mode (left, right, top, bottom) - * @see Gravity - */ - void onStartResize(int mode); - /** - * Called when the resize is going to start - * - * @param mode The resize mode (left, right, top, bottom) - * @param delta The delta motion - * @see Gravity - */ - void onResize(int mode, int delta); - /** - * Called when the resize was ended - * - * @param mode The resize mode (left, right, top, bottom) - * @see Gravity - */ - void onEndResize(int mode); - /** - * Called when the resize was cancelled - * - * @param mode The resize mode (left, right, top, bottom) - * @see Gravity - */ - void onCancel(); - } - - private int mNeededPadding; - - private ImageView mLeftHandle; - private ImageView mRightHandle; - private ImageView mTopHandle; - private ImageView mBottomHandle; - - private float mExtraHandlingSpace; - - private View mHandle; - - private float mLastTouchX; - private float mLastTouchY; - - private OnResizeListener mOnResizeListener; - - /** - * Constructor of ResizeFrame. - * - * @param context The current context - */ - public ResizeFrame(Context context) { - super(context); - init(); - } - - /** - * Constructor of ResizeFrame. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public ResizeFrame(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of ResizeFrame. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - * @param defStyle The default style to apply to this view. If 0, no style - * will be applied (beyond what is included in the theme). This may - * either be an attribute resource, whose value will be retrieved - * from the current theme, or an explicit style resource. - */ - public ResizeFrame(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Method that initializes the view - */ - @SuppressWarnings("boxing") - private void init() { - setBackgroundResource(R.drawable.resize_frame); - setPadding(0, 0, 0, 0); - - BitmapFactory.Options o = new BitmapFactory.Options(); - o.inJustDecodeBounds = true; - o.inTargetDensity = DisplayMetrics.DENSITY_DEFAULT; - BitmapFactory.decodeResource(getContext().getResources(), R.drawable.resize_handle_left, o); - mNeededPadding = (int)(o.outWidth / 1.5f); - - LayoutParams lp; - mLeftHandle = new ImageView(getContext()); - mLeftHandle.setImageResource(R.drawable.resize_handle_left); - mLeftHandle.setTag(Gravity.LEFT); - lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE); - lp.addRule(RelativeLayout.CENTER_VERTICAL); - addView(mLeftHandle, lp); - - mRightHandle = new ImageView(getContext()); - mRightHandle.setImageResource(R.drawable.resize_handle_right); - mRightHandle.setTag(Gravity.RIGHT); - lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE); - lp.addRule(RelativeLayout.CENTER_VERTICAL); - addView(mRightHandle, lp); - - mTopHandle = new ImageView(getContext()); - mTopHandle.setImageResource(R.drawable.resize_handle_top); - mTopHandle.setTag(Gravity.TOP); - lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); - lp.addRule(RelativeLayout.CENTER_HORIZONTAL); - addView(mTopHandle, lp); - - mBottomHandle = new ImageView(getContext()); - mBottomHandle.setImageResource(R.drawable.resize_handle_bottom); - mBottomHandle.setTag(Gravity.BOTTOM); - lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); - lp.addRule(RelativeLayout.CENTER_HORIZONTAL); - addView(mBottomHandle, lp); - - mExtraHandlingSpace = getResources().getDimension(R.dimen.resize_frame_extra_handling_space); - } - - /** - * Method that set the callback for resize events - * - * @param onResizeListener The callback - */ - public void setOnResizeListener(OnResizeListener onResizeListener) { - mOnResizeListener = onResizeListener; - } - - /** - * Method that hides the view - */ - public void hide() { - setVisibility(View.GONE); - } - - /** - * Method that shows the view - */ - public void show() { - setVisibility(View.VISIBLE); - } - - /** - * Method that returns the extra padding to draw the handlers - * - * @return The extra padding space - */ - public int getNeededPadding() { - return mNeededPadding; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onTouchEvent(MotionEvent ev) { - final int action = ev.getAction(); - final float x = ev.getX(); - final float y = ev.getY(); - - switch (action) { - case MotionEvent.ACTION_DOWN: - mHandle = getHandleFromCoordinates(x, y); - if (mHandle != null) { - // Start moving the resize frame - mLastTouchX = x; - mLastTouchY = y; - - // Start motion - if (mOnResizeListener != null) { - mOnResizeListener.onStartResize(((Integer)mHandle.getTag()).intValue()); - } - return true; - } - break; - - case MotionEvent.ACTION_MOVE: - if (mHandle != null) { - // Resize - if (mOnResizeListener != null) { - int handle = ((Integer)mHandle.getTag()).intValue(); - int delta = - handle == Gravity.LEFT || handle == Gravity.RIGHT - ? Math.round(x - mLastTouchX) - : Math.round(y - mLastTouchY); - mOnResizeListener.onResize(handle, delta); - invalidate(); - } - mLastTouchX = x; - mLastTouchY = y; - return true; - } - break; - - case MotionEvent.ACTION_UP: - if (mHandle != null) { - if (mOnResizeListener != null) { - mOnResizeListener.onEndResize(((Integer)mHandle.getTag()).intValue()); - return true; - } - cancelMotion(); - break; - } - - //$FALL-THROUGH$ - case MotionEvent.ACTION_CANCEL: - cancelMotion(); - break; - - default: - break; - } - - return false; - } - - /** - * Cancel motions - */ - private void cancelMotion() { - mHandle = null; - mLastTouchX = 0; - mLastTouchY = 0; - if (mOnResizeListener != null) { - mOnResizeListener.onCancel(); - } - } - - /** - * Method that returns the resize handle touch from the the screen coordinates - * - * @param x The x coordinate - * @param y The y coordinate - * @return View The handle view or null if no handle touched - */ - private View getHandleFromCoordinates(float x, float y) { - final View[] handles = {mLeftHandle, mRightHandle, mTopHandle, mBottomHandle}; - for (View v : handles) { - if ((v.getLeft() - mExtraHandlingSpace) < x && (v.getRight() + mExtraHandlingSpace) > x && - (v.getTop() - mExtraHandlingSpace) < y && (v.getBottom() + mExtraHandlingSpace) > y) { - return v; - } - } - return null; - } -} diff --git a/src/com/ruesga/android/wallpapers/photophase/widgets/VerticalEndlessScroller.java b/src/com/ruesga/android/wallpapers/photophase/widgets/VerticalEndlessScroller.java deleted file mode 100644 index c82cf8c..0000000 --- a/src/com/ruesga/android/wallpapers/photophase/widgets/VerticalEndlessScroller.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2013 Jorge Ruesga - * - * 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.ruesga.android.wallpapers.photophase.widgets; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ScrollView; - -/** - * A scroll view that notifies the end of the scroll to create new views - * dynamically. - */ -public class VerticalEndlessScroller extends ScrollView { - - /** - * Interface to communicate end-scroll events - */ - public interface OnEndScrollListener { - /** - * Called when the scroll reachs the end of the scroll - */ - void onEndScroll(); - } - - private OnEndScrollListener mCallback; - private int mEndPadding = 0; - private boolean mSwitch = false; - - /** - * Constructor of VerticalEndlessScroller. - * - * @param context The current context - */ - public VerticalEndlessScroller(Context context) { - super(context); - } - - /** - * Constructor of VerticalEndlessScroller. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public VerticalEndlessScroller(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Constructor of VerticalEndlessScroller. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - * @param defStyle The default style to apply to this view. If 0, no style - * will be applied (beyond what is included in the theme). This may - * either be an attribute resource, whose value will be retrieved - * from the current theme, or an explicit style resource. - */ - public VerticalEndlessScroller(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * Method that set the callback for notify end-scroll events - * - * @param callback The callback - */ - public void setCallback(OnEndScrollListener callback) { - mCallback = callback; - } - - /** - * Method that set the end padding for fired the event - * - * @param endPadding The end padding - */ - public void setEndPadding(int endPadding) { - this.mEndPadding = endPadding; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - // We take the last child in the scrollview - View view = getChildAt(getChildCount() - 1); - int diff = (view.getBottom() - (getHeight() + getScrollY())); - if ((!mSwitch && diff <= mEndPadding)) { - if (mCallback != null) { - mCallback.onEndScroll(); - mSwitch = true; - return; - } - } else if (diff > mEndPadding) { - mSwitch = false; - } - } - -} diff --git a/src/org/cyanogenmod/wallpapers/photophase/AndroidHelper.java b/src/org/cyanogenmod/wallpapers/photophase/AndroidHelper.java new file mode 100644 index 0000000..5b3aff1 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/AndroidHelper.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.view.ViewConfiguration; + +/** + * A helper class with useful methods for deal with android. + */ +public final class AndroidHelper { + + /** + * Method that returns if the device is a tablet + * + * @param ctx The current context + * @return boolean If device is a table + */ + public static boolean isTablet(Context ctx) { + Configuration configuration = ctx.getResources().getConfiguration(); + return (configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) + >= Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + /** + * Method that returns if an option menu has to be displayed + * + * @param ctx The current context + * @return boolean If an option menu has to be displayed + */ + public static boolean showOptionsMenu(Context ctx) { + // Show overflow button? + return !ViewConfiguration.get(ctx).hasPermanentMenuKey(); + } + + /** + * This method converts dp unit to equivalent device specific value in pixels. + * + * @param ctx The current context + * @param dp A value in dp (Device independent pixels) unit + * @return float A float value to represent Pixels equivalent to dp according to device + */ + public static float convertDpToPixel(Context ctx, float dp) { + Resources resources = ctx.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + return dp * (metrics.densityDpi / 160f); + } + + /** + * This method converts device specific pixels to device independent pixels. + * + * @param ctx The current context + * @param px A value in px (pixels) unit + * @return A float value to represent dp equivalent to px value + */ + public static float convertPixelsToDp(Context ctx, float px) { + Resources resources = ctx.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + return px / (metrics.densityDpi / 160f); + } + + /** + * Calculate the dimension of the status bar + * + * @param context The current context + * @return The height of the status bar + */ + public static int calculateStatusBarHeight(Context context) { + // CyanogenMod specific featured (DO NOT RELAY IN INTERNAL VARS) + boolean hiddenStatusBar = + Settings.System.getInt(context.getContentResolver(), "expanded_desktop_state", 0) == 1 && + Settings.System.getInt(context.getContentResolver(), "expanded_desktop_style", 0) == 2; + int result = 0; + if (!hiddenStatusBar && !(context instanceof Activity)) { + int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = context.getResources().getDimensionPixelSize(resourceId); + } + } + return result; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/Colors.java b/src/org/cyanogenmod/wallpapers/photophase/Colors.java new file mode 100644 index 0000000..925e058 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/Colors.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.content.Context; +import android.content.res.Resources; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; + +/** + * A class that defines some wallpaper GLColor colors. + */ +public class Colors { + + private static GLColor sBackground = new GLColor(0); + private static GLColor sOverlay = new GLColor(0); + + /** + * This method should be called on initialization for load the preferences color + */ + public static void register(Context ctx) { + Resources res = ctx.getResources(); + sBackground = Preferences.General.getBackgroundColor(); + sOverlay = new GLColor(res.getColor(R.color.wallpaper_overlay_color)); + } + + public static GLColor getBackground() { + return sBackground; + } + + public static void setBackground(GLColor background) { + Colors.sBackground = background; + } + + public static GLColor getOverlay() { + return sOverlay; + } + + public static void setOverlay(GLColor overlay) { + Colors.sOverlay = overlay; + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/EGLWallpaperService.java b/src/org/cyanogenmod/wallpapers/photophase/EGLWallpaperService.java new file mode 100644 index 0000000..9effe8c --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/EGLWallpaperService.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.service.wallpaper.WallpaperService; +import android.view.SurfaceHolder; + +/** + * An abstract class for using a {@link GLSurfaceView} inside a {@link WallpaperService}. + */ +public abstract class EGLWallpaperService extends WallpaperService { + + /** + * A listener interface for the {@link GLESWallpaperService.GLESEngine} engine class. + */ + public interface EGLEngineListener { + // No methods + } + + /** + * An EGL implementation of {@link android.service.wallpaper.WallpaperService.Engine} that + * uses {@link GLSurfaceView}. + */ + public class EGLEngine extends Engine { + /** + * The internal {@link GLSurfaceView}. + */ + class WallpaperGLSurfaceView extends GLSurfaceView { + + /** + * @see GLSurfaceView + */ + WallpaperGLSurfaceView(Context context) { + super(context); + } + + /** + * {@inheritDoc} + */ + @Override + public SurfaceHolder getHolder() { + return getSurfaceHolder(); + } + + /** + * Should be called when the {@link GLSurfaceView} is not needed anymore. + */ + public void onDestroy() { + super.onDetachedFromWindow(); + } + } + + private WallpaperGLSurfaceView mGlSurfaceView; + + /** + * Method that sets the EGL engine listener + * + * @param listener The EGL engine listener + */ + public void setEGLEngineListener(EGLEngineListener listener) { + // No methods + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(SurfaceHolder surfaceHolder) { + super.onCreate(surfaceHolder); + mGlSurfaceView = createWallpaperGLSurfaceView(); + } + + /** + * Method that returns the internal {@link GLSurfaceView} + * + * @return GLSurfaceView The internal {@link GLSurfaceView}. + */ + protected GLSurfaceView getGlSurfaceView() { + return mGlSurfaceView; + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + super.onDestroy(); + mGlSurfaceView.onDestroy(); + } + + /** + * Override this method if a {@link GLSurfaceView} wrapper is needed, for example + * if you need implements some eve + * + * @return WallpaperGLSurfaceView The specialized EGL {@link GLSurfaceView}. + */ + public WallpaperGLSurfaceView createWallpaperGLSurfaceView() { + return new WallpaperGLSurfaceView(EGLWallpaperService.this); + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/FixedQueue.java b/src/org/cyanogenmod/wallpapers/photophase/FixedQueue.java new file mode 100644 index 0000000..398785f --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/FixedQueue.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class that represent a FIFO queue with a fixed size. When the queue reach the maximum defined + * size then extract the next element from the queue. + * @param The type of object to hold. + */ +@SuppressWarnings("unchecked") +public class FixedQueue { + + /** + * An exception thrown when the queue hasn't more elements + */ + public static class EmptyQueueException extends Exception { + private static final long serialVersionUID = 1L; + } + + private final Object[] mQueue; + private final int mSize; + private int mHead; + private int mTail; + + /** + * Constructor of FixedQueue + * + * @param size The size of the queue. The limit of objects in queue. Beyond this limits + * the older objects are recycled. + */ + public FixedQueue(int size) { + super(); + this.mQueue = new Object[size]; + this.mSize = size; + this.mHead = 0; + this.mTail = 0; + } + + /** + * Method that inserts a new object to the queue. + * + * @param o The object to insert + * @return The object inserted (for concatenation purpose) + */ + public T insert(T o) { + synchronized (this.mQueue) { + if (o == null) throw new NullPointerException(); + if (this.mQueue[this.mHead] != null) { + try { + noSynchronizedRemove(); + } catch (Throwable ex) {/**NON BLOCK**/} + } + this.mQueue[this.mHead] = o; + this.mHead++; + if (this.mHead >= this.mSize) { + this.mHead = 0; + } + return o; + } + } + + /** + * Method that extract the first element in the queue + * + * @return The item extracted + * @throws EmptyQueueException If the queue hasn't element + */ + public T remove() throws EmptyQueueException { + synchronized (this.mQueue) { + return noSynchronizedRemove(); + } + } + + /** + * Method that extract all the items from the queue + * + * @return The items extracted + * @throws EmptyQueueException If the queue hasn't element + */ + public List removeAll() throws EmptyQueueException { + synchronized (this.mQueue) { + if (isEmpty()) throw new EmptyQueueException(); + List l = new ArrayList(); + while (!isEmpty()) { + l.add(noSynchronizedRemove()); + } + return l; + } + } + + /** + * Method that retrieves the first element in the queue. This method doesn't remove the item + * from queue. + * + * @return The item retrieved + * @throws EmptyQueueException If the queue hasn't element + */ + public T peek() throws EmptyQueueException { + synchronized (this.mQueue) { + T o = (T)this.mQueue[this.mTail]; + if (o == null) throw new EmptyQueueException(); + return o; + } + + } + + /** + * Method that retrieves all the items from the queue. This method doesn't remove any item + * from queue. + * + * @return The items retrieved + * @throws EmptyQueueException If the queue hasn't element + */ + public List peekAll() throws EmptyQueueException { + synchronized (this.mQueue) { + if (isEmpty()) throw new EmptyQueueException(); + List l = new ArrayList(); + int head = this.mHead; + int tail = this.mTail; + do { + l.add((T)this.mQueue[tail]); + tail++; + if (tail >= this.mSize) { + tail = 0; + } + } while (head != tail); + return l; + } + } + + /** + * Method that returns if the queue is empty + * + * @return boolean If the queue is empty + */ + public boolean isEmpty() { + synchronized (this.mQueue) { + return this.mQueue[this.mTail] == null; + } + } + + /** + * Method that returns the number of items in the queue + * + * @return int The number of items + */ + public int items() { + int cc = 0; + int head = this.mHead; + int tail = this.mTail; + do { + if (this.mQueue[tail] == null) { + return cc; + } + cc++; + tail++; + if (tail >= this.mSize) { + tail = 0; + } + } while (head != tail); + return cc; + } + + /** + * Method that remove one item without synchronization (for be called from + * synchronized method). + * + * @return The item extracted + * @throws EmptyQueueException If the queue hasn't element + */ + private T noSynchronizedRemove() throws EmptyQueueException { + T o = (T)this.mQueue[this.mTail]; + if (o == null) throw new EmptyQueueException(); + this.mQueue[this.mTail] = null; + this.mTail++; + if (this.mTail >= this.mSize) { + this.mTail = 0; + } + return o; + } + + /** + * Method that returns the size of this queue. + * + * @return The size of this queue + */ + public int size() { + return mSize; + } +} \ No newline at end of file diff --git a/src/org/cyanogenmod/wallpapers/photophase/GLES20WallpaperService.java b/src/org/cyanogenmod/wallpapers/photophase/GLES20WallpaperService.java new file mode 100644 index 0000000..fdd8b5b --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/GLES20WallpaperService.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + + + + +/** + * An abstract implementation of {@link EGLWallpaperService} based on GLES. + */ +public abstract class GLES20WallpaperService extends GLESWallpaperService { + + /** + * {@inheritDoc} + */ + @Override + public Engine onCreateEngine() { + return new GLES20Engine(); + } + + /** + * A class that extends the {@link GLESWallpaperService.GLESEngine} to add support for + * GLES20. + */ + class GLES20Engine extends GLESWallpaperService.GLESEngine { + /** + * {@inheritDoc} + */ + @Override + void initialize() { + // Request an OpenGL ES 2.x compatible context. + getGlSurfaceView().setEGLContextClientVersion(2); + getGlSurfaceView().setEGLConfigChooser(false); + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/GLESSurfaceDispatcher.java b/src/org/cyanogenmod/wallpapers/photophase/GLESSurfaceDispatcher.java new file mode 100644 index 0000000..7634a63 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/GLESSurfaceDispatcher.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.opengl.GLSurfaceView; + +/** + * A class responsible of dispatch GLES commands inside the main GLThread. + */ +public class GLESSurfaceDispatcher { + + private final GLSurfaceView mSurface; + + /** + * Constructor of GLESSurfaceDispatcher + * + * @param v The associated GLES surface view + */ + public GLESSurfaceDispatcher(GLSurfaceView v) { + super(); + mSurface = v; + } + + /** + * Method that dispatch a GLES commands inside the main GLThread. + * + * @param r The runnable that execute the GLES commands + */ + public void dispatch(Runnable r) { + this.mSurface.queueEvent(r); + } + + /** + * Method that set the render mode + * + * @param mode The GLES render mode + */ + public void setRenderMode(int mode) { + if (mSurface.getRenderMode() != mode) { + mSurface.setRenderMode(mode); + } + } + + /** + * Method that requests a render to the surface. + */ + public void requestRender() { + mSurface.requestRender(); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/GLESWallpaperService.java b/src/org/cyanogenmod/wallpapers/photophase/GLESWallpaperService.java new file mode 100644 index 0000000..8ddb443 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/GLESWallpaperService.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.view.SurfaceHolder; + + +/** + * An abstract implementation of {@link EGLWallpaperService} based on GLES. + */ +public abstract class GLESWallpaperService extends EGLWallpaperService { + + /** + * A listener interface for the {@link GLESWallpaperService.GLESEngine} engine class. + */ + public interface GLESEngineListener extends EGLEngineListener { + /** + * Method invoked when the EGL surface is starting to initialize. + * + * @param view GLSurfaceView The EGL view + */ + void onInitializeEGLView(GLSurfaceView view); + + /** + * Method invoked when the EGL surface is recycled. + * + * @param view GLSurfaceView The EGL view + * @param renderer The renderer associated + */ + void onDestroyEGLView(GLSurfaceView view, Renderer renderer); + + /** + * Method invoked when the EGL surface was initialized. + * + * @param view GLSurfaceView The EGL view + */ + void onEGLViewInitialized(GLSurfaceView view); + + /** + * Method invoked when the EGL context is paused + * + * @param renderer The renderer associated + */ + void onPause(Renderer renderer); + + /** + * Method invoked when the EGL context is resumed + * + * @param renderer The renderer associated + */ + void onResume(Renderer renderer); + } + + /** + * {@inheritDoc} + */ + @Override + public Engine onCreateEngine() { + return new GLESEngine(); + } + + /** + * A class that extends the {@link EGLWallpaperService.EGLEngine} to add support for + * GLES. + */ + class GLESEngine extends EGLWallpaperService.EGLEngine { + + private GLESEngineListener mListener = null; + private WallpaperGLSurfaceView mWallpaperGLSurfaceView = null; + private Renderer mRenderer = null; + + private boolean mRendererHasBeenSet; + private boolean mPauseOnPreview; + + /** + * Method that sets the EGL engine listener + * + * @param listener The EGL engine listener + */ + public void setGLESEngineListener(GLESEngineListener listener) { + mListener = listener; + setEGLEngineListener(listener); + } + + /** + * Method that sets the {@link GLSurfaceView} to use. + * + * @param wallpaperGLSurfaceView A {@link GLSurfaceView} + */ + public void setWallpaperGLSurfaceView(WallpaperGLSurfaceView wallpaperGLSurfaceView) { + mWallpaperGLSurfaceView = wallpaperGLSurfaceView; + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(SurfaceHolder surfaceHolder) { + super.onCreate(surfaceHolder); + + // Notify initialization + if (mListener != null) { + mListener.onInitializeEGLView(getGlSurfaceView()); + } + + // Initialize the GLES context + initialize(); + + // Set the renderer to our user-defined renderer. + mRenderer = getNewRenderer(getGlSurfaceView()); + getGlSurfaceView().setRenderer(mRenderer); + mRendererHasBeenSet = true; + + // Notify that the EGL is initialized + if (mListener != null) { + mListener.onEGLViewInitialized(getGlSurfaceView()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + super.onDestroy(); + + // Notify initialization + if (mListener != null) { + mListener.onDestroyEGLView(getGlSurfaceView(), mRenderer); + } + mRenderer = null; + } + + /** + * Method that initializes + */ + void initialize() { + // Request an OpenGL ES 1.x compatible context. + getGlSurfaceView().setEGLContextClientVersion(1); + } + + /** + * Returns the renderer used by the engine + * + * @return Renderer The renderer + */ + protected Renderer getRenderer() { + return mRenderer; + } + + /** + * {@inheritDoc} + */ + @Override + public WallpaperGLSurfaceView createWallpaperGLSurfaceView() { + // Check whether to use a proprietary GLSurfaceView reference or an internal one + if (mWallpaperGLSurfaceView != null) { + return mWallpaperGLSurfaceView; + } + return super.createWallpaperGLSurfaceView(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onVisibilityChanged(boolean visible) { + super.onVisibilityChanged(visible); + if (mRendererHasBeenSet) { + if (visible) { + getGlSurfaceView().onResume(); + getGlSurfaceView().setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + mListener.onResume(mRenderer); + } else { + // Check that the user is not previewing the live wallpaper; if they are, then + // if they open up a settings dialog that appears over the preview, we don’t + // want to pause rendering + boolean preview = isPreview(); + if (!preview || (preview && mPauseOnPreview)) { + getGlSurfaceView().setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + getGlSurfaceView().onPause(); + mListener.onPause(mRenderer); + } + } + } + } + + /** + * Method that determines if the surface view should be paused on preview mode + * + * @return boolean whether the surface view should be paused on preview mode + */ + public boolean isPauseOnPreview() { + return mPauseOnPreview; + } + + /** + * Method that sets if the surface view should be paused on preview mode + * + * @param pauseOnPreview whether the surface view should be paused on preview mode + */ + public void setPauseOnPreview(boolean pauseOnPreview) { + this.mPauseOnPreview = pauseOnPreview; + } + } + + /** + * Method that return a new EGL renderer. + * + * @param view The view that will be associated to the renderer + * @return Renderer The new EGL renderer. + */ + public abstract Renderer getNewRenderer(GLSurfaceView view); +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java new file mode 100644 index 0000000..4228abf --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.AsyncTask.Status; +import android.provider.MediaStore; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A class that load asynchronously the paths of all media stored in the device. + * This class only seek at the specified paths + */ +public class MediaPictureDiscoverer { + + private static final String TAG = "MediaPictureDiscoverer"; + + private static final boolean DEBUG = false; + + /** + * An interface that is called when new data is ready. + */ + public interface OnMediaPictureDiscoveredListener { + /** + * Called when the starting to fetch data + * + * @param userRequest If the user requested this media discovery + */ + void onStartMediaDiscovered(boolean userRequest); + /** + * Called when the all the data is ready + * + * @param images All the images paths found + * @param userRequest If the user requested this media discovery + */ + void onEndMediaDiscovered(File[] images, boolean userRequest); + /** + * Called when the partial data is ready + * + * @param images All the images paths found + * @param userRequest If the user requested this media discovery + */ + void onPartialMediaDiscovered(File[] images, boolean userRequest); + } + + /** + * The asynchronous task for query the MediaStore + */ + private class AsyncDiscoverTask extends AsyncTask> { + + private final ContentResolver mFinalContentResolver; + private final OnMediaPictureDiscoveredListener mFinalCallback; + private final Set mFilter; + private final Set mLastAlbums; + private final Set mNewAlbums; + private final boolean mIsAutoSelectNewAlbums; + private final boolean mUserRequest; + + /** + * Constructor of AsyncDiscoverTask + * + * @param cr The {@link ContentResolver} + * @param cb The {@link OnMediaPictureDiscoveredListener} listener + * @param userRequest If the request was generated by the user + */ + public AsyncDiscoverTask( + ContentResolver cr, OnMediaPictureDiscoveredListener cb, boolean userRequest) { + super(); + mFinalContentResolver = cr; + mFinalCallback = cb; + mFilter = Preferences.Media.getSelectedMedia(); + mLastAlbums = Preferences.Media.getLastDiscorevedAlbums(); + mIsAutoSelectNewAlbums = Preferences.Media.isAutoSelectNewAlbums(); + mNewAlbums = new HashSet(); + mUserRequest = userRequest; + } + + /** + * {@inheritDoc} + */ + @Override + protected List doInBackground(Void...params) { + try { + // Start progress + publishProgress(new File[]{}); + + // The columns to read + final String[] projection = {MediaStore.MediaColumns.DATA}; + + // Query external content + List paths = + getPictures( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + projection, + null, + null); + if (DEBUG) { + int cc = paths.size(); + Log.v(TAG, "Pictures found (" + cc + "):"); + for (int i = 0; i < cc; i++) { + Log.v(TAG, "\t" + paths.get(i)); + } + } + return paths; + + } catch (Exception e) { + Log.e(TAG, "AsyncDiscoverTask failed.", e); + + // Return and empty list + return new ArrayList(); + } finally { + // Save the filter (could have new albums) + Preferences.Media.setSelectedMedia(mContext, mFilter); + Preferences.Media.setLastDiscorevedAlbums(mContext, mNewAlbums); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onProgressUpdate(File... values) { + if (mFinalCallback != null) { + if (values == null || values.length == 0) { + mFinalCallback.onStartMediaDiscovered(mUserRequest); + } else { + mFinalCallback.onPartialMediaDiscovered(values, mUserRequest); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(List result) { + if (mFinalCallback != null) { + mFinalCallback.onEndMediaDiscovered(result.toArray(new File[result.size()]), mUserRequest); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onCancelled(List result) { + // Nothing found + if (mFinalCallback != null) { + // Overwrite the user request setting. If the task is cancelled then + // there is no notification to send to the user + mFinalCallback.onEndMediaDiscovered(new File[]{}, false); + } + } + + /** + * Method that return all the media store pictures for the content uri + * + * @param uri The content uri where to search + * @param projection The field data to return + * @param where A filter + * @param args The filter arguments + * @return List The pictures found + */ + private List getPictures( + Uri uri, String[] projection, String where, String[] args) { + long start = System.currentTimeMillis(); + List paths = new ArrayList(); + List partial = new ArrayList(); + Cursor c = mFinalContentResolver.query(uri, projection, where, args, null); + if (c != null) { + try { + int i = 0; + while (c.moveToNext()) { + // Only valid files (those i can read) + String p = c.getString(0); + if (p != null) { + File f = new File(p); + catalog(f); + + // Check if is a valid filter + if (matchFilter(f)) { + paths.add(f); + partial.add(f); + } + } + + // Publish partial data + if (i % 5 == 0 && partial.size() > 0) { + publishProgress(partial.toArray(new File[partial.size()])); + partial.clear(); + } + i++; + } + } finally { + try { + c.close(); + } catch (Exception e) { + // Ignore: handle exception + } + } + } + long end = System.currentTimeMillis(); + if (DEBUG) Log.v(TAG, "Media reloaded in " + (end - start) + " miliseconds"); + return paths; + } + + /** + * Method that checks if the picture match the preferences filter + * + * @param picture The picture to check + * @return boolean whether the picture match the filter + */ + private boolean matchFilter(File picture) { + return mFilter.contains(picture.getAbsolutePath()) || + mFilter.contains(picture.getParentFile().getAbsolutePath()); + } + + /** + * Method that catalog the file (set its album and determine if is a new album) + * + * @param f The file to catalog + */ + private void catalog(File f) { + File parent = f.getParentFile(); + String albumPath = parent.getAbsolutePath(); + + // Add to new albums + mNewAlbums.add(albumPath); + + // Is a new album? + if (!mLastAlbums.contains(albumPath)) { + // Is in the filter? + if (mIsAutoSelectNewAlbums && !mFilter.contains(albumPath)) { + // Add the album to the selected filter + mFilter.add(parent.getAbsolutePath()); + } + } + } + } + + /*package*/ final Context mContext; + private final OnMediaPictureDiscoveredListener mCallback; + + private AsyncDiscoverTask mTask; + + /** + * Constructor of MediaPictureDiscoverer. + * + * @param ctx The current context + * @param callback A callback to returns the data when it gets ready + */ + public MediaPictureDiscoverer(Context ctx, OnMediaPictureDiscoveredListener callback) { + super(); + mContext = ctx; + mCallback = callback; + } + + /** + * Method that request a new reload of the media store picture data. + * + * @param userRequest If the request was generated by the user + */ + public synchronized void discover(boolean userRequest) { + if (mTask != null && mTask.getStatus().compareTo(Status.FINISHED) != 0 && + !mTask.isCancelled()) { + mTask.cancel(true); + } + mTask = new AsyncDiscoverTask(mContext.getContentResolver(), mCallback, userRequest); + mTask.execute(); + } + + /** + * Method that destroy the references of this class + */ + public void recycle() { + if (mTask != null && !mTask.isCancelled()) { + mTask.cancel(true); + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoFrame.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoFrame.java new file mode 100644 index 0000000..78fc265 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoFrame.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.content.Context; +import android.graphics.RectF; +import android.opengl.GLES20; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + + +/** + * A GLES square geometry that represents one photo frame for show in the wallpaper. + */ +public class PhotoFrame implements TextureRequestor { + + /** + * @hide + */ + public static final int COORDS_PER_VERTER = 3; + + // The default texture coordinates (fit to frame) + private static final float[] DEFAULT_TEXTURE_COORDS = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f + }; + + + private final TextureManager mTextureManager; + + private final float[] mFrameVertex, mPhotoVertex; + private final float mFrameWidth, mFrameHeight; + private final float mPhotoWidth, mPhotoHeight; + + + private FloatBuffer mPositionBuffer; + private FloatBuffer mTextureBuffer; + + private GLESTextureInfo mTextureInfo; + + private final GLColor mBackgroundColor; + + private boolean mLoaded; + + private final Object mSync = new Object(); + + /** + * Constructor of PhotoFrame. + * + * @param ctx The current context + * @param textureManager The texture manager + * @param frameVertex A 4 dimension array with the coordinates per vertex plus padding + * @param photoVertex A 4 dimension array with the coordinates per vertex without padding + * @param color Background color + */ + public PhotoFrame(Context ctx, TextureManager textureManager, + float[] frameVertex, float[] photoVertex, GLColor color) { + super(); + mLoaded = false; + mBackgroundColor = color; + mTextureManager = textureManager; + + // Save dimensions + mFrameVertex = frameVertex; + mFrameWidth = frameVertex[6] - frameVertex[4]; + mFrameHeight = frameVertex[1] - frameVertex[5]; + mPhotoVertex = photoVertex; + mPhotoWidth = photoVertex[6] - photoVertex[4]; + mPhotoHeight = photoVertex[5] - photoVertex[1]; + + // Initialize vertex byte buffer for shape coordinates + ByteBuffer bb = ByteBuffer.allocateDirect(photoVertex.length * 4); // (# of coordinate values * 4 bytes per float) + bb.order(ByteOrder.nativeOrder()); + mPositionBuffer = bb.asFloatBuffer(); + mPositionBuffer.put(photoVertex); + mPositionBuffer.position(0); + + // Load the texture + mTextureInfo = null; + + // Request a new image for this frame + textureManager.request(this); + } + + /** + * {@inheritDoc} + */ + @Override + public RectF getRequestorDimensions() { + return new RectF(0, 0, mPhotoWidth, mPhotoHeight); + } + + /** + * {@inheritDoc} + */ + @Override + public void setTextureHandle(GLESTextureInfo ti) { + // If the picture is invalid request a new texture + if (ti == null || ti.handle <= 0) { + mTextureManager.request(this); + return; + } + + // Full frame picture + setTextureHandle(ti, DEFAULT_TEXTURE_COORDS); + mLoaded = true; + } + + /** + * Internal method that expose the texture coordinates to set + * + * @param ti The texture info + * @param textureCoords The texture coordinates + */ + private void setTextureHandle(GLESTextureInfo ti, final float[] textureCoords) { + // Recycle the previous handle + if (mTextureInfo != null) { + if (GLES20.glIsTexture(mTextureInfo.handle)) { + int[] textures = new int[]{mTextureInfo.handle}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + if (mTextureInfo.bitmap != null) { + mTextureInfo.bitmap.recycle(); + mTextureInfo.bitmap = null; + } + } + + // Initialize vertex byte buffer for shape coordinates + ByteBuffer bb = ByteBuffer.allocateDirect(textureCoords.length * 4); // (# of coordinate values * 4 bytes per float) + bb.order(ByteOrder.nativeOrder()); + synchronized (mSync) { + // Swap buffers + mTextureBuffer = bb.asFloatBuffer(); + mTextureBuffer.put(textureCoords); + mTextureBuffer.position(0); + } + mTextureInfo = ti; + } + + /** + * Method that returns the frame vertex + * + * @return float[] The frame vertex + */ + public float[] getFrameVertex() { + return mFrameVertex; + } + + /** + * Method that returns the frame width + * + * @return float The frame width + */ + public float getFrameWidth() { + return mFrameWidth; + } + + /** + * Method that returns the frame height + * + * @return float The frame height + */ + public float getFrameHeight() { + return mFrameHeight; + } + + /** + * Method that returns the photo vertex + * + * @return float[] The photo vertex + */ + public float[] getPhotoVertex() { + return mPhotoVertex; + } + + /** + * Method that returns the photo width + * + * @return float The photo width + */ + public float getPhotoWidth() { + return mPhotoWidth; + } + + /** + * Method that returns the photo height + * + * @return float The photo height + */ + public float getPhotoHeight() { + return mPhotoHeight; + } + + /** + * Method that returns the position vertex buffer + * + * @return FloatBuffer The position vertex buffer + */ + public FloatBuffer getPositionBuffer() { + return mPositionBuffer; + } + + /** + * Method that returns the texture buffer + * + * @return FloatBuffer The texture buffer + */ + public FloatBuffer getTextureBuffer() { + synchronized (mSync) { + return mTextureBuffer; + } + } + + /** + * Method that returns the texture handle + * + * @return int The texture handle + */ + public int getTextureHandle() { + if (mTextureInfo != null) { + return mTextureInfo.handle; + } + return -1; + } + + /** + * Method that returns the texture info + * + * @return GLESTextureInfo The texture info + */ + public GLESTextureInfo getTextureInfo() { + return mTextureInfo; + } + + /** + * Method that returns the background color of the frame + * + * @return GLColor The background color + */ + public GLColor getBackgroundColor() { + return mBackgroundColor; + } + + /** + * Method that returns if the frame is loaded (has its picture loaded) + * + * @return boolean If the frame is loaded (has its picture loaded) + */ + public boolean isLoaded() { + return mLoaded; + } + + /** + * Request a recycle of the references of the object + */ + public void recycle() { + if (mTextureInfo != null && mTextureInfo.handle != 0) { + int[] textures = new int[]{mTextureInfo.handle}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + mTextureInfo = null; + + if (mPositionBuffer != null) { + mPositionBuffer.clear(); + } + if (mTextureBuffer != null) { + mTextureBuffer.clear(); + } + mPositionBuffer = null; + mTextureBuffer = null; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseActivity.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseActivity.java new file mode 100644 index 0000000..973618d --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseActivity.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.app.Activity; +import android.content.Intent; +import android.opengl.GLSurfaceView; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.Window; +import android.view.WindowManager; + +import org.cyanogenmod.wallpapers.photophase.preferences.PhotoPhasePreferences; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider; + +/** + * A testing activity to simulate the PhotoPhase Live Wallpaper inside an GLES activity. + */ +public class PhotoPhaseActivity extends Activity implements OnTouchListener { + + private static final String TAG = "PhotoPhaseActivity"; + + private static final boolean DEBUG = false; + + private GLSurfaceView mGLSurfaceView; + private PhotoPhaseRenderer mRenderer; + + /** + * {@inheritDoc} + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + if (DEBUG) Log.d(TAG, "onCreate"); + super.onCreate(savedInstanceState); + + boolean preserveEglCtx = getResources().getBoolean(R.bool.config_preserve_egl_context); + + // Instance the application + PreferencesProvider.reload(this); + Colors.register(this); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + // Configure the EGL context + mGLSurfaceView = new GLSurfaceView(getApplicationContext()); + mGLSurfaceView.setEGLContextClientVersion(2); + mGLSurfaceView.setEGLConfigChooser(false); + mRenderer = new PhotoPhaseRenderer(this, new GLESSurfaceDispatcher(mGLSurfaceView)); + mGLSurfaceView.setRenderer(mRenderer); + mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + mGLSurfaceView.setPreserveEGLContextOnPause(preserveEglCtx); + mGLSurfaceView.setOnTouchListener(this); + setContentView(mGLSurfaceView); + + mRenderer.onCreate(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + if (DEBUG) Log.d(TAG, "onDestroy"); + super.onDestroy(); + mRenderer.onDestroy(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onResume() { + super.onResume(); + if (DEBUG) Log.d(TAG, "onResume"); + mGLSurfaceView.onResume(); + mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + if (mRenderer != null) { + mRenderer.onResume(); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPause() { + super.onPause(); + if (DEBUG) Log.d(TAG, "onPause"); + mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + mRenderer.onPause(); + mGLSurfaceView.onPause(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main, menu); + return super.onCreateOptionsMenu(menu); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.mnu_settings: + Intent settings = new Intent(this, PhotoPhasePreferences.class); + startActivity(settings); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onTouch(View v, MotionEvent event) { + int action = event.getAction(); + float x = event.getX(); + float y = event.getY(); + switch (action) { + case MotionEvent.ACTION_UP: + mRenderer.onTouch(x, y); + return true; + + default: + break; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + Log.i(TAG, "onLowMemory"); + // Pause the wallpaper and destroy the cached textures + mRenderer.onPause(); + mRenderer.onLowMemory(); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java new file mode 100644 index 0000000..f57d53b --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java @@ -0,0 +1,661 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; +import android.content.res.Resources.NotFoundException; +import android.graphics.PointF; +import android.graphics.Rect; +import android.media.effect.EffectContext; +import android.net.Uri; +import android.opengl.GLES20; +import android.opengl.GLException; +import android.opengl.GLSurfaceView; +import android.opengl.Matrix; +import android.os.Handler; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.preferences.TouchAction; +import org.cyanogenmod.wallpapers.photophase.shapes.ColorShape; +import org.cyanogenmod.wallpapers.photophase.shapes.OopsShape; +import org.cyanogenmod.wallpapers.photophase.transitions.Transition; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +/** + * The EGL renderer of PhotoPhase Live Wallpaper. + */ +public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { + + private static final String TAG = "PhotoPhaseRenderer"; + + private static final boolean DEBUG = false; + + private final long mInstance; + private static long sInstances; + + /*package*/ final Context mContext; + /*package*/ EffectContext mEffectContext; + private final Handler mHandler; + /*package*/ final GLESSurfaceDispatcher mDispatcher; + /*package*/ TextureManager mTextureManager; + + /*package*/ PhotoPhaseWallpaperWorld mWorld; + /*package*/ ColorShape mOverlay; + /*package*/ OopsShape mOopsShape; + + /*package*/ long mLastRunningTransition; + + private long mLastTouchTime; + private static final long TOUCH_BARRIER_TIME = 1000L; + + /*package*/ int mWidth = -1; + /*package*/ int mHeight = -1; + private int mStatusBarHeight = 0; + /*package*/ int mMeasuredHeight = -1; + + private final float[] mMVPMatrix = new float[16]; + private final float[] mProjMatrix = new float[16]; + private final float[] mVMatrix = new float[16]; + + private final Object mDrawing = new Object(); + + /*package*/ final Object mMediaSync = new Object(); + private PendingIntent mMediaScanIntent; + + private final BroadcastReceiver mSettingsChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Check what flags are been requested + boolean recreateWorld = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, false); + boolean redraw = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, false); + boolean emptyTextureQueue = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_EMPTY_TEXTURE_QUEUE, false); + boolean mediaReload = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, false); + boolean mediaIntervalChanged = intent.getBooleanExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_INTERVAL_CHANGED, false); + if (emptyTextureQueue) { + if (mTextureManager != null) { + mTextureManager.emptyTextureQueue(true); + } + } + if (mediaReload) { + synchronized (mMediaSync) { + if (mTextureManager != null) { + boolean userReloadRequest = + intent.getBooleanExtra( + PreferencesProvider.EXTRA_ACTION_MEDIA_USER_RELOAD_REQUEST, false); + mTextureManager.reloadMedia(userReloadRequest); + scheduleOrCancelMediaScan(); + } + } + } + if (mediaIntervalChanged) { + scheduleOrCancelMediaScan(); + } + if (recreateWorld && mWorld != null) { + // Recreate the wallpaper world + try { + mWorld.recreateWorld(mWidth, mMeasuredHeight); + } catch (GLException e) { + Log.e(TAG, "Cannot recreate the wallpaper world.", e); + } + } + if (redraw) { + mDispatcher.requestRender(); + } + } + }; + + private final Runnable mTransitionThread = new Runnable() { + /** + * {@inheritDoc} + */ + @Override + public void run() { + // Run in GLES's thread + mDispatcher.dispatch(new Runnable() { + @Override + public void run() { + try { + // Select a new transition + mWorld.selectRandomTransition(); + mLastRunningTransition = System.currentTimeMillis(); + + // Now force continuously render while transition is applied + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + } catch (Throwable ex) { + Log.e(TAG, "Something was wrong selecting the transition", ex); + } + } + }); + } + }; + + /** + * Constructor of PhotoPhaseRenderer + * + * @param ctx The current context + * @param dispatcher The GLES dispatcher + */ + public PhotoPhaseRenderer(Context ctx, GLESSurfaceDispatcher dispatcher) { + super(); + mContext = ctx; + mHandler = new Handler(); + mDispatcher = dispatcher; + mInstance = sInstances; + sInstances++; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (mInstance ^ (mInstance >>> 32)); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PhotoPhaseRenderer other = (PhotoPhaseRenderer) obj; + if (mInstance != other.mInstance) + return false; + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "PhotoPhaseRenderer [instance: " + mInstance + "]"; + } + + /** + * Method called when renderer is created + */ + public void onCreate() { + if (DEBUG) Log.d(TAG, "onCreate [" + mInstance + "]"); + // Register a receiver to listen for media reload request + IntentFilter filter = new IntentFilter(); + filter.addAction(PreferencesProvider.ACTION_SETTINGS_CHANGED); + mContext.registerReceiver(mSettingsChangedReceiver, filter); + + // Check whether the media scan is active + int interval = Preferences.Media.getRefreshFrecuency(); + if (interval != Preferences.Media.MEDIA_RELOAD_DISABLED) { + // Schedule a media scan + scheduleMediaScan(interval); + } + } + + /** + * Method called when renderer is destroyed + */ + public void onDestroy() { + if (DEBUG) Log.d(TAG, "onDestroy [" + mInstance + "]"); + // Register a receiver to listen for media reload request + mContext.unregisterReceiver(mSettingsChangedReceiver); + recycle(); + if (mEffectContext != null) { + mEffectContext.release(); + } + mEffectContext = null; + mWidth = -1; + mHeight = -1; + mMeasuredHeight = -1; + } + + /** + * Method called when system runs under low memory + */ + public void onLowMemory() { + mTextureManager.emptyTextureQueue(false); + } + + /** + * Method called when the renderer should be paused + */ + public void onPause() { + if (DEBUG) Log.d(TAG, "onPause [" + mInstance + "]"); + mHandler.removeCallbacks(mTransitionThread); + if (mTextureManager != null) { + mTextureManager.setPause(true); + } + } + + /** + * Method called when the renderer should be resumed + */ + public void onResume() { + if (DEBUG) Log.d(TAG, "onResume [" + mInstance + "]"); + if (mTextureManager != null) { + mTextureManager.setPause(false); + } + } + + /** + * Method called when the renderer should process a touch event over the screen + * + * @param x The x coordinate + * @param y The y coordinate + */ + public void onTouch(float x , float y) { + if (mWorld != null) { + // Do user action + TouchAction touchAction = Preferences.General.getTouchAction(); + if (touchAction.compareTo(TouchAction.NONE) == 0) { + //Ignore + } else { + // Avoid to handle multiple touchs + long touchTime = System.currentTimeMillis(); + long diff = touchTime - mLastTouchTime; + mLastTouchTime = touchTime; + if (diff < TOUCH_BARRIER_TIME) { + return; + } + + // Retrieve the photo frame for its coordinates + final PhotoFrame frame = mWorld.getFrameFromCoordinates(new PointF(x, y)); + if (frame == null) { + Log.w(TAG, "No frame from coordenates"); + return; + } + + // Apply the action + if (touchAction.compareTo(TouchAction.TRANSITION) == 0) { + try { + // Select the frame with a transition + // Run in GLES's thread + mDispatcher.dispatch(new Runnable() { + @Override + public void run() { + // Select a new transition + deselectCurrentTransition(); + mWorld.selectTransition(frame); + mLastRunningTransition = System.currentTimeMillis(); + + // Now force continuously render while transition is applied + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + } + }); + + } catch (NotFoundException ex) { + Log.e(TAG, "The frame not exists " + frame.getTextureInfo().path, ex); + } + + } else if (touchAction.compareTo(TouchAction.OPEN) == 0) { + // Open the image + try { + Uri uri = getUriFromFrame(frame); + if (uri != null) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.setDataAndType(uri, "image/*"); + mContext.startActivity(intent); + } + } catch (ActivityNotFoundException ex) { + Log.e(TAG, "Open activity not found for " + frame.getTextureInfo().path, ex); + } + + } else if (touchAction.compareTo(TouchAction.SHARE) == 0) { + // Send the image + try { + Uri uri = getUriFromFrame(frame); + if (uri != null) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.setType("image/*"); + intent.putExtra(Intent.EXTRA_STREAM, uri); + mContext.startActivity(intent); + } + } catch (ActivityNotFoundException ex) { + Log.e(TAG, "Send activity not found for " + frame.getTextureInfo().path, ex); + } + } + } + } + } + + /** + * Method that returns an Uri reference from a photo frame + * + * @param frame The photo frame + * @return Uri The image uri + */ + private static Uri getUriFromFrame(final PhotoFrame frame) { + // Sanity checks + GLESTextureInfo info = frame.getTextureInfo(); + if (info == null) { + Log.e(TAG, "The frame has not a valid reference right now." + + "Touch action is not available."); + return null; + } + if (info.path == null || !info.path.isFile()) { + Log.e(TAG, "The image do not exists. Touch action is not available."); + return null; + } + + // Return the uri from the path + return Uri.fromFile(frame.getTextureInfo().path); + } + + /** + * Method that deselect the current transition + */ + /*package*/ synchronized void deselectCurrentTransition() { + mHandler.removeCallbacks(mTransitionThread); + mWorld.deselectTransition(mMVPMatrix); + mLastRunningTransition = 0; + } + + /*package*/ void scheduleOrCancelMediaScan() { + int interval = Preferences.Media.getRefreshFrecuency(); + if (interval != Preferences.Media.MEDIA_RELOAD_DISABLED) { + scheduleMediaScan(interval); + } else { + cancelMediaScan(); + } + } + + /** + * Method that schedules a new media scan + * + * @param interval The new interval + */ + private void scheduleMediaScan(int interval) { + AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + + Intent i = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); + i.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); + mMediaScanIntent = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); + + long milliseconds = Preferences.Media.getRefreshFrecuency() * 1000L; + am.set(AlarmManager.RTC, System.currentTimeMillis() + milliseconds, mMediaScanIntent); + } + + /** + * Method that cancels a pending media scan + */ + private void cancelMediaScan() { + if (mMediaScanIntent != null) { + AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + am.cancel(mMediaScanIntent); + mMediaScanIntent = null; + } + } + + /** + * Method that destroy all the internal references + */ + private void recycle() { + if (DEBUG) Log.d(TAG, "recycle [" + mInstance + "]"); + synchronized (mDrawing) { + // Remove any pending handle + if (mHandler != null && mTransitionThread != null) { + mHandler.removeCallbacks(mTransitionThread); + } + + // Delete the world + if (mWorld != null) mWorld.recycle(); + if (mTextureManager != null) mTextureManager.recycle(); + if (mOverlay != null) mOverlay.recycle(); + if (mOopsShape != null) mOopsShape.recycle(); + mWorld = null; + mTextureManager = null; + mOverlay = null; + mOopsShape = null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { + if (DEBUG) Log.d(TAG, "onSurfaceCreated [" + mInstance + "]"); + + mWidth = -1; + mHeight = -1; + mMeasuredHeight = -1; + mStatusBarHeight = 0; + + // We have a 2d (fake) scenario, disable all unnecessary tests. Deep are + // necessary for some 3d effects + GLES20.glDisable(GL10.GL_DITHER); + GLESUtil.glesCheckError("glDisable"); + GLES20.glDisable(GL10.GL_CULL_FACE); + GLESUtil.glesCheckError("glDisable"); + GLES20.glEnable(GL10.GL_DEPTH_TEST); + GLESUtil.glesCheckError("glEnable"); + GLES20.glDepthMask(false); + GLESUtil.glesCheckError("glDepthMask"); + GLES20.glDepthFunc(GLES20.GL_LEQUAL); + GLESUtil.glesCheckError("glDepthFunc"); + + // Create an effect context + if (mEffectContext != null) { + mEffectContext.release(); + } + mEffectContext = EffectContext.createWithCurrentGlContext(); + + // Create the texture manager and recycle the old one + if (mTextureManager == null) { + // Precalculate the window size for the TextureManager. In onSurfaceChanged + // the best fixed size will be set. The disposition size is simple for a better + // performance of the internal arrays + final Configuration conf = mContext.getResources().getConfiguration(); + int orientation = mContext.getResources().getConfiguration().orientation; + int w = (int) AndroidHelper.convertDpToPixel(mContext, conf.screenWidthDp); + int h = (int) AndroidHelper.convertDpToPixel(mContext, conf.screenHeightDp); + Rect dimensions = new Rect(0, 0, w, h); + int cc = (orientation == Configuration.ORIENTATION_PORTRAIT) + ? Preferences.Layout.getPortraitDisposition().size() + : Preferences.Layout.getLandscapeDisposition().size(); + + // Recycle the current texture manager and create a new one + recycle(); + mTextureManager = new TextureManager( + mContext, mHandler, mEffectContext, mDispatcher, cc, dimensions); + } else { + mTextureManager.updateEffectContext(mEffectContext); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onSurfaceChanged(GL10 glUnused, int width, int height) { + if (DEBUG) Log.d(TAG, "onSurfaceChanged [" + mInstance + "," + width + "x" + height + "]"); + + // Check if the size was changed + if (mWidth == width && mHeight == height) { + return; + } + + // Save the width and height to avoid recreate the world + mWidth = width; + mHeight = height; + mStatusBarHeight = AndroidHelper.calculateStatusBarHeight(mContext); + mMeasuredHeight = mHeight + mStatusBarHeight; + + // Calculate a better fixed size for the pictures + Rect dimensions = Utils.isTablet(mContext) + ? new Rect(0, 0, width / 2, height / 2) + : new Rect(0, 0, width / 4, height / 4); + Rect screenDimensions = new Rect(0, mStatusBarHeight, width, height); + mTextureManager.setDimensions(dimensions); + mTextureManager.setScreenDimesions(screenDimensions); + mTextureManager.setPause(false); + + // Create the wallpaper (destroy the previous) + if (mWorld != null) { + mWorld.recycle(); + } + mWorld = new PhotoPhaseWallpaperWorld(mContext, mTextureManager); + + // Create the overlay shape + final float[] vertex = { + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f + }; + mOverlay = new ColorShape(mContext, vertex, Colors.getOverlay()); + + // Create the Oops shape + mOopsShape = new OopsShape(mContext, R.string.no_pictures_oops_msg); + + // Set the viewport and the fustrum + GLES20.glViewport(0, -mStatusBarHeight, mWidth, mHeight); + GLESUtil.glesCheckError("glViewport"); + Matrix.frustumM(mProjMatrix, 0, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 2.0f); + + // Recreate the wallpaper world + try { + mWorld.recreateWorld(width, mMeasuredHeight); + } catch (GLException e) { + Log.e(TAG, "Cannot recreate the wallpaper world.", e); + } + + // Force an immediate redraw of the screen (draw thread could be in dirty mode only) + deselectCurrentTransition(); + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDrawFrame(GL10 glUnused) { + synchronized (mDrawing) { + // Set the projection, view and model + GLES20.glViewport(0, -mStatusBarHeight, mWidth, mHeight); + Matrix.setLookAtM(mVMatrix, 0, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); + Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0); + + if (mTextureManager != null) { + if (mTextureManager.getStatus() == 1 && mTextureManager.isEmpty()) { + // Advise the user and stop + drawOops(); + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + + } else { + // Draw the background + drawBackground(); + + if (mWorld != null) { + // Now draw the world (all the photo frames with effects) + mWorld.draw(mMVPMatrix); + + // Check if we have some pending transition or transition has + // exceed its timeout + if (Preferences.General.Transitions.getTransitionInterval() > 0) { + if (!mWorld.hasRunningTransition() || firedTransitionTimeout()) { + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + + // Now start a delayed thread to generate the next effect + deselectCurrentTransition(); + mLastRunningTransition = 0; + mHandler.postDelayed(mTransitionThread, + Preferences.General.Transitions.getTransitionInterval()); + } + } else { + // Just display the initial frames and never make transitions + if (!mWorld.hasRunningTransition() || firedTransitionTimeout()) { + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + } + } + + // Draw the overlay + drawOverlay(); + } + } + } + + } + } + + /** + * Check whether the transition has exceed the timeout + * + * @return boolean if the transition has exceed the timeout + */ + private boolean firedTransitionTimeout() { + long now = System.currentTimeMillis(); + long diff = now - mLastRunningTransition; + return mLastRunningTransition != 0 && diff > Transition.MAX_TRANSTION_TIME; + } + + /** + * Method that draws the background of the wallpaper + */ + private static void drawBackground() { + GLColor bg = Colors.getBackground(); + GLES20.glClearColor(bg.r, bg.g, bg.b, bg.a); + GLESUtil.glesCheckError("glClearColor"); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + GLESUtil.glesCheckError("glClear"); + } + + /** + * Method that draws the overlay of the wallpaper + */ + private void drawOverlay() { + if (mOverlay != null) { + mOverlay.setAlpha(Preferences.General.getWallpaperDim() / 100.0f); + mOverlay.draw(mMVPMatrix); + } + } + + /** + * Method that draws the oops message + */ + private void drawOops() { + if (mOopsShape != null) { + mOopsShape.draw(mMVPMatrix); + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java new file mode 100644 index 0000000..625212c --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.app.ActivityManager; +import android.app.WallpaperManager; +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.opengl.GLSurfaceView.Renderer; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.ViewConfiguration; + +import org.cyanogenmod.wallpapers.photophase.GLESWallpaperService.GLESEngineListener; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider; + +import java.util.ArrayList; +import java.util.List; + + +/** + * The PhotoPhase Live Wallpaper service. + */ +public class PhotoPhaseWallpaper + extends GLES20WallpaperService implements GLESEngineListener { + + private static final String TAG = "PhotoPhaseWallpaper"; + + private static final boolean DEBUG = false; + + private List mRenderers; + private PhotoPhaseWallpaperEngine mEngine; + + private boolean mPreserveEGLContext; + + // List of the current top activities. Tap should be ignored when this acitivities are + // in the foreground + static final String[] TOP_ACTIVITIES = {"com.android.internal.app.ChooserActivity"}; + + /** + * {@inheritDoc} + */ + @Override + public void onCreate() { + if (DEBUG) Log.d(TAG, "onCreate"); + super.onCreate(); + + // Load the configuration + mPreserveEGLContext = getResources().getBoolean(R.bool.config_preserve_egl_context); + mRenderers = new ArrayList(); + + // Instance the application + PreferencesProvider.reload(this); + Colors.register(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + if (DEBUG) Log.d(TAG, "onDestroy"); + super.onDestroy(); + for (PhotoPhaseRenderer renderer : mRenderers) { + renderer.onDestroy(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Engine onCreateEngine() { + mEngine = new PhotoPhaseWallpaperEngine(this); + return mEngine; + } + + /** + * A wallpaper engine implementation using GLES. + */ + class PhotoPhaseWallpaperEngine extends GLES20WallpaperService.GLES20Engine { + + private final Handler mHandler; + /*package*/ final ActivityManager mActivityManager; + + /** + * Constructor of PhotoPhaseWallpaperEngine + * + * @param wallpaper The wallpaper service reference + */ + PhotoPhaseWallpaperEngine(PhotoPhaseWallpaper wallpaper) { + super(); + mHandler = new Handler(); + mActivityManager = (ActivityManager)getApplication().getSystemService(ACTIVITY_SERVICE); + setOffsetNotificationsEnabled(false); + setTouchEventsEnabled(false); + setGLESEngineListener(wallpaper); + setWallpaperGLSurfaceView(new PhotoPhaseWallpaperGLSurfaceView(wallpaper)); + setPauseOnPreview(true); + } + + /** + * Out custom GLSurfaceView class to let us access all events stuff. + */ + class PhotoPhaseWallpaperGLSurfaceView extends WallpaperGLSurfaceView { + /** + * The constructor of PhotoPhaseWallpaperGLSurfaceView. + * + * @param context The current context + */ + public PhotoPhaseWallpaperGLSurfaceView(Context context) { + super(context); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Bundle onCommand(final String action, final int x, final int y, final int z, + final Bundle extras, final boolean resultRequested) { + if (action.compareTo(WallpaperManager.COMMAND_TAP) == 0) { + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + // Only if the wallpaper is visible after a long press and + // not in preview mode + if (isVisible() && !isPreview()) { + List taskInfo = + mActivityManager.getRunningTasks(1); + String topActivity = taskInfo.get(0).topActivity.getClassName(); + for (String activity : TOP_ACTIVITIES) { + if (activity.compareTo(topActivity) == 0) { + // Ignore tap event + return; + } + } + + // Pass the x and y position to the renderer + ((PhotoPhaseRenderer)getRenderer()).onTouch(x, y); + } + } + }, ViewConfiguration.getLongPressTimeout() + 100L); + } + return super.onCommand(action, x, y, z, extras, resultRequested); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + Log.i(TAG, "onLowMemory"); + for (PhotoPhaseRenderer renderer : mRenderers) { + // Pause the wallpaper and destroy the cached textures + renderer.onPause(); + renderer.onLowMemory(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onInitializeEGLView(GLSurfaceView view) { + if (DEBUG) Log.d(TAG, "onInitializeEGLView"); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroyEGLView(GLSurfaceView view, Renderer renderer) { + if (DEBUG) Log.d(TAG, "onDestroyEGLView" + renderer); + mRenderers.remove(renderer); + ((PhotoPhaseRenderer)renderer).onPause(); + ((PhotoPhaseRenderer)renderer).onDestroy(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onEGLViewInitialized(GLSurfaceView view) { + view.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); + view.setPreserveEGLContextOnPause(mPreserveEGLContext); + } + + /** + * {@inheritDoc} + */ + @Override + public void onPause(Renderer renderer) { + if (DEBUG) Log.d(TAG, "onPause: " + renderer); + ((PhotoPhaseRenderer)renderer).onPause(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onResume(Renderer renderer) { + if (DEBUG) Log.d(TAG, "onResume: " + renderer); + ((PhotoPhaseRenderer)renderer).onResume(); + } + + /** + * {@inheritDoc} + */ + @Override + public Renderer getNewRenderer(GLSurfaceView view) { + if (DEBUG) Log.d(TAG, "getNewRenderer()"); + PhotoPhaseRenderer renderer = new PhotoPhaseRenderer(this, new GLESSurfaceDispatcher(view)); + renderer.onCreate(); + mRenderers.add(renderer); + if (DEBUG) Log.d(TAG, "renderer" + renderer); + return renderer; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaperWorld.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaperWorld.java new file mode 100644 index 0000000..0310b58 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaperWorld.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PointF; +import android.graphics.RectF; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.transitions.Transition; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class that represents the wallpapers with all its photo frames. + */ +public class PhotoPhaseWallpaperWorld { + + private static final String TAG = "PhotoPhaseWallpaperWorld"; + + private static final boolean DEBUG = false; + + // The frame padding + private static final int PHOTO_FRAME_PADDING = 2; + + private final Context mContext; + private final TextureManager mTextureManager; + + private List mPhotoFrames; + private List mTransitions; + private final List mUnusedTransitions; + + private List mTransitionsQueue; + private List mUsedTransitionsQueue; + private int mCurrent; + + private int mWidth; + private int mHeight; + + private boolean mRecycled; + + /** + * Constructor PhotoPhaseWallpaperWorld + * + * @param ctx The current context + * @param textureManager The texture manager + */ + public PhotoPhaseWallpaperWorld( + Context ctx, TextureManager textureManager) { + super(); + mContext = ctx; + mTextureManager = textureManager; + mCurrent = -1; + mUnusedTransitions = new ArrayList(); + mRecycled = false; + } + + /** + * Method that returns an unused transition for the type of transition + * + * @param type The type of transition + * @return Transition The unused transition + */ + private Transition getUnusedTransition(TRANSITIONS type) { + for (Transition transition : mUnusedTransitions) { + if (transition.getType().compareTo(type) == 0) { + mUnusedTransitions.remove(transition); + return transition; + } + } + return null; + } + + /** + * Method that returns or creates a transition for the type of transition + * + * @param type The type of transition + * @param frame The frame which the effect will be applied to + * @return Transition The unused transition + */ + private Transition getOrCreateTransition(TRANSITIONS type, PhotoFrame frame) { + Transition transition = getUnusedTransition(type); + if (transition == null) { + transition = Transitions.createTransition(mContext, mTextureManager, type, frame); + } + transition.reset(); + return transition; + } + + /** + * Method that ensures the transitions queue + */ + private void ensureTransitionsQueue() { + if (mTransitionsQueue.isEmpty()) { + mTransitionsQueue.addAll(mUsedTransitionsQueue); + mUsedTransitionsQueue.clear(); + } + } + + /** + * Method that selects a transition and assign it to a random photo frame. + */ + public void selectRandomTransition() { + // Ensure queue + ensureTransitionsQueue(); + + // Get a random frame to which apply the transition + int item = Utils.getNextRandom(0, mTransitionsQueue.size() - 1); + int pos = mTransitionsQueue.remove(item).intValue(); + mUsedTransitionsQueue.add(Integer.valueOf(pos)); + PhotoFrame frame = mPhotoFrames.get(pos); + + // Select the transition + selectTransition(frame, pos); + } + + /** + * Method that selects a transition and assign it to the photo frame. + * + * @param frame The photo frame to select + */ + public void selectTransition(PhotoFrame frame) { + // Ensure queue + ensureTransitionsQueue(); + + // Get a random frame to which apply the transition + int pos = mPhotoFrames.indexOf(frame); + if (pos == -1) { + return; + } + mTransitionsQueue.remove(Integer.valueOf(pos)); + mUsedTransitionsQueue.add(Integer.valueOf(pos)); + + // Select the transition + selectTransition(frame, pos); + } + + /** + * Method that selects a transition and assign it to a photo frame. + * + * @param frame The frame to select + * @param pos The position + */ + private void selectTransition(PhotoFrame frame, int pos) { + // Create or use a transition + Transition transition = null; + boolean isSelectable = false; + while (transition == null || !isSelectable) { + boolean isRandom = Preferences.General.Transitions.getTransitionTypes().length > 1; + TRANSITIONS type = Transitions.getNextTypeOfTransition(frame); + transition = getOrCreateTransition(type, frame); + isSelectable = transition.isSelectable(frame); + if (!isSelectable) { + mUnusedTransitions.add(transition); + if (!isRandom) { + // If is not possible to select a valid transition then select a swap + // transition (this one doesn't relies on any selection) + transition = getOrCreateTransition(TRANSITIONS.SWAP, frame); + isSelectable = true; + } + } + } + mTransitions.set(pos, transition); + transition.select(frame); + mCurrent = pos; + } + + /** + * Method that deselect the current transition. + * + * @param matrix The model-view-projection matrix + */ + public void deselectTransition(float[] matrix) { + if (mCurrent != -1 && mCurrent < mTransitions.size()) { + // Retrieve the finally target + Transition currentTransition = mTransitions.get(mCurrent); + PhotoFrame currentTarget = currentTransition.getTarget(); + PhotoFrame finalTarget = currentTransition.getTransitionTarget(); + mUnusedTransitions.add(currentTransition); + + if (finalTarget != null) { + Transition transition = getOrCreateTransition(TRANSITIONS.NO_TRANSITION, finalTarget); + mTransitions.set(mCurrent, transition); + + currentTarget.recycle(); + mPhotoFrames.set(mCurrent, finalTarget); + transition.select(finalTarget); + + // Draw the transition once + transition.apply(matrix); + } + mCurrent = -1; + } + } + + /** + * Method that removes all internal references. + */ + public void recycle() { + // Destroy the previous world + if (mTransitions != null) { + int cc = mTransitions.size() - 1; + for (int i = cc; i >= 0; i--) { + Transition transition = mTransitions.get(i); + transition.recycle(); + mTransitions.remove(i); + } + } + mCurrent = -1; + if (mUnusedTransitions != null) { + int cc = mUnusedTransitions.size() - 1; + for (int i = cc; i >= 0; i--) { + Transition transition = mUnusedTransitions.get(i); + transition.recycle(); + mUnusedTransitions.remove(i); + } + } + if (mTransitionsQueue != null) { + mTransitionsQueue.clear(); + } + if (mUsedTransitionsQueue != null) { + mUsedTransitionsQueue.clear(); + } + mRecycled = true; + } + + /** + * Method that returns if there are any transition running in the world. + * + * @return boolean If there are any transition running in the world + */ + public boolean hasRunningTransition() { + if (mTransitions != null) { + for (Transition transition : mTransitions) { + if (transition.isRunning()) { + return true; + } + } + } + return false; + } + + /** + * Method that creates and fills the world with {@link PhotoFrame} objects. + * + * @param w The new width dimension + * @param h The new height dimension + */ + public synchronized void recreateWorld(int w, int h) { + if (DEBUG) Log.d(TAG, "Recreating the world. New surface: " + w + "x" + h); + + // Destroy the previous world + if (mRecycled) { + recycle(); + mRecycled = false; + } + + // Save the new dimensions of the wallpaper + mWidth = w; + mHeight = h; + + // Calculate the new world + int orientation = mContext.getResources().getConfiguration().orientation; + boolean portrait = orientation == Configuration.ORIENTATION_PORTRAIT; + int cols = portrait ? Preferences.Layout.getCols() : Preferences.Layout.getRows(); + int rows = portrait ? Preferences.Layout.getRows() : Preferences.Layout.getCols(); + float cellw = 2.0f / cols; + float cellh = 2.0f / rows; + List dispositions = portrait + ? Preferences.Layout.getPortraitDisposition() + : Preferences.Layout.getLandscapeDisposition(); + if (DEBUG) Log.d(TAG, + "Dispositions: " + dispositions.size() + " | " + String.valueOf(dispositions)); + mPhotoFrames = new ArrayList(dispositions.size()); + mTransitions = new ArrayList(dispositions.size()); + mTransitionsQueue = new ArrayList(dispositions.size()); + mUsedTransitionsQueue = new ArrayList(dispositions.size()); + int i = 0; + for (Disposition disposition : dispositions) { + // Create the photo frame + float[] frameVertices = getVerticesFromDisposition(disposition, cellw, cellh); + float[] photoVertices = getFramePadding(frameVertices, portrait ? w : h, portrait ? h : w); + PhotoFrame frame = + new PhotoFrame( + mContext, + mTextureManager, + frameVertices, + photoVertices, + Colors.getBackground()); + mPhotoFrames.add(frame); + + // Assign a null transition to the photo frame + Transition transition = getOrCreateTransition(TRANSITIONS.NO_TRANSITION, frame); + transition.select(frame); + mTransitions.add(transition); + + mTransitionsQueue.add(Integer.valueOf(i)); + i++; + } + } + + /** + * Method that returns a photo frame from a coordinates in screen + * + * @param coordinates The coordinates + * @return The photo frame reference or null if none found + */ + public PhotoFrame getFrameFromCoordinates(PointF coordinates) { + // Translate pixels coordinates to GLES coordinates + float tx = ((coordinates.x * 2) / mWidth) - 1; + float ty = (((coordinates.y * 2) / mHeight) - 1) * -1; + + // Locate the frame + for (PhotoFrame frame : mPhotoFrames) { + RectF vertex = Utils.rectFromVertex(frame.getPhotoVertex()); + if (vertex.left < tx && vertex.right > tx && vertex.top > ty && vertex.bottom < ty) { + return frame; + } + } + return null; + } + + /** + * Method that draws all the photo frames. + * + * @param matrix The model-view-projection matrix + */ + public void draw(float[] matrix) { + // Apply every transition + if (mTransitions != null) { + // First draw the non-running transitions; then the active ones + for (Transition transition : mTransitions) { + if (!transition.isRunning()) { + transition.apply(matrix); + } + } + for (Transition transition : mTransitions) { + if (transition.isRunning()) { + transition.apply(matrix); + } + } + } + } + + /** + * Method that returns a coordinates per vertex array from a disposition + * + * @param disposition The source disposition + * @param cellw The cell width based on the surface + * @param cellh The cell height based on the surface + * @return float[] The coordinates per vertex array + */ + private static float[] getVerticesFromDisposition( + Disposition disposition, float cellw, float cellh) { + return new float[] + { + // bottom left + -1.0f + (disposition.x * cellw), + 1.0f - ((disposition.y * cellh) + (disposition.h * cellh)), + + // bottom right + -1.0f + ((disposition.x * cellw) + (disposition.w * cellw)), + 1.0f - ((disposition.y * cellh) + (disposition.h * cellh)), + + // top left + -1.0f + (disposition.x * cellw), + 1.0f - (disposition.y * cellh), + + // top right + -1.0f + ((disposition.x * cellw) + (disposition.w * cellw)), + 1.0f - (disposition.y * cellh) + }; + } + + /** + * Method that applies a padding to the frame + * + * @param texCoords The source coordinates + * @param screenWidth The screen width + * @param screenHeight The screen height + * @return float[] The new coordinates + */ + private static float[] getFramePadding(float[] coords, int screenWidth, int screenHeight) { + float[] paddingCoords = new float[coords.length]; + System.arraycopy(coords, 0, paddingCoords, 0, coords.length); + final float pxw = (1 / (float)screenWidth) * PHOTO_FRAME_PADDING; + final float pxh = (1 / (float)screenHeight) * PHOTO_FRAME_PADDING; + paddingCoords[0] += pxw; + paddingCoords[1] += pxh; + paddingCoords[2] -= pxw; + paddingCoords[3] += pxh; + paddingCoords[4] += pxw; + paddingCoords[5] -= pxh; + paddingCoords[6] -= pxw; + paddingCoords[7] -= pxh; + return paddingCoords; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/StorageHelper.java b/src/org/cyanogenmod/wallpapers/photophase/StorageHelper.java new file mode 100644 index 0000000..9d73313 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/StorageHelper.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * A helper class to deal with android storage + */ +public final class StorageHelper { + + private static final String TAG = "StorageHelper"; + + private static final String EXTERNAL_REGEXP = "(?i).*vold.*(fuse|vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*"; + + /** + * Method that returns all the external mounts + * + * @return List All the external mounts + */ + public static List getExternalMounts() { + final List out = new ArrayList(); + + // Execute the mount command to list mounts + final StringBuilder sb = new StringBuilder(); + try { + final Process process = + new ProcessBuilder().command("mount") + .redirectErrorStream(true).start(); + process.waitFor(); + final InputStream is = process.getInputStream(); + byte[] buffer = new byte[1024]; + int read = 0; + while ((read = is.read(buffer, 0, 1024)) != -1) { + sb.append(new String(buffer, 0, read)); + } + is.close(); + } catch (IOException ioex) { + Log.e(TAG, "Failed to list external mounts", ioex); + } catch (InterruptedException iex) { + Log.e(TAG, "Failed to list external mounts", iex); + } + + // Parse the output + final String[] lines = sb.toString().split("\n"); + for (String line : lines) { + if (!line.toLowerCase(Locale.US).contains("asec")) { + if (line.matches(EXTERNAL_REGEXP)) { + String[] parts = line.split(" "); + for (String part : parts) { + if (part.startsWith("/")) { + if (!part.toLowerCase(Locale.US).contains("vold")) { + out.add(part); + } + } + } + } + } + } + return out; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java new file mode 100644 index 0000000..6c8de05 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.RectF; +import android.media.ThumbnailUtils; +import android.media.effect.Effect; +import android.media.effect.EffectContext; +import android.opengl.GLES20; +import android.os.Handler; +import android.util.Log; +import android.widget.Toast; + +import org.cyanogenmod.wallpapers.photophase.FixedQueue.EmptyQueueException; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; +import org.cyanogenmod.wallpapers.photophase.MediaPictureDiscoverer.OnMediaPictureDiscoveredListener; +import org.cyanogenmod.wallpapers.photophase.effects.Effects; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A class that manages the acquisition of new textures. + */ +public class TextureManager implements OnMediaPictureDiscoveredListener { + + private static final String TAG = "TextureManager"; + + private static final int QUEUE_SIZE = 3; + + final Context mContext; + final Handler mHandler; + Effects mEffects; + final Object mSync; + final List mPendingRequests; + final FixedQueue mQueue = new FixedQueue(QUEUE_SIZE); + BackgroundPictureLoaderThread mBackgroundTask; + /*protected*/ final MediaPictureDiscoverer mPictureDiscoverer; + + /*package*/ Rect mScreenDimensions; + /*package*/ Rect mDimensions; + + final GLESSurfaceDispatcher mDispatcher; + + // The status of the texture manager: + // 0 - Loading + // 1 - Loaded + // 2 - Error + private byte mStatus; + + /** + * A private runnable that will run in the GLThread + */ + /*package*/ class PictureDispatcher implements Runnable { + File mImage; + GLESTextureInfo ti = null; + final Object mWait = new Object(); + + /** + * {@inheritDoc} + */ + @Override + public void run() { + try { + Effect effect = null; + synchronized (mEffects) { + effect = mEffects.getNextEffect(); + } + + // Load and bind to the GLES context. The effect is applied when the image + // is associated to the destination target (only if aspect ratio will be applied) + if (!Preferences.General.isFixAspectRatio()) { + ti = GLESUtil.loadTexture( + mImage, mDimensions, effect, mDimensions, false); + } else { + ti = GLESUtil.loadTexture(mImage, mDimensions, null, null, false); + ti.effect = effect; + } + + synchronized (mSync) { + // Notify the new images to all pending frames + if (mPendingRequests.size() > 0) { + // Invalid textures are also reported, so requestor can handle it + TextureRequestor requestor = mPendingRequests.remove(0); + fixAspectRatio(requestor, ti); + requestor.setTextureHandle(ti); + + // Clean up memory + if (ti.bitmap != null) { + ti.bitmap.recycle(); + ti.bitmap = null; + } + + } else { + // Add to the queue (only valid textures) + if (ti.handle > 0) { + mQueue.insert(ti); + } + } + } + + } catch (Throwable e) { + Log.e(TAG, "Something was wrong loading the texture: " + + mImage.getAbsolutePath(), e); + + } finally { + // Notify that we have a new image + synchronized (mWait) { + mWait.notify(); + } + } + } + } + + /** + * Constructor of TextureManager + * + * @param ctx The current context + * @param effectCtx The current effect context + * @param dispatcher The GLES dispatcher + * @param requestors The number of requestors + * @param screenDimensions The screen dimensions + */ + public TextureManager(final Context ctx, final Handler handler, final EffectContext effectCtx, + GLESSurfaceDispatcher dispatcher, int requestors, Rect screenDimensions) { + super(); + mContext = ctx; + mHandler = handler; + mEffects = new Effects(effectCtx); + mDispatcher = dispatcher; + mScreenDimensions = screenDimensions; + mDimensions = screenDimensions; // For now, use the screen dimensions as the preferred dimensions for bitmaps + mSync = new Object(); + mPendingRequests = new ArrayList(requestors); + mPictureDiscoverer = new MediaPictureDiscoverer(mContext, this); + + // Run the media discovery thread + mBackgroundTask = new BackgroundPictureLoaderThread(); + mBackgroundTask.mTaskPaused = false; + reloadMedia(false); + } + + /** + * Method that update the effect context if the EGL context change + * + * @param effectCtx The new effect context + */ + protected void updateEffectContext(final EffectContext effectCtx) { + synchronized (mEffects) { + if (mEffects != null) { + mEffects.release(); + mEffects = null; + } + mEffects = new Effects(effectCtx); + } + emptyTextureQueue(true); + } + + /** + * Method that allow to change the preferred dimensions of the bitmaps loaded + * + * @param dimensions The new dimensions + */ + public void setDimensions(Rect dimensions) { + mDimensions = dimensions; + } + + /** + * Method that allow to change the screen dimensions + * + * @param dimensions The new dimensions + */ + public void setScreenDimesions(Rect dimensions) { + mScreenDimensions = dimensions; + } + + /** + * Method that returns if the texture manager is paused + * + * @return boolean whether the texture manager is paused + */ + public boolean isPaused() { + return mBackgroundTask != null && mBackgroundTask.mTaskPaused; + } + + /** + * Method that pauses the internal threads + * + * @param pause If the thread is paused (true) or resumed (false) + */ + public synchronized void setPause(boolean pause) { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mTaskPaused = pause; + if (!mBackgroundTask.mTaskPaused) { + mBackgroundTask.mLoadSync.notify(); + } + } + } + + /** + * Method that reload the references of media pictures + * + * @param userRequest If the request was generated by the user + */ + void reloadMedia(final boolean userRequest) { + Log.d(TAG, "Reload media picture data"); + // Discovery new media + // GLThread doesn't run in the UI thread and AsyncThread can't create a + // valid handler in ICS (it's fixed in JB+) so we force to run the async + // thread in a valid UI thread + mHandler.post(new Runnable() { + @Override + public void run() { + mPictureDiscoverer.discover(userRequest); + } + }); + } + + /** + * Method that request a new picture for the {@link TextureRequestor} + * + * @param requestor The requestor of the texture + */ + public void request(TextureRequestor requestor) { + synchronized (mSync) { + try { + GLESTextureInfo ti = mQueue.remove(); + fixAspectRatio(requestor, ti); + requestor.setTextureHandle(ti); + + // Clean up memory + if (ti.bitmap != null) { + ti.bitmap.recycle(); + ti.bitmap = null; + } + + } catch (EmptyQueueException eqex) { + // Add to queue of pending request to be notified when + // we have a new bitmap in the queue + mPendingRequests.add(requestor); + } + } + + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mLoadSync.notify(); + } + } + + /** + * Method that removes all the textures from the queue + * + * @param reload Forces a reload of the queue + */ + public void emptyTextureQueue(boolean reload) { + synchronized (mSync) { + // Recycle the textures + try { + List all = mQueue.removeAll(); + for (GLESTextureInfo info : all) { + if (GLES20.glIsTexture(info.handle)) { + int[] textures = new int[] {info.handle}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + // Return the bitmap + info.bitmap.recycle(); + info.bitmap = null; + } + } catch (EmptyQueueException eqex) { + // Ignore + } + + // Remove all pictures in the queue + try { + mQueue.removeAll(); + } catch (EmptyQueueException ex) { + // Ignore + } + + // Reload the queue + if (reload) { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.resetAvailableImages(); + mBackgroundTask.mLoadSync.notify(); + } + } + } + } + + /** + * Method that cancels a request did it previously. + * + * @param requestor The requestor of the texture + */ + public void cancelRequest(TextureRequestor requestor) { + synchronized (mSync) { + if (mPendingRequests.contains(requestor)) { + mPendingRequests.remove(requestor); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onStartMediaDiscovered(boolean userRequest) { + // No images but thread should start here to received partial data + this.mStatus = 0; // Loading + if (mBackgroundTask != null) { + mBackgroundTask.setAvailableImages(new File[]{}); + if (!mBackgroundTask.mRun) { + mBackgroundTask.start(); + } else { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mLoadSync.notify(); + } + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onPartialMediaDiscovered(File[] images, boolean userRequest) { + if (mBackgroundTask != null) { + mBackgroundTask.setPartialAvailableImages(images); + } + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("boxing") + public void onEndMediaDiscovered(File[] images, boolean userRequest) { + // Now we have the paths of the images to use. Notify to the thread to + // load pictures in background + if (mBackgroundTask != null) { + mBackgroundTask.setAvailableImages(images); + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mLoadSync.notify(); + } + this.mStatus = 1; // Loaded + + // Audit + int found = images == null ? 0 : images.length; + Log.d(TAG, "Media picture data reloaded: " + found + " images found."); + if (userRequest) { + CharSequence msg = + String.format(mContext.getResources().getQuantityText( + R.plurals.msg_media_reload_complete, found).toString(), found); + Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); + } + } else { + this.mStatus = 2; // Error + } + } + + /** + * Method that destroy the references of this class + */ + public void recycle() { + // Destroy the media discovery task + mPictureDiscoverer.recycle(); + mEffects.release(); + + // Destroy the background task + if (mBackgroundTask != null) { + mBackgroundTask.mRun = false; + try { + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.interrupt(); + } + } catch (Exception e) { + // Ignore + } + } + mBackgroundTask = null; + } + + + /** + * Returns the status of the texture manager + * + * @return byte The status + */ + public byte getStatus() { + return mStatus; + } + + /** + * Returns if the texture manager is empty + * + * @return boolean If the texture manager is empty + */ + public boolean isEmpty() { + return mBackgroundTask != null && mBackgroundTask.mEmpty; + } + + /** + * Method that fix the aspect ratio of a image to fit the destination target + * + * @param request The requestor target + * @param ti The original texture information + * @param effect The effect to apply to the destination picture + */ + /*package*/ void fixAspectRatio(TextureRequestor requestor, GLESTextureInfo ti) { + // Check if we have to apply any correction to the image + if (Preferences.General.isFixAspectRatio()) { + // Transform requestor dimensions to screen dimensions + RectF dimens = requestor.getRequestorDimensions(); + Rect pixels = new Rect( + 0, + 0, + (int)(mScreenDimensions.width() * dimens.width() / 2), + (int)(mScreenDimensions.height() * dimens.height() / 2)); + + // Create a thumbnail of the image + Bitmap thumb = ThumbnailUtils.extractThumbnail( + ti.bitmap, + pixels.width(), + pixels.height(), + ThumbnailUtils.OPTIONS_RECYCLE_INPUT); + GLESTextureInfo dst = GLESUtil.loadTexture(thumb, ti.effect, pixels); + + // Destroy references + int[] textures = new int[]{ti.handle}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + if (ti.bitmap != null) { + ti.bitmap.recycle(); + ti.bitmap = null; + } + + // Swap references + ti.bitmap = dst.bitmap; + ti.handle = dst.handle; + ti.effect = null; + } + } + + /** + * An internal thread to load pictures in background + */ + private class BackgroundPictureLoaderThread extends Thread { + + final Object mLoadSync = new Object(); + boolean mRun; + boolean mTaskPaused; + + /*package*/ boolean mEmpty; + private final List mNewImages; + private final List mUsedImages; + + /** + * Constructor of BackgroundPictureLoaderThread. + */ + public BackgroundPictureLoaderThread() { + super(); + mNewImages = new ArrayList(); + mUsedImages = new ArrayList(); + } + + /** + * Method that sets the current available images. + * + * @param images The current images + */ + public void setAvailableImages(File[] images) { + synchronized (mLoadSync) { + mNewImages.clear(); + mNewImages.addAll(Arrays.asList(images)); + + // Retain used images + int count = mUsedImages.size() - 1; + for (int i = count; i >= 0; i--) { + File image = mUsedImages.get(i); + if (!mNewImages.contains(image)) { + mUsedImages.remove(image); + } else { + mNewImages.remove(image); + } + } + + mEmpty = images.length == 0; + } + } + + /** + * Method that adds some available images. + * + * @param images The current images + */ + public void setPartialAvailableImages(File[] images) { + synchronized (mLoadSync) { + mNewImages.addAll(Arrays.asList(images)); + mEmpty = images.length == 0; + } + } + + /** + * Method that reset the current available images queue. + */ + public void resetAvailableImages() { + synchronized (mLoadSync) { + mNewImages.addAll(mUsedImages); + mUsedImages.clear(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void run() { + mRun = true; + while (mRun) { + // Check if we need to load more images + while (!mTaskPaused && TextureManager.this.mQueue.items() < TextureManager.this.mQueue.size()) { + File image = null; + synchronized (mLoadSync) { + // Swap arrays if needed + if (mNewImages.size() == 0) { + mNewImages.addAll(mUsedImages); + mUsedImages.clear(); + } + if (mNewImages.size() == 0) { + if (!mEmpty) { + reloadMedia(false); + } + break; + } + + // Extract a random image + int low = 0; + int high = mNewImages.size() - 1; + image = mNewImages.remove(Utils.getNextRandom(low, high)); + } + + // Run commands in the GLThread + if (!mRun) break; + PictureDispatcher pd = new PictureDispatcher(); + pd.mImage = image; + mDispatcher.dispatch(pd); + + // Wait until the texture is loaded + try { + synchronized (pd.mWait) { + pd.mWait.wait(); + } + } catch (Exception e) { + // Ignore + } + + // Add to used images + mUsedImages.add(image); + } + + // Wait for new request + synchronized (mLoadSync) { + try { + mLoadSync.wait(); + } catch (Exception e) { + // Ignore + } + } + } + } + + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/TextureRequestor.java b/src/org/cyanogenmod/wallpapers/photophase/TextureRequestor.java new file mode 100644 index 0000000..9c62eb2 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/TextureRequestor.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase; + +import android.graphics.RectF; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; + +/** + * An interface that defines an object as able to request textures. + */ +public interface TextureRequestor { + + /** + * Method that set the texture handle requested. + * + * @param ti The texture information + */ + void setTextureHandle(GLESTextureInfo ti); + + /** + * Method that returns the dimension of the requestor + * + * @return RectF The dimensions of the requestor + */ + RectF getRequestorDimensions(); +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java b/src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java new file mode 100644 index 0000000..228f827 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.animations; + +import android.view.View; +import android.view.View.OnClickListener; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; + +import org.cyanogenmod.wallpapers.photophase.model.Album; +import org.cyanogenmod.wallpapers.photophase.widgets.AlbumInfo; +import org.cyanogenmod.wallpapers.photophase.widgets.AlbumPictures; +import org.cyanogenmod.wallpapers.photophase.widgets.AlbumPictures.CallbacksListener; + +/** + * A class that manages a flip 3d effect of an album + */ +public class AlbumsFlip3dAnimationController { + + private static final int DURATION = 200; + + View mFront; + View mBack; + boolean mFrontFace; + + /** + * Constructor of AlbumsFlip3dAnimationController + * + * @param front The front view + * @param back The back view + */ + public AlbumsFlip3dAnimationController(AlbumInfo front, AlbumPictures back) { + super(); + mFront = front; + mBack = back; + mBack.setVisibility(View.GONE); + mFrontFace = true; + } + + /** + * Method that register the controller + */ + public void register() { + getFrontView().setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + getBackView().setVisibility(View.INVISIBLE); + applyAnimation(false); + } + }); + ((AlbumPictures)getBackView()).addCallBackListener(new CallbacksListener() { + @Override + public void onBackButtonClick(View v) { + getBackView().setVisibility(View.INVISIBLE); + applyAnimation(true); + } + + @Override + public void onSelectionChanged(Album album) { + // Ignore + } + }); + } + + /** + * Method that unregister the controller + */ + public void unregister() { + getFrontView().setOnClickListener(null); + getBackView().setOnClickListener(null); + } + + /** + * Method that reset the controller + */ + public void reset() { + if (!mFrontFace) { + applyAnimation(true); + } + } + + /** + * Method that applies the animation over the views + * + * @param inverse Applies the inverse animation + */ + /*package*/ void applyAnimation(boolean inverse) { + applyTransformation(getFrontView(), 0, 90 * (inverse ? -1 : 1), true); + } + + /*package*/ void applyTransformation(final View v, float start, float end, final boolean step1) { + final float centerX = v.getWidth() / 2.0f; + final float centerY = v.getHeight() / 2.0f; + + final Flip3dAnimation anim = new Flip3dAnimation(start, end, centerX, centerY); + anim.setDuration(DURATION); + anim.setFillAfter(true); + anim.setInterpolator(new AccelerateInterpolator()); + + anim.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + if (!step1) { + getBackView().setVisibility(View.VISIBLE); + } + getFrontView().setOnClickListener(null); + getBackView().setOnClickListener(null); + } + + @Override + public void onAnimationRepeat(Animation animation) { + // Ignore + } + + @Override + public void onAnimationEnd(Animation animation) { + getFrontView().setAnimation(null); + getBackView().setAnimation(null); + if (step1) { + getFrontView().setVisibility(View.INVISIBLE); + applyTransformation(getBackView(), -90 * (!mFrontFace ? -1 : 1), 0, false); + } else { + mFrontFace = !mFrontFace; + getBackView().setVisibility(View.GONE); + if (mFrontFace) { + getFrontView().setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + getBackView().setVisibility(View.INVISIBLE); + applyAnimation(false); + } + }); + } else { + ((AlbumPictures)getFrontView()).onShow(); + } + } + } + }); + v.startAnimation(anim); + } + + /*package*/ View getFrontView() { + return mFrontFace ? mFront : mBack; + } + + /*package*/ View getBackView() { + return !mFrontFace ? mFront : mBack; + } +} + diff --git a/src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java b/src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java new file mode 100644 index 0000000..a0e52aa --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.animations; + +import android.animation.IntEvaluator; +import android.view.View; +import android.view.ViewGroup; + +/** + * A class with helpful evaluators + */ +public class Evaluators { + + /** + * A width evaluator + */ + public static class WidthEvaluator extends IntEvaluator { + private View mView; + + /** + * Constructor of WidthEvaluator + * + * @param v The view + */ + public WidthEvaluator(View v) { + super(); + mView = v; + } + + @Override + public Integer evaluate(float fraction, Integer startValue, Integer endValue) { + Integer num = super.evaluate(fraction, startValue, endValue); + ViewGroup.LayoutParams params = mView.getLayoutParams(); + params.width = num.intValue(); + mView.setLayoutParams(params); + return num; + } + } + + /** + * A height evaluator + */ + public static class HeightEvaluator extends IntEvaluator { + private View mView; + + /** + * Constructor of HeightEvaluator + * + * @param v The view + */ + public HeightEvaluator(View v) { + super(); + mView = v; + } + + @Override + public Integer evaluate(float fraction, Integer startValue, Integer endValue) { + Integer num = super.evaluate(fraction, startValue, endValue); + ViewGroup.LayoutParams params = mView.getLayoutParams(); + params.height = num.intValue(); + mView.setLayoutParams(params); + return num; + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimation.java b/src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimation.java new file mode 100644 index 0000000..0196dc5 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimation.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.animations; + +import android.graphics.Camera; +import android.graphics.Matrix; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +/** + * A 3d flit animation + */ +public class Flip3dAnimation extends Animation { + + private final float mFromDegrees; + private final float mToDegrees; + private final float mCenterX; + private final float mCenterY; + private Camera mCamera; + + /** + * Constructor of Flip3dAnimation + * + * @param fromDegrees From origin degrees + * @param toDegrees To destination degrees + * @param centerX The center horizontal position + * @param centerY The center vertical position + */ + public Flip3dAnimation(float fromDegrees, float toDegrees, float centerX, float centerY) { + mFromDegrees = fromDegrees; + mToDegrees = toDegrees; + mCenterX = centerX; + mCenterY = centerY; + } + + /** + * {@inheritDoc} + */ + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + mCamera = new Camera(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + final float fromDegrees = mFromDegrees; + float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); + + final float centerX = mCenterX; + final float centerY = mCenterY; + final Camera camera = mCamera; + + final Matrix matrix = t.getMatrix(); + + camera.save(); + camera.rotateY(degrees); + camera.getMatrix(matrix); + camera.restore(); + + matrix.preTranslate(-centerX, -centerY); + matrix.postTranslate(centerX, centerY); + } + +} + diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/BlurEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/BlurEffect.java new file mode 100644 index 0000000..465cb99 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/BlurEffect.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A blur effect
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class BlurEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " float step = 0.02;\n" + + " vec3 c1 = texture2D(tex_sampler, vec2(v_texcoord.s - step, v_texcoord.t - step)).bgr;\n" + + " vec3 c2 = texture2D(tex_sampler, vec2(v_texcoord.s + step, v_texcoord.t + step)).bgr;\n" + + " vec3 c3 = texture2D(tex_sampler, vec2(v_texcoord.s - step, v_texcoord.t + step)).bgr;\n" + + " vec3 c4 = texture2D(tex_sampler, vec2(v_texcoord.s + step, v_texcoord.t - step)).bgr;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = (c1 + c2 + c3 + c4) / 4.0;\n" + + "}"; + + /** + * Constructor of BlurEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public BlurEffect(EffectContext ctx, String name) { + super(ctx, BlurEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/Effects.java b/src/org/cyanogenmod/wallpapers/photophase/effects/Effects.java new file mode 100644 index 0000000..0ab3f6c --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/Effects.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.effects; + +import android.graphics.Color; +import android.media.effect.Effect; +import android.media.effect.EffectContext; +import android.media.effect.EffectFactory; + +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A class that manages all the supported effects + */ +public class Effects { + + /** + * Enumeration of the supported effects + */ + public enum EFFECTS { + /** + * @see PhotoPhaseEffectFactory#EFFECT_NULL + */ + NO_EFFECT, + /** + * @see EffectFactory#EFFECT_AUTOFIX + */ + AUTOFIX, + /** + * @see PhotoPhaseEffectFactory#EFFECT_BLUR + */ + BLUR, + /** + * @see EffectFactory#EFFECT_CROSSPROCESS + */ + CROSSPROCESS, + /** + * @see EffectFactory#EFFECT_DOCUMENTARY + */ + DOCUMENTARY, + /** + * @see EffectFactory#EFFECT_DUOTONE + */ + DUOTONE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_EMBOSS + */ + EMBOSS, + /** + * @see EffectFactory#EFFECT_FISHEYE + */ + FISHEYE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_GLOW + */ + GLOW, + /** + * @see EffectFactory#EFFECT_GRAIN + */ + GRAIN, + /** + * @see EffectFactory#EFFECT_GRAYSCALE + */ + GRAYSCALE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_HALFTONE + */ + HALFTONE, + /** + * @see EffectFactory#EFFECT_LOMOISH + */ + LOMOISH, + /** + * @see PhotoPhaseEffectFactory#EFFECT_MIRROR + */ + MIRROR, + /** + * @see EffectFactory#EFFECT_NEGATIVE + */ + NEGATIVE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_OUTLINE + */ + OUTLINE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_PIXELATE + */ + PIXELATE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_POPART + */ + POPART, + /** + * @see EffectFactory#EFFECT_POSTERIZE + */ + POSTERIZE, + /** + * @see EffectFactory#EFFECT_SATURATE + */ + SATURATE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_SCANLINES + */ + SCANLINES, + /** + * @see EffectFactory#EFFECT_SEPIA + */ + SEPIA, + /** + * @see EffectFactory#EFFECT_TEMPERATURE + */ + TEMPERATURE, + /** + * @see EffectFactory#EFFECT_TINT + */ + TINT, + /** + * @see EffectFactory#EFFECT_VIGNETTE + */ + VIGNETTE; + + /** + * Method that returns the effect from its ordinal position + * + * @param ordinal The ordinal position + * @return EFFECTS The effect or null if wasn't found + */ + public static EFFECTS fromOrdinal(int ordinal) { + for (EFFECTS effect : EFFECTS.values()) { + if (effect.ordinal() == ordinal) { + return effect; + } + } + return null; + } + } + + private final Map mCachedEffects; + private final EffectContext mEffectContext; + + /** + * Constructor of Effects + * + * @param effectContext The current effect context + */ + public Effects(EffectContext effectContext) { + super(); + mCachedEffects = new HashMap(); + mEffectContext = effectContext; + } + + /** + * Method that that release the cached data + */ + public void release() { + if (mCachedEffects != null) { + for (Effect effect : mCachedEffects.values()) { + effect.release(); + } + mCachedEffects.clear(); + } + } + + /** + * Method that return the next effect to use with the picture. + * + * @return Effect The next effect to use or null if no need to apply any effect + */ + @SuppressWarnings("boxing") + public Effect getNextEffect() { + // Get a new instance of a effect factory + EffectFactory effectFactory = mEffectContext.getFactory(); + Effect effect = null; + + // Get an effect based on the user preference + List effects = Arrays.asList(Preferences.General.Effects.getEffectTypes()); + EFFECTS nextEffect = null; + if (effects.size() > 0) { + int low = 0; + int high = effects.size() - 1; + int pos = Utils.getNextRandom(low, high); + nextEffect = effects.get(pos); + } + if (nextEffect == null) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_NULL)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_NULL); + mCachedEffects.put(nextEffect, effect); + } + return effect; + } + + // Has a cached effect? + if (mCachedEffects.containsKey(nextEffect)) { + return mCachedEffects.get(nextEffect); + } + + // Select the effect if is available + if (nextEffect.compareTo(EFFECTS.AUTOFIX) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_AUTOFIX)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_AUTOFIX); + effect.setParameter("scale", 0.5f); + } + } else if (nextEffect.compareTo(EFFECTS.BLUR) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_BLUR)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_BLUR); + } + } else if (nextEffect.compareTo(EFFECTS.CROSSPROCESS) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_CROSSPROCESS)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_CROSSPROCESS); + } + } else if (nextEffect.compareTo(EFFECTS.DOCUMENTARY) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_DOCUMENTARY)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_DOCUMENTARY); + } + } else if (nextEffect.compareTo(EFFECTS.DUOTONE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_DUOTONE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); + effect.setParameter("first_color", Color.parseColor("#FF8CACFF")); + effect.setParameter("second_color", Color.WHITE); + } + } else if (nextEffect.compareTo(EFFECTS.EMBOSS) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_EMBOSS)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_EMBOSS); + } + } else if (nextEffect.compareTo(EFFECTS.FISHEYE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_FISHEYE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_FISHEYE); + effect.setParameter("scale", 1.0f); + } + } else if (nextEffect.compareTo(EFFECTS.GLOW) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_GLOW)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_GLOW); + } + } else if (nextEffect.compareTo(EFFECTS.GRAIN) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_GRAIN)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAIN); + effect.setParameter("strength", 1.0f); + } + } else if (nextEffect.compareTo(EFFECTS.GRAYSCALE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_GRAYSCALE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAYSCALE); + } + } else if (nextEffect.compareTo(EFFECTS.HALFTONE) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_HALFTONE)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_HALFTONE); + effect.setParameter("strength", 8.0f); + } + } else if (nextEffect.compareTo(EFFECTS.MIRROR) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_MIRROR)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_MIRROR); + } + } else if (nextEffect.compareTo(EFFECTS.LOMOISH) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_LOMOISH)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_LOMOISH); + } + } else if (nextEffect.compareTo(EFFECTS.NEGATIVE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_NEGATIVE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_NEGATIVE); + } + } else if (nextEffect.compareTo(EFFECTS.OUTLINE) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_OUTLINE)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_OUTLINE); + } + } else if (nextEffect.compareTo(EFFECTS.PIXELATE) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_PIXELATE)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_PIXELATE); + effect.setParameter("strength", 8.0f); + } + } else if (nextEffect.compareTo(EFFECTS.POPART) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_POPART)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_POPART); + } + } else if (nextEffect.compareTo(EFFECTS.POSTERIZE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_POSTERIZE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_POSTERIZE); + } + } else if (nextEffect.compareTo(EFFECTS.SATURATE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_SATURATE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_SATURATE); + effect.setParameter("scale", .5f); + } + } else if (nextEffect.compareTo(EFFECTS.SCANLINES) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_SCANLINES)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_SCANLINES); + } + } else if (nextEffect.compareTo(EFFECTS.SEPIA) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_SEPIA)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_SEPIA); + } + } else if (nextEffect.compareTo(EFFECTS.TEMPERATURE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_TEMPERATURE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_TEMPERATURE); + effect.setParameter("scale", .9f); + } + } else if (nextEffect.compareTo(EFFECTS.TINT) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_TINT)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_TINT); + } + } else if (nextEffect.compareTo(EFFECTS.VIGNETTE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_VIGNETTE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_VIGNETTE); + effect.setParameter("scale", .5f); + } + } + + // Instead of not to apply any effect, just use one null effect to follow the same + // effect model. This allow to use the same height when Effect.apply is applied for all + // the frames + if (effect == null && EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_NULL)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_NULL); + nextEffect = EFFECTS.NO_EFFECT; + } + + // Cache the effects + mCachedEffects.put(nextEffect, effect); + return effect; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/EmbossEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/EmbossEffect.java new file mode 100644 index 0000000..691ef39 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/EmbossEffect.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * An emboss effect
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class EmbossEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "void main(void)\n" + + "{\n" + + " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + + " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + + " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 rr = -4.0 * t1 - 4.0 * t2 - 4.0 * t4 + 12.0 * t5;\n" + + " float y = (rr.r + rr.g + rr.b) / 3.0;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = vec3(y, y, y) + 0.3;\n" + + "}"; + + /** + * Constructor of EmbossEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public EmbossEffect(EffectContext ctx, String name) { + super(ctx, EmbossEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/GlowEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/GlowEffect.java new file mode 100644 index 0000000..8520e09 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/GlowEffect.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A glow effect
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class GlowEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "void main(void)\n" + + "{\n" + + " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + + " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + + " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 xx= t1 + 2.0*t2 + t3 - t7 - 2.0*t8 - t9;\n" + + " vec3 yy = t1 - t3 + 2.0*t4 - 2.0*t6 + t7 - t9;\n" + + " vec3 rr = sqrt(xx * xx + yy * yy);\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = rr * 2.0 * t5;\n" + + "}"; + + /** + * Constructor of GlowEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public GlowEffect(EffectContext ctx, String name) { + super(ctx, GlowEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/HalftoneEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/HalftoneEffect.java new file mode 100644 index 0000000..07da491 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/HalftoneEffect.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; +import android.opengl.GLES20; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; + +/** + * A halftone effect
+ * + * + * + * + * + * + * + *
Parameter nameMeaningValid values
strengthThe halftone strength.Positive float (>0). Higher numbers produce smallest points.
+ */ +public class HalftoneEffect extends PhotoPhaseEffect { + + private static final String TAG = "HalftoneEffect"; + + private static final String STRENGTH_PARAMETER = "strength"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "uniform float strength;\n" + + "void main(void)\n" + + "{\n" + + " float offx = floor(v_texcoord.s / (strength * step_w));\n" + + " float offy = floor(v_texcoord.t / (strength * step_h));\n" + + " vec3 res = texture2D(tex_sampler, vec2(offx * strength * step_w , offy * strength * step_h)).bgr;\n" + + " vec2 prc = fract(v_texcoord.st / vec2(strength * step_w, strength * step_h));\n" + + " vec2 pw = pow(abs(prc - 0.5), vec2(2.0));\n" + + " float rs = pow(0.45, 2.0);\n" + + " float gr = smoothstep(rs - 0.1, rs + 0.1, pw.x + pw.y);\n" + + " float y = (res.r + res.g + res.b) / 3.0; \n" + + " vec3 ra = res / y;\n" + + " float ls = 0.3;\n" + + " float lb = ceil(y / ls);\n" + + " float lf = ls * lb + 0.3;\n" + + " res = lf * res;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = mix(res, vec3(0.1, 0.1, 0.1), gr);\n" + + "}"; + + private float mStrength = 16.0f; + private int mStepsHandle; + + /** + * Constructor of HalftoneEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public HalftoneEffect(EffectContext ctx, String name) { + super(ctx, HalftoneEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + + /** + * {@inheritDoc} + */ + @Override + void init(String vertexShader, String fragmentShader) { + super.init(vertexShader, fragmentShader); + + // Parameters + mStepsHandle = GLES20.glGetUniformLocation(mProgram, "strength"); + GLESUtil.glesCheckError("glGetUniformLocation"); + } + + /** + * {@inheritDoc} + */ + @Override + void applyParameters() { + // Set parameters + GLES20.glUniform1f(mStepsHandle, mStrength); + GLESUtil.glesCheckError("glUniform1f"); + } + + /** + * {@inheritDoc} + */ + @Override + public void setParameter(String parameterKey, Object value) { + if (parameterKey.compareTo(STRENGTH_PARAMETER) == 0) { + try { + float strength = Float.parseFloat(value.toString()); + if (strength <= 0) { + Log.w(TAG, "strength parameter must be >= 0"); + return; + } + mStrength = strength; + } catch (NumberFormatException ex) { + // Ignore + } + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/MirrorEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/MirrorEffect.java new file mode 100644 index 0000000..27233d2 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/MirrorEffect.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A mirror effect
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class MirrorEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " vec2 off = vec2(0.0, 0.0);\n" + + " if (v_texcoord.t > 0.5) {\n" + + " off.t = 1.0 - v_texcoord.t;\n" + + " off.s = v_texcoord.s;\n" + + " } else {\n" + + " off = v_texcoord;\n" + + " }\n" + + " vec3 color = texture2D(tex_sampler, vec2(off)).bgr;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = color;\n" + + "}"; + + /** + * Constructor of MirrorEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public MirrorEffect(EffectContext ctx, String name) { + super(ctx, MirrorEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/NullEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/NullEffect.java new file mode 100644 index 0000000..5a8b520 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/NullEffect.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * This effect only copies the source texture to the destination texture.
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class NullEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" + + "}"; + + /** + * Constructor of NullEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public NullEffect(EffectContext ctx, String name) { + super(ctx, NullEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + + /** + * {@inheritDoc} + */ + @Override + void apply(int inputTexId) { + // Nothing to draw + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/OutlineEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/OutlineEffect.java new file mode 100644 index 0000000..8dc7854 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/OutlineEffect.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * An outline effect (highlight edges)
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class OutlineEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "void main(void)\n" + + "{\n" + + " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + + " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + + " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 xx= t1 + 2.0*t2 + t3 - t7 - 2.0*t8 - t9;\n" + + " vec3 yy = t1 - t3 + 2.0*t4 - 2.0*t6 + t7 - t9;\n" + + " vec3 rr = sqrt(xx * xx + yy * yy);\n" + + " float y = (rr.r + rr.g + rr.b) / 3.0;\n" + + " if (y > 0.2)\n" + + " rr = vec3(0.0, 0.0, 0.0);\n" + + " else\n" + + " rr = vec3(1.0, 1.0, 1.0);\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = rr;\n" + + "}"; + + /** + * Constructor of OutlineEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public OutlineEffect(EffectContext ctx, String name) { + super(ctx, OutlineEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffect.java new file mode 100644 index 0000000..2fd0e2b --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffect.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.Effect; +import android.media.effect.EffectContext; +import android.media.effect.EffectFactory; +import android.opengl.GLES20; +import android.opengl.GLUtils; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * An abstract class definition for all the PhotoPhase custom effects + */ +public abstract class PhotoPhaseEffect extends Effect { + + private static final int FLOAT_SIZE_BYTES = 4; + + private static final String MCA_IDENTITY_EFFECT = "IdentityEffect"; + + static final String VERTEX_SHADER = + "attribute vec4 a_position;\n" + + "attribute vec2 a_texcoord;\n" + + "varying vec2 v_texcoord;\n" + + "void main() {\n" + + " gl_Position = vec4(a_position.xy, 0.0, 1.0);\n" + + " gl_Position = sign(gl_Position);\n" + + " v_texcoord = a_texcoord;\n" + + "}\n"; + + private static final float[] TEX_VERTICES = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; + private static final float[] POS_VERTICES = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; + + private final int GL_STATE_FBO = 0; + private final int GL_STATE_PROGRAM = 1; + private final int GL_STATE_ARRAYBUFFER = 2; + private final int GL_STATE_COUNT = 3; + + private int[] mOldState = new int[GL_STATE_COUNT]; + + private final EffectContext mEffectContext; + private final String mName; + + private Effect mIdentityEffect; + + int mProgram; + int mTexSamplerHandle; + int mTexCoordHandle; + int mPosCoordHandle; + + FloatBuffer mTexVertices; + FloatBuffer mPosVertices; + + /** + * An abstract constructor of Effect to follow the rules + * defined by {@link EffectFactory}. + * + * @param ctx The effect context + * @param name The effect name + */ + public PhotoPhaseEffect(EffectContext ctx, String name) { + super(); + mEffectContext = ctx; + mName = name; + + // Stand on MCA identity effect for the initialization work + EffectFactory effectFactory = mEffectContext.getFactory(); + mIdentityEffect = effectFactory.createEffect(MCA_IDENTITY_EFFECT); + } + + /** + * Method that initializes the effect + */ + void init(String vertexShader, String fragmentShader) { + // Create program + mProgram = GLESUtil.createProgram(vertexShader, fragmentShader); + + // Bind attributes and uniforms + mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram, "tex_sampler"); + GLESUtil.glesCheckError("glGetUniformLocation"); + mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position"); + GLESUtil.glesCheckError("glGetAttribLocation"); + + // Setup coordinate buffers + mTexVertices = ByteBuffer.allocateDirect( + TEX_VERTICES.length * FLOAT_SIZE_BYTES) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mTexVertices.put(TEX_VERTICES).position(0); + mPosVertices = ByteBuffer.allocateDirect( + POS_VERTICES.length * FLOAT_SIZE_BYTES) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + mPosVertices.put(POS_VERTICES).position(0); + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return mName; + } + + /** + * Method that returns the effect context + * + * @return EffectContext The effect context + */ + public EffectContext getEffectContext() { + return mEffectContext; + } + + /** + * {@inheritDoc} + */ + @Override + public final synchronized void apply(int inputTexId, int width, int height, int outputTexId) { + // Save the GLES state + saveGLState(); + + try { + // Create a framebuffer object and call the effect apply method to draw the effect + int[] fb = new int[1]; + GLES20.glGenFramebuffers(1, fb, 0); + GLESUtil.glesCheckError("glGenFramebuffers"); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[0]); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Render on the whole framebuffer + GLES20.glViewport(0, 0, width, height); + GLESUtil.glesCheckError("glViewport"); + + // Create a new output texture (Use the MCA identity to clone the input to the output) + mIdentityEffect.apply(inputTexId, width, height, outputTexId); + + // Create the framebuffer + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20. GL_TEXTURE_2D, outputTexId, 0); + GLESUtil.glesCheckError("glFramebufferTexture2D"); + + // Check if the buffer was built successfully + if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { + // Something when wrong. Throw an exception + GLESUtil.glesCheckError("glCheckFramebufferStatus"); + int error = GLES20.glGetError(); + throw new android.opengl.GLException(error, GLUtils.getEGLErrorString(error)); + } + + // Apply the effect + apply(inputTexId); + + } finally { + // Restore the GLES state + restoreGLState(); + } + + } + + /** + * {@inheritDoc} + */ + @Override + public void setParameter(String parameterKey, Object value) { + // Ignore + } + + /** + * {@inheritDoc} + */ + @Override + public void release() { + if (GLES20.glIsProgram(mProgram)) { + GLES20.glDeleteProgram(mProgram); + GLESUtil.glesCheckError("glDeleteProgram"); + } + mTexVertices = null; + mPosVertices = null; + } + + /** + * Method that applies the effect. + * + * @param inputTexId The input texture + */ + void apply(int inputTexId) { + // Use our shader program + GLES20.glUseProgram(mProgram); + GLESUtil.glesCheckError("glUseProgram"); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the vertex attributes + GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTexVertices); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTexCoordHandle); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mPosVertices); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPosCoordHandle); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Set parameters + applyParameters(); + + // Set the input texture + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, inputTexId); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTexSamplerHandle, 0); + GLESUtil.glesCheckError("glUniform1i"); + + // Draw + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + GLESUtil.glesCheckError("glClearColor"); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + GLESUtil.glesCheckError("glClear"); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawArrays"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mTexCoordHandle); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mPosCoordHandle); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Method that applies the parameters of the effect. + */ + void applyParameters() { + // Do nothing + } + + + private final void saveGLState() { + GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, mOldState, GL_STATE_FBO); + GLES20.glGetIntegerv(GLES20.GL_CURRENT_PROGRAM, mOldState, GL_STATE_PROGRAM); + GLES20.glGetIntegerv(GLES20.GL_ARRAY_BUFFER_BINDING, mOldState, GL_STATE_ARRAYBUFFER); + } + + private final void restoreGLState() { + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mOldState[GL_STATE_FBO]); + GLES20.glUseProgram(mOldState[GL_STATE_PROGRAM]); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mOldState[GL_STATE_ARRAYBUFFER]); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java b/src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java new file mode 100644 index 0000000..450bb47 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.effects; + +/** + * A class that defines the own PhotoPhase's effects implementation. This class follows the + * rules of the MCA aosp library. + */ +public class PhotoPhaseEffectFactory { + + /** + *

Applies a blur effect to the image.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_BLUR = "org.cyanogenmod.wallpapers.photophase.effects.BlurEffect"; + + /** + *

Applies an emboss effect to the image.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_EMBOSS = "org.cyanogenmod.wallpapers.photophase.effects.EmbossEffect"; + + /** + *

Applies a glow effect to the image.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_GLOW = "org.cyanogenmod.wallpapers.photophase.effects.GlowEffect"; + + /** + *

Applies a halftone effect to the image.

+ *

Available parameters:

+ * + * + * + * + * + * + * + *
Parameter nameMeaningValid values
strengthThe halftone steps multiplier.Positive float (>0). Higher numbers produce smallest points
+ */ + public static final String EFFECT_HALFTONE = "org.cyanogenmod.wallpapers.photophase.effects.HalftoneEffect"; + + /** + *

Applies a mirror effect to the image.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_MIRROR = "org.cyanogenmod.wallpapers.photophase.effects.MirrorEffect"; + + /** + *

Doesn't apply any effect.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_NULL = "org.cyanogenmod.wallpapers.photophase.effects.NullEffect"; + + /** + *

Applies an outline effect to the image.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_OUTLINE = "org.cyanogenmod.wallpapers.photophase.effects.OutlineEffect"; + + /** + *

Applies a pixelate effect to the image.

+ *

Available parameters:

+ * + * + * + * + * + * + * + *
Parameter nameMeaningValid values
strengthThe pixelate steps multiplier.Positive float (>0). Higher numbers produce more pixelation.
+ */ + public static final String EFFECT_PIXELATE = "org.cyanogenmod.wallpapers.photophase.effects.PixelateEffect"; + + /** + *

Applies a pop art (Warhol) effect to the image.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_POPART = "org.cyanogenmod.wallpapers.photophase.effects.PopArtEffect"; + + /** + *

Applies a TV scan line effect to the image.

+ *

Available parameters:

+ * + * + *
Parameter nameMeaningValid values
+ */ + public static final String EFFECT_SCANLINES = "org.cyanogenmod.wallpapers.photophase.effects.ScanlinesEffect"; +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/PixelateEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/PixelateEffect.java new file mode 100644 index 0000000..fe37d4c --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/PixelateEffect.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; +import android.opengl.GLES20; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; + +/** + * A pixelate effect
+ * + * + * + * + * + * + * + *
Parameter nameMeaningValid values
strengthThe pixelate strength.Positive float (>0). Higher numbers produce more pixelation.
+ */ +public class PixelateEffect extends PhotoPhaseEffect { + + private static final String TAG = "PixelateEffect"; + + private static final String STRENGTH_PARAMETER = "strength"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "uniform float strength;\n" + + "void main(void)\n" + + "{\n" + + " float offx = floor(v_texcoord.s / (strength * step_w));\n" + + " float offy = floor(v_texcoord.t / (strength * step_h));\n" + + " vec3 res = texture2D(tex_sampler, vec2(offx * strength * step_w , offy * strength * step_h)).bgr;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = res;\n" + + "}"; + + private float mStrength = 8.0f; + private int mStepsHandle; + + /** + * Constructor of PixelateEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public PixelateEffect(EffectContext ctx, String name) { + super(ctx, PixelateEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + + /** + * {@inheritDoc} + */ + @Override + void init(String vertexShader, String fragmentShader) { + super.init(vertexShader, fragmentShader); + + // Parameters + mStepsHandle = GLES20.glGetUniformLocation(mProgram, "strength"); + GLESUtil.glesCheckError("glGetUniformLocation"); + } + + /** + * {@inheritDoc} + */ + @Override + void applyParameters() { + // Set parameters + GLES20.glUniform1f(mStepsHandle, mStrength); + GLESUtil.glesCheckError("glUniform1f"); + } + + /** + * {@inheritDoc} + */ + @Override + public void setParameter(String parameterKey, Object value) { + if (parameterKey.compareTo(STRENGTH_PARAMETER) == 0) { + try { + float strength = Float.parseFloat(value.toString()); + if (strength <= 0) { + Log.w(TAG, "strength parameter must be >= 0"); + return; + } + mStrength = strength; + } catch (NumberFormatException ex) { + // Ignore + } + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/PopArtEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/PopArtEffect.java new file mode 100644 index 0000000..df2b18c --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/PopArtEffect.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A pop art (Warhol) effect
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class PopArtEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " vec3 col = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " float y = 0.3 *col.r + 0.59 * col.g + 0.11 * col.b;\n" + + " y = y < 0.3 ? 0.0 : (y < 0.6 ? 0.5 : 1.0);\n" + + " if (y == 0.5)\n" + + " col = vec3(0.8, 0.0, 0.0);\n" + + " else if (y == 1.0)\n" + + " col = vec3(0.9, 0.9, 0.0);\n" + + " else\n" + + " col = vec3(0.0, 0.0, 0.0);\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = col;\n" + + "}"; + + /** + * Constructor of PopArtEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public PopArtEffect(EffectContext ctx, String name) { + super(ctx, PopArtEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/effects/ScanlinesEffect.java b/src/org/cyanogenmod/wallpapers/photophase/effects/ScanlinesEffect.java new file mode 100644 index 0000000..cd78307 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/effects/ScanlinesEffect.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ +// +// Based on the shaders of Max Maischein of App-VideoMixer: +// http://cpansearch.perl.org/src/CORION/App-VideoMixer-0.02/filters/scanlines.glsl +// + +package org.cyanogenmod.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A TV scanline effect
+ * + * + *
Parameter nameMeaningValid values
+ */ +public class ScanlinesEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "uniform float offset;\n" + + "float frequency = 83.0;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " float global_pos = (v_texcoord.y + offset) * frequency;\n" + + " float wave_pos = cos((fract(global_pos) - 0.5)*3.14);\n" + + " vec4 pel = texture2D(tex_sampler, v_texcoord);\n" + + " gl_FragColor = mix(vec4(0,0,0,0), pel, wave_pos);\n" + + "}"; + + /** + * Constructor of ScanlinesEffect. + * + * @param ctx The effect context + * @param name The effect name + */ + public ScanlinesEffect(EffectContext ctx, String name) { + super(ctx, ScanlinesEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/model/Album.java b/src/org/cyanogenmod/wallpapers/photophase/model/Album.java new file mode 100644 index 0000000..83f067d --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/model/Album.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.model; + +import android.graphics.drawable.Drawable; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class that represents an album + */ +public class Album implements Comparable, Cloneable { + + private Drawable mIcon; + private String mPath; + private String mName; + private String mDate; + private boolean mSelected; + private List mItems; + private List mSelectedItems; + + public Drawable getIcon() { + return mIcon; + } + + public void setIcon(Drawable icon) { + this.mIcon = icon; + } + + public String getPath() { + return mPath; + } + + public void setPath(String path) { + this.mPath = path; + } + + public String getName() { + return mName; + } + + public void setName(String name) { + this.mName = name; + } + + public String getDate() { + return mDate; + } + + public void setDate(String date) { + this.mDate = date; + } + + public boolean isSelected() { + return mSelected; + } + + public void setSelected(boolean selected) { + this.mSelected = selected; + } + + public List getItems() { + return mItems; + } + + public void setItems(List items) { + this.mItems = items; + } + + public List getSelectedItems() { + return mSelectedItems; + } + + public void setSelectedItems(List selectedItems) { + this.mSelectedItems = selectedItems; + } + + @Override + public int compareTo(Album another) { + return mPath.compareTo(another.mPath); + } + + @Override + public Object clone() { + Album album = new Album(); + album.mIcon = mIcon; + album.mPath = mPath; + album.mName = mName; + album.mDate = mDate; + album.mItems = new ArrayList(mItems); + album.mSelectedItems = new ArrayList(mSelectedItems); + album.mSelected = mSelected; + return album; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java b/src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java new file mode 100644 index 0000000..35cc0e9 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.model; + +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; + +/** + * A class that holds a {@link PhotoFrame} disposition. + */ +public class Disposition implements Comparable { + /** + * Column + */ + public int x; + /** + * Row + */ + public int y; + /** + * Columns width + */ + public int w; + /** + * Rows height + */ + public int h; + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + h; + result = prime * result + w; + result = prime * result + x; + result = prime * result + y; + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Disposition other = (Disposition) obj; + if (h != other.h) + return false; + if (w != other.w) + return false; + if (x != other.x) + return false; + if (y != other.y) + return false; + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Disposition [x=" + x + ", y=" + y + ", w=" + w + ", h=" + h + "]"; + } + + /** + * {@inheritDoc} + */ + @Override + public int compareTo(Disposition another) { + if (x == another.x && y == another.y && w == another.w && h == another.h) { + return 0; + } + if (x < another.x) { + return -1; + } + if (x > another.x) { + return 1; + } + if (y < another.y) { + return -1; + } + if (y > another.y) { + return 1; + } + if (w < another.w) { + return -1; + } + if (w > another.w) { + return 1; + } + if (h < another.h) { + return -1; + } + return 1; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java new file mode 100644 index 0000000..6e41308 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.AsyncTask.Status; +import android.os.Bundle; +import android.preference.PreferenceFragment; +import android.provider.MediaStore; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.animations.AlbumsFlip3dAnimationController; +import org.cyanogenmod.wallpapers.photophase.model.Album; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.widgets.AlbumInfo; +import org.cyanogenmod.wallpapers.photophase.widgets.AlbumPictures; +import org.cyanogenmod.wallpapers.photophase.widgets.CardLayout; +import org.cyanogenmod.wallpapers.photophase.widgets.VerticalEndlessScroller; +import org.cyanogenmod.wallpapers.photophase.widgets.VerticalEndlessScroller.OnEndScrollListener; + +import java.io.File; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * A fragment class for select the picture that will be displayed on the wallpaper + */ +public class ChoosePicturesFragment extends PreferenceFragment implements OnEndScrollListener { + + private static final String TAG = "ChoosePicturesFragment"; + + private static final boolean DEBUG = false; + + private static final int AMOUNT_OF_ADDED_STEPS = 5; + + private final AsyncTask mAlbumsLoaderTask = new AsyncTask() { + /** + * {@inheritDoc} + */ + @Override + protected Void doInBackground(Void... params) { + // Query all the external content and classify the pictures in albums and load the cards + DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); + Album album = null; + unregister(); + Cursor c = mContentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + new String[]{ MediaStore.MediaColumns.DATA }, + null, + null, + MediaStore.MediaColumns.DATA); + if (c != null) { + try { + long start = System.currentTimeMillis(); + if (DEBUG) Log.v(TAG, "Media library:"); + while (c.moveToNext()) { + // Only valid files (those i can read) + String p = c.getString(0); + if (DEBUG) Log.v(TAG, "\t" + p); + if (p != null) { + File f = new File(p); + if (f.isFile() && f.canRead()) { + File path = f.getParentFile(); + String name = path.getName(); + if (album == null || album.getPath().compareTo(path.getAbsolutePath()) != 0) { + if (album != null) { + mAlbums.add(album); + mOriginalAlbums.add((Album)album.clone()); + } + album = new Album(); + album.setPath(path.getAbsolutePath()); + album.setName(name); + album.setDate(df.format(new Date(path.lastModified()))); + album.setSelected(isSelectedItem(album.getPath())); + album.setItems(new ArrayList()); + album.setSelectedItems(new ArrayList()); + } + album.getItems().add(f.getAbsolutePath()); + if (isSelectedItem(f.getAbsolutePath())) { + album.getSelectedItems().add(f.getAbsolutePath()); + } + } + } + } + + // Add the last album + if (album != null) { + mAlbums.add(album); + mOriginalAlbums.add((Album)album.clone()); + } + long end = System.currentTimeMillis(); + if (DEBUG) Log.v(TAG, "Library loaded in " + (end - start) + " miliseconds"); + + } finally { + c.close(); + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(Void result) { + Resources res = getActivity().getResources(); + int size = (int)(res.getDimension(R.dimen.album_size) + + res.getDimension(R.dimen.small_margin)); + mScroller.setEndPadding(size * AMOUNT_OF_ADDED_STEPS); + int height = mScroller.getMeasuredHeight(); + int steps = (height / size) + AMOUNT_OF_ADDED_STEPS; + + // Create the views an force a redraw the items + mAlbumViews = new ArrayList(mAlbums.size()); + for (Album item : mAlbums) { + mAlbumViews.add(createAlbumView(item)); + } + doEndScroll(steps, true); + + // We not need Hardware acceleration anymore (no more animations) + // Disable drawing cache + mScroller.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + mScroller.setDrawingCacheEnabled(false); + mScroller.setSmoothScrollingEnabled(true); + } + }; + + /*package*/ ContentResolver mContentResolver; + + /*package*/ List mAlbums; + /*package*/ List mAlbumViews; + /*package*/ List mOriginalAlbums; + /*package*/ List mAnimationControllers; + + /*package*/ Set mSelectedAlbums; + private Set mOriginalSelectedAlbums; + + /*package*/ VerticalEndlessScroller mScroller; + private CardLayout mAlbumsPanel; + + /*package*/ boolean mSelectionChanged; + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mContentResolver = getActivity().getContentResolver(); + + // Create an empty album + mAlbums = new ArrayList(); + mOriginalAlbums = new ArrayList(); + mAnimationControllers = new ArrayList(); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + // Load the albums user selection + mOriginalSelectedAlbums = Preferences.Media.getSelectedMedia(); + mSelectedAlbums = new HashSet(mOriginalSelectedAlbums); + mSelectionChanged = false; + + setHasOptionsMenu(true); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + super.onDestroy(); + if (mAlbumsLoaderTask.getStatus().compareTo(Status.PENDING) == 0) { + mAlbumsLoaderTask.cancel(true); + } + unbindDrawables(mAlbumsPanel); + unregister(); + + // Notify that the settings was changed + Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); + if (mSelectionChanged) { + intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); + intent.putExtra(PreferencesProvider.EXTRA_FLAG_EMPTY_TEXTURE_QUEUE, Boolean.TRUE); + intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); + } + getActivity().sendBroadcast(intent); + } + + /*package*/ void unregister() { + mAlbums.clear(); + mOriginalAlbums.clear(); + mAnimationControllers.clear(); + } + + /** + * Method that unbind all the drawables for a view + * + * @param view The root view + */ + private void unbindDrawables(View view) { + if (view.getBackground() != null) { + view.getBackground().setCallback(null); + } + if (view instanceof ViewGroup) { + for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { + unbindDrawables(((ViewGroup) view).getChildAt(i)); + } + ((ViewGroup) view).removeAllViews(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // Inflate the layout for this fragment + mScroller = + (VerticalEndlessScroller)inflater.inflate( + R.layout.choose_picture_fragment, container, false); + mScroller.setCallback(this); + mAlbumsPanel = (CardLayout)mScroller.findViewById(R.id.albums_panel); + + // Force Hardware acceleration + if (!mScroller.isHardwareAccelerated()) { + mScroller.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + if (!mAlbumsPanel.isHardwareAccelerated()) { + mAlbumsPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + + // Load the albums + mAlbumsLoaderTask.execute(); + + return mScroller; + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.albums, menu); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.mnu_ok: + getActivity().finish(); + return true; + case R.id.mnu_restore: + restoreData(); + return true; + case R.id.mnu_invert_all: + invertAll(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * Method that restores the albums to its original state + */ + private void restoreData() { + // Restore and the albums the selection + mSelectedAlbums = new HashSet(mOriginalSelectedAlbums); + mAlbums.clear(); + for (Album album : mOriginalAlbums) { + mAlbums.add((Album)album.clone()); + } + + // Update all the views + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + updateAll(); + } + + /** + * Method that inverts the selection of all the albums + */ + private void invertAll() { + // Restore and the albums the selection + mSelectedAlbums = new HashSet(); + for (Album album : mAlbums) { + album.setSelected(!album.isSelected()); + album.setSelectedItems(new ArrayList()); + if (album.isSelected()) { + mSelectedAlbums.add(album.getPath()); + } else { + mSelectedAlbums.addAll(album.getSelectedItems()); + } + } + + // Update all the views + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + updateAll(); + } + + /** + * Method that updates the current state of all the albums + */ + private void updateAll() { + // Update every view (albums and views should have the same size) + int count = mAlbumsPanel.getChildCount(); + for (int i = 0; i < count; i++) { + Album album = mAlbums.get(i); + View v = mAlbumsPanel.getChildAt(i); + AlbumInfo albumInfo = (AlbumInfo)v.findViewById(R.id.album_info); + AlbumPictures albumPictures = (AlbumPictures)v.findViewById(R.id.album_pictures); + albumInfo.updateView(album); + albumPictures.updateView(album, true); + } + + // Restore the preference + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + mSelectionChanged = true; + + // Restore all the animations states + for (AlbumsFlip3dAnimationController controller : mAnimationControllers) { + controller.reset(); + } + } + + /** + * Method that creates a new album to the card layout + * + * @param album The album to create + * @return View The view create + */ + View createAlbumView(final Album album) { + LayoutInflater li = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + final View albumView = li.inflate(R.layout.album, mAlbumsPanel, false); + final AlbumInfo albumInfo = (AlbumInfo)albumView.findViewById(R.id.album_info); + final AlbumPictures albumPictures = (AlbumPictures)albumView.findViewById(R.id.album_pictures); + + // Load the album info + albumInfo.post(new Runnable() { + @Override + public void run() { + albumInfo.updateView(album); + } + }); + if (album.isSelected()) { + albumInfo.setSelected(true); + } + albumInfo.addCallBackListener(new AlbumInfo.CallbacksListener() { + @Override + public void onAlbumSelected(Album ref) { + // Remove all pictures of the album and add the album reference + removeAlbumItems(ref); + mSelectedAlbums.add(ref.getPath()); + ref.setSelected(true); + albumPictures.updateView(ref, true); + + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + mSelectionChanged = true; + } + + @Override + public void onAlbumDeselected(Album ref) { + // Remove all pictures of the album + removeAlbumItems(ref); + ref.setSelected(false); + albumPictures.updateView(ref, true); + + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + mSelectionChanged = true; + } + + + }); + + // Load the album picture data + albumPictures.updateView(album, false); + albumPictures.addCallBackListener(new AlbumPictures.CallbacksListener() { + @Override + public void onBackButtonClick(View v) { + // Ignored + } + + @Override + public void onSelectionChanged(Album ref) { + // Remove, add, and persist the selection + removeAlbumItems(ref); + mSelectedAlbums.addAll(ref.getSelectedItems()); + ref.setSelected(false); + albumInfo.updateView(ref); + + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + mSelectionChanged = true; + } + }); + + // Register the animation controller + AlbumsFlip3dAnimationController controller = new AlbumsFlip3dAnimationController(albumInfo, albumPictures); + controller.register(); + mAnimationControllers.add(controller); + + return albumView; + } + + /** + * Method that checks if an item is selected + * + * @param item The item + * @return boolean if an item is selected + */ + /*package*/ boolean isSelectedItem(String item) { + Iterator it = mSelectedAlbums.iterator(); + while (it.hasNext()) { + String albumPath = it.next(); + if (item.compareTo(albumPath) == 0) { + return true; + } + } + return false; + } + + /** + * Method that removes the reference to all the items and itself + * + * @param ref The album + */ + /*package*/ void removeAlbumItems(Album ref) { + Iterator it = mSelectedAlbums.iterator(); + while (it.hasNext()) { + String item = it.next(); + String parent = new File(item).getParent(); + if (parent.compareTo(ref.getPath()) == 0) { + it.remove(); + } else if (item.compareTo(ref.getPath()) == 0) { + it.remove(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onEndScroll() { + doEndScroll(AMOUNT_OF_ADDED_STEPS, false); + } + + /** + * Method that performs a scroll creating new items + * + * @param amount The amount of items to create + * @param animate If the add should be animated + */ + /*package*/ synchronized void doEndScroll(int amount, boolean animate) { + for (int i = 0; i < amount; i++) { + //Add to the panel of cards + if (mAlbumViews == null || mAlbumViews.isEmpty()) { + break; + } + mAlbumsPanel.addCard(mAlbumViews.remove(0), animate); + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java new file mode 100644 index 0000000..b0204c3 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.PreferenceFragment; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView; +import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView.OnFrameSelectedListener; +import org.cyanogenmod.wallpapers.photophase.widgets.ResizeFrame; + +import java.util.List; + +/** + * An abstract fragment class that allow to choose the layout disposition of the wallpaper. + */ +public abstract class DispositionFragment + extends PreferenceFragment implements OnFrameSelectedListener { + + private Runnable mRedraw = new Runnable() { + @Override + public void run() { + if (getActivity() == null) return; + try { + mDispositionView.setDispositions(getUserDispositions(), getCols(), getRows()); + } catch (Exception ex) { + // Ignored + } + } + }; + + /*package*/ DispositionView mDispositionView; + + private MenuItem mDeleteMenu; + + /** + * Constructor of DispositionFragment + */ + public DispositionFragment() { + super(); + } + + /** + * Method that returns the current user preference for the disposition + * + * @return List The current user preference dispositions + */ + public abstract List getUserDispositions(); + + /** + * Method that returns the default preference for the disposition + * + * @return List The default preference dispositions + */ + public abstract List getDefaultDispositions(); + + /** + * Method that request to save the dispositions + * + * @param dispositions The dispositions to save + */ + public abstract void saveDispositions(List dispositions); + + /** + * Method that returns the number of rows to use + * + * @return int The number of rows + */ + public abstract int getRows(); + + /** + * Method that returns the number of cols to use + * + * @return int The number of cols + */ + public abstract int getCols(); + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + setHasOptionsMenu(true); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // Inflate the layout for this fragment + ViewGroup v = (ViewGroup)inflater.inflate(R.layout.choose_disposition_fragment, container, false); + mDispositionView = (DispositionView)v.findViewById(R.id.disposition_view); + mDispositionView.setResizeFrame((ResizeFrame)v.findViewById(R.id.resize_frame)); + mDispositionView.setOnFrameSelectedListener(this); + mDispositionView.post(mRedraw); + return v; + } + + /** + * {@inheritDoc} + */ + @Override + public void onDestroyView() { + super.onDestroyView(); + if (mDispositionView != null) { + mDispositionView.removeCallbacks(mRedraw); + if (mDispositionView.isChanged()) { + saveDispositions(mDispositionView.getDispositions()); + } + } + + // Notify that the settings was changed + Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); + if (mDispositionView.isChanged()) { + intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); + intent.putExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, Boolean.TRUE); + } + getActivity().sendBroadcast(intent); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.dispositions, menu); + mDeleteMenu = menu.findItem(R.id.mnu_delete); + if (mDeleteMenu != null) { + mDeleteMenu.setVisible(false); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.mnu_ok: + getActivity().finish(); + return true; + case R.id.mnu_restore: + restoreData(); + return true; + case R.id.mnu_delete: + deleteFrame(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * Method that restores the disposition view to the default state + */ + private void restoreData() { + mDispositionView.setDispositions(getUserDispositions(), getCols(), getRows()); + } + + /** + * Method that restores the disposition view to the default state + */ + private void deleteFrame() { + mDispositionView.deleteCurrentFrame(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onFrameSelectedListener(View v) { + if (mDeleteMenu != null) { + mDeleteMenu.setVisible(true); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onFrameUnselectedListener() { + if (mDeleteMenu != null) { + mDeleteMenu.setVisible(false); + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/GeneralPreferenceFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/GeneralPreferenceFragment.java new file mode 100644 index 0000000..4dd6da8 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/GeneralPreferenceFragment.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.ListPreference; +import android.preference.MultiSelectListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceFragment; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.Colors; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.preferences.SeekBarProgressPreference.OnDisplayProgress; +import org.cyanogenmod.wallpapers.photophase.widgets.ColorPickerPreference; + +/** + * A fragment class with all the general settings + */ +public class GeneralPreferenceFragment extends PreferenceFragment { + + private static final String TAG = "GeneralPreferenceFragment"; + + private static final boolean DEBUG = false; + + private SeekBarProgressPreference mWallpaperDim; + private ColorPickerPreference mBackgroundColor; + private ListPreference mTouchActions; + private CheckBoxPreference mFixAspectRatio; + private MultiSelectListPreference mTransitionsTypes; + private SeekBarProgressPreference mTransitionsInterval; + private MultiSelectListPreference mEffectsTypes; + + boolean mRedrawFlag; + boolean mEmptyTextureQueueFlag; + + private final OnPreferenceChangeListener mOnChangeListener = new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) { + String key = preference.getKey(); + if (DEBUG) Log.d(TAG, "Preference changed: " + key + "=" + newValue); + if (key.compareTo("ui_wallpaper_dim") == 0) { + mRedrawFlag = true; + } else if (key.compareTo("ui_background_color") == 0) { + mRedrawFlag = true; + int color = ((Integer)newValue).intValue(); + Colors.setBackground(new GLColor(color)); + } else if (key.compareTo("ui_fix_aspect_ratio") == 0) { + mRedrawFlag = true; + } else if (key.compareTo("ui_transition_types") == 0) { + mRedrawFlag = true; + } else if (key.compareTo("ui_transition_interval") == 0) { + mRedrawFlag = true; + } else if (key.compareTo("ui_effect_types") == 0) { + mRedrawFlag = true; + mEmptyTextureQueueFlag = true; + } + return true; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + super.onDestroy(); + + // Reload the settings + PreferencesProvider.reload(getActivity()); + + // Notify that the settings was changed + Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); + if (mRedrawFlag) { + intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); + } + if (mEmptyTextureQueueFlag) { + intent.putExtra(PreferencesProvider.EXTRA_FLAG_EMPTY_TEXTURE_QUEUE, Boolean.TRUE); + } + getActivity().sendBroadcast(intent); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + final Resources res = getActivity().getResources(); + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_general); + + mWallpaperDim = (SeekBarProgressPreference)findPreference("ui_wallpaper_dim"); + mWallpaperDim.setFormat(getString(R.string.pref_general_settings_wallpaper_dim_format)); + mWallpaperDim.setOnPreferenceChangeListener(mOnChangeListener); + + mBackgroundColor = (ColorPickerPreference)findPreference("ui_background_color"); + mBackgroundColor.setOnPreferenceChangeListener(mOnChangeListener); + + mTouchActions = (ListPreference)findPreference("ui_touch_action"); + mTouchActions.setOnPreferenceChangeListener(mOnChangeListener); + + mFixAspectRatio = (CheckBoxPreference)findPreference("ui_fix_aspect_ratio"); + mFixAspectRatio.setOnPreferenceChangeListener(mOnChangeListener); + + mTransitionsTypes = (MultiSelectListPreference)findPreference("ui_transition_types"); + mTransitionsTypes.setOnPreferenceChangeListener(mOnChangeListener); + + final int[] transitionsIntervals = res.getIntArray(R.array.transitions_intervals_values); + mTransitionsInterval = (SeekBarProgressPreference)findPreference("ui_transition_interval"); + mTransitionsInterval.setFormat(getString(R.string.pref_general_transitions_interval_format)); + mTransitionsInterval.setMax(transitionsIntervals.length - 1); + int transitionInterval = prefs.getInt("ui_transition_interval", + Preferences.General.Transitions.DEFAULT_TRANSITION_INTERVAL_INDEX); + if (transitionInterval > (transitionsIntervals.length - 1)) { + mTransitionsInterval.setProgress( + Preferences.General.Transitions.DEFAULT_TRANSITION_INTERVAL_INDEX); + } + mTransitionsInterval.setOnDisplayProgress(new OnDisplayProgress() { + @Override + public String onDisplayProgress(int progress) { + return String.valueOf(transitionsIntervals[progress] / 1000); + } + }); + mTransitionsInterval.setOnPreferenceChangeListener(mOnChangeListener); + + mEffectsTypes = (MultiSelectListPreference)findPreference("ui_effect_types"); + mEffectsTypes.setOnPreferenceChangeListener(mOnChangeListener); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java new file mode 100644 index 0000000..77267e6 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.pm.ActivityInfo; +import android.os.Bundle; + +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; + +import java.util.List; + +/** + * A fragment class that allow to choose the layout disposition of the wallpaper for landscape + * screen. + */ +public class LandscapeDispositionFragment extends DispositionFragment { + + /** + * Constructor of LandscapeDispositionFragment + */ + public LandscapeDispositionFragment() { + super(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + + /** + * {@inheritDoc} + */ + @Override + public List getUserDispositions() { + return Preferences.Layout.getLandscapeDisposition(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getDefaultDispositions() { + return DispositionUtil.toDispositions( + Preferences.Layout.DEFAULT_LANDSCAPE_DISPOSITION); + } + + /** + * {@inheritDoc} + */ + @Override + public void saveDispositions(List dispositions) { + Preferences.Layout.setLandscapeDisposition(getActivity(), dispositions); + } + + /** + * {@inheritDoc} + */ + @Override + public int getRows() { + // inverted + return Preferences.Layout.getCols(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getCols() { + // inverted + return Preferences.Layout.getRows(); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/LayoutPreferenceFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/LayoutPreferenceFragment.java new file mode 100644 index 0000000..564c425 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/LayoutPreferenceFragment.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.Context; +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import org.cyanogenmod.wallpapers.photophase.R; + +/** + * A fragment class with the layout disposition + */ +public class LayoutPreferenceFragment extends PreferenceFragment { + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_layout); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/MediaPreferenceFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/MediaPreferenceFragment.java new file mode 100644 index 0000000..e03116e --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/MediaPreferenceFragment.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceFragment; +import android.util.Log; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; + +/** + * A fragment class with all the media settings + */ +public class MediaPreferenceFragment extends PreferenceFragment { + + private static final String TAG = "MediaPreferenceFragment"; + + private static final boolean DEBUG = false; + + ListPreference mRefreshInterval; + Preference mRefreshNow; + + boolean mMediaIntevalChangedFlag; + + private final OnPreferenceChangeListener mOnChangeListener = new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) { + String key = preference.getKey(); + if (DEBUG) Log.d(TAG, "Preference changed: " + key + "=" + newValue); + if (key.compareTo("ui_media_refresh_interval") == 0) { + setRefreshIntervalSummary(Integer.valueOf(String.valueOf(newValue)).intValue()); + mMediaIntevalChangedFlag = true; + } + return true; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public void onDestroy() { + super.onDestroy(); + + // Reload the settings + PreferencesProvider.reload(getActivity()); + + // Notify that the settings was changed + Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); + if (mMediaIntevalChangedFlag) { + intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_INTERVAL_CHANGED, Boolean.TRUE); + } + getActivity().sendBroadcast(intent); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Change the preference manager + getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + + // Add the preferences + addPreferencesFromResource(R.xml.preferences_media); + + mRefreshInterval = (ListPreference)findPreference("ui_media_refresh_interval"); + setRefreshIntervalSummary(Preferences.Media.getRefreshFrecuency()); + mRefreshInterval.setOnPreferenceChangeListener(mOnChangeListener); + + mRefreshNow = findPreference("ui_media_refresh_now"); + mRefreshNow.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + // Request a refresh of the media data + Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); + intent.putExtra(PreferencesProvider.EXTRA_FLAG_MEDIA_RELOAD, Boolean.TRUE); + intent.putExtra(PreferencesProvider.EXTRA_ACTION_MEDIA_USER_RELOAD_REQUEST, Boolean.TRUE); + getActivity().sendBroadcast(intent); + return true; + } + }); + } + + /** + * Method that set the refresh interval summary + * + * @param interval The interval value + */ + void setRefreshIntervalSummary(int interval) { + String v = String.valueOf(interval); + String[] labels = getResources().getStringArray(R.array.refresh_intervals_labels); + String[] values = getResources().getStringArray(R.array.refresh_intervals_values); + int cc = values.length; + for (int i = 0; i < cc; i++) { + if (values[i].compareTo(String.valueOf(v)) == 0) { + v = labels[i]; + break; + } + } + String summary = + (interval == 0) + ? getString(R.string.pref_media_settings_refresh_interval_disable) + : getString(R.string.pref_media_settings_refresh_interval_summary, v); + mRefreshInterval.setSummary(summary); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java new file mode 100644 index 0000000..4e8c46e --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.app.ActionBar; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.view.MenuItem; + +import org.cyanogenmod.wallpapers.photophase.R; + +import java.util.List; + +/** + * The PhotoPhase Live Wallpaper preferences. + */ +public class PhotoPhasePreferences extends PreferenceActivity { + + /** + * {@inheritDoc} + */ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //Initialize action bars + initTitleActionBar(); + } + + /** + * Method that initializes the titlebar of the activity. + */ + private void initTitleActionBar() { + //Configure the action bar options + getActionBar().setDisplayOptions( + ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE); + getActionBar().setDisplayHomeAsUpEnabled(true); + } + + /** + * {@inheritDoc} + */ + @Override + public void onBuildHeaders(List
target) { + loadHeadersFromResource(R.xml.preferences_headers, target); + + // Retrieve the about header + Header aboutHeader = target.get(target.size() - 1); + try { + String appver = + this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName; + aboutHeader.summary = getString(R.string.pref_about_summary, appver); + } catch (Exception e) { + aboutHeader.summary = getString(R.string.pref_about_summary, ""); //$NON-NLS-1$ + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java new file mode 100644 index 0000000..8763e1e --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.pm.ActivityInfo; +import android.os.Bundle; + +import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; + +import java.util.List; + +/** + * A fragment class that allow to choose the layout disposition of the wallpaper for portrait + * screen. + */ +public class PortraitDispositionFragment extends DispositionFragment { + + /** + * Constructor of PortraitDispositionFragment + */ + public PortraitDispositionFragment() { + super(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + /** + * {@inheritDoc} + */ + @Override + public List getUserDispositions() { + return Preferences.Layout.getPortraitDisposition(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getDefaultDispositions() { + return DispositionUtil.toDispositions( + Preferences.Layout.DEFAULT_PORTRAIT_DISPOSITION); + } + + /** + * {@inheritDoc} + */ + @Override + public void saveDispositions(List dispositions) { + Preferences.Layout.setPortraitDisposition(getActivity(), dispositions); + } + + /** + * {@inheritDoc} + */ + @Override + public int getRows() { + return Preferences.Layout.getRows(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getCols() { + return Preferences.Layout.getCols(); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java new file mode 100644 index 0000000..1bf8359 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.res.Resources; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.effects.Effects.EFFECTS; +import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A class that holds all the preferences of the wallpaper + */ +@SuppressWarnings("boxing") +public final class PreferencesProvider { + + /** + * Internal broadcast action to communicate that some setting was changed + * @see #EXTRA_FLAG_REDRAW + * {@hide} + */ + public static final String ACTION_SETTINGS_CHANGED = "org.cyanogenmod.wallpapers.photophase.actions.SETTINGS_CHANGED"; + + /** + * An extra setting that indicates that the changed setting request a whole recreation of the wallpaper world + * {@hide} + */ + public static final String EXTRA_FLAG_RECREATE_WORLD = "flag_recreate_world"; + + /** + * An extra setting that indicates that the changed setting request a redraw of the wallpaper + * {@hide} + */ + public static final String EXTRA_FLAG_REDRAW = "flag_redraw"; + + /** + * An extra setting that indicates that the changed setting request to empty the texture queue + * {@hide} + */ + public static final String EXTRA_FLAG_EMPTY_TEXTURE_QUEUE = "flag_empty_texture_queue"; + + /** + * An extra setting that indicates that the changed setting request a reload of the media data + * {@hide} + */ + public static final String EXTRA_FLAG_MEDIA_RELOAD = "flag_media_reload"; + + /** + * An extra setting that indicates that the changed setting notifies that the media + * interval was changed + * {@hide} + */ + public static final String EXTRA_FLAG_MEDIA_INTERVAL_CHANGED = "flag_media_interval_changed"; + + /** + * An extra setting that indicates that the media reload becomes from a user + * + * @see #EXTRA_FLAG_MEDIA_RELOAD + * {@hide} + */ + public static final String EXTRA_ACTION_MEDIA_USER_RELOAD_REQUEST = "action_media_user_reload_req"; + + /** + * The shared preferences file + */ + public static final String PREFERENCES_FILE = "org.cyanogenmod.wallpapers.photophase"; + + private static Map mPreferences = new HashMap(); + + /** + * @hide + */ + /*package*/ static int[] TRANSITIONS_INTERVALS; + + /** + * Method that loads the all the preferences of the application + * + * @param context The current context + */ + public static void reload(Context context) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); + mPreferences = preferences.getAll(); + + final Resources res = context.getResources(); + TRANSITIONS_INTERVALS = res.getIntArray(R.array.transitions_intervals_values); + } + + /** + * Method that returns a integer property value. + * + * @param key The preference key + * @param def The default value + * @return int The integer property value + */ + static int getInt(String key, int def) { + return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Integer ? + (Integer) mPreferences.get(key) : def; + } + + /** + * Method that returns a long property value. + * + * @param key The preference key + * @param def The default value + * @return long The long property value + */ + static long getLong(String key, long def) { + return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Long ? + (Long) mPreferences.get(key) : def; + } + + /** + * Method that returns a boolean property value. + * + * @param key The preference key + * @param def The default value + * @return boolean The boolean property value + */ + static boolean getBoolean(String key, boolean def) { + return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Boolean ? + (Boolean) mPreferences.get(key) : def; + } + + /** + * Method that returns a string property value. + * + * @param key The preference key + * @param def The default value + * @return String The string property value + */ + static String getString(String key, String def) { + return mPreferences.containsKey(key) && mPreferences.get(key) instanceof String ? + (String) mPreferences.get(key) : def; + } + + /** + * Method that returns a string set property value. + * + * @param key The preference key + * @param def The default value + * @return Set The string property value + */ + @SuppressWarnings("unchecked") + static Set getStringSet(String key, Set def) { + return mPreferences.containsKey(key) && mPreferences.get(key) instanceof Set ? + (Set) mPreferences.get(key) : def; + } + + /** + * A class for access to the preferences of the application + */ + public static class Preferences { + /** + * General preferences + */ + public static class General { + private static final GLColor DEFAULT_BACKGROUND_COLOR = new GLColor("#ff202020"); + + /** + * Method that returns the wallpaper dimmed value. + * + * @return float If the wallpaper dimmed value (0-black, 100-black) + */ + public static float getWallpaperDim() { + return getInt("ui_wallpaper_dim", 0); + } + + /** + * Method that returns the background color + * + * @return GLColor The background color + */ + public static GLColor getBackgroundColor() { + int color = getInt("ui_background_color", 0); + if (color == 0) { + return DEFAULT_BACKGROUND_COLOR; + } + return new GLColor(color); + } + + /** + * Return the current user preference of the action to do when a frame is tap. + * + * @return TouchAction The action (default NONE) + */ + public static TouchAction getTouchAction() { + return TouchAction.fromValue(Integer.valueOf(getString("ui_touch_action", "0"))); + } + + /** + * Return the current user preference about fix or not fix the aspect ratio + * of the image by cropping the image. + * + * @return boolean Indicates if the image should be cropped + */ + public static boolean isFixAspectRatio() { + return getBoolean("ui_fix_aspect_ratio", true); + } + + /** + * Transitions preferences + */ + public static class Transitions { + /** + * The default transition interval + */ + public static final int DEFAULT_TRANSITION_INTERVAL_INDEX = 2; + + /** + * Return the current user preference about the transition to apply to + * the pictures of the wallpaper. + * + * @return TRANSITIONS[] The transition to apply to the wallpaper's pictures + */ + public static TRANSITIONS[] getTransitionTypes() { + Set set = getStringSet("ui_transition_types", new HashSet()); + if (set.isEmpty()) { + // Return all the transitions if no one is selected + return TRANSITIONS.getValidTranstions(); + } + String[] values = set.toArray(new String[set.size()]); + int count = values.length; + TRANSITIONS[] transitions = new TRANSITIONS[count]; + for (int i = 0; i < count; i++) { + transitions[i] = TRANSITIONS.fromOrdinal(Integer.valueOf(values[i])); + } + return transitions; + } + + /** + * Method that returns how often the transitions are triggered. + * + * @return int The milliseconds in which the next transition will be triggered + */ + public static int getTransitionInterval() { + int interval = getInt("ui_transition_interval", + DEFAULT_TRANSITION_INTERVAL_INDEX); + return TRANSITIONS_INTERVALS[interval]; + } + } + + /** + * Effects preferences + */ + public static class Effects { + /** + * Return the current user preference about the effect to apply to + * the pictures of the wallpaper. + * + * @return EFFECTS[] The effects to apply to the wallpaper's pictures + */ + public static EFFECTS[] getEffectTypes() { + Set defaults = new HashSet(); + defaults.add(String.valueOf(EFFECTS.NO_EFFECT.ordinal())); + Set set = getStringSet("ui_effect_types", defaults); + String[] values = set.toArray(new String[set.size()]); + int count = values.length; + EFFECTS[] effects = new EFFECTS[count]; + for (int i = 0; i < count; i++) { + effects[i] = EFFECTS.fromOrdinal(Integer.valueOf(values[i])); + } + return effects; + } + } + } + + /** + * Media preferences + */ + public static class Media { + /** + * Constant that indicates that the media reload is disabled + */ + public static final int MEDIA_RELOAD_DISABLED = 0; + + /** + * Method that returns the frequency with which the media is updated. + * + * @return int The interval in seconds between updates. 0 means that updates are disabled + */ + public static int getRefreshFrecuency() { + return Integer.valueOf(getString("ui_media_refresh_interval", String.valueOf(MEDIA_RELOAD_DISABLED))); + } + + /** + * Method that returns if the app must be select new albums when they are discovered. + * + * @return boolean If the app must be select new albums when they are discovered. + */ + public static boolean isAutoSelectNewAlbums() { + return getBoolean("ui_media_auto_select_new", Boolean.TRUE); + } + + // Internal settings (non-UI) + /** + * Method that returns the list of albums and pictures to be displayed + * + * @return Set The list of albums and pictures to be displayed + */ + public static Set getSelectedMedia() { + return getStringSet("media_selected_media", new HashSet()); + } + + /** + * Method that returns the list of albums and pictures to be displayed + * + * @param context The current context + * @param selection The new list of albums and pictures to be displayed + */ + public static synchronized void setSelectedMedia(Context context, Set selection) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); + Editor editor = preferences.edit(); + editor.putStringSet("media_selected_media", selection); + editor.commit(); + reload(context); + } + + /** + * Method that returns the list of the name of the albums seen by the + * last media discovery scan. + * + * @return Set The list of albums and pictures to be displayed + */ + public static Set getLastDiscorevedAlbums() { + return getStringSet("media_last_disvored_albums", new HashSet()); + } + + /** + * Method that sets the list of the name of the albums seen by the + * last media discovery scan. + * + * @param context The current context + * @param albums The albums seen by the last media discovery scan + */ + public static synchronized void setLastDiscorevedAlbums(Context context, Set albums) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); + Editor editor = preferences.edit(); + editor.putStringSet("media_last_disvored_albums", albums); + editor.commit(); + reload(context); + } + } + + /** + * Layout preferences + */ + public static class Layout { + + private static final int DEFAULT_COLS = 4; + private static final int DEFAULT_ROWS = 7; + public static final String DEFAULT_PORTRAIT_DISPOSITION = "0x0:2x1|0x2:1x3|0x4:3x6|2x2:3x3|3x0:3x0|3x1:3x1"; + public static final String DEFAULT_LANDSCAPE_DISPOSITION = "0x0:2x3|3x0:5x1|3x2:4x3|5x2:6x3|6x0:6x0|6x1:6x1"; + + /** + * Method that returns the rows of the wallpaper. + * + * @return int The rows of the wallpaper + */ + public static int getRows() { + return getInt("ui_layout_rows", DEFAULT_ROWS); + } + + /** + * Method that returns the columns of the wallpaper. + * + * @return int The columns of the wallpaper + */ + public static int getCols() { + return getInt("ui_layout_cols", DEFAULT_COLS); + } + + /** + * Returns the disposition of the photo frames in the wallpaper on portrait screen. The + * setting is stored as 0x0:1x2|2x2:3x4|..., which it means (position x=0, y=0, + * 1 cells width, 2 cells height, ...). + * + * @return List The photo frames dispositions + */ + public static List getPortraitDisposition() { + return DispositionUtil.toDispositions( + getString("ui_layout_portrait_disposition", DEFAULT_PORTRAIT_DISPOSITION)); + } + + /** + * Sets the disposition of the photo frames in the wallpaper on landscape screen. + * + * @param context The current context + * @param dispositions The photo frames dispositions + */ + public static void setPortraitDisposition(Context context, List dispositions) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); + Editor editor = preferences.edit(); + editor.putString("ui_layout_portrait_disposition", + DispositionUtil.fromDispositions(dispositions)); + editor.commit(); + reload(context); + } + + /** + * Returns the disposition of the photo frames in the wallpaper on landscape screen. The + * setting is stored as 0x0:1x2|2x2:3x4|..., which it means (position x=0, y=0, + * 1 cells width, 2 cells height, ...). + * + * @return List The photo frames dispositions + */ + public static List getLandscapeDisposition() { + return DispositionUtil.toDispositions( + getString("ui_layout_landscape_disposition", DEFAULT_LANDSCAPE_DISPOSITION)); + } + + /** + * Sets the disposition of the photo frames in the wallpaper on landscape screen. + * + * @param context The current context + * @param dispositions The photo frames dispositions + */ + public static void setLandscapeDisposition(Context context, List dispositions) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); + Editor editor = preferences.edit(); + editor.putString("ui_layout_landscape_disposition", + DispositionUtil.fromDispositions(dispositions)); + editor.commit(); + reload(context); + } + } + + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarPreference.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarPreference.java new file mode 100644 index 0000000..e8b6d2e --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarPreference.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; + +import org.cyanogenmod.wallpapers.photophase.R; + +/** + * A preference with a seekbar widget + */ +public class SeekBarPreference extends Preference implements OnSeekBarChangeListener { + + private int mProgress; + private int mMax; + private boolean mTrackingTouch; + + /** + * Constructor of SeekBarPreference + * + * @param context The current context + * @param attrs The attributes of the view + * @param defStyle The resource with the style + */ + public SeekBarPreference( + Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setMax(100); + setLayoutResource(R.layout.preference_widget_seekbar); + } + + /** + * Constructor of SeekBarPreference + * + * @param context The current context + * @param attrs The attributes of the view + */ + public SeekBarPreference(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructor of SeekBarPreference + * + * @param context The current context + */ + public SeekBarPreference(Context context) { + this(context, null); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onBindView(View view) { + super.onBindView(view); + SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar); + seekBar.setOnSeekBarChangeListener(this); + seekBar.setMax(mMax); + seekBar.setProgress(mProgress); + seekBar.setEnabled(isEnabled()); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("boxing") + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + setProgress(restoreValue ? getPersistedInt(mProgress) : (Integer) defaultValue); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("boxing") + protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInt(index, 0); + } + + /** + * Allows a Preference to intercept key events without having focus. + * For example, SeekBarPreference uses this to intercept +/- to adjust + * the progress. + * + * @param v The view + * @param keyCode The key code + * @param event The key event + * @return True if the Preference handled the key. Returns false by default. + */ + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.getAction() != KeyEvent.ACTION_UP) { + if (keyCode == KeyEvent.KEYCODE_PLUS + || keyCode == KeyEvent.KEYCODE_EQUALS) { + setProgress(getProgress() + 1); + return true; + } + if (keyCode == KeyEvent.KEYCODE_MINUS) { + setProgress(getProgress() - 1); + return true; + } + } + return false; + } + + /** + * Method that set the maximum progress + * + * @param max The maximum progress + */ + public void setMax(int max) { + if (max != mMax) { + mMax = max; + notifyChanged(); + } + } + + /** + * Method that set the actual progress + * + * @param progress The actual progress + */ + public void setProgress(int progress) { + setProgress(progress, true); + } + + /** + * Method that set the actual progress + * + * @param progress The actual progress + * @param notifyChanged Whether notify if the progress was changed + */ + protected void setProgress(int progress, boolean notifyChanged) { + int p = progress; + if (p > mMax) { + p = mMax; + } + if (p < 0) { + p = 0; + } + if (p != mProgress) { + mProgress = p; + persistInt(p); + if (notifyChanged) { + notifyChanged(); + } + } + } + + /** + * Method that returns the current progress + * + * @return int The current progress + */ + public int getProgress() { + return mProgress; + } + + /** + * Persist the seekBar's progress value if callChangeListener + * + * returns boolean True, otherwise set the seekBar's progress to the stored value + */ + @SuppressWarnings("boxing") + void syncProgress(SeekBar seekBar) { + int progress = seekBar.getProgress(); + if (progress != mProgress) { + if (callChangeListener(progress)) { + setProgress(progress, false); + } else { + seekBar.setProgress(mProgress); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser && !mTrackingTouch) { + syncProgress(seekBar); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + mTrackingTouch = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + mTrackingTouch = false; + if (seekBar.getProgress() != mProgress) { + syncProgress(seekBar); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Parcelable onSaveInstanceState() { + /* + * Suppose a client uses this preference type without persisting. We + * must save the instance state so it is able to, for example, survive + * orientation changes. + */ + + final Parcelable superState = super.onSaveInstanceState(); + if (isPersistent()) { + // No need to save instance state since it's persistent + return superState; + } + + // Save the instance state + final SavedState myState = new SavedState(superState); + myState.progress = mProgress; + myState.max = mMax; + return myState; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (!state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + // Restore the instance state + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + mProgress = myState.progress; + mMax = myState.max; + notifyChanged(); + } + + /** + * SavedState, a subclass of {@link BaseSavedState}, will store the state + * of MyPreference, a subclass of Preference. + *

+ * It is important to always call through to super methods. + */ + private static class SavedState extends BaseSavedState { + int progress; + int max; + + public SavedState(Parcel source) { + super(source); + + // Restore the click counter + progress = source.readInt(); + max = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + + // Save the click counter + dest.writeInt(progress); + dest.writeInt(max); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @SuppressWarnings({"unused", "hiding"}) + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarProgressPreference.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarProgressPreference.java new file mode 100644 index 0000000..64b9b90 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/SeekBarProgressPreference.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +import org.cyanogenmod.wallpapers.photophase.R; + +/** + * A preference with a seekbar widget that display the progress + */ +public class SeekBarProgressPreference extends SeekBarPreference { + + /** + * Interface to intercept the progress value to display on screen + */ + public interface OnDisplayProgress { + /** + * Method invoked when a progress value is going to display on screen + * + * @param progress The real progress + * @return The progress to display + */ + String onDisplayProgress(int progress); + } + + private String mFormat; + private OnDisplayProgress mOnDisplayProgress; + + TextView mTextView; + + /** + * Constructor of SeekBarProgressPreference + * + * @param context The current context + * @param attrs The attributes of the view + * @param defStyle The resource with the style + */ + public SeekBarProgressPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Constructor of SeekBarProgressPreference + * + * @param context The current context + * @param attrs The attributes of the view + */ + public SeekBarProgressPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Constructor of SeekBarProgressPreference + * + * @param context The current context + */ + public SeekBarProgressPreference(Context context) { + super(context); + init(); + } + + /** + * Method that initializes the preference + */ + void init() { + mFormat = "%s"; + mOnDisplayProgress = null; + setWidgetLayoutResource(R.layout.preference_widget_seekbar_progress); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onBindView(View view) { + super.onBindView(view); + mTextView = (TextView) view.findViewById(R.id.text); + setText(); + } + + /** + * Method that set the actual progress + * + * @param progress The actual progress + * @param notifyChanged Whether notify if the progress was changed + */ + @Override + protected void setProgress(int progress, boolean notifyChanged) { + super.setProgress(progress, notifyChanged); + setText(); + } + + /** + * Method that displays the progress value + */ + private void setText() { + if (mTextView != null) { + String value = String.valueOf(getProgress()); + if (mOnDisplayProgress != null) { + value = mOnDisplayProgress.onDisplayProgress(getProgress()); + } + mTextView.setText(String.format(mFormat, value)); + } + } + + /** + * Method that sets the callback to intercept the progress value before it will be + * displayed on screen. + * + * @param onDisplayProgress The callback + */ + public void setOnDisplayProgress(OnDisplayProgress onDisplayProgress) { + this.mOnDisplayProgress = onDisplayProgress; + } + + /** + * Method that set the format of the progress + * + * @param format The format of the string progress + */ + public void setFormat(String format) { + mFormat = format; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/TouchAction.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/TouchAction.java new file mode 100644 index 0000000..498c1bd --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/TouchAction.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.preferences; + +/** + * An enumeration with all the touch actions supported + */ +public enum TouchAction { + /** + * No action + */ + NONE(0), + /** + * Force transition of the frame + */ + TRANSITION(1), + /** + * Open the picture of the frame + */ + OPEN(2), + /** + * Share/send the picture of the frame + */ + SHARE(3); + + private final int mValue; + + /** + * Constructor of TouchAction + * + * @param id The unique identifier + */ + private TouchAction(int value) { + mValue = value; + } + + /** + * Method that returns the value + * + * @return int The value + */ + public int getValue() { + return mValue; + } + + /** + * Method that gets the reference of a TouchAction from its value + * + * @param value The value + * @return TouchAction The reference + */ + public static final TouchAction fromValue(int value) { + if (value == TRANSITION.mValue) return TRANSITION; + if (value == OPEN.mValue) return OPEN; + if (value == SHARE.mValue) return SHARE; + return NONE; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/shapes/ColorShape.java b/src/org/cyanogenmod/wallpapers/photophase/shapes/ColorShape.java new file mode 100644 index 0000000..20b5fa4 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/shapes/ColorShape.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.shapes; + +import android.content.Context; +import android.opengl.GLES20; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.R; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * A shape plus color. + */ +public class ColorShape implements DrawableShape { + + private int mProgramHandler; + private int mPositionHandler; + private int mColorHandler; + private int mMatrixHandler; + private FloatBuffer mVertexBuffer; + + private final GLColor mColor; + + /** + * Constructor of ColorShape. + * + * @param ctx The current context + * @param vertex The vertext data + * @param color The color + */ + public ColorShape(Context ctx, float[] vertex, GLColor color) { + super(); + mColor = color; + + mProgramHandler = GLESUtil.createProgram( + ctx.getResources(), + R.raw.color_vertex_shader, + R.raw.color_fragment_shader); + mPositionHandler = GLES20.glGetAttribLocation(mProgramHandler, "aPosition"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mColorHandler = GLES20.glGetAttribLocation(mProgramHandler, "aColor"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mMatrixHandler = GLES20.glGetUniformLocation(mProgramHandler, "uMVPMatrix"); + GLESUtil.glesCheckError("glGetUniformLocation"); + + // Initialize vertex byte buffer for shape coordinates + ByteBuffer bb = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) + bb.order(ByteOrder.nativeOrder()); + mVertexBuffer = bb.asFloatBuffer(); + mVertexBuffer.put(vertex); + mVertexBuffer.position(0); + } + + /** + * {@inheritDoc} + */ + @Override + public void draw(float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Enable properties + if (mColor.a != 1.0f) { + GLES20.glEnable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glEnable"); + GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_ALPHA); + GLESUtil.glesCheckError("glBlendFunc"); + } + + // Set the program and its attributes + GLES20.glUseProgram(mProgramHandler); + GLESUtil.glesCheckError("glUseProgram"); + + // Position + mVertexBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandler, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandler); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Color + GLES20.glVertexAttrib4f(mColorHandler, mColor.r, mColor.g, mColor.b, mColor.a); + GLESUtil.glesCheckError("glVertexAttrib4f"); + + // Apply the projection and view transformation + GLES20.glUniformMatrix4fv(mMatrixHandler, 1, false, matrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Draw the photo frame + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandler); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mColorHandler); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + + // Disable properties + if (mColor.a != 1.0f) { + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + } + } + + /** + * Method that sets the alpha color of the shape + * + * @param value The new alpha color of the shape + */ + public void setAlpha(float value) { + mColor.a = value; + } + + /** + * Method that destroy all the internal references + */ + public void recycle() { + if (GLES20.glIsProgram(mProgramHandler)) { + GLES20.glDeleteProgram(mProgramHandler); + GLESUtil.glesCheckError("glDeleteProgram"); + } + mProgramHandler = 0; + mPositionHandler = 0; + mColorHandler = 0; + mMatrixHandler = 0; + mVertexBuffer.clear(); + mVertexBuffer = null; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/shapes/DrawableShape.java b/src/org/cyanogenmod/wallpapers/photophase/shapes/DrawableShape.java new file mode 100644 index 0000000..7866dcc --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/shapes/DrawableShape.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.shapes; + +/** + * An interface that defines an object as a drawable shape. + */ +public interface DrawableShape { + + /** + * Method that request redraw of the shape. + * + * @param matrix The model-view-projection matrix + */ + void draw(float[] matrix); +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java b/src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java new file mode 100644 index 0000000..ad69687 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.shapes; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.opengl.GLES20; + +import org.cyanogenmod.wallpapers.photophase.Colors; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo; +import org.cyanogenmod.wallpapers.photophase.R; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * A shape to draw an oops message + */ +public class OopsShape implements DrawableShape { + + private static final int VERTEX_SHADER = R.raw.default_vertex_shader; + private static final int FRAGMENT_SHADER = R.raw.default_fragment_shader; + + // The texture coordinates + private static final float[] TEXTURE_COORDS = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f + }; + + // The vertex position coordinates + private static final float[] VERTEX_COORDS_PORTRAIT = { + -0.75f, -0.5f, + 0.75f, -0.5f, + -0.75f, 0.5f, + 0.75f, 0.5f + }; + private static final float[] VERTEX_COORDS_LANDSCAPE = { + -0.5f, -0.75f, + 0.5f, -0.75f, + -0.5f, 0.75f, + 0.5f, 0.75f + }; + + private FloatBuffer mPositionBuffer; + private FloatBuffer mTextureBuffer; + + private int[] mProgramHandlers; + private int[] mTextureHandlers; + private int[] mPositionHandlers; + private int[] mTextureCoordHandlers; + private int[] mMVPMatrixHandlers; + + private String mMessage; + + private GLESTextureInfo mOopsImageTexture; + private GLESTextureInfo mOopsTextTexture; + + /** + * Constructor of OopsShape + * + * @param ctx The current context + * @param resourceMessageId The resource identifier with the message + */ + public OopsShape(Context ctx, int resourceMessageId) { + super(); + + int orientation = ctx.getResources().getConfiguration().orientation; + float[] vertex = VERTEX_COORDS_PORTRAIT; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + vertex = VERTEX_COORDS_LANDSCAPE; + } + + // Load the buffers + ByteBuffer bb1 = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) + bb1.order(ByteOrder.nativeOrder()); + mPositionBuffer = bb1.asFloatBuffer(); + mPositionBuffer.put(vertex); + mPositionBuffer.position(0); + // - + ByteBuffer bb2 = ByteBuffer.allocateDirect(TEXTURE_COORDS.length * 4); // (# of coordinate values * 4 bytes per float) + bb2.order(ByteOrder.nativeOrder()); + mTextureBuffer = bb2.asFloatBuffer(); + mTextureBuffer.put(TEXTURE_COORDS); + mTextureBuffer.position(0); + + // Initialize the structures + mProgramHandlers = new int[2]; + mTextureHandlers = new int[2]; + mPositionHandlers = new int[2]; + mTextureCoordHandlers = new int[2]; + mMVPMatrixHandlers = new int[2]; + + // Create all the params + for (int i = 0; i < 2; i++) { + mProgramHandlers[i] = + GLESUtil.createProgram( + ctx.getResources(), VERTEX_SHADER, FRAGMENT_SHADER); + mTextureHandlers[i] = + GLES20.glGetAttribLocation(mProgramHandlers[i], "sTexture"); + mPositionHandlers[i] = + GLES20.glGetAttribLocation(mProgramHandlers[i], "aPosition"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mTextureCoordHandlers[i] = + GLES20.glGetAttribLocation(mProgramHandlers[i], "aTextureCoord"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mMVPMatrixHandlers[i] = + GLES20.glGetUniformLocation(mProgramHandlers[i], "uMVPMatrix"); + GLESUtil.glesCheckError("glGetUniformLocation"); + } + + // Get the localized message + mMessage = ctx.getString(resourceMessageId); + + // Load the textures + mOopsImageTexture = GLESUtil.loadTexture(ctx, R.drawable.bg_cid_oops, null, null, false); + Bitmap textBitmap = text2Bitmap(ctx, mMessage); + mOopsTextTexture = GLESUtil.loadTexture(textBitmap, null, null); + + // Recycle + mOopsImageTexture.bitmap.recycle(); + mOopsImageTexture.bitmap = null; + textBitmap.recycle(); + textBitmap = null; + mOopsTextTexture.bitmap.recycle(); + mOopsTextTexture.bitmap = null; + } + + /** + * {@inheritDoc} + */ + @Override + public void draw(float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Clear background + GLColor bg = Colors.getBackground(); + GLES20.glClearColor(bg.r, bg.g, bg.b, bg.a); + GLESUtil.glesCheckError("glClearColor"); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); + GLESUtil.glesCheckError("glClear"); + + // Enable blend + GLES20.glEnable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glEnable"); + GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_COLOR); + GLESUtil.glesCheckError("glBlendFunc"); + + // Draw the textures + drawTexture(matrix, 0, mOopsImageTexture.handle); + drawTexture(matrix, 1, mOopsTextTexture.handle); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + } + + /** + * Method that draws a texture + * + * @param matrix The model-view-projection matrix + * @param index The index of the texture + * @param texture The texture handler + */ + private void drawTexture(float[] matrix, int index, int texture) { + // Use our shader program + GLES20.glUseProgram(mProgramHandlers[index]); + GLESUtil.glesCheckError("glUseProgram()"); + + // Apply the projection and view transformation + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[index], 1, false, matrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Texture + GLES20.glVertexAttribPointer(mTextureCoordHandlers[index], 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[index]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + GLES20.glVertexAttribPointer(mPositionHandlers[index], 2, GLES20.GL_FLOAT, false, 0, mPositionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[index]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Set the input textures + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[index], 0); + GLESUtil.glesCheckError("glUniform1i"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[index]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[index]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Method that requests to remove the internal references and resources. + */ + public void recycle() { + // Remove textures + if (mOopsImageTexture != null && mOopsImageTexture.handle != 0) { + int[] textures = new int[]{mOopsImageTexture.handle}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + mOopsImageTexture = null; + if (mOopsTextTexture != null && mOopsTextTexture.handle != 0) { + int[] textures = new int[]{mOopsTextTexture.handle}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + mOopsTextTexture = null; + + // Remove buffers + if (mPositionBuffer != null) { + mPositionBuffer.clear(); + } + if (mTextureBuffer != null) { + mTextureBuffer.clear(); + } + mPositionBuffer = null; + mTextureBuffer = null; + + for (int i = 0; i < 2; i++) { + if (GLES20.glIsProgram(mProgramHandlers[i])) { + GLES20.glDeleteProgram(mProgramHandlers[i]); + GLESUtil.glesCheckError("glDeleteProgram(" + i + ")"); + } + mProgramHandlers[i] = 0; + mTextureHandlers[i] = 0; + mPositionHandlers[i] = 0; + mTextureCoordHandlers[i] = 0; + mMVPMatrixHandlers[i] = 0; + } + } + + /** + * Method that converts a text to a bitmap + * + * @param ctx The current context + * @param text The text to draw to the bitmap + * @return Bitmap The bitmap with the text + */ + public Bitmap text2Bitmap(Context ctx, String text) { + Paint paint = new Paint(); + Typeface font = Typeface.createFromAsset(ctx.getAssets(), "fonts/Roboto-Bold.ttf"); + paint.setTypeface(font); + paint.setColor(Color.WHITE); + paint.setTextSize(24.0f); + paint.setAntiAlias(true); + paint.setTextAlign(Paint.Align.CENTER); + Bitmap src = mOopsImageTexture.bitmap; + Bitmap image = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(image); + canvas.drawText(text, src.getWidth()/2, src.getHeight() - (src.getHeight() * 0.33f), paint); + return image; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java b/src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java new file mode 100644 index 0000000..bc07b17 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.tasks; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.widget.ImageView; + +import org.cyanogenmod.wallpapers.photophase.utils.BitmapUtils; + +import java.io.File; + +/** + * A class for load images associated to a ImageView in background. + */ +public class AsyncPictureLoaderTask extends AsyncTask { + + private final Context mContext; + private final ImageView mView; + + /** + * Constructor of AsyncPictureLoaderTask + * + * @param context The current context + * @param v The associated view + */ + public AsyncPictureLoaderTask(Context context, ImageView v) { + super(); + mContext = context; + mView = v; + } + + /** + * {@inheritDoc} + */ + @Override + protected Drawable doInBackground(File... params) { + int width = mView.getMeasuredWidth(); + int height = mView.getMeasuredHeight(); + Bitmap bitmap = BitmapUtils.decodeBitmap(params[0], width, height); + if (bitmap != null) { + return new BitmapDrawable(mContext.getResources(), bitmap); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(Drawable result) { + mView.setImageDrawable(result); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/CubeTransition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/CubeTransition.java new file mode 100644 index 0000000..bfe300b --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/CubeTransition.java @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLException; +import android.opengl.Matrix; +import android.os.SystemClock; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A transition that applies a cube effect transition to the picture. + */ +public class CubeTransition extends Transition { + + /** + * The enumeration of all possibles window movements + */ + public enum WINDOW_MODES { + /** + * Open the picture from left to right + */ + LEFT_TO_RIGHT, + /** + * Open the picture from right to left + */ + RIGHT_TO_LEFT + } + + private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; + private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; + + private static final float TRANSITION_TIME = 1000.0f; + + private static final float SCALE_AMOUNT = 0.2f; + + private WINDOW_MODES mMode; + + private boolean mRunning; + private long mTime; + + private FloatBuffer mPositionBuffer; + private float[] mTranslationMatrix; + private float[] mVertex; + + private float mAmount; + + /** + * Constructor of CubeTransition + * + * @param ctx The current context + * @param tm The texture manager + */ + public CubeTransition(Context ctx, TextureManager tm) { + super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); + + // Initialized + mTranslationMatrix = new float[16]; + reset(); + } + + /** + * {@inheritDoc} + */ + @Override + public TRANSITIONS getType() { + return TRANSITIONS.WINDOW; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasTransitionTarget() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return mRunning; + } + + /** + * {@inheritDoc} + */ + @Override + public void select(PhotoFrame target) { + super.select(target); + mAmount = getAmount(); + + // Create the interal buffer + float[] vertex = target.getFrameVertex(); + if (mPositionBuffer == null) { + ByteBuffer bb = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) + bb.order(ByteOrder.nativeOrder()); + mPositionBuffer = bb.asFloatBuffer(); + } + if (mVertex == null) { + mVertex = new float[vertex.length]; + } + + // Random mode + List modes = + new ArrayList( + Arrays.asList(WINDOW_MODES.values())); + int low = 0; + int high = modes.size() - 1; + mMode = modes.get(Utils.getNextRandom(low, high)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectable(PhotoFrame frame) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + mTime = -1; + mRunning = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void apply(float[] matrix) throws GLException { + // Check internal vars + if (mTarget == null || + mTarget.getPositionBuffer() == null || + mTarget.getTextureBuffer() == null) { + return; + } + if (mTransitionTarget == null || + mTransitionTarget.getPositionBuffer() == null || + mTransitionTarget.getTextureBuffer() == null) { + return; + } + + // Set the time the first time + if (mTime == -1) { + mTime = SystemClock.uptimeMillis(); + } + + // Calculate the delta time + final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; + + // Apply the transition + if (delta < 1) { + applyDstTransition(delta, matrix); + applySrcTransition(delta, matrix); + } else { + applyFinalTransition(matrix); + } + + // Transition ending + if (delta == 1) { + mRunning = false; + } + } + + /** + * Apply the source transition + * + * @param delta The delta time + * @param matrix The model-view-projection matrix + */ + private void applySrcTransition(float delta, float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Set the program + useProgram(0); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the input texture + int textureHandle = mTarget.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[0], 0); + GLESUtil.glesCheckError("glBindTexture"); + + // Texture + FloatBuffer textureBuffer = mTarget.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + setInternalVertex(); + float interpolation = delta > 0.5f + ? 1 - delta + : delta; + float w = Math.abs(mVertex[6] - mVertex[4]); + switch (mMode) { + case RIGHT_TO_LEFT: + mVertex[1] -= interpolation * mAmount; + mVertex[5] += interpolation * mAmount; + mVertex[4] += w * delta; + mVertex[0] = mVertex[4]; + break; + case LEFT_TO_RIGHT: + mVertex[3] -= interpolation * mAmount; + mVertex[7] += interpolation * mAmount; + mVertex[6] -= w * delta; + mVertex[2] = mVertex[6]; + break; + default: + break; + } + mPositionBuffer.position(0); + mPositionBuffer.put(mVertex); + mPositionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, mPositionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Calculate the delta angle and the translation and rotate parameters + float angle = 0.0f; + float translateX = 0.0f; + float rotateY = 0.0f; + switch (mMode) { + case RIGHT_TO_LEFT: + angle = delta * 90; + rotateY = -1.0f; + translateX = mVertex[2] * -1; + break; + case LEFT_TO_RIGHT: + angle = delta * -90; + rotateY = -1.0f; + translateX = mVertex[0] * -1; + break; + + default: + break; + } + + // Apply the projection and view transformation + Matrix.setIdentityM(matrix, 0); + Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, 0.0f, 0.0f); + Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, 0.0f, rotateY, 0.0f); + Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, 0.0f, 0.0f); + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, mTranslationMatrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Apply the destination transition + * + * @param delta The delta time + * @param matrix The model-view-projection matrix + */ + private void applyDstTransition(float delta, float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Set the program + useProgram(1); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the input texture + int textureHandle = mTransitionTarget.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[1], 0); + GLESUtil.glesCheckError("glBindTexture"); + + // Texture + FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + setInternalVertex(); + float interpolation = delta > 0.5f + ? 1 - delta + : delta; + float w = Math.abs(mVertex[6] - mVertex[4]); + switch (mMode) { + case LEFT_TO_RIGHT: + mVertex[1] -= interpolation * mAmount; + mVertex[5] += interpolation * mAmount; + mVertex[4] += w * (1 - delta); + mVertex[0] = mVertex[4]; + break; + case RIGHT_TO_LEFT: + mVertex[3] -= interpolation * mAmount; + mVertex[7] += interpolation * mAmount; + mVertex[6] -= w * (1 - delta); + mVertex[2] = mVertex[6]; + break; + default: + break; + } + mPositionBuffer.position(0); + mPositionBuffer.put(mVertex); + mPositionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, mPositionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Calculate the delta angle and the translation and rotate parameters + float angle = 0.0f; + float translateX = 0.0f; + float rotateY = 0.0f; + switch (mMode) { + case LEFT_TO_RIGHT: + angle = 90 - (delta * 90); + rotateY = -1.0f; + translateX = mVertex[2] * -1; + break; + case RIGHT_TO_LEFT: + angle = -90 + (delta * 90); + rotateY = -1.0f; + translateX = mVertex[0] * -1; + break; + + default: + break; + } + + // Apply the projection and view transformation + Matrix.setIdentityM(matrix, 0); + Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, 0.0f, 0.0f); + Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, 0.0f, rotateY, 0.0f); + Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, 0.0f, 0.0f); + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, mTranslationMatrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Apply the destination transition (just draw the image) + * + * @param matrix The model-view-projection matrix + */ + private void applyFinalTransition(float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Use our shader program + useProgram(1); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Apply the projection and view transformation + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, matrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Texture + FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + FloatBuffer positionBuffer = mTransitionTarget.getPositionBuffer(); + positionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Set the input texture + int textureHandle = mTransitionTarget.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[1], 0); + GLESUtil.glesCheckError("glUniform1i"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Method that prepares the internal vertex array + */ + private void setInternalVertex() { + float[] originalVertex = mTarget.getFrameVertex(); + System.arraycopy(originalVertex, 0, mVertex, 0, originalVertex.length); + } + + /** + * Return the scale amount to apply to the transition + * + * @return float The scale amount + */ + private float getAmount() { + return ((mTarget.getFrameWidth() * SCALE_AMOUNT) / 2); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/FadeTransition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/FadeTransition.java new file mode 100644 index 0000000..ce50fa4 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/FadeTransition.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLException; +import android.os.SystemClock; + +import org.cyanogenmod.wallpapers.photophase.Colors; +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.shapes.ColorShape; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +/** + * A transition that applies a fade transition to the picture. + */ +public class FadeTransition extends NullTransition { + + private static final float TRANSITION_TIME = 600.0f; + + private boolean mRunning; + private long mTime; + + ColorShape mOverlay; + + /** + * Constructor of FadeTransition + * + * @param ctx The current context + * @param tm The texture manager + */ + public FadeTransition(Context ctx, TextureManager tm) { + super(ctx, tm); + } + + /** + * {@inheritDoc} + */ + @Override + public TRANSITIONS getType() { + return TRANSITIONS.FADE; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasTransitionTarget() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return mRunning; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + super.reset(); + mTime = -1; + mRunning = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void select(PhotoFrame target) { + super.select(target); + mOverlay = new ColorShape(mContext, target.getFrameVertex(), Colors.getBackground()); + mOverlay.setAlpha(0); + } + + /** + * {@inheritDoc} + */ + @Override + public void apply(float[] matrix) throws GLException { + // Check internal vars + if (mTarget == null || + mTarget.getPositionBuffer() == null || + mTarget.getTextureBuffer() == null) { + return; + } + if (mTransitionTarget == null || + mTransitionTarget.getPositionBuffer() == null || + mTransitionTarget.getTextureBuffer() == null) { + return; + } + + // Set the time the first time + if (mTime == -1) { + mTime = SystemClock.uptimeMillis(); + } + + final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; + if (delta <= 0.5) { + // Draw the src target + draw(mTarget, matrix); + mOverlay.setAlpha(delta * 2.0f); + } else { + // Draw the dst target + draw(mTransitionTarget, matrix); + mOverlay.setAlpha((1 - delta) * 2.0f); + } + mOverlay.draw(matrix); + + // Transition ended + if (delta == 1) { + mRunning = false; + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/FlipTransition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/FlipTransition.java new file mode 100644 index 0000000..8d0bf79 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/FlipTransition.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLException; +import android.opengl.Matrix; +import android.os.SystemClock; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +import java.nio.FloatBuffer; + +/** + * A transition that applies a translation transition to the picture. + */ +public class FlipTransition extends Transition { + + /** + * The enumeration of all possibles translations movements + */ + public enum FLIP_MODES { + /** + * Flip the picture horizontally + */ + HORIZONTAL, + /** + * Flip the picture vertically + */ + VERTICAL + } + + private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; + private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; + + private static final float TRANSITION_TIME = 600.0f; + + private FLIP_MODES mMode; + + private float[] mTranslationMatrix; + + private boolean mRunning; + private long mTime; + + /** + * Constructor of FlipTransition + * + * @param ctx The current context + * @param tm The texture manager + */ + public FlipTransition(Context ctx, TextureManager tm) { + super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); + + // Initialized + mTranslationMatrix = new float[16]; + reset(); + } + + /** + * {@inheritDoc} + */ + @Override + public TRANSITIONS getType() { + return TRANSITIONS.FLIP; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasTransitionTarget() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return mRunning; + } + + /** + * {@inheritDoc} + */ + @Override + public void select(PhotoFrame target) { + super.select(target); + + // Random mode + FLIP_MODES[] modes = FLIP_MODES.values(); + int low = 0; + int high = modes.length - 1; + mMode = modes[Utils.getNextRandom(low, high)]; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectable(PhotoFrame frame) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + mTime = -1; + mRunning = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void apply(float[] matrix) throws GLException { + // Check internal vars + if (mTarget == null || + mTarget.getPositionBuffer() == null || + mTarget.getTextureBuffer() == null) { + return; + } + if (mTransitionTarget == null || + mTransitionTarget.getPositionBuffer() == null || + mTransitionTarget.getTextureBuffer() == null) { + return; + } + + // Set the time the first time + if (mTime == -1) { + mTime = SystemClock.uptimeMillis(); + } + + // Calculate the delta time + final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; + + // Apply the transition + applyTransition(delta, matrix, delta <= 0.5 ? mTarget : mTransitionTarget); + + // Transition ending + if (delta == 1) { + mRunning = false; + } + } + + /** + * Apply the transition + * + * @param delta The delta time + * @param matrix The model-view-projection matrix + * @param target The photo frame target + */ + private void applyTransition(float delta, float[] matrix, PhotoFrame target) { + // Retrieve the index of the structures + int index = delta <= 0.5f ? 0 : 1; + + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Set the program + useProgram(index); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the input texture + int textureHandle = target.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[index], 0); + GLESUtil.glesCheckError("glBindTexture"); + + // Texture + FloatBuffer textureBuffer = target.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[index], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[index]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + FloatBuffer positionBuffer = target.getPositionBuffer(); + positionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[index], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[index]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Calculate the delta angle and the translation and rotate parameters + float angle = (delta * 90) / 0.5f; + if (index == 1) { + angle = 90 - ((delta - 0.5f) * 90) / 0.5f; + } + float translateX = 0.0f; + float translateY = 0.0f; + float rotateX = 0.0f; + float rotateY = 0.0f; + switch (mMode) { + case HORIZONTAL: + rotateY = -1.0f; + translateX = (mTarget.getFrameVertex()[2] - ((mTarget.getFrameVertex()[2] - mTarget.getFrameVertex()[0]) / 2)) * -1; + break; + case VERTICAL: + rotateX = -1.0f; + translateY = (mTarget.getFrameVertex()[5] - ((mTarget.getFrameVertex()[5] - mTarget.getFrameVertex()[1]) / 2)) * -1; + break; + + default: + break; + } + + // Apply the projection and view transformation + Matrix.setIdentityM(matrix, 0); + Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, -translateY, 0.0f); + Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, rotateX, rotateY, 0.0f); + Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, translateY, 0.0f); + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[index], 1, false, mTranslationMatrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[index]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[index]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/NullTransition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/NullTransition.java new file mode 100644 index 0000000..24344d6 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/NullTransition.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLException; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +import java.nio.FloatBuffer; +/** + * A special transition that does nothing other than draw the {@link PhotoFrame} + * on the screen continually. No transition is done. + */ +public class NullTransition extends Transition { + + private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader}; + private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader}; + + /** + * Constructor of NullTransition + * + * @param ctx The current context + * @param tm The texture manager + */ + public NullTransition(Context ctx, TextureManager tm) { + super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); + } + + /** + * {@inheritDoc} + */ + @Override + public void select(PhotoFrame target) { + super.select(target); + } + + /** + * {@inheritDoc} + */ + @Override + public TRANSITIONS getType() { + return TRANSITIONS.NO_TRANSITION; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasTransitionTarget() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return mTarget == null || !mTarget.isLoaded(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectable(PhotoFrame frame) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + // Nothing to do + } + + /** + * {@inheritDoc} + */ + @Override + public void apply(float[] matrix) throws GLException { + // Check internal vars + if (mTarget == null || + mTarget.getPositionBuffer() == null || + mTarget.getTextureBuffer() == null) { + return; + } + + // Draw the current target + draw(mTarget, matrix); + } + + /** + * Method that draws the picture texture + * + * @param target The target to draw + * @param matrix The model-view-projection matrix + */ + protected void draw(PhotoFrame target, float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Use our shader program + useProgram(0); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Apply the projection and view transformation + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, matrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Texture + FloatBuffer textureBuffer = target.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + FloatBuffer positionBuffer = target.getPositionBuffer(); + positionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Set the input texture + int textureHandle = target.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[0], 0); + GLESUtil.glesCheckError("glUniform1i"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/SwapTransition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/SwapTransition.java new file mode 100644 index 0000000..d5cf15f --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/SwapTransition.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLException; +import android.os.SystemClock; + +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +/** + * A simple transition that swap an image after the transition time is ended. + */ +public class SwapTransition extends NullTransition { + + private static final float TRANSITION_TIME = 250.0f; + + private boolean mRunning; + private long mTime; + + /** + * Constructor of SwapTransition + * + * @param ctx The current context + * @param tm The texture manager + */ + public SwapTransition(Context ctx, TextureManager tm) { + super(ctx, tm); + } + + /** + * {@inheritDoc} + */ + @Override + public TRANSITIONS getType() { + return TRANSITIONS.SWAP; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasTransitionTarget() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return mRunning; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + super.reset(); + mTime = -1; + mRunning = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void apply(float[] matrix) throws GLException { + // Check internal vars + if (mTarget == null || + mTarget.getPositionBuffer() == null || + mTarget.getTextureBuffer() == null) { + return; + } + if (mTransitionTarget == null || + mTransitionTarget.getPositionBuffer() == null || + mTransitionTarget.getTextureBuffer() == null) { + return; + } + + // Set the time the first time + if (mTime == -1) { + mTime = SystemClock.uptimeMillis(); + } + + // Calculate the delta time + final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; + + // Apply the transition + boolean ended = delta == 1; + draw(ended ? mTransitionTarget : mTarget, matrix); + mRunning = !ended; + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/Transition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/Transition.java new file mode 100644 index 0000000..cdd5438 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/Transition.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLES20; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +/** + * The base class of all transitions that can be applied to the {@link PhotoFrame} classes. + */ +public abstract class Transition { + + public static final long MAX_TRANSTION_TIME = 1500L; + + protected final Context mContext; + private final TextureManager mTextureManager; + + protected int[] mProgramHandlers; + protected int[] mTextureHandlers; + protected int[] mPositionHandlers; + protected int[] mTextureCoordHandlers; + protected int[] mMVPMatrixHandlers; + + protected PhotoFrame mTarget; + protected PhotoFrame mTransitionTarget; + + private final int[] mVertexShader; + private final int[] mFragmentShader; + + /** + * Constructor of Transition + * + * @param ctx The current context + * @param tm The current texture manager + * @param vertexShader The vertex shaders of the programs + * @param fragmentShader The fragment shaders of the programs + */ + public Transition(Context ctx, TextureManager tm, int[] vertexShader, int[] fragmentShader) { + super(); + mContext = ctx; + mTextureManager = tm; + mVertexShader = vertexShader; + mFragmentShader = fragmentShader; + + // Compile every program + assert mVertexShader.length != mFragmentShader.length; + int cc = mVertexShader.length; + mProgramHandlers = new int[cc]; + mTextureHandlers = new int[cc]; + mPositionHandlers = new int[cc]; + mTextureCoordHandlers = new int[cc]; + mMVPMatrixHandlers = new int[cc]; + for (int i = 0; i < cc; i++) { + createProgram(i); + } + } + + /** + * Method that requests to apply this transition. + * + * @param target The target photo frame + */ + public void select(PhotoFrame target) { + mTarget = target; + if (hasTransitionTarget()) { + // Load the transition frame and request a picture for it + mTransitionTarget = + new PhotoFrame( + mContext, + mTextureManager, + mTarget.getFrameVertex(), + mTarget.getPhotoVertex(), + mTarget.getBackgroundColor()); + } + } + + /** + * Method that returns the target of the transition. + * + * @return PhotoFrame The target of the transition + */ + public PhotoFrame getTarget() { + return mTarget; + } + + /** + * Method that returns the transition target of the transition. + * + * @return PhotoFrame The transition target of the transition + */ + public PhotoFrame getTransitionTarget() { + return mTransitionTarget; + } + + /** + * Method that returns if the transition is selectable for the passed frame. + * + * @param frame The frame which the transition should be applied to + * @return boolean If the transition is selectable for the passed frame + */ + public abstract boolean isSelectable(PhotoFrame frame); + + /** + * Method that resets the current status of the transition. + */ + public abstract void reset(); + + /** + * Method that returns the type of transition. + * + * @return TRANSITIONS The type of transition + */ + public abstract TRANSITIONS getType(); + + /** + * Method that requests to apply this transition. + * + * @param matrix The model-view-projection matrix + */ + public abstract void apply(float[] matrix); + + /** + * Method that returns if the transition is being transition. + * + * @return boolean If the transition is being transition. + */ + public abstract boolean isRunning(); + + /** + * Method that return if the transition has a secondary target + * + * @return boolean If the transition has a secondary target + */ + public abstract boolean hasTransitionTarget(); + + /** + * Method that creates the program + */ + protected void createProgram(int index) { + mProgramHandlers[index] = + GLESUtil.createProgram( + mContext.getResources(), mVertexShader[index], mFragmentShader[index]); + mTextureHandlers[index] = + GLES20.glGetAttribLocation(mProgramHandlers[index], "sTexture"); + mPositionHandlers[index] = + GLES20.glGetAttribLocation(mProgramHandlers[index], "aPosition"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mTextureCoordHandlers[index] = + GLES20.glGetAttribLocation(mProgramHandlers[index], "aTextureCoord"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mMVPMatrixHandlers[index] = + GLES20.glGetUniformLocation(mProgramHandlers[index], "uMVPMatrix"); + GLESUtil.glesCheckError("glGetUniformLocation"); + } + + /** + * Method that set the program to use + * + * @param index The index of the program to use + */ + protected void useProgram(int index) { + if (!GLES20.glIsProgram(mProgramHandlers[index])) { + createProgram(index); + } + GLES20.glUseProgram(mProgramHandlers[index]); + GLESUtil.glesCheckError("glUseProgram()"); + } + + /** + * Method that requests to the transition to remove its internal references and resources. + */ + public void recycle() { + int cc = mProgramHandlers.length; + for (int i = 0; i < cc; i++) { + if (GLES20.glIsProgram(mProgramHandlers[i])) { + GLES20.glDeleteProgram(mProgramHandlers[i]); + GLESUtil.glesCheckError("glDeleteProgram"); + } + mProgramHandlers[i] = -1; + mTextureHandlers[i] = -1; + mPositionHandlers[i] = -1; + mTextureCoordHandlers[i] = -1; + mMVPMatrixHandlers[i] = -1; + } + mTransitionTarget = null; + mTarget = null; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/Transitions.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/Transitions.java new file mode 100644 index 0000000..86dfeb8 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/Transitions.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; + +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; + +import java.util.Arrays; +import java.util.List; + + +/** + * A class that manages all the supported transitions + */ +public class Transitions { + + /** + * Enumeration of the supported transitions + */ + public enum TRANSITIONS { + /** + * @see NullTransition + */ + NO_TRANSITION, + /** + * @see CubeTransition + */ + CUBE, + /** + * @see FadeTransition + */ + FADE, + /** + * @see FlipTransition + */ + FLIP, + /** + * @see SwapTransition + */ + SWAP, + /** + * @see TranslateTransition + */ + TRANSLATION, + /** + * @see WindowTransition + */ + WINDOW; + + /** + * Method that returns the transition from its ordinal position + * + * @param ordinal The ordinal position + * @return TRANSITIONS The transition or null if wasn't found + */ + public static TRANSITIONS fromOrdinal(int ordinal) { + for (TRANSITIONS transition : TRANSITIONS.values()) { + if (transition.ordinal() == ordinal) { + return transition; + } + } + return null; + } + + /** + * Method that the returns an array with all the valid transitions (NO_TRANSITION is not + * a valid one). + * + * @return TRANSITIONS[] The valid transitions + */ + public static TRANSITIONS[] getValidTranstions() { + TRANSITIONS[] src = TRANSITIONS.values(); + TRANSITIONS[] dst = new TRANSITIONS[src.length-1]; + System.arraycopy(src, 1, dst, 0, src.length-1); + return dst; + } + } + + /** + * Method that return the next type of transition to apply the picture. + * + * @param frame The frame which the translation will be applied to + * @return TRANSITIONS The next type of transition to apply + */ + public static TRANSITIONS getNextTypeOfTransition(PhotoFrame frame) { + // Get a transition based on the user preference + List transitions = + Arrays.asList(Preferences.General.Transitions.getTransitionTypes()); + TRANSITIONS nextTransition = null; + if (transitions.size() > 0) { + int low = 0; + int high = transitions.size() - 1; + int pos = Utils.getNextRandom(low, high); + nextTransition = transitions.get(pos); + } + if (nextTransition == null) { + return TRANSITIONS.NO_TRANSITION; + } + + // Select the transition if is available + if (nextTransition.compareTo(TRANSITIONS.SWAP) == 0) { + return TRANSITIONS.SWAP; + } else if (nextTransition.compareTo(TRANSITIONS.FADE) == 0) { + return TRANSITIONS.FADE; + } else if (nextTransition.compareTo(TRANSITIONS.TRANSLATION) == 0) { + return TRANSITIONS.TRANSLATION; + } else if (nextTransition.compareTo(TRANSITIONS.FLIP) == 0) { + return TRANSITIONS.FLIP; + } else if (nextTransition.compareTo(TRANSITIONS.WINDOW) == 0) { + return TRANSITIONS.WINDOW; + } else if (nextTransition.compareTo(TRANSITIONS.CUBE) == 0) { + return TRANSITIONS.CUBE; + } + return TRANSITIONS.NO_TRANSITION; + } + + /** + * Method that creates a new transition. + * + * @param ctx The current context + * @param tm The texture manager + * @param type The type of transition + * @param frame The frame which the translation will be applied to + * @return Transition The next transition to apply + */ + public static Transition createTransition( + Context ctx, TextureManager tm, TRANSITIONS type, PhotoFrame frame) { + if (type.compareTo(TRANSITIONS.SWAP) == 0) { + return new SwapTransition(ctx, tm); + } else if (type.compareTo(TRANSITIONS.FADE) == 0) { + return new FadeTransition(ctx, tm); + } else if (type.compareTo(TRANSITIONS.TRANSLATION) == 0) { + return new TranslateTransition(ctx, tm); + } else if (type.compareTo(TRANSITIONS.FLIP) == 0) { + return new FlipTransition(ctx, tm); + } else if (type.compareTo(TRANSITIONS.WINDOW) == 0) { + return new WindowTransition(ctx, tm); + } else if (type.compareTo(TRANSITIONS.CUBE) == 0) { + return new CubeTransition(ctx, tm); + } + return new NullTransition(ctx, tm); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/TranslateTransition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/TranslateTransition.java new file mode 100644 index 0000000..eb1080b --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/TranslateTransition.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLException; +import android.opengl.Matrix; +import android.os.SystemClock; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A transition that applies a translation transition to the picture. + */ +public class TranslateTransition extends Transition { + + /** + * The enumeration of all possibles translations movements + */ + public enum TRANSLATE_MODES { + /** + * Translate the picture from left to right + */ + LEFT_TO_RIGHT, + /** + * Translate the picture from right to left + */ + RIGHT_TO_LEFT, + /** + * Translate the picture from up to down + */ + UP_TO_DOWN, + /** + * Translate the picture from down to up + */ + DOWN_TO_UP + } + + private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; + private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; + + private static final float TRANSITION_TIME = 600.0f; + + private TRANSLATE_MODES mMode; + + private boolean mRunning; + private long mTime; + + /** + * Constructor of TranslateTransition + * + * @param ctx The current context + * @param tm The texture manager + */ + public TranslateTransition(Context ctx, TextureManager tm) { + super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); + + // Initialized + reset(); + } + + /** + * {@inheritDoc} + */ + @Override + public TRANSITIONS getType() { + return TRANSITIONS.TRANSLATION; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasTransitionTarget() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return mRunning; + } + + /** + * {@inheritDoc} + */ + @Override + public void select(PhotoFrame target) { + super.select(target); + + // Discard all non-supported modes + List modes = + new ArrayList( + Arrays.asList(TRANSLATE_MODES.values())); + float[] vertex = target.getFrameVertex(); + if (vertex[4] != -1.0f) { + modes.remove(TRANSLATE_MODES.RIGHT_TO_LEFT); + } + if (vertex[6] != 1.0f) { + modes.remove(TRANSLATE_MODES.LEFT_TO_RIGHT); + } + if (vertex[5] != 1.0f) { + modes.remove(TRANSLATE_MODES.DOWN_TO_UP); + } + if (vertex[1] != -1.0f) { + modes.remove(TRANSLATE_MODES.UP_TO_DOWN); + } + + // Random mode + int low = 0; + int high = modes.size() - 1; + mMode = modes.get(Utils.getNextRandom(low, high)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectable(PhotoFrame frame) { + float[] vertex = frame.getFrameVertex(); + if (vertex[4] == -1.0f || vertex[6] == 1.0f || + vertex[5] == 1.0f || vertex[1] == -1.0f) { + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + mTime = -1; + mRunning = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void apply(float[] matrix) throws GLException { + // Check internal vars + if (mTarget == null || + mTarget.getPositionBuffer() == null || + mTarget.getTextureBuffer() == null) { + return; + } + if (mTransitionTarget == null || + mTransitionTarget.getPositionBuffer() == null || + mTransitionTarget.getTextureBuffer() == null) { + return; + } + + // Set the time the first time + if (mTime == -1) { + mTime = SystemClock.uptimeMillis(); + } + + // Calculate the delta time + final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; + + // Apply the transition + applyTransitionToDst(delta, matrix); + if (delta < 1) { + applyTransitionToSrc(delta, matrix); + } + + // Transition ending + if (delta == 1) { + mRunning = false; + } + } + + /** + * Apply the transition to the source frame + * + * @param delta The delta time + * @param matrix The model-view-projection matrix + */ + private void applyTransitionToSrc(float delta, float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Set the program + useProgram(0); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the input texture + int textureHandle = mTarget.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[0], 0); + GLESUtil.glesCheckError("glBindTexture"); + + // Texture + FloatBuffer textureBuffer = mTarget.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + FloatBuffer positionBuffer = mTarget.getPositionBuffer(); + positionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Calculate the delta distance + float distance = + (mMode.compareTo(TRANSLATE_MODES.LEFT_TO_RIGHT) == 0 || mMode.compareTo(TRANSLATE_MODES.RIGHT_TO_LEFT) == 0) + ? mTarget.getFrameWidth() + : mTarget.getFrameHeight(); + if (mMode.compareTo(TRANSLATE_MODES.RIGHT_TO_LEFT) == 0 || mMode.compareTo(TRANSLATE_MODES.DOWN_TO_UP) == 0) { + distance *= -1; + } + distance *= delta; + boolean vertical = (mMode.compareTo(TRANSLATE_MODES.UP_TO_DOWN) == 0 || mMode.compareTo(TRANSLATE_MODES.DOWN_TO_UP) == 0); + + // Apply the projection and view transformation + float[] translationMatrix = new float[16]; + if (vertical) { + Matrix.translateM(translationMatrix, 0, matrix, 0, 0.0f, distance, 0.0f); + } else { + Matrix.translateM(translationMatrix, 0, matrix, 0, distance, 0.0f, 0.0f); + } + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, translationMatrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Apply the transition to the destination frame + * + * @param delta The delta time + * @param matrix The model-view-projection matrix + */ + private void applyTransitionToDst(float delta, float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Set the program + useProgram(1); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the input texture + int textureHandle = mTransitionTarget.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[1], 0); + GLESUtil.glesCheckError("glUniform1i"); + + // Texture + FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + FloatBuffer positionBuffer = mTransitionTarget.getPositionBuffer(); + positionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Apply the projection and view transformation + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, matrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/transitions/WindowTransition.java b/src/org/cyanogenmod/wallpapers/photophase/transitions/WindowTransition.java new file mode 100644 index 0000000..b09cfaa --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/transitions/WindowTransition.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.transitions; + +import android.content.Context; +import android.opengl.GLES20; +import android.opengl.GLException; +import android.opengl.Matrix; +import android.os.SystemClock; +import android.view.animation.AccelerateInterpolator; + +import org.cyanogenmod.wallpapers.photophase.utils.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.utils.Utils; +import org.cyanogenmod.wallpapers.photophase.PhotoFrame; +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.TextureManager; +import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A transition that applies a window effect transition to the picture. + */ +public class WindowTransition extends Transition { + + /** + * The enumeration of all possibles window movements + */ + public enum WINDOW_MODES { + /** + * Open the picture from left to right + */ + LEFT_TO_RIGHT, + /** + * Open the picture from right to left + */ + RIGHT_TO_LEFT + } + + private static final int[] VERTEX_SHADER = {R.raw.default_vertex_shader, R.raw.default_vertex_shader}; + private static final int[] FRAGMENT_SHADER = {R.raw.default_fragment_shader, R.raw.default_fragment_shader}; + + private static final float TRANSITION_TIME = 800.0f; + + private static final float SCALE_AMOUNT = 0.2f; + + private WINDOW_MODES mMode; + + private float[] mTranslationMatrix; + + private boolean mRunning; + private long mTime; + + private AccelerateInterpolator mInterpolation; + private float mAmount; + + /** + * Constructor of WindowTransition + * + * @param ctx The current context + * @param tm The texture manager + */ + public WindowTransition(Context ctx, TextureManager tm) { + super(ctx, tm, VERTEX_SHADER, FRAGMENT_SHADER); + + // Initialized + mTranslationMatrix = new float[16]; + reset(); + } + + /** + * {@inheritDoc} + */ + @Override + public TRANSITIONS getType() { + return TRANSITIONS.WINDOW; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasTransitionTarget() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRunning() { + return mRunning; + } + + /** + * {@inheritDoc} + */ + @Override + public void select(PhotoFrame target) { + super.select(target); + mInterpolation = new AccelerateInterpolator(); + mAmount = getAmount(); + + // Discard all non-supported modes + List modes = + new ArrayList( + Arrays.asList(WINDOW_MODES.values())); + float[] vertex = target.getFrameVertex(); + if (vertex[4] != -1.0f) { + modes.remove(WINDOW_MODES.RIGHT_TO_LEFT); + } + if (vertex[6] != 1.0f) { + modes.remove(WINDOW_MODES.LEFT_TO_RIGHT); + } + + // Random mode + int low = 0; + int high = modes.size() - 1; + mMode = modes.get(Utils.getNextRandom(low, high)); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSelectable(PhotoFrame frame) { + float[] vertex = frame.getFrameVertex(); + if (vertex[4] == -1.0f || vertex[6] == 1.0f) { + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + mTime = -1; + mRunning = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void apply(float[] matrix) throws GLException { + // Check internal vars + if (mTarget == null || + mTarget.getPositionBuffer() == null || + mTarget.getTextureBuffer() == null) { + return; + } + if (mTransitionTarget == null || + mTransitionTarget.getPositionBuffer() == null || + mTransitionTarget.getTextureBuffer() == null) { + return; + } + + // Set the time the first time + if (mTime == -1) { + mTime = SystemClock.uptimeMillis(); + } + + // Calculate the delta time + final float delta = Math.min(SystemClock.uptimeMillis() - mTime, TRANSITION_TIME) / TRANSITION_TIME; + + // Apply the transition + applyDstTransition(matrix); + if (delta < 1) { + applySrcTransition(delta, matrix); + } + + // Transition ending + if (delta == 1) { + mRunning = false; + } + } + + /** + * Apply the source transition + * + * @param delta The delta time + * @param matrix The model-view-projection matrix + * @param target The photo frame target + */ + private void applySrcTransition(float delta, float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Set the program + useProgram(0); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the input texture + int textureHandle = mTarget.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[0], 0); + GLESUtil.glesCheckError("glBindTexture"); + + // Texture + FloatBuffer textureBuffer = mTarget.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[0], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + float[] vertex = cloneVertex(); + float interpolation = mInterpolation.getInterpolation(delta); + switch (mMode) { + case LEFT_TO_RIGHT: + vertex[1] -= interpolation * mAmount; + vertex[5] += interpolation * mAmount; + break; + case RIGHT_TO_LEFT: + vertex[3] -= interpolation * mAmount; + vertex[7] += interpolation * mAmount; + break; + default: + break; + } + ByteBuffer bb = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float) + bb.order(ByteOrder.nativeOrder()); + FloatBuffer positionBuffer = bb.asFloatBuffer(); + positionBuffer.put(vertex); + positionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[0], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Calculate the delta angle and the translation and rotate parameters + float angle = 0.0f; + float translateX = 0.0f; + float rotateY = 0.0f; + switch (mMode) { + case LEFT_TO_RIGHT: + angle = delta * 90; + rotateY = -1.0f; + translateX = mTarget.getFrameVertex()[2] * -1; + break; + case RIGHT_TO_LEFT: + angle = delta * -90; + rotateY = -1.0f; + translateX = mTarget.getFrameVertex()[0] * -1; + break; + + default: + break; + } + + // Apply the projection and view transformation + Matrix.setIdentityM(matrix, 0); + Matrix.translateM(mTranslationMatrix, 0, matrix, 0, -translateX, 0.0f, 0.0f); + Matrix.rotateM(mTranslationMatrix, 0, mTranslationMatrix, 0, angle, 0.0f, rotateY, 0.0f); + Matrix.translateM(mTranslationMatrix, 0, mTranslationMatrix, 0, translateX, 0.0f, 0.0f); + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[0], 1, false, mTranslationMatrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[0]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Apply the destination transition (just draw the image) + * + * @param matrix The model-view-projection matrix + */ + private void applyDstTransition(float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Use our shader program + useProgram(1); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Apply the projection and view transformation + GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[1], 1, false, matrix, 0); + GLESUtil.glesCheckError("glUniformMatrix4fv"); + + // Texture + FloatBuffer textureBuffer = mTransitionTarget.getTextureBuffer(); + textureBuffer.position(0); + GLES20.glVertexAttribPointer(mTextureCoordHandlers[1], 2, GLES20.GL_FLOAT, false, 0, textureBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Position + FloatBuffer positionBuffer = mTransitionTarget.getPositionBuffer(); + positionBuffer.position(0); + GLES20.glVertexAttribPointer(mPositionHandlers[1], 2, GLES20.GL_FLOAT, false, 0, positionBuffer); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Set the input texture + int textureHandle = mTransitionTarget.getTextureHandle(); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTextureHandlers[1], 0); + GLESUtil.glesCheckError("glUniform1i"); + + // Draw + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawElements"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mPositionHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[1]); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Method that copy the vertex array + * + * @return The copy of the vertex + */ + private float[] cloneVertex() { + float[] originalVertex = mTarget.getFrameVertex(); + float[] vertex = new float[originalVertex.length]; + System.arraycopy(originalVertex, 0, vertex, 0, originalVertex.length); + return vertex; + } + + /** + * Return the scale amount to apply to the transition + * + * @return float The scale amount + */ + private float getAmount() { + return ((mTarget.getFrameWidth() * SCALE_AMOUNT) / 2); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/utils/BitmapUtils.java b/src/org/cyanogenmod/wallpapers/photophase/utils/BitmapUtils.java new file mode 100644 index 0000000..f7b32b5 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/utils/BitmapUtils.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.utils; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; +import android.graphics.Matrix; +import android.media.ExifInterface; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * A helper class for deal with Bitmaps + */ +public class BitmapUtils { + + /** + * Method that decodes a bitmap + * + * @param bitmap The bitmap buffer to decode + * @return Bitmap The decoded bitmap + */ + public static Bitmap decodeBitmap(InputStream bitmap) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferQualityOverSpeed = false; + options.inPreferredConfig = Bitmap.Config.RGB_565; + return BitmapFactory.decodeStream(bitmap, null, options); + } + + /** + * Method that decodes a bitmap + * + * @param file The bitmap file to decode + * @param reqWidth The request width + * @param reqHeight The request height + * @return Bitmap The decoded bitmap + */ + public static Bitmap decodeBitmap(File file, int reqWidth, int reqHeight) { + // First decode with inJustDecodeBounds=true to check dimensions + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(file.getAbsolutePath(), options); + + // Calculate inSampleSize (use 1024 as maximum size, the minimum supported + // by all the gles20 devices) + options.inSampleSize = calculateBitmapRatio( + options, + Math.min(reqWidth, 1024), + Math.min(reqHeight, 1024)); + + // Decode the bitmap with inSampleSize set + options.inJustDecodeBounds = false; + options.inPreferQualityOverSpeed = false; + options.inPurgeable = true; + options.inInputShareable = true; + options.inDither = true; + Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options); + if (bitmap == null) { + return null; + } + + // Test if the bitmap has exif format, and decode properly + Bitmap out = decodeExifBitmap(file, bitmap); + if (!out.equals(bitmap)) { + bitmap.recycle(); + bitmap = null; + } + return out; + } + + /** + * Method that decodes an Exif bitmap + * + * @param file The file to decode + * @param src The bitmap reference + * @return Bitmap The decoded bitmap + */ + private static Bitmap decodeExifBitmap(File file, Bitmap src) { + try { + // Try to load the bitmap as a bitmap file + ExifInterface exif = new ExifInterface(file.getAbsolutePath()); + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); + if (orientation == 0) { + return src; + } + Matrix matrix = new Matrix(); + if (orientation == 6) { + matrix.postRotate(90); + } else if (orientation == 3) { + matrix.postRotate(180); + } else if (orientation == 8) { + matrix.postRotate(270); + } + // Rotate the bitmap + return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); + } catch (IOException e) { + // Ignore + } + return src; + } + + /** + * Method that calculate the bitmap size prior to decode + * + * @param options The bitmap factory options + * @param reqWidth The request width + * @param reqHeight The request height + * @return int The picture ratio + */ + private static int calculateBitmapRatio(Options options, int reqWidth, int reqHeight) { + // Raw height and width of image + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + // Calculate ratios of height and width to requested height and width + final int heightRatio = Math.round((float) height / (float) reqHeight); + final int widthRatio = Math.round((float) width / (float) reqWidth); + + // Choose the smallest ratio as inSampleSize value, this will guarantee + // a final image with both dimensions larger than or equal to the + // requested height and width. + inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; + } + + return inSampleSize; + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java b/src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java new file mode 100644 index 0000000..ddd8d3a --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.utils; + +import android.graphics.Rect; + +import org.cyanogenmod.wallpapers.photophase.model.Disposition; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A helper class with disposition utils + */ +public final class DispositionUtil { + + /** + * Method that converts a disposition string to a disposition reference + * + * @param value The value to convert + * @return List The dispositions reference + */ + public static List toDispositions(String value) { + String[] v = value.split("\\|"); + List dispositions = new ArrayList(v.length); + for (String s : v) { + String[] s1 = s.split(":"); + String[] s2 = s1[0].split("x"); + String[] s3 = s1[1].split("x"); + Disposition disposition = new Disposition(); + disposition.x = Integer.parseInt(s2[0]); + disposition.y = Integer.parseInt(s2[1]); + disposition.w = Integer.parseInt(s3[0]) - disposition.x + 1; + disposition.h = Integer.parseInt(s3[1]) - disposition.y + 1; + dispositions.add(disposition); + } + Collections.sort(dispositions); + return dispositions; + } + + /** + * Method that converts a disposition reference to a disposition string + * + * @param dispositions The value to convert + * @return String The dispositions string + */ + public static String fromDispositions(List dispositions) { + Collections.sort(dispositions); + StringBuilder sb = new StringBuilder(); + int count = dispositions.size(); + for (int i = 0; i < count; i++) { + Disposition disposition = dispositions.get(i); + sb.append(disposition.x) + .append("x") + .append(disposition.y) + .append(":") + .append(disposition.x + disposition.w - 1) + .append("x") + .append(disposition.y + disposition.h - 1); + if (i < (count - 1)) { + sb.append("|"); + } + } + return sb.toString(); + } + + /** + * Method that transform the disposition to a byte matrix + * + * @param dispositions The + * @return byte[][] The boolean matrix of the disposition + */ + public static byte[][] toMatrix(List dispositions, int cols, int rows) { + byte[][] matrix = new byte[rows][cols]; + for (Disposition disposition : dispositions) { + int count = disposition.y + disposition.h; + for (int row = disposition.y; row < count; row++) { + int count2 = disposition.x + disposition.w; + for (int col = disposition.x; col < count2; col++) { + matrix[row][col] = 1; + } + } + } + return matrix; + } + + /** + * Method that returns a disposition from a {@link Rect} reference + * + * @return Disposition The disposition + */ + public static Disposition fromRect(Rect r) { + Disposition disposition = new Disposition(); + disposition.x = r.left; + disposition.y = r.top; + disposition.w = r.width(); + disposition.h = r.height(); + return disposition; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/utils/GLESUtil.java b/src/org/cyanogenmod/wallpapers/photophase/utils/GLESUtil.java new file mode 100644 index 0000000..4707431 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/utils/GLESUtil.java @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.utils; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Rect; +import android.media.effect.Effect; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.util.Log; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + + +/** + * A helper class with some useful methods for deal with GLES. + */ +public final class GLESUtil { + + private static final String TAG = "GLESUtil"; + + private static final boolean DEBUG = false; + + private static final Object sSync = new Object(); + + /** + * A helper class to deal with OpenGL float colors. + */ + public static class GLColor { + + private static final float MAX_COLOR = 255.0f; + + /** + * Red + */ + public float r; + /** + * Green + */ + public float g; + /** + * Blue + */ + public float b; + /** + * Alpha + */ + public float a; + + /** + * Constructor of GLColor from ARGB + * + * @param a Alpha + * @param r Red + * @param g Green + * @param b Alpha + */ + public GLColor(int a, int r, int g, int b) { + this.a = a / MAX_COLOR; + this.r = r / MAX_COLOR; + this.g = g / MAX_COLOR; + this.b = b / MAX_COLOR; + } + + /** + * Constructor of GLColor from ARGB. + * + * @param argb An #AARRGGBB string + */ + public GLColor(String argb) { + int color = Color.parseColor(argb); + this.a = Color.alpha(color) / MAX_COLOR; + this.r = Color.red(color) / MAX_COLOR; + this.g = Color.green(color) / MAX_COLOR; + this.b = Color.blue(color) / MAX_COLOR; + } + + /** + * Constructor of GLColor from ARGB. + * + * @param argb An #AARRGGBB number + */ + public GLColor(int argb) { + this.a = Color.alpha(argb) / MAX_COLOR; + this.r = Color.red(argb) / MAX_COLOR; + this.g = Color.green(argb) / MAX_COLOR; + this.b = Color.blue(argb) / MAX_COLOR; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(a); + result = prime * result + Float.floatToIntBits(b); + result = prime * result + Float.floatToIntBits(g); + result = prime * result + Float.floatToIntBits(r); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + GLColor other = (GLColor) obj; + if (Float.floatToIntBits(a) != Float.floatToIntBits(other.a)) + return false; + if (Float.floatToIntBits(b) != Float.floatToIntBits(other.b)) + return false; + if (Float.floatToIntBits(g) != Float.floatToIntBits(other.g)) + return false; + if (Float.floatToIntBits(r) != Float.floatToIntBits(other.r)) + return false; + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "#"+Integer.toHexString(Color.argb((int)a, (int)r, (int)g, (int)b)); + } + } + + /** + * Class that holds some information about a GLES texture + */ + public static class GLESTextureInfo { + /** + * Handle of the texture + */ + public int handle = 0; + /** + * The bitmap reference + */ + public Bitmap bitmap; + /** + * The path to the texture + */ + public File path; + /** + * The effect to apply + */ + public Effect effect; + } + + /** + * Method that load a vertex shader and returns its handler identifier. + * + * @param src The source shader + * @return int The handler identifier of the shader + */ + public static int loadVertexShader(String src) { + return loadShader(src, GLES20.GL_VERTEX_SHADER); + } + + /** + * Method that load a fragment shader and returns its handler identifier. + * + * @param src The source shader + * @return int The handler identifier of the shader + */ + public static int loadFragmentShader(String src) { + return loadShader(src, GLES20.GL_FRAGMENT_SHADER); + } + + /** + * Method that load a shader and returns its handler identifier. + * + * @param src The source shader + * @param type The type of shader + * @return int The handler identifier of the shader + */ + public static int loadShader(String src, int type) { + int[] compiled = new int[1]; + // Create, load and compile the shader + int shader = GLES20.glCreateShader(type); + GLESUtil.glesCheckError("glCreateShader"); + if (shader <= 0) { + Log.e(TAG, "Cannot create a shader"); + return 0; + } + GLES20.glShaderSource(shader, src); + GLESUtil.glesCheckError("glShaderSource"); + GLES20.glCompileShader(shader); + GLESUtil.glesCheckError("glesCheckError"); + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); + GLESUtil.glesCheckError("glesCheckError"); + if (compiled[0] <= 0) { + String msg = "Shader compilation error trace:\n" + GLES20.glGetShaderInfoLog(shader); + Log.e(TAG, msg); + return 0; + } + return shader; + } + + /** + * Method that create a new program from its shaders (vertex and fragment) + * + * @param res A resources reference + * @param vertexShaderId The vertex shader glsl resource + * @param fragmentShaderId The fragment shader glsl resource + * @return int The handler identifier of the program + */ + public static int createProgram(Resources res, int vertexShaderId, int fragmentShaderId) { + return createProgram( + readResource(res, vertexShaderId), + readResource(res, fragmentShaderId)); + } + + /** + * Method that create a new program from its shaders (vertex and fragment) + * + * @param vertexShaderSrc The vertex shader + * @param fragmentShaderSrc The fragment shader + * @return int The handler identifier of the program. + */ + public static int createProgram(String vertexShaderSrc, String fragmentShaderSrc) { + int vshader = 0; + int fshader = 0; + int progid = 0; + int[] link = new int[1]; + + try { + // Check that we have valid shaders + if (vertexShaderSrc == null || fragmentShaderSrc == null) { + return 0; + } + + // Load the vertex and fragment shaders + vshader = loadVertexShader(vertexShaderSrc); + fshader = loadFragmentShader(fragmentShaderSrc); + + // Create the programa ref + progid = GLES20.glCreateProgram(); + GLESUtil.glesCheckError("glCreateProgram"); + if (progid <= 0) { + String msg = "Cannot create a program"; + Log.e(TAG, msg); + return 0; + } + + // Attach the shaders + GLES20.glAttachShader(progid, vshader); + GLESUtil.glesCheckError("glAttachShader"); + GLES20.glAttachShader(progid, fshader); + GLESUtil.glesCheckError("glAttachShader"); + + // Link the program + GLES20.glLinkProgram(progid); + GLESUtil.glesCheckError("glLinkProgram"); + + GLES20.glGetProgramiv(progid, GLES20.GL_LINK_STATUS, link, 0); + GLESUtil.glesCheckError("glGetProgramiv"); + if (link[0] <= 0) { + String msg = "Program compilation error trace:\n" + GLES20.glGetProgramInfoLog(progid); + Log.e(TAG, msg); + return 0; + } + + // Return the program + return progid; + + } finally { + // Delete the shaders + if (vshader != 0) { + GLES20.glDeleteShader(vshader); + GLESUtil.glesCheckError("glDeleteShader"); + } + if (fshader != 0) { + GLES20.glDeleteShader(fshader); + GLESUtil.glesCheckError("glDeleteShader"); + } + } + } + + /** + * Method that loads a texture from a file. + * + * @param file The image file + * @param dimensions The desired dimensions + * @param effect The effect to apply to the image or null if no effect is needed + * @param dimen The new dimensions + * @param recycle If the bitmap should be recycled + * @return GLESTextureInfo The texture info + */ + public static GLESTextureInfo loadTexture( + File file, Rect dimensions, Effect effect, Rect dimen, boolean recycle) { + Bitmap bitmap = null; + try { + // Decode and associate the bitmap (invert the desired dimensions) + bitmap = BitmapUtils.decodeBitmap(file, dimensions.height(), dimensions.width()); + if (bitmap == null) { + Log.e(TAG, "Failed to decode the file bitmap"); + return new GLESTextureInfo(); + } + + if (DEBUG) Log.d(TAG, "image: " + file.getAbsolutePath()); + GLESTextureInfo ti = loadTexture(bitmap, effect, dimen); + ti.path = file; + return ti; + + } catch (Exception e) { + String msg = "Failed to generate a valid texture from file: " + file.getAbsolutePath(); + Log.e(TAG, msg, e); + return new GLESTextureInfo(); + + } finally { + // Recycle the bitmap + if (bitmap != null && recycle) { + bitmap.recycle(); + bitmap = null; + } + } + } + + /** + * Method that loads a texture from a resource context. + * + * @param ctx The current context + * @param resourceId The resource identifier + * @param effect The effect to apply to the image or null if no effect is needed + * @param dimen The new dimensions + * @param recycle If the bitmap should be recycled + * @return GLESTextureInfo The texture info + */ + public static GLESTextureInfo loadTexture( + Context ctx, int resourceId, Effect effect, Rect dimen, boolean recycle) { + Bitmap bitmap = null; + InputStream raw = null; + try { + // Decode and associate the bitmap + raw = ctx.getResources().openRawResource(resourceId); + bitmap = BitmapUtils.decodeBitmap(raw); + if (bitmap == null) { + String msg = "Failed to decode the resource bitmap"; + Log.e(TAG, msg); + return new GLESTextureInfo(); + } + + if (DEBUG) Log.d(TAG, "resourceId: " + resourceId); + GLESTextureInfo ti = loadTexture(bitmap, effect, dimen); + return ti; + + } catch (Exception e) { + String msg = "Failed to generate a valid texture from resource: " + resourceId; + Log.e(TAG, msg, e); + return new GLESTextureInfo(); + + } finally { + // Close the buffer + try { + if (raw != null) { + raw.close(); + } + } catch (IOException e) { + // Ignore. + } + // Recycle the bitmap + if (bitmap != null && recycle) { + bitmap.recycle(); + bitmap = null; + } + } + } + + /** + * Method that loads texture from a bitmap reference. + * + * @param bitmap The bitmap reference + * @param effect The effect to apply to the image or null if no effect is needed + * @param dimen The new dimensions + * @return GLESTextureInfo The texture info + */ + public static GLESTextureInfo loadTexture(Bitmap bitmap, Effect effect, Rect dimen) { + // Check that we have a valid image name reference + if (bitmap == null) { + return new GLESTextureInfo(); + } + + int num = effect == null ? 1 : 2; + + int[] textureHandles = new int[num]; + GLES20.glGenTextures(num, textureHandles, 0); + GLESUtil.glesCheckError("glGenTextures"); + if (textureHandles[0] <= 0 || (effect != null && textureHandles[1] <= 0)) { + Log.e(TAG, "Failed to generate a valid texture"); + return new GLESTextureInfo(); + } + + // Bind the texture to the name + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[0]); + GLESUtil.glesCheckError("glBindTexture"); + + // Set the texture properties + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); + GLESUtil.glesCheckError("glTexParameteri"); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + GLESUtil.glesCheckError("glTexParameteri"); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLESUtil.glesCheckError("glTexParameteri"); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLESUtil.glesCheckError("glTexParameteri"); + + // Load the texture + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); + if (!GLES20.glIsTexture(textureHandles[0])) { + Log.e(TAG, "Failed to load a valid texture"); + return new GLESTextureInfo(); + } + + // Has a effect? + int handle = textureHandles[0]; + if (effect != null) { + // Apply the effect (we need a thread-safe call here) + synchronized (sSync) { + // No more than 1024 (the minimum supported by all the gles20 devices) + int w = Math.min(dimen.width(), 1024); + int h = Math.min(dimen.width(), 1024); + effect.apply(textureHandles[0], w, h, textureHandles[1]); + } + handle = textureHandles[1]; + + // Delete the unused texture + int[] textures = {textureHandles[0]}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + + // Return the texture handle identifier and the associated info + GLESTextureInfo ti = new GLESTextureInfo(); + ti.handle = handle; + ti.bitmap = bitmap; + ti.path = null; + return ti; + } + + /** + * Method that checks if an GLES error is present + * + * @param func The GLES function to check + * @return boolean If there was an error + */ + public static boolean glesCheckError(String func) { + int error = GLES20.glGetError(); + if (error != 0) { + Log.e(TAG, "GLES20 Error (" + glesGetErrorModule() + ") (" + func + "): " + + GLUtils.getEGLErrorString(error)); + return true; + } + return false; + } + + /** + * Method that returns the line and module that generates the current error + * + * @return String The line and module + */ + private static String glesGetErrorModule() { + try { + return String.valueOf(Thread.currentThread().getStackTrace()[4]); + } catch (IndexOutOfBoundsException ioobEx) { + // Ignore + } + return ""; + } + + /** + * Method that read a resource. + * + * @param res The resources reference + * @param resId The resource identifier + * @return String The shader source + * @throws IOException If an error occurs while loading the resource + */ + private static String readResource(Resources res, int resId) { + Reader reader = new InputStreamReader(res.openRawResource(resId)); + try { + final int BUFFER = 1024; + char[] data = new char[BUFFER]; + int read = 0; + StringBuilder sb = new StringBuilder(); + while ((read = reader.read(data, 0, BUFFER)) != -1) { + sb.append(data, 0, read); + } + return sb.toString(); + } catch (Exception e) { + Log.e(TAG, "Failed to read the resource " + resId); + return null; + } finally { + try { + reader.close(); + } catch (Exception ex) { + // Ignore + } + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java b/src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java new file mode 100644 index 0000000..53d44f3 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.utils; + +import android.graphics.Rect; +import java.util.Stack; + +/** + * The maximal empty rectangle algorithm that allows to find the rectangle with the maximal + * area that could be create in empty areas (in this case 0 in a byte matrix) + */ +// +// Based on the source discussed at http://discuss.leetcode.com/questions/260/maximal-rectangle +// +public final class MERAlgorithm { + + /** + * Method that returns the maximal empty rectangle (MER) for a matrix of bytes (1/0) + * + * @param matrix The matrix + * @return Rect The maximal empty rectangle + */ + public static Rect getMaximalEmptyRectangle(byte[][] matrix) { + // Check matrix + int rows = matrix.length; + if (rows == 0) return null; + int cols = matrix[0].length; + + // Convert to histogram + int[][] histogram = toHistogram(matrix); + + // Find the maximal area of every histogram + Rect maxRect = new Rect(); + for (int i = 0; i < rows; ++i) { + Rect rect = maximalRectangle(histogram[i], i); + if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { + maxRect = rect; + } + } + return ensureBounds(maxRect, cols, rows); + } + + /** + * Method that ensure the bounds of the max rectangle + * + * @param rect The rectangle to check + * @param cols The number of cols + * @param rows The number of rows + * @return Rect The rectangle checked + */ + private static Rect ensureBounds(Rect rect, int cols, int rows) { + if (rect.right - rect.left >= cols) rect.right = cols; + if (rect.bottom - rect.top >= rows) rect.bottom = rows; + return rect; + } + + /** + * Method that returns the maximal rectangle for an histogram of areas + * + * @return Rect The maximal rectangle histogram/area + */ + @SuppressWarnings("boxing") + private static Rect maximalRectangle(int[] histogram, int row) { + Stack stack = new Stack(); + int length = histogram.length; + Rect maxRect = new Rect(); + int i = 0; + while (i < length) { + if (stack.isEmpty() || histogram[i] >= histogram[stack.peek()]) { + stack.push(i++); + } else { + Rect rect = new Rect(); + rect.left = stack.pop(); + rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1)); + rect.top = row - histogram[rect.left] + 1; + rect.bottom = rect.top + histogram[rect.left]; + if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { + maxRect = rect; + } + } + } + while (!stack.isEmpty()) { + Rect rect = new Rect(); + rect.left = stack.pop(); + rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1)); + rect.top = row - histogram[rect.left] + 1; + rect.bottom = rect.top + histogram[rect.left]; + if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { + maxRect = rect; + } + } + return maxRect; + } + + /** + * Method that converts the empty areas to a histogram + * + * @param matrix The matrix where to find the MER + * return int[][] The histogram of empty areas + */ + private static int[][] toHistogram(byte[][] matrix) { + int rows = matrix.length; + int cols = matrix[0].length; + int[][] histogram = new int[rows][cols]; + for (int h=0; h < cols; h++) { + if (matrix[0][h] == 0) { + histogram[0][h] = 1; + } + } + for (int w=1; w < rows; w++) { + for (int h=0; h < cols; h++) { + if (matrix[w][h] == 1) { + continue; + } + histogram[w][h] = histogram[w-1][h] + 1; + } + } + return histogram; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/utils/Utils.java b/src/org/cyanogenmod/wallpapers/photophase/utils/Utils.java new file mode 100644 index 0000000..65d7f97 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/utils/Utils.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.utils; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.RectF; +import android.util.DisplayMetrics; + +import java.util.Random; + +/** + * A helper class with utilities + */ +public class Utils { + + private static Random sRandom = new Random(); + + /** + * This method converts dp unit to equivalent device specific value in pixels. + * + * @param ctx The current context + * @param dp A value in dp (Device independent pixels) unit + * @return float A float value to represent Pixels equivalent to dp according to device + */ + public static float convertDpToPixel(Context ctx, float dp) { + Resources resources = ctx.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + return dp * (metrics.densityDpi / 160f); + } + + /** + * Used to determine if the device is a tablet or not + * + * @param context The {@link Context} to use. + * @return True if the device is a tablet, false otherwise. + */ + public static final boolean isTablet(final Context context) { + final int layout = context.getResources().getConfiguration().screenLayout; + return (layout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + /** + * Method that converts a rect from a vertex data + * + * @param vertex The vertex array + * @return RectF The rect data + */ + public static RectF rectFromVertex(float[] vertex) { + RectF rect = new RectF(); + rect.left = vertex[0]; + rect.top = vertex[7]; + rect.right = vertex[6]; + rect.bottom = vertex[1]; + return rect; + } + + /** + * Method that returns a random number between two numbers. + * + * @param low The low number + * @param high The high number + * @return int The random number + */ + public static int getNextRandom(int low, int high) { + return low + (int)(sRandom.nextDouble() * ((high - low) + 1)); + } +} \ No newline at end of file diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java new file mode 100644 index 0000000..8e335c3 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.content.res.Resources; +import android.os.AsyncTask.Status; +import android.util.AttributeSet; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageView; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.model.Album; +import org.cyanogenmod.wallpapers.photophase.tasks.AsyncPictureLoaderTask; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A view that contains the info about an album + */ +public class AlbumInfo extends RelativeLayout + implements OnClickListener, OnMenuItemClickListener { + + /** + * A convenient listener for receive events of the AlbumPictures class + * + */ + public interface CallbacksListener { + /** + * Invoked when an album was selected + * + * @param album The album + */ + void onAlbumSelected(Album album); + + /** + * Invoked when an album was deselected + * + * @param album The album + */ + void onAlbumDeselected(Album album); + } + + private List mCallbacks; + + /*package*/ Album mAlbum; + + /*package*/ AsyncPictureLoaderTask mTask; + + /*package*/ ImageView mIcon; + private TextView mSelectedItems; + private TextView mName; + private TextView mItems; + private View mOverflowButton; + + /** + * Constructor of AlbumInfo. + * + * @param context The current context + */ + public AlbumInfo(Context context) { + super(context); + init(); + } + + /** + * Constructor of AlbumInfo. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public AlbumInfo(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Constructor of AlbumInfo. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public AlbumInfo(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Method that initializes the internal references + */ + private void init() { + mCallbacks = new ArrayList(); + } + + /** + * Method that adds the class that will be listen for events of this class + * + * @param callback The callback class + */ + public void addCallBackListener(CallbacksListener callback) { + this.mCallbacks.add(callback); + } + + /** + * Method that removes the class from the current callbacks + * + * @param callback The callback class + */ + public void removeCallBackListener(CallbacksListener callback) { + this.mCallbacks.remove(callback); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + mIcon = (ImageView)findViewById(R.id.album_thumbnail); + mSelectedItems = (TextView)findViewById(R.id.album_selected_items); + mName = (TextView)findViewById(R.id.album_name); + mItems = (TextView)findViewById(R.id.album_items); + mOverflowButton = findViewById(R.id.overflow); + mOverflowButton.setOnClickListener(this); + + updateView(mAlbum); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + // Cancel pending tasks + if (mTask != null && mTask.getStatus().compareTo(Status.PENDING) == 0) { + mTask.cancel(true); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onClick(View v) { + if (v.equals(mOverflowButton)) { + PopupMenu popup = new PopupMenu(getContext(), v); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.album_actions, popup.getMenu()); + onPreparePopupMenu(popup.getMenu()); + popup.setOnMenuItemClickListener(this); + popup.show(); + return; + } + } + + /** + * Method called prior to show the popup menu + * + * @param popup The popup menu + */ + public void onPreparePopupMenu(Menu popup) { + if (isSelected()) { + popup.findItem(R.id.mnu_select_album).setVisible(false); + } else { + popup.findItem(R.id.mnu_deselect_album).setVisible(false); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.mnu_select_album: + doSelection(true); + break; + + case R.id.mnu_deselect_album: + doSelection(false); + break; + + default: + return false; + } + return true; + } + + /** + * Method that select/deselect the album + * + * @param selected whether the album is selected + */ + public void doSelection(boolean selected) { + setSelected(selected); + mAlbum.setSelected(selected); + mAlbum.setSelectedItems(new ArrayList()); + updateView(mAlbum); + notifySelectionChanged(); + } + + /** + * Method that notifies to all the registered callbacks that the selection + * was changed + */ + private void notifySelectionChanged() { + for (CallbacksListener callback : mCallbacks) { + if (mAlbum.isSelected()) { + callback.onAlbumSelected(mAlbum); + } else { + callback.onAlbumDeselected(mAlbum); + } + } + } + + /** + * Method that updates the view + * + * @param album The album data + */ + @SuppressWarnings("boxing") + public void updateView(Album album) { + mAlbum = album; + + if (mAlbum != null && mIcon != null) { + Resources res = getContext().getResources(); + + int selectedItems = mAlbum.getSelectedItems().size(); + String count = String.valueOf(selectedItems); + if (selectedItems > 99) { + count += "+"; + } + mSelectedItems.setText(count); + mSelectedItems.setVisibility(mAlbum.isSelected() ? View.INVISIBLE : View.VISIBLE); + mName.setText(mAlbum.getName()); + int items = mAlbum.getItems().size(); + mItems.setText(String.format(res.getQuantityText( + R.plurals.album_number_of_pictures, items).toString(), items)); + setSelected(album.isSelected()); + + if (mTask == null) { + post(new Runnable() { + @Override + public void run() { + // Show as icon, the first picture + mTask = new AsyncPictureLoaderTask(getContext(), mIcon); + mTask.execute(new File(mAlbum.getItems().get(0))); + } + }); + } + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java new file mode 100644 index 0000000..5d0c60f --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.LinearLayout; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.model.Album; + +import java.util.ArrayList; +import java.util.List; + +/** + * A view that contains the pictures of an album + */ +public class AlbumPictures extends RelativeLayout + implements OnClickListener, OnMenuItemClickListener { + + private static final int SELECTION_SELECT_ALL = 1; + private static final int SELECTION_DESELECT_ALL = 2; + private static final int SELECTION_INVERT = 3; + + /** + * A convenient listener for receive events of the AlbumPictures class + * + */ + public interface CallbacksListener { + /** + * Invoked when the user pressed the back button + */ + void onBackButtonClick(View v); + + /** + * Invoked when the selection was changed + * + * @param album The album + */ + void onSelectionChanged(Album album); + } + + private List mCallbacks; + + private Handler mHandler; + + /*package*/ PicturesView mScroller; + /*package*/ LinearLayout mHolder; + private View mBackButton; + private View mOverflowButton; + + private boolean mInitialized; + + /*package*/ Album mAlbum; + + /** + * Constructor of AlbumPictures. + * + * @param context The current context + */ + public AlbumPictures(Context context) { + super(context); + init(); + } + + /** + * Constructor of AlbumPictures. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public AlbumPictures(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Constructor of AlbumPictures. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public AlbumPictures(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Method that initializes the internal references + */ + private void init() { + mCallbacks = new ArrayList(); + mHandler = new Handler(); + mInitialized = false; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mScroller = (PicturesView)findViewById(R.id.album_pictures_scroller); + mHolder = (LinearLayout)findViewById(R.id.album_pictures_holder); + mBackButton = findViewById(R.id.back); + mBackButton.setOnClickListener(this); + mOverflowButton = findViewById(R.id.overflow); + mOverflowButton.setOnClickListener(this); + TextView title = (TextView)findViewById(R.id.album_pictures_title); + title.setText(mAlbum.getName()); + + updateView(mAlbum, false); + } + + /** + * Method that adds the class that will be listen for events of this class + * + * @param callback The callback class + */ + public void addCallBackListener(CallbacksListener callback) { + this.mCallbacks.add(callback); + } + + /** + * Method that removes the class from the current callbacks + * + * @param callback The callback class + */ + public void removeCallBackListener(CallbacksListener callback) { + this.mCallbacks.remove(callback); + } + + /** + * Method that set the data of the view + * + * @param album The album data + * @param recreate If the view should be recreated + */ + public void updateView(Album album, boolean recreate) { + mAlbum = album; + recreateView(false); + } + + /** + * Method that recreates the the view + * + * @param propagateShow If should propagate the show event + */ + private void recreateView(final boolean propagateShow) { + if (mHolder != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + int pictures = mHolder.getChildCount(); + if (pictures != mAlbum.getItems().size()) { + // Recreate the pictures + final LayoutInflater inflater = (LayoutInflater) getContext(). + getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mScroller.cancelTasks(); + mHolder.removeAllViews(); + for (final String picture : mAlbum.getItems()) { + View v = createPicture(inflater, picture, isPictureSelected(picture)); + mHolder.addView(v); + } + } else { + int i = 0; + for (final String picture : mAlbum.getItems()) { + View v = mHolder.getChildAt(i); + v.setSelected(isPictureSelected(picture)); + i++; + } + } + if (propagateShow) { + mScroller.onShow(); + } + } + }); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onClick(View v) { + // Check which is the view pressed + if (v.equals(mBackButton)) { + for (CallbacksListener callback : mCallbacks) { + callback.onBackButtonClick(v); + } + return; + } + if (v.equals(mOverflowButton)) { + PopupMenu popup = new PopupMenu(getContext(), v); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.pictures_actions, popup.getMenu()); + popup.setOnMenuItemClickListener(this); + popup.show(); + return; + } + + // A picture view + v.setSelected(!v.isSelected()); + notifySelectionChanged(); + } + + /** + * Method that notifies to all the registered callbacks that the selection + * was changed + */ + private void notifySelectionChanged() { + List selection = new ArrayList(); + int count = mHolder.getChildCount(); + for (int i = 0; i < count; i++) { + View v = mHolder.getChildAt(i); + if (v.isSelected()) { + selection.add((String)v.getTag()); + } + } + mAlbum.setSelectedItems(selection); + mAlbum.setSelected(false); + + for (CallbacksListener callback : mCallbacks) { + callback.onSelectionChanged(mAlbum); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.mnu_select_all: + doSelection(SELECTION_SELECT_ALL); + break; + + case R.id.mnu_deselect_all: + doSelection(SELECTION_DESELECT_ALL); + break; + + case R.id.mnu_invert_selection: + doSelection(SELECTION_INVERT); + break; + + default: + return false; + } + return true; + } + + /** + * Operate over the selection of the pictures of this album. + * + * @param action Takes the next values: + *

    + *
  • SELECTION_SELECT_ALL: select all
  • + *
  • SELECTION_DESELECT_ALL: deselect all
  • + *
  • SELECTION_INVERT: invert selection
  • + *
+ */ + private void doSelection(int action) { + int count = mHolder.getChildCount(); + for (int i = 0; i < count; i++) { + View v = mHolder.getChildAt(i); + + boolean selected = true; + if (action == SELECTION_DESELECT_ALL) { + selected = false; + } else if (action == SELECTION_INVERT) { + selected = !v.isSelected(); + } + v.setSelected(selected); + } + notifySelectionChanged(); + } + + /** + * Method invoked when the view is displayed + */ + public void onShow() { + if (!mInitialized) { + mInitialized = true; + recreateView(true); + } + } + + /** + * Method that creates a new picture view + * + * @param inflater The inflater of the parent view + * @param picture The path of the picture + * @param selected If the picture is selected + */ + /*package*/ View createPicture(LayoutInflater inflater, String picture, boolean selected) { + final View v = inflater.inflate(R.layout.picture_item, mHolder, false); + v.setTag(picture); + v.setSelected(selected); + v.setOnClickListener(this); + return v; + } + + /** + * Method that check if a picture is selected + * + * @param picture The picture to check + * @return boolean whether the picture is selected + */ + /*package*/ boolean isPictureSelected(String picture) { + for (String item : mAlbum.getSelectedItems()) { + if (item.compareTo(picture) == 0) { + return true; + } + } + return false; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java new file mode 100644 index 0000000..e1953cd --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.AnimationUtils; +import android.widget.LinearLayout; + +import org.cyanogenmod.wallpapers.photophase.R; + +/** + * A "Google Now Card Layout" like layout + */ +public class CardLayout extends LinearLayout { + + boolean inverted = false; + + /** + * Constructor of CardLayout. + * + * @param context The current context + */ + public CardLayout(Context context) { + super(context); + } + + /** + * Constructor of CardLayout. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public CardLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Constructor of CardLayout. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public CardLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Add a new card to the layout + * + * @param card The card view to add + * @param animate If the add should be animated + */ + public void addCard(final View card, final boolean animate) { + post(new Runnable() { + @Override + public void run() { + addView(card); + if (animate) { + if (inverted) { + card.startAnimation(AnimationUtils.loadAnimation( + getContext(), R.anim.cards_animation_up_right)); + } else { + card.startAnimation(AnimationUtils.loadAnimation( + getContext(), R.anim.cards_animation_up_left)); + } + inverted = !inverted; + } + } + }); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/ColorPickerPreference.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/ColorPickerPreference.java new file mode 100644 index 0000000..42e701d --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/ColorPickerPreference.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import afzkl.development.mColorPicker.views.ColorDialogView; +import afzkl.development.mColorPicker.views.ColorPanelView; + +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.SharedPreferences; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.DialogPreference; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; + +import org.cyanogenmod.wallpapers.photophase.R; + +/** + * A {@link Preference} that allow to select/pick a color in a new window dialog. + */ +public class ColorPickerPreference extends DialogPreference { + + private ColorPanelView mColorPicker; + private int mColor; + + private ColorDialogView mColorDlg; + + /** + * Constructor of ColorPickerPreference + * + * @param context The current context + */ + public ColorPickerPreference(Context context) { + this(context, null); + } + + /** + * Constructor of ColorPickerPreference + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the preference. + */ + public ColorPickerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setWidgetLayoutResource(R.layout.color_picker_pref_item); + } + + /** + * Returns the color of the picker. + * + * @return The color of the picker. + */ + public int getColor() { + return this.mColor; + } + + /** + * Sets the color of the picker and saves it to the {@link SharedPreferences}. + * + * @param color The new color. + */ + public void setColor(int color) { + // Always persist/notify the first time; don't assume the field's default of false. + final boolean changed = this.mColor != color; + if (changed) { + this.mColor = color; + // when called from onSetInitialValue the view is still not set + if (this.mColorPicker != null) { + this.mColorPicker.setColor(color); + } + persistInt(color); + if (changed) { + notifyDependencyChange(shouldDisableDependents()); + notifyChanged(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return Integer.valueOf(a.getColor(index, 0)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + setColor(restoreValue ? getPersistedInt(0) : ((Integer)defaultValue).intValue()); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPrepareDialogBuilder(Builder builder) { + super.onPrepareDialogBuilder(builder); + + // Configure the dialog + this.mColorDlg = new ColorDialogView(getContext()); + this.mColorDlg.setColor(this.mColor); + this.mColorDlg.showAlphaSlider(true); + this.mColorDlg.setAlphaSliderText( + getContext().getString(R.string.color_picker_alpha_slider_text)); + this.mColorDlg.setCurrentColorText( + getContext().getString(R.string.color_picker_current_text)); + this.mColorDlg.setNewColorText( + getContext().getString(R.string.color_picker_new_text)); + this.mColorDlg.setColorLabelText( + getContext().getString(R.string.color_picker_color)); + builder.setView(this.mColorDlg); + + // The color is selected by the user and confirmed by clicking ok + builder.setPositiveButton(android.R.string.ok, new OnClickListener() { + @Override + @SuppressWarnings("synthetic-access") + public void onClick(DialogInterface dialog, int which) { + int color = ColorPickerPreference.this.mColorDlg.getColor(); + if (callChangeListener(Integer.valueOf(color))) { + setColor(color); + } + dialog.dismiss(); + } + }); + } + + + /** + * {@inheritDoc} + */ + @Override + protected void onBindView(View view) { + super.onBindView(view); + View v = view.findViewById(R.id.color_picker); + if (v != null && v instanceof ColorPanelView) { + this.mColorPicker = (ColorPanelView)v; + this.mColorPicker.setColor(this.mColor); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + if (isPersistent()) { + // No need to save instance state since it's persistent + return superState; + } + + final SavedState myState = new SavedState(superState); + myState.color = getColor(); + return myState; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state == null || !state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + setColor(myState.color); + } + + /** + * A class for managing the instance state of a {@link ColorPickerPreference}. + */ + static class SavedState extends BaseSavedState { + int color; + + /** + * Constructor of SavedState + * + * @param source The source + */ + public SavedState(Parcel source) { + super(source); + this.color = source.readInt(); + } + + /** + * Constructor of SavedState + * + * @param superState The parcelable state + */ + public SavedState(Parcelable superState) { + super(superState); + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(this.color); + } + + /** + * A class that generates instances of the SavedState class from a Parcel. + */ + @SuppressWarnings("hiding") + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + /** + * {@inheritDoc} + */ + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + /** + * {@inheritDoc} + */ + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java new file mode 100644 index 0000000..22d00a6 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Rect; +import android.os.Vibrator; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.View.OnLongClickListener; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.Toast; +import android.widget.ImageView.ScaleType; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.animations.Evaluators; +import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; +import org.cyanogenmod.wallpapers.photophase.utils.MERAlgorithm; +import org.cyanogenmod.wallpapers.photophase.widgets.ResizeFrame.OnResizeListener; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A class that allow to select the frames disposition visually + */ +public class DispositionView extends RelativeLayout implements OnLongClickListener, OnResizeListener { + + /** + * An interface to communicate the selection/unselection of a frame + */ + public interface OnFrameSelectedListener { + /** + * Invoked when a frame is selected + * + * @param v The frame view selected + */ + void onFrameSelectedListener(View v); + /** + * Invoked when a frame is unselected + */ + void onFrameUnselectedListener(); + } + + private boolean mChanged; + private List mDispositions; + private int mCols; + private int mRows; + + /*package*/ View mTarget; + private ResizeFrame mResizeFrame; + private int mInternalPadding; + private Rect mOldResizeFrameLocation; + + private OnFrameSelectedListener mOnFrameSelectedListener; + + private Vibrator mVibrator; + + /** + * Constructor of DispositionView. + * + * @param context The current context + */ + public DispositionView(Context context) { + super(context); + init(); + } + + /** + * Constructor of DispositionView. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public DispositionView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Constructor of DispositionView. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public DispositionView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Initialize the view + */ + private void init() { + mVibrator = (Vibrator)getContext().getSystemService(Context.VIBRATOR_SERVICE); + mInternalPadding = (int)getResources().getDimension(R.dimen.disposition_frame_padding); + } + + /** + * Method that returns the dispositions drawn on this view + * + * @return List The dispositions drawn + */ + public List getDispositions() { + return mDispositions; + } + + /** + * Method that sets the disposition to draw on this view + * + * @param dispositions The dispositions to draw + * @param cols The number of cols + * @param rows The number of rows + */ + public void setDispositions( + List dispositions, int cols, int rows) { + mDispositions = dispositions; + mCols = cols; + mRows = rows; + + // Remove all the current views and add the new ones + recreateDispositions(true); + mResizeFrame.setVisibility(View.GONE); + mChanged = false; + } + + /** + * Method that sets the resize frame view + * + * @param resizeFrame The resize frame view + */ + public void setResizeFrame(ResizeFrame resizeFrame) { + mResizeFrame = resizeFrame; + mResizeFrame.setOnResizeListener(this); + } + + /** + * Method that set the listener for listen frame selection/unselection events + * + * @param onFrameSelectedListener The callback + */ + public void setOnFrameSelectedListener(OnFrameSelectedListener onFrameSelectedListener) { + this.mOnFrameSelectedListener = onFrameSelectedListener; + } + + /** + * Method that returns if the view was changed + * + * @return boolean true if the view was changed + */ + public boolean isChanged() { + return mChanged; + } + + /** + * Method that recreates all the dispositions + * + * @param animate If the recreate should be done with an animation + */ + private void recreateDispositions(boolean animate) { + // Remove all the current views and add the new ones + removeAllViews(); + for (Disposition disposition : mDispositions) { + createFrame(getLocationFromDisposition(disposition), animate); + } + mOldResizeFrameLocation = null; + mTarget = null; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameUnselectedListener(); + } + } + + /** + * Method that request the deletion of the current selected frame + */ + @SuppressWarnings("boxing") + public void deleteCurrentFrame() { + if (mTarget == null) return; + + final Disposition targetDisposition = resizerToDisposition(); + + // Get valid dispositions to move + final List adjacents = findAdjacentsDispositions(targetDisposition); + if (adjacents == null) { + // Nothing to do + Toast.makeText(getContext(), + R.string.pref_disposition_unable_delete_advise, Toast.LENGTH_SHORT).show(); + return; + } + + // Hide resizer + mResizeFrame.setVisibility(View.GONE); + + // Animate adjacents views + List animators = new ArrayList(); + animators.add(ObjectAnimator.ofFloat(mTarget, "scaleX", 1.0f, 0.0f)); + animators.add(ObjectAnimator.ofFloat(mTarget, "scaleY", 1.0f, 0.0f)); + + Disposition first = null; + for (Disposition adjacent : adjacents) { + // Extract the view and remove from dispositions + View v = findViewFromRect(getLocationFromDisposition(adjacent)); + mDispositions.remove(adjacent); + + // Clone first disposition + if (first == null) { + first = new Disposition(); + first.x = adjacent.x; + first.y = adjacent.y; + first.w = adjacent.w; + first.h = adjacent.h; + } + + // Add animators and fix the adjacent + if (v != null) { + if (first.x < targetDisposition.x) { + // From Left to Right + int width = mTarget.getWidth() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width)); + + // Update the adjacent + adjacent.w += targetDisposition.w; + mDispositions.add(adjacent); + + } else if (first.x > targetDisposition.x) { + // From Right to Left + int width = mTarget.getWidth() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width)); + animators.add(ObjectAnimator.ofFloat(v, "x", v.getX(), mTarget.getX())); + + // Update the adjacent + adjacent.x = targetDisposition.x; + adjacent.w += targetDisposition.w; + mDispositions.add(adjacent); + + } else if (first.y < targetDisposition.y) { + // From Top to Bottom + int height = mTarget.getHeight() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height)); + + // Update the adjacent + adjacent.h += targetDisposition.h; + mDispositions.add(adjacent); + + } else if (first.y > targetDisposition.y) { + // From Bottom to Top + int height = mTarget.getHeight() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height)); + animators.add(ObjectAnimator.ofFloat(v, "y", v.getY(), mTarget.getY())); + + // Update the adjacent + adjacent.y = targetDisposition.y; + adjacent.h += targetDisposition.h; + mDispositions.add(adjacent); + } + } + } + if (animators.size() > 0) { + AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(animators); + animSet.setDuration(getResources().getInteger(R.integer.disposition_hide_anim)); + animSet.setInterpolator(new AccelerateInterpolator()); + animSet.addListener(new AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + // Ignore + } + @Override + public void onAnimationRepeat(Animator animation) { + // Ignore + } + @Override + public void onAnimationEnd(Animator animation) { + finishDeleteAnimation(targetDisposition); + } + + @Override + public void onAnimationCancel(Animator animation) { + finishDeleteAnimation(targetDisposition); + } + }); + animSet.start(); + } + } + + /** + * Method that finalizes the delete animation + * + * @param target The disposition target + */ + /*package*/ void finishDeleteAnimation(Disposition target) { + removeView(mTarget); + mDispositions.remove(target); + Collections.sort(mDispositions); + mChanged = true; + + // Clean status + mOldResizeFrameLocation = null; + mTarget = null; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameUnselectedListener(); + } + } + + /** + * Method that create a new frame to be drawn in the specified location + * + * @param r The location relative to the parent layout + * @return v The new view + */ + private View createFrame(Rect r, boolean animate) { + int padding = (int)getResources().getDimension(R.dimen.disposition_frame_padding); + final ImageView v = new ImageView(getContext()); + v.setImageResource(R.drawable.ic_camera); + v.setScaleType(ScaleType.CENTER); + v.setBackgroundColor(getResources().getColor(R.color.disposition_frame_bg_color)); + RelativeLayout.LayoutParams params = + new RelativeLayout.LayoutParams(r.width() - padding, r.height() - padding); + v.setX(r.left + padding); + v.setY(r.top + padding); + v.setOnLongClickListener(this); + if (animate) { + v.setVisibility(View.INVISIBLE); + } + addView(v, params); + + // Animate the view + if (animate) { + post(new Runnable() { + @Override + public void run() { + Animation anim = AnimationUtils.loadAnimation( + getContext(), R.anim.display_with_bounce); + anim.setFillBefore(true); + anim.setFillAfter(true); + v.startAnimation(anim); + } + }); + } + + return v; + } + + /** + * Method that returns the location of the frame from its disposition + * + * @param disposition The source disposition + * @return Rect The location on parent view + */ + private Rect getLocationFromDisposition(Disposition disposition) { + int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); + int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); + int cw = w / mCols; + int ch = h / mRows; + + Rect location = new Rect(); + location.left = disposition.x * cw; + location.top = disposition.y * ch; + location.right = location.left + disposition.w * cw; + location.bottom = location.top + disposition.h * ch; + return location; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onLongClick(View v) { + if (!selectTarget(v)) return false; + mVibrator.vibrate(300); + return true; + } + + @Override + public void onStartResize(int mode) { + mOldResizeFrameLocation = new Rect( + mResizeFrame.getLeft(), + mResizeFrame.getTop(), + mResizeFrame.getRight(), + mResizeFrame.getBottom()); + } + + @Override + public void onResize(int mode, int delta) { + if (mTarget == null) return; + + int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); + int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); + int minWidth = (w / mCols) + (w / mCols) / 2; + int minHeight = (h / mRows) + (h / mRows) / 2; + + FrameLayout.LayoutParams params = + (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); + switch (mode) { + case Gravity.LEFT: + float newpos = mResizeFrame.getX() + delta; + if ((delta < 0 && newpos < (getPaddingLeft() * -1)) || + (delta > 0 && newpos > (mResizeFrame.getX() + params.width - minWidth))) { + return; + } + mResizeFrame.setX(newpos); + params.width -= delta; + break; + case Gravity.RIGHT: + if ((delta < 0 && ((params.width + delta) < minWidth)) || + (delta > 0 && (mResizeFrame.getX() + delta + params.width) > (getPaddingLeft() + getMeasuredWidth()))) { + return; + } + params.width += delta; + break; + case Gravity.TOP: + newpos = mResizeFrame.getY() + delta; + if ((delta < 0 && newpos < (getPaddingTop() * -1)) || + (delta > 0 && newpos > (mResizeFrame.getY() + params.height - minHeight))) { + return; + } + mResizeFrame.setY(newpos); + params.height -= delta; + break; + case Gravity.BOTTOM: + if ((delta < 0 && ((params.height + delta) < minHeight)) || + (delta > 0 && (mResizeFrame.getY() + delta + params.height) > (getPaddingTop() + getMeasuredHeight()))) { + return; + } + params.height += delta; + break; + + default: + break; + } + mResizeFrame.setLayoutParams(params); + } + + /** + * {@inheritDoc} + */ + @Override + public void onEndResize(final int mode) { + if (mTarget == null) return; + + // Compute the removed dispositions + computeRemovedDispositions(mode); + recreateDispositions(false); + computeNewDispositions(mode); + + // Finish resize (select the target and create the new dispositions) + post(new Runnable() { + @Override + public void run() { + // Select the target + View v = findTargetFromResizeFrame(); + if (v != null) { + selectTarget(v); + } + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCancel() { + if (mOldResizeFrameLocation != null) { + mTarget.setLeft(mOldResizeFrameLocation.left); + mTarget.setRight(mOldResizeFrameLocation.right); + mTarget.setTop(mOldResizeFrameLocation.top); + mTarget.setBottom(mOldResizeFrameLocation.bottom); + } + mOldResizeFrameLocation = null; + mTarget = null; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameUnselectedListener(); + } + } + + /** + * Method that returns the target view for the current resize frame + * + * @return The target view + */ + /*package*/ View findTargetFromResizeFrame() { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View v = getChildAt(i); + if (v.getX() < (mResizeFrame.getX() + (mResizeFrame.getWidth() / 2)) && + (v.getX() + v.getWidth()) > (mResizeFrame.getX() + (mResizeFrame.getWidth() / 2)) && + v.getY() < (mResizeFrame.getY() + (mResizeFrame.getHeight() / 2)) && + (v.getY() + v.getHeight()) > (mResizeFrame.getY() + (mResizeFrame.getHeight() / 2))) { + return v; + } + } + return null; + } + + /** + * Method that returns the view under the rect + * + * @return The view + */ + private View findViewFromRect(Rect r) { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View v = getChildAt(i); + if (v.getX() < (r.left + (r.width() / 2)) && + (v.getX() + v.getWidth()) > (r.left + (r.width() / 2)) && + v.getY() < (r.top + (r.height() / 2)) && + (v.getY() + v.getHeight()) > (r.top + (r.height() / 2))) { + return v; + } + } + return null; + } + + /** + * Method that select a view as the target of to resize + * + * @param v The target view + */ + /*package*/ boolean selectTarget(View v) { + //Do not do long click if we do not have a target + if (mTarget != null && v.equals(mTarget)) return false; + + // Show the resize frame view just in place of the current clicked view + mResizeFrame.hide(); + FrameLayout.LayoutParams frameParams = + (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); + int padding = mInternalPadding + mResizeFrame.getNeededPadding(); + frameParams.width = v.getWidth() + (padding * 2); + frameParams.height = v.getHeight() + (padding * 2); + mResizeFrame.setX(v.getX() - padding); + mResizeFrame.setY(v.getY() - padding); + mResizeFrame.show(); + + // Save the new view + mTarget = v; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameSelectedListener(v); + } + return true; + } + + /** + * Computes the removed layout disposition based on the actual resize frame + * + * @param mode The resize mode + */ + private void computeRemovedDispositions(int mode) { + // Transform the resizer to a dispositions object + Disposition resizer = resizerToDisposition(); + + // Delete all overlapped + int count = mDispositions.size(); + for (int i = count - 1; i >= 0; i--) { + Disposition disposition = mDispositions.get(i); + if (!isVisible(disposition) || isOverlapped(resizer, disposition)) { + mDispositions.remove(disposition); + } + } + + // Add the new disposition + mDispositions.add(resizer); + Collections.sort(mDispositions); + + mChanged = true; + } + + /** + * Computes the new layout disposition based on the actual resize frame + * + * @param mode The resize mode + */ + private void computeNewDispositions(int mode) { + // Fill the empty areas + do { + byte[][] dispositionMatrix = DispositionUtil.toMatrix(mDispositions, mCols, mRows); + Rect rect = MERAlgorithm.getMaximalEmptyRectangle(dispositionMatrix); + if (rect.width() == 0 && rect.height() == 0) { + // No more empty areas + break; + } + Disposition disposition = DispositionUtil.fromRect(rect); + createFrame(getLocationFromDisposition(disposition), true); + mDispositions.add(disposition); + } while (true); + + // Now the view was changed and should be reported + Collections.sort(mDispositions); + mChanged = true; + } + + /** + * Method that converts the resize frame to a dispostion reference + * + * @return Disposition The disposition reference + */ + private Disposition resizerToDisposition() { + int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); + int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); + int cw = w / mCols; + int ch = h / mRows; + + //Remove overlapped areas + Disposition resizer = new Disposition(); + resizer.x = Math.round(mResizeFrame.getX() / cw); + resizer.y = Math.round(mResizeFrame.getY() / ch); + resizer.w = Math.round(mResizeFrame.getWidth() / cw); + resizer.h = Math.round(mResizeFrame.getHeight() / ch); + + // Fix disposition (limits) + resizer.x = Math.max(resizer.x, 0); + resizer.y = Math.max(resizer.y, 0); + resizer.w = Math.min(resizer.w, mCols - resizer.x); + resizer.h = Math.min(resizer.h, mRows - resizer.y); + + return resizer; + } + + /** + * Method that returns all dispositions that matched exactly (in one side) with + * the argument disposition. + * + * @param disposition The disposition to check + */ + private List findAdjacentsDispositions(Disposition disposition) { + if (mDispositions.size() <= 1) return null; + + // Check left size + if (disposition.x != 0) { + List dispositions = new ArrayList(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.x + d.w) == disposition.x && + (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.h; + } + if (sum == disposition.h) { + return dispositions; + } + } + // Check top size + if (disposition.y != 0) { + List dispositions = new ArrayList(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.y + d.h) == disposition.y && + (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.w; + } + if (sum == disposition.w) { + return dispositions; + } + } + // Check right size + if ((disposition.x + disposition.w) != mCols) { + List dispositions = new ArrayList(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.x) == (disposition.x + disposition.w) && + (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.h; + } + if (sum == disposition.h) { + return dispositions; + } + } + // Check bottom size + if ((disposition.y + disposition.h) != mRows) { + List dispositions = new ArrayList(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.y) == (disposition.y + disposition.h) && + (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.w; + } + if (sum == disposition.w) { + return dispositions; + } + } + return null; + } + + /** + * Method that checks if a dispositions overlaps another other disposition + * + * @param d1 One disposition + * @param d2 Another disposition + * @return boolean true if d1 overlaps d2 + */ + private static boolean isOverlapped(Disposition d1, Disposition d2) { + Rect r1 = new Rect(d1.x, d1.y, d1.x + d1.w, d1.y + d1.h); + Rect r2 = new Rect(d2.x, d2.y, d2.x + d2.w, d2.y + d2.h); + return r1.intersect(r2); + } + + /** + * Method that checks if a dispositions is visible + * + * @param d The disposition to check + * @return boolean true if d is visible + */ + private static boolean isVisible(Disposition d) { + return d.w > 0 && d.h > 0; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java new file mode 100644 index 0000000..0744751 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.graphics.Rect; +import android.os.AsyncTask.Status; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ImageView; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.tasks.AsyncPictureLoaderTask; + +import java.io.File; +import java.util.HashMap; +import java.util.Iterator; + +/** + * A view that contains all the pictures of an album + */ +public class PicturesView extends HorizontalScrollView { + + private HashMap mTasks; + private Handler mHandler; + + /** + * Constructor of PicturesView. + * + * @param context The current context + */ + public PicturesView(Context context) { + super(context); + init(); + } + + /** + * Constructor of PicturesView. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public PicturesView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Constructor of PicturesView. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public PicturesView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Method that initializes the structures of this class + */ + private void init() { + mTasks = new HashMap(); + mHandler = new Handler(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + cancelTasks(); + } + + /** + * Method that removes all tasks + */ + public void cancelTasks() { + // Cancel all the pending task + Iterator it = mTasks.values().iterator(); + while (it.hasNext()) { + AsyncPictureLoaderTask task = it.next(); + if (task.getStatus().compareTo(Status.PENDING) == 0) { + task.cancel(true); + } + } + mTasks.clear(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + // Estimated velocity (in some moment we must obtain some scrolling with an estimated + // velocity below of 3) + int velocity = Math.abs(l - oldl); + if (velocity <= 3) { + mHandler.post(new Runnable() { + @Override + public void run() { + requestLoadOfPendingPictures(); + } + }); + } + } + + /** + * Method invoked when the view is displayed + */ + public void onShow() { + mHandler.post(new Runnable() { + @Override + public void run() { + requestLoadOfPendingPictures(); + } + }); + } + + /** + * Method that load in background all visible and pending pictures + */ + /*package*/ void requestLoadOfPendingPictures() { + // Get the visible rect + Rect r = new Rect(); + getHitRect(r); + + // Get all the image views + ViewGroup vg = (ViewGroup)getChildAt(0); + int count = vg.getChildCount(); + for (int i = 0; i < count; i++) { + ViewGroup picView = (ViewGroup)vg.getChildAt(i); + File image = new File((String)picView.getTag()); + if (picView.getLocalVisibleRect(r) && !mTasks.containsKey(image)) { + ImageView iv = (ImageView)picView.findViewById(R.id.picture_thumbnail); + AsyncPictureLoaderTask task = new AsyncPictureLoaderTask(getContext(), iv); + task.execute(image); + mTasks.put(image, task); + } + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/ResizeFrame.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/ResizeFrame.java new file mode 100644 index 0000000..218a4e4 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/ResizeFrame.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.graphics.BitmapFactory; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import org.cyanogenmod.wallpapers.photophase.R; + +/** + * The hold view to resize a frame. A square with 4 handles in every border + * to drag and resize a view + */ +public class ResizeFrame extends RelativeLayout { + + /** + * An interface to communicate resize event states + */ + public interface OnResizeListener { + /** + * Called when the resize is going to start + * + * @param mode The resize mode (left, right, top, bottom) + * @see Gravity + */ + void onStartResize(int mode); + /** + * Called when the resize is going to start + * + * @param mode The resize mode (left, right, top, bottom) + * @param delta The delta motion + * @see Gravity + */ + void onResize(int mode, int delta); + /** + * Called when the resize was ended + * + * @param mode The resize mode (left, right, top, bottom) + * @see Gravity + */ + void onEndResize(int mode); + /** + * Called when the resize was cancelled + * + * @param mode The resize mode (left, right, top, bottom) + * @see Gravity + */ + void onCancel(); + } + + private int mNeededPadding; + + private ImageView mLeftHandle; + private ImageView mRightHandle; + private ImageView mTopHandle; + private ImageView mBottomHandle; + + private float mExtraHandlingSpace; + + private View mHandle; + + private float mLastTouchX; + private float mLastTouchY; + + private OnResizeListener mOnResizeListener; + + /** + * Constructor of ResizeFrame. + * + * @param context The current context + */ + public ResizeFrame(Context context) { + super(context); + init(); + } + + /** + * Constructor of ResizeFrame. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public ResizeFrame(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Constructor of ResizeFrame. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public ResizeFrame(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Method that initializes the view + */ + @SuppressWarnings("boxing") + private void init() { + setBackgroundResource(R.drawable.resize_frame); + setPadding(0, 0, 0, 0); + + BitmapFactory.Options o = new BitmapFactory.Options(); + o.inJustDecodeBounds = true; + o.inTargetDensity = DisplayMetrics.DENSITY_DEFAULT; + BitmapFactory.decodeResource(getContext().getResources(), R.drawable.resize_handle_left, o); + mNeededPadding = (int)(o.outWidth / 1.5f); + + LayoutParams lp; + mLeftHandle = new ImageView(getContext()); + mLeftHandle.setImageResource(R.drawable.resize_handle_left); + mLeftHandle.setTag(Gravity.LEFT); + lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE); + lp.addRule(RelativeLayout.CENTER_VERTICAL); + addView(mLeftHandle, lp); + + mRightHandle = new ImageView(getContext()); + mRightHandle.setImageResource(R.drawable.resize_handle_right); + mRightHandle.setTag(Gravity.RIGHT); + lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE); + lp.addRule(RelativeLayout.CENTER_VERTICAL); + addView(mRightHandle, lp); + + mTopHandle = new ImageView(getContext()); + mTopHandle.setImageResource(R.drawable.resize_handle_top); + mTopHandle.setTag(Gravity.TOP); + lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE); + lp.addRule(RelativeLayout.CENTER_HORIZONTAL); + addView(mTopHandle, lp); + + mBottomHandle = new ImageView(getContext()); + mBottomHandle.setImageResource(R.drawable.resize_handle_bottom); + mBottomHandle.setTag(Gravity.BOTTOM); + lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); + lp.addRule(RelativeLayout.CENTER_HORIZONTAL); + addView(mBottomHandle, lp); + + mExtraHandlingSpace = getResources().getDimension(R.dimen.resize_frame_extra_handling_space); + } + + /** + * Method that set the callback for resize events + * + * @param onResizeListener The callback + */ + public void setOnResizeListener(OnResizeListener onResizeListener) { + mOnResizeListener = onResizeListener; + } + + /** + * Method that hides the view + */ + public void hide() { + setVisibility(View.GONE); + } + + /** + * Method that shows the view + */ + public void show() { + setVisibility(View.VISIBLE); + } + + /** + * Method that returns the extra padding to draw the handlers + * + * @return The extra padding space + */ + public int getNeededPadding() { + return mNeededPadding; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + final float x = ev.getX(); + final float y = ev.getY(); + + switch (action) { + case MotionEvent.ACTION_DOWN: + mHandle = getHandleFromCoordinates(x, y); + if (mHandle != null) { + // Start moving the resize frame + mLastTouchX = x; + mLastTouchY = y; + + // Start motion + if (mOnResizeListener != null) { + mOnResizeListener.onStartResize(((Integer)mHandle.getTag()).intValue()); + } + return true; + } + break; + + case MotionEvent.ACTION_MOVE: + if (mHandle != null) { + // Resize + if (mOnResizeListener != null) { + int handle = ((Integer)mHandle.getTag()).intValue(); + int delta = + handle == Gravity.LEFT || handle == Gravity.RIGHT + ? Math.round(x - mLastTouchX) + : Math.round(y - mLastTouchY); + mOnResizeListener.onResize(handle, delta); + invalidate(); + } + mLastTouchX = x; + mLastTouchY = y; + return true; + } + break; + + case MotionEvent.ACTION_UP: + if (mHandle != null) { + if (mOnResizeListener != null) { + mOnResizeListener.onEndResize(((Integer)mHandle.getTag()).intValue()); + return true; + } + cancelMotion(); + break; + } + + //$FALL-THROUGH$ + case MotionEvent.ACTION_CANCEL: + cancelMotion(); + break; + + default: + break; + } + + return false; + } + + /** + * Cancel motions + */ + private void cancelMotion() { + mHandle = null; + mLastTouchX = 0; + mLastTouchY = 0; + if (mOnResizeListener != null) { + mOnResizeListener.onCancel(); + } + } + + /** + * Method that returns the resize handle touch from the the screen coordinates + * + * @param x The x coordinate + * @param y The y coordinate + * @return View The handle view or null if no handle touched + */ + private View getHandleFromCoordinates(float x, float y) { + final View[] handles = {mLeftHandle, mRightHandle, mTopHandle, mBottomHandle}; + for (View v : handles) { + if ((v.getLeft() - mExtraHandlingSpace) < x && (v.getRight() + mExtraHandlingSpace) > x && + (v.getTop() - mExtraHandlingSpace) < y && (v.getBottom() + mExtraHandlingSpace) > y) { + return v; + } + } + return null; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java new file mode 100644 index 0000000..67a1f17 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ScrollView; + +/** + * A scroll view that notifies the end of the scroll to create new views + * dynamically. + */ +public class VerticalEndlessScroller extends ScrollView { + + /** + * Interface to communicate end-scroll events + */ + public interface OnEndScrollListener { + /** + * Called when the scroll reachs the end of the scroll + */ + void onEndScroll(); + } + + private OnEndScrollListener mCallback; + private int mEndPadding = 0; + private boolean mSwitch = false; + + /** + * Constructor of VerticalEndlessScroller. + * + * @param context The current context + */ + public VerticalEndlessScroller(Context context) { + super(context); + } + + /** + * Constructor of VerticalEndlessScroller. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public VerticalEndlessScroller(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Constructor of VerticalEndlessScroller. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + public VerticalEndlessScroller(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Method that set the callback for notify end-scroll events + * + * @param callback The callback + */ + public void setCallback(OnEndScrollListener callback) { + mCallback = callback; + } + + /** + * Method that set the end padding for fired the event + * + * @param endPadding The end padding + */ + public void setEndPadding(int endPadding) { + this.mEndPadding = endPadding; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + // We take the last child in the scrollview + View view = getChildAt(getChildCount() - 1); + int diff = (view.getBottom() - (getHeight() + getScrollY())); + if ((!mSwitch && diff <= mEndPadding)) { + if (mCallback != null) { + mCallback.onEndScroll(); + mSwitch = true; + return; + } + } else if (diff > mEndPadding) { + mSwitch = false; + } + } + +} -- cgit v1.2.3