/* * Copyright (C) 2015 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.ActivityManager.RunningTaskInfo; import android.app.WallpaperManager; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.graphics.Point; import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView.Renderer; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceActivity; import android.util.Log; import android.util.TypedValue; import android.view.ViewConfiguration; import com.ruesga.android.wallpapers.photophase.GLESWallpaperService.GLESEngineListener; import com.ruesga.android.wallpapers.photophase.preferences.ChoosePicturesFragment; import com.ruesga.android.wallpapers.photophase.preferences.PhotoPhasePreferences; 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<Renderer> mRenderers; private PhotoPhaseWallpaperEngine mEngine; private boolean mPreserveEGLContext; // List of the current top activities. Tap should be ignored when this acitivities are // in the foreground private 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<>(); } /** * {@inheritDoc} */ @Override public void onDestroy() { if (DEBUG) Log.d(TAG, "onDestroy"); super.onDestroy(); for (Renderer renderer : mRenderers) { ((PhotoPhaseRenderer) 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; final ActivityManager mActivityManager; private long mLastTouch = -1; private final Point mLastPoint = new Point(); private static final int DOUBLE_TAP_MIN_TIME = 40; private final int mDoubleTapSlop; /** * Constructor of <code>PhotoPhaseWallpaperEngine<code> * * @param wallpaper The wallpaper service reference */ PhotoPhaseWallpaperEngine(PhotoPhaseWallpaper wallpaper) { super(); mHandler = new Handler(); mActivityManager = (ActivityManager)getApplication().getSystemService(ACTIVITY_SERVICE); setTouchEventsEnabled(false); setGLESEngineListener(wallpaper); setWallpaperGLSurfaceView(new PhotoPhaseWallpaperGLSurfaceView(wallpaper)); setPauseOnPreview(true); mDoubleTapSlop = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); } /** * Out custom GLSurfaceView class to let us access all events stuff. */ class PhotoPhaseWallpaperGLSurfaceView extends WallpaperGLSurfaceView { /** * The constructor of <code>PhotoPhaseWallpaperGLSurfaceView</code>. * * @param context The current context */ public PhotoPhaseWallpaperGLSurfaceView(Context context) { super(context); } } @Override public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) { if (xOffsetStep != Float.POSITIVE_INFINITY && xOffsetStep != Float.NEGATIVE_INFINITY) { ((PhotoPhaseRenderer) getRenderer()).onOffsetChanged(xOffset, yOffset); } else { ((PhotoPhaseRenderer) getRenderer()).onOffsetChanged(-1, yOffset); } } /** * {@inheritDoc} */ @Override public Bundle onCommand(final String action, final int x, final int y, final int z, final Bundle extras, final boolean resultRequested) { // Ignore commands in preview mode final Context ctx = PhotoPhaseWallpaper.this; if (action.compareTo(WallpaperManager.COMMAND_TAP) == 0) { if (!AndroidHelper.hasReadExternalStoragePermissionGranted(getApplicationContext())) { // Open the album settings Intent i = new Intent(getApplicationContext(), PhotoPhasePreferences.class); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, ChoosePicturesFragment.class.getName()); i.putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true); startActivity(i); return super.onCommand(action, x, y, z, extras, resultRequested); } if (isPreview()) { return super.onCommand(action, x, y, z, extras, resultRequested); } if (isDoubleTap(ctx, x, y)) { // Pass the x and y position to the renderer ((PhotoPhaseRenderer)getRenderer()).onTouch(x, y, true); } else if (!PreferencesProvider.Preferences.General.Touch.getTouchMode(ctx)) { mHandler.postDelayed(new Runnable() { @Override @SuppressWarnings("deprecation") public void run() { // Only if the wallpaper is visible after a long press and // not in preview mode if (isVisible() && !isPreview()) { // This is still valid, because we need the HOME task which is still // part of the list after LOLLIPOP, and valid prior to this api List<RunningTaskInfo> taskInfo = mActivityManager.getRunningTasks(1); if (taskInfo.size() > 0 && taskInfo.get(0).topActivity != null) { 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, false); } } }, ViewConfiguration.getLongPressTimeout() + 100L); } mLastTouch = System.currentTimeMillis(); mLastPoint.set(x, y); } return super.onCommand(action, x, y, z, extras, resultRequested); } private boolean isDoubleTap(Context ctx, final int x, final int y) { if (PreferencesProvider.Preferences.General.Touch.getTouchMode(ctx)) { // User preference is double tap long diff = System.currentTimeMillis() - mLastTouch; if (diff > 0 && diff >= DOUBLE_TAP_MIN_TIME && diff <= ViewConfiguration.getDoubleTapTimeout()) { if (Math.abs(mLastPoint.x - x) < mDoubleTapSlop && Math.abs(mLastPoint.y - y) < mDoubleTapSlop) { return true; } } } return false; } } /** * {@inheritDoc} */ @Override public void onLowMemory() { super.onLowMemory(); Log.i(TAG, "onLowMemory"); for (Renderer renderer : mRenderers) { // Pause the wallpaper and destroy the cached textures ((PhotoPhaseRenderer) renderer).onPause(); ((PhotoPhaseRenderer) renderer).onLowMemory(); } } /** * {@inheritDoc} */ @Override public void onInitializeEGLView(GLSurfaceView view) { if (DEBUG) Log.d(TAG, "onInitializeEGLView"); view.getHolder().setFormat(PixelFormat.RGBA_8888); view.setPreserveEGLContextOnPause(mPreserveEGLContext); } /** * {@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); } /** * {@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), mEngine.isPreview()); renderer.onCreate(); mRenderers.add(renderer); if (DEBUG) Log.d(TAG, "renderer" + renderer); return renderer; } }