/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.widget; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.TextureView; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; import com.android.settings.R; /** * A full width preference that hosts a MP4 video or a {@link AnimatedVectorDrawableCompat}. */ public class VideoPreference extends Preference { private static final String TAG = "VideoPreference"; private final Context mContext; @VisibleForTesting AnimationController mAnimationController; @VisibleForTesting boolean mAnimationAvailable; private float mAspectRatio = 1.0f; private int mPreviewId; private int mAnimationId; private int mVectorAnimationId; private int mHeight = LinearLayout.LayoutParams.MATCH_PARENT - 1; // video height in pixels private TextureView mVideo; private ImageView mPreviewImage; private ImageView mPlayButton; public VideoPreference(Context context) { super(context); mContext = context; initialize(context, null); } public VideoPreference(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; initialize(context, attrs); } private void initialize(Context context, AttributeSet attrs) { TypedArray attributes = context.getTheme().obtainStyledAttributes( attrs, R.styleable.VideoPreference, 0, 0); try { // if these are already set that means they were set dynamically and don't need // to be loaded from xml mAnimationAvailable = false; mAnimationId = mAnimationId == 0 ? attributes.getResourceId(R.styleable.VideoPreference_animation, 0) : mAnimationId; mPreviewId = mPreviewId == 0 ? attributes.getResourceId(R.styleable.VideoPreference_preview, 0) : mPreviewId; mVectorAnimationId = attributes.getResourceId( R.styleable.VideoPreference_vectorAnimation, 0); if (mPreviewId == 0 && mAnimationId == 0 && mVectorAnimationId == 0) { setVisible(false); return; } initAnimationController(); if (mAnimationController != null && mAnimationController.getDuration() > 0) { setVisible(true); setLayoutResource(R.layout.video_preference); mAnimationAvailable = true; updateAspectRatio(); } else { setVisible(false); } } catch (Exception e) { Log.w(TAG, "Animation resource not found. Will not show animation."); } finally { attributes.recycle(); } } @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); if (!mAnimationAvailable) { return; } mVideo = (TextureView) holder.findViewById(R.id.video_texture_view); mPreviewImage = (ImageView) holder.findViewById(R.id.video_preview_image); mPlayButton = (ImageView) holder.findViewById(R.id.video_play_button); final AspectRatioFrameLayout layout = (AspectRatioFrameLayout) holder.findViewById( R.id.video_container); mPreviewImage.setImageResource(mPreviewId); layout.setAspectRatio(mAspectRatio); if (mHeight >= LinearLayout.LayoutParams.MATCH_PARENT) { layout.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, mHeight)); } if (mAnimationController != null) { mAnimationController.attachView(mVideo, mPreviewImage, mPlayButton); } } @Override public void onDetached() { releaseAnimationController(); super.onDetached(); } /** * Called from {@link VideoPreferenceController} when the view is onResume */ public void onViewVisible() { initAnimationController(); } /** * Called from {@link VideoPreferenceController} when the view is onPause */ public void onViewInvisible() { releaseAnimationController(); } /** * Sets the video for this preference. If a previous video was set this one will override it * and properly release any resources and re-initialize the preference to play the new video. * * @param videoId The raw res id of the video * @param previewId The drawable res id of the preview image to use if the video fails to load. */ public void setVideo(int videoId, int previewId) { mAnimationId = videoId; mPreviewId = previewId; releaseAnimationController(); initialize(mContext, null); } private void initAnimationController() { if (mVectorAnimationId != 0) { mAnimationController = new VectorAnimationController(mContext, mVectorAnimationId); return; } if (mAnimationId != 0) { mAnimationController = new MediaAnimationController(mContext, mAnimationId); if (mVideo != null) { mAnimationController.attachView(mVideo, mPreviewImage, mPlayButton); } } } private void releaseAnimationController() { if (mAnimationController != null) { mAnimationController.release(); mAnimationController = null; } } public boolean isAnimationAvailable() { return mAnimationAvailable; } /** * sets the height of the video preference * * @param height in dp */ public void setHeight(float height) { mHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, mContext.getResources().getDisplayMetrics()); } @VisibleForTesting void updateAspectRatio() { mAspectRatio = mAnimationController.getVideoWidth() / (float) mAnimationController.getVideoHeight(); } /** * Handle animation operations. */ interface AnimationController { /** * Pauses the animation. */ void pause(); /** * Starts the animation. */ void start(); /** * Releases the animation object. */ void release(); /** * Attaches the animation to UI view. */ void attachView(TextureView video, View preview, View playButton); /** * Returns the animation Width. */ int getVideoWidth(); /** * Returns the animation Height. */ int getVideoHeight(); /** * Returns the animation duration. */ int getDuration(); /** * Returns if the animation is playing. */ boolean isPlaying(); } }