blob: fe916691604d3a7273b373aa5253ac7a513e0898 [file] [log] [blame]
Fan Zhang33b0d912016-11-09 11:35:10 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.widget;
18
Fan Zhang33b0d912016-11-09 11:35:10 -080019import android.content.Context;
20import android.content.res.TypedArray;
Fan Zhang33b0d912016-11-09 11:35:10 -080021import android.util.AttributeSet;
22import android.util.Log;
Beverly2f414182019-05-22 10:46:34 -040023import android.util.TypedValue;
Fan Zhang33b0d912016-11-09 11:35:10 -080024import android.view.TextureView;
25import android.view.View;
26import android.widget.ImageView;
Beverly2f414182019-05-22 10:46:34 -040027import android.widget.LinearLayout;
Fan Zhang33b0d912016-11-09 11:35:10 -080028
Fan Zhangc7162cd2018-06-18 15:21:41 -070029import androidx.annotation.VisibleForTesting;
30import androidx.preference.Preference;
31import androidx.preference.PreferenceViewHolder;
Raff Tsai953da2e2019-10-31 14:03:34 +080032import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
Fan Zhangc7162cd2018-06-18 15:21:41 -070033
Fan Zhang23f8d592018-08-28 15:11:40 -070034import com.android.settings.R;
35
Fan Zhang33b0d912016-11-09 11:35:10 -080036/**
Raff Tsai953da2e2019-10-31 14:03:34 +080037 * A full width preference that hosts a MP4 video or a {@link AnimatedVectorDrawableCompat}.
Fan Zhang33b0d912016-11-09 11:35:10 -080038 */
39public class VideoPreference extends Preference {
40
41 private static final String TAG = "VideoPreference";
42 private final Context mContext;
43
Lei Yu0a358ba2018-03-30 09:50:02 -070044 @VisibleForTesting
Raff Tsai953da2e2019-10-31 14:03:34 +080045 AnimationController mAnimationController;
Lei Yu0a358ba2018-03-30 09:50:02 -070046 @VisibleForTesting
47 boolean mAnimationAvailable;
hughchen507218b2020-02-27 14:16:11 +080048
Beverly2f414182019-05-22 10:46:34 -040049 private float mAspectRatio = 1.0f;
Raff Tsai953da2e2019-10-31 14:03:34 +080050 private int mPreviewId;
Salvador Martinez1053ec02019-03-20 10:52:52 -070051 private int mAnimationId;
Raff Tsai953da2e2019-10-31 14:03:34 +080052 private int mVectorAnimationId;
Beverly2f414182019-05-22 10:46:34 -040053 private int mHeight = LinearLayout.LayoutParams.MATCH_PARENT - 1; // video height in pixels
Raff Tsai9547fa72019-11-05 13:24:08 +080054 private TextureView mVideo;
55 private ImageView mPreviewImage;
56 private ImageView mPlayButton;
Salvador Martinez1053ec02019-03-20 10:52:52 -070057
58 public VideoPreference(Context context) {
59 super(context);
60 mContext = context;
61 initialize(context, null);
62 }
Fan Zhang33b0d912016-11-09 11:35:10 -080063
64 public VideoPreference(Context context, AttributeSet attrs) {
65 super(context, attrs);
66 mContext = context;
Salvador Martinez1053ec02019-03-20 10:52:52 -070067 initialize(context, attrs);
68 }
69
70 private void initialize(Context context, AttributeSet attrs) {
Fan Zhang33b0d912016-11-09 11:35:10 -080071 TypedArray attributes = context.getTheme().obtainStyledAttributes(
72 attrs,
Salvador Martinez1053ec02019-03-20 10:52:52 -070073 R.styleable.VideoPreference,
Fan Zhang33b0d912016-11-09 11:35:10 -080074 0, 0);
75 try {
Salvador Martinez1053ec02019-03-20 10:52:52 -070076 // if these are already set that means they were set dynamically and don't need
77 // to be loaded from xml
LuK13379c3ab972019-10-09 16:08:12 +020078 mAnimationAvailable = false;
Salvador Martinez1053ec02019-03-20 10:52:52 -070079 mAnimationId = mAnimationId == 0
80 ? attributes.getResourceId(R.styleable.VideoPreference_animation, 0)
81 : mAnimationId;
Raff Tsai953da2e2019-10-31 14:03:34 +080082 mPreviewId = mPreviewId == 0
83 ? attributes.getResourceId(R.styleable.VideoPreference_preview, 0)
84 : mPreviewId;
85 mVectorAnimationId = attributes.getResourceId(
86 R.styleable.VideoPreference_vectorAnimation, 0);
87 if (mPreviewId == 0 && mAnimationId == 0 && mVectorAnimationId == 0) {
LuK13379c3ab972019-10-09 16:08:12 +020088 setVisible(false);
Salvador Martinez1053ec02019-03-20 10:52:52 -070089 return;
90 }
Raff Tsai953da2e2019-10-31 14:03:34 +080091 initAnimationController();
92 if (mAnimationController != null && mAnimationController.getDuration() > 0) {
Fan Zhang26ce68c2017-03-07 15:58:35 -080093 setVisible(true);
Fan Zhang33b0d912016-11-09 11:35:10 -080094 setLayoutResource(R.layout.video_preference);
Fan Zhang33b0d912016-11-09 11:35:10 -080095 mAnimationAvailable = true;
Lei Yu0a358ba2018-03-30 09:50:02 -070096 updateAspectRatio();
Fan Zhang26ce68c2017-03-07 15:58:35 -080097 } else {
98 setVisible(false);
Fan Zhang33b0d912016-11-09 11:35:10 -080099 }
100 } catch (Exception e) {
101 Log.w(TAG, "Animation resource not found. Will not show animation.");
102 } finally {
103 attributes.recycle();
104 }
105 }
106
107 @Override
108 public void onBindViewHolder(PreferenceViewHolder holder) {
109 super.onBindViewHolder(holder);
110
111 if (!mAnimationAvailable) {
112 return;
113 }
114
Raff Tsai9547fa72019-11-05 13:24:08 +0800115 mVideo = (TextureView) holder.findViewById(R.id.video_texture_view);
116 mPreviewImage = (ImageView) holder.findViewById(R.id.video_preview_image);
117 mPlayButton = (ImageView) holder.findViewById(R.id.video_play_button);
Lei Yu0a358ba2018-03-30 09:50:02 -0700118 final AspectRatioFrameLayout layout = (AspectRatioFrameLayout) holder.findViewById(
119 R.id.video_container);
120
Raff Tsai9547fa72019-11-05 13:24:08 +0800121 mPreviewImage.setImageResource(mPreviewId);
Beverly2f414182019-05-22 10:46:34 -0400122 layout.setAspectRatio(mAspectRatio);
123 if (mHeight >= LinearLayout.LayoutParams.MATCH_PARENT) {
124 layout.setLayoutParams(new LinearLayout.LayoutParams(
125 LinearLayout.LayoutParams.MATCH_PARENT, mHeight));
126 }
Sunny Shaob808eb02019-11-08 10:53:53 +0800127 if (mAnimationController != null) {
128 mAnimationController.attachView(mVideo, mPreviewImage, mPlayButton);
129 }
Salvador Martinez1053ec02019-03-20 10:52:52 -0700130 }
131
Fan Zhang33b0d912016-11-09 11:35:10 -0800132 @Override
133 public void onDetached() {
Raff Tsai953da2e2019-10-31 14:03:34 +0800134 releaseAnimationController();
Fan Zhang33b0d912016-11-09 11:35:10 -0800135 super.onDetached();
136 }
137
Raff Tsai68927632019-11-01 10:37:27 +0800138 /**
139 * Called from {@link VideoPreferenceController} when the view is onResume
140 */
141 public void onViewVisible() {
Raff Tsai953da2e2019-10-31 14:03:34 +0800142 initAnimationController();
Fan Zhang33b0d912016-11-09 11:35:10 -0800143 }
144
Raff Tsai68927632019-11-01 10:37:27 +0800145 /**
146 * Called from {@link VideoPreferenceController} when the view is onPause
147 */
Fan Zhang33b0d912016-11-09 11:35:10 -0800148 public void onViewInvisible() {
Raff Tsai953da2e2019-10-31 14:03:34 +0800149 releaseAnimationController();
Raff Tsai2831b142019-01-02 17:07:22 +0800150 }
151
Salvador Martinez1053ec02019-03-20 10:52:52 -0700152 /**
153 * Sets the video for this preference. If a previous video was set this one will override it
154 * and properly release any resources and re-initialize the preference to play the new video.
155 *
Raff Tsai953da2e2019-10-31 14:03:34 +0800156 * @param videoId The raw res id of the video
Salvador Martinez1053ec02019-03-20 10:52:52 -0700157 * @param previewId The drawable res id of the preview image to use if the video fails to load.
158 */
159 public void setVideo(int videoId, int previewId) {
160 mAnimationId = videoId;
Raff Tsai953da2e2019-10-31 14:03:34 +0800161 mPreviewId = previewId;
162 releaseAnimationController();
Salvador Martinez1053ec02019-03-20 10:52:52 -0700163 initialize(mContext, null);
164 }
165
Raff Tsai953da2e2019-10-31 14:03:34 +0800166 private void initAnimationController() {
167 if (mVectorAnimationId != 0) {
168 mAnimationController = new VectorAnimationController(mContext, mVectorAnimationId);
169 return;
170 }
171 if (mAnimationId != 0) {
172 mAnimationController = new MediaAnimationController(mContext, mAnimationId);
Raff Tsai9547fa72019-11-05 13:24:08 +0800173 if (mVideo != null) {
174 mAnimationController.attachView(mVideo, mPreviewImage, mPlayButton);
175 }
Raff Tsai2831b142019-01-02 17:07:22 +0800176 }
177 }
178
Raff Tsai953da2e2019-10-31 14:03:34 +0800179 private void releaseAnimationController() {
180 if (mAnimationController != null) {
181 mAnimationController.release();
182 mAnimationController = null;
Fan Zhang33b0d912016-11-09 11:35:10 -0800183 }
184 }
Doris Linga6897872017-07-11 10:38:24 -0700185
LuK13379c3ab972019-10-09 16:08:12 +0200186 public boolean isAnimationAvailable() {
187 return mAnimationAvailable;
188 }
189
Beverly2f414182019-05-22 10:46:34 -0400190 /**
191 * sets the height of the video preference
Raff Tsai953da2e2019-10-31 14:03:34 +0800192 *
Beverly2f414182019-05-22 10:46:34 -0400193 * @param height in dp
194 */
195 public void setHeight(float height) {
196 mHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height,
197 mContext.getResources().getDisplayMetrics());
198 }
199
Lei Yu0a358ba2018-03-30 09:50:02 -0700200 @VisibleForTesting
201 void updateAspectRatio() {
Raff Tsai953da2e2019-10-31 14:03:34 +0800202 mAspectRatio = mAnimationController.getVideoWidth()
203 / (float) mAnimationController.getVideoHeight();
204 }
205
206 /**
207 * Handle animation operations.
208 */
209 interface AnimationController {
210 /**
211 * Pauses the animation.
212 */
213 void pause();
214
215 /**
216 * Starts the animation.
217 */
218 void start();
219
220 /**
221 * Releases the animation object.
222 */
223 void release();
224
225 /**
226 * Attaches the animation to UI view.
227 */
228 void attachView(TextureView video, View preview, View playButton);
229
230 /**
231 * Returns the animation Width.
232 */
233 int getVideoWidth();
234
235 /**
236 * Returns the animation Height.
237 */
238 int getVideoHeight();
239
240 /**
241 * Returns the animation duration.
242 */
243 int getDuration();
244
245 /**
246 * Returns if the animation is playing.
247 */
248 boolean isPlaying();
Lei Yu0a358ba2018-03-30 09:50:02 -0700249 }
Fan Zhang33b0d912016-11-09 11:35:10 -0800250}