1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.settings.gestures; 16 17 import android.app.LoaderManager; 18 import android.content.ContentResolver; 19 import android.content.Context; 20 import android.content.Loader; 21 import android.content.res.TypedArray; 22 import android.graphics.Bitmap; 23 import android.graphics.drawable.BitmapDrawable; 24 import android.graphics.SurfaceTexture; 25 import android.media.MediaMetadataRetriever; 26 import android.media.MediaPlayer; 27 import android.net.Uri; 28 import android.os.Bundle; 29 import android.support.v14.preference.SwitchPreference; 30 import android.support.v7.preference.PreferenceViewHolder; 31 import android.view.View; 32 import android.view.Surface; 33 import android.view.TextureView; 34 import android.widget.ImageView; 35 import android.util.AttributeSet; 36 import android.util.Log; 37 38 import com.android.settings.R; 39 import com.android.settings.utils.AsyncLoader; 40 41 /** 42 * Preference item for a gesture with a switch to signify if it should be enabled. 43 * This shows the title and description of the gesture along with an animation showing how to do 44 * the gesture 45 */ 46 public final class GesturePreference extends SwitchPreference implements 47 LoaderManager.LoaderCallbacks<Bitmap> { 48 private static final String TAG = "GesturePreference"; 49 private final Context mContext; 50 51 private Uri mVideoPath; 52 private MediaPlayer mMediaPlayer; 53 private boolean mAnimationAvailable; 54 private boolean mVideoReady; 55 private boolean mScrolling; 56 private BitmapDrawable mPreviewImage; 57 58 public GesturePreference(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 mContext = context; 61 setLayoutResource(R.layout.gesture_preference); 62 TypedArray attributes = context.getTheme().obtainStyledAttributes( 63 attrs, 64 R.styleable.GesturePreference, 65 0, 0); 66 try { 67 int animation = attributes.getResourceId(R.styleable.GesturePreference_animation, 0); 68 mVideoPath = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 69 .authority(context.getPackageName()) 70 .appendPath(String.valueOf(animation)) 71 .build(); 72 mMediaPlayer = MediaPlayer.create(mContext, mVideoPath); 73 if (mMediaPlayer != null) { 74 mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() { 75 @Override 76 public void onSeekComplete(MediaPlayer mp) { 77 mVideoReady = true; 78 } 79 }); 80 81 mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { 82 @Override 83 public void onPrepared(MediaPlayer mediaPlayer) { 84 mediaPlayer.setLooping(true); 85 } 86 }); 87 } 88 mAnimationAvailable = true; 89 90 } catch (Exception e) { 91 Log.w(TAG, "Animation resource not found. Will not show animation."); 92 } finally { 93 attributes.recycle(); 94 } 95 } 96 97 @Override 98 public void onBindViewHolder(PreferenceViewHolder holder) { 99 super.onBindViewHolder(holder); 100 final TextureView video = (TextureView) holder.findViewById(R.id.gesture_video); 101 final ImageView imageView = (ImageView) holder.findViewById(R.id.gesture_image); 102 final ImageView playButton = (ImageView) holder.findViewById(R.id.gesture_play_button); 103 final View animationFrame = holder.findViewById(R.id.gesture_animation_frame); 104 105 if (!mAnimationAvailable) { 106 animationFrame.setVisibility(View.GONE); 107 return; 108 } 109 110 video.setOnClickListener(new View.OnClickListener() { 111 @Override 112 public void onClick(View v) { 113 if (mMediaPlayer != null) { 114 if (mMediaPlayer.isPlaying()) { 115 mMediaPlayer.pause(); 116 playButton.setVisibility(View.VISIBLE); 117 } else { 118 mMediaPlayer.start(); 119 playButton.setVisibility(View.GONE); 120 } 121 } 122 } 123 }); 124 125 video.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { 126 @Override 127 public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, 128 int height) { 129 if (mMediaPlayer != null) { 130 mMediaPlayer.setSurface(new Surface(surfaceTexture)); 131 mVideoReady = false; 132 mMediaPlayer.seekTo(0); 133 } 134 } 135 136 @Override 137 public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, 138 int height) { 139 } 140 141 @Override 142 public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { 143 if (mPreviewImage != null && imageView.getDrawable() == null) { 144 imageView.setImageDrawable(mPreviewImage); 145 } 146 imageView.setVisibility(View.VISIBLE); 147 return false; 148 } 149 150 @Override 151 public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { 152 if (mVideoReady && imageView.getVisibility() == View.VISIBLE) { 153 imageView.setVisibility(View.GONE); 154 } else if (mScrolling) { 155 mScrolling = false; 156 if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { 157 mMediaPlayer.pause(); 158 playButton.setVisibility(View.VISIBLE); 159 } 160 } 161 } 162 }); 163 164 if (mPreviewImage != null) { 165 imageView.setImageDrawable(mPreviewImage); 166 } 167 168 } 169 170 @Override 171 public void onDetached() { 172 if (mMediaPlayer != null) { 173 mMediaPlayer.stop(); 174 mMediaPlayer.reset(); 175 mMediaPlayer.release(); 176 } 177 super.onDetached(); 178 } 179 180 void setScrolling(boolean scrolling) { 181 mScrolling = scrolling; 182 } 183 184 void loadPreview(LoaderManager manager, int id) { 185 Loader<Bitmap> loader = manager.initLoader(id, Bundle.EMPTY, this); 186 } 187 188 void onViewVisible() { 189 if (mVideoReady && mMediaPlayer != null && !mMediaPlayer.isPlaying()) { 190 mMediaPlayer.seekTo(0); 191 } 192 } 193 194 private static final class PreviewRetriever extends AsyncLoader<Bitmap> { 195 private Uri mVideoPath; 196 197 public PreviewRetriever(Context context, Uri videoPath) { 198 super(context); 199 mVideoPath = videoPath; 200 } 201 202 @Override 203 public Bitmap loadInBackground() { 204 MediaMetadataRetriever mediaMetadata = new MediaMetadataRetriever(); 205 try { 206 mediaMetadata.setDataSource(getContext(), mVideoPath); 207 return mediaMetadata.getFrameAtTime(0); 208 } catch (Exception e) { 209 Log.w(TAG, "Unable to get animation preview."); 210 } finally { 211 mediaMetadata.release(); 212 } 213 return null; 214 } 215 216 @Override 217 public void onDiscardResult(final Bitmap result) { 218 if (result != null && !result.isRecycled()) { 219 result.recycle(); 220 } 221 } 222 223 } 224 225 @Override 226 public Loader<Bitmap> onCreateLoader(int id, Bundle args) { 227 return new PreviewRetriever(mContext, mVideoPath); 228 } 229 230 @Override 231 public void onLoadFinished(final Loader<Bitmap> loader, final Bitmap bitmap) { 232 if (bitmap != null) { 233 mPreviewImage = new BitmapDrawable(mContext.getResources(), bitmap); 234 } 235 } 236 237 @Override 238 public void onLoaderReset(Loader<Bitmap> loader) { 239 } 240 241 } 242