blob: f60337f13f8ddf61ad2ec178b3175b0d9b8baa6a [file] [log] [blame]
/*
* Copyright (C) 2015 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.tv.ui;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.support.annotation.IntDef;
import android.transition.Fade;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.transition.TransitionValues;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.data.api.Channel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class TvTransitionManager extends TransitionManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SCENE_TYPE_EMPTY,
SCENE_TYPE_CHANNEL_BANNER,
SCENE_TYPE_INPUT_BANNER,
SCENE_TYPE_KEYPAD_CHANNEL_SWITCH,
SCENE_TYPE_SELECT_INPUT
})
public @interface SceneType {}
public static final int SCENE_TYPE_EMPTY = 0;
public static final int SCENE_TYPE_CHANNEL_BANNER = 1;
public static final int SCENE_TYPE_INPUT_BANNER = 2;
public static final int SCENE_TYPE_KEYPAD_CHANNEL_SWITCH = 3;
public static final int SCENE_TYPE_SELECT_INPUT = 4;
private final MainActivity mMainActivity;
private final ViewGroup mSceneContainer;
private final ChannelBannerView mChannelBannerView;
private final InputBannerView mInputBannerView;
private final KeypadChannelSwitchView mKeypadChannelSwitchView;
private final SelectInputView mSelectInputView;
private final FrameLayout mEmptyView;
private ViewGroup mCurrentSceneView;
private Animator mEnterAnimator;
private Animator mExitAnimator;
private boolean mInitialized;
private Scene mEmptyScene;
private Scene mChannelBannerScene;
private Scene mInputBannerScene;
private Scene mKeypadChannelSwitchScene;
private Scene mSelectInputScene;
private Scene mCurrentScene;
private Listener mListener;
public TvTransitionManager(
MainActivity mainActivity,
ViewGroup sceneContainer,
ChannelBannerView channelBannerView,
InputBannerView inputBannerView,
KeypadChannelSwitchView keypadChannelSwitchView,
SelectInputView selectInputView) {
mMainActivity = mainActivity;
mSceneContainer = sceneContainer;
mChannelBannerView = channelBannerView;
mInputBannerView = inputBannerView;
mKeypadChannelSwitchView = keypadChannelSwitchView;
mSelectInputView = selectInputView;
mEmptyView =
(FrameLayout)
mMainActivity
.getLayoutInflater()
.inflate(R.layout.empty_info_banner, sceneContainer, false);
mCurrentSceneView = mEmptyView;
}
public void goToEmptyScene(boolean withAnimation) {
if (mCurrentScene == mEmptyScene) {
return;
}
initIfNeeded();
if (withAnimation) {
mEmptyView.setAlpha(1.0f);
transitionTo(mEmptyScene);
} else {
TransitionManager.go(mEmptyScene, null);
// When transition is null, transition got stuck without calling endTransitions.
TransitionManager.endTransitions(mEmptyScene.getSceneRoot());
// Since Fade.OUT transition doesn't run, we need to set alpha manually.
mEmptyView.setAlpha(0);
}
}
public void goToChannelBannerScene() {
initIfNeeded();
Channel channel = mMainActivity.getCurrentChannel();
if (channel != null && channel.isPassthrough()) {
if (mCurrentScene != mInputBannerScene) {
// Show the input banner instead.
LayoutParams lp = (LayoutParams) mInputBannerView.getLayoutParams();
lp.width =
mCurrentScene == mSelectInputScene
? mSelectInputView.getWidth()
: FrameLayout.LayoutParams.WRAP_CONTENT;
mInputBannerView.setLayoutParams(lp);
mInputBannerView.updateLabel();
transitionTo(mInputBannerScene);
}
} else if (mCurrentScene != mChannelBannerScene) {
transitionTo(mChannelBannerScene);
}
}
public void goToKeypadChannelSwitchScene() {
initIfNeeded();
if (mCurrentScene != mKeypadChannelSwitchScene) {
transitionTo(mKeypadChannelSwitchScene);
}
}
public void goToSelectInputScene() {
initIfNeeded();
if (mCurrentScene != mSelectInputScene) {
mSelectInputView.setCurrentChannel(mMainActivity.getCurrentChannel());
transitionTo(mSelectInputScene);
}
}
public boolean isSceneActive() {
return mCurrentScene != mEmptyScene;
}
public boolean isKeypadChannelSwitchActive() {
return mCurrentScene != null && mCurrentScene == mKeypadChannelSwitchScene;
}
public boolean isSelectInputActive() {
return mCurrentScene != null && mCurrentScene == mSelectInputScene;
}
public void setListener(Listener listener) {
mListener = listener;
}
public void initIfNeeded() {
if (mInitialized) {
return;
}
mEnterAnimator =
AnimatorInflater.loadAnimator(mMainActivity, R.animator.channel_banner_enter);
mExitAnimator =
AnimatorInflater.loadAnimator(mMainActivity, R.animator.channel_banner_exit);
mEmptyScene = new Scene(mSceneContainer, (View) mEmptyView);
mEmptyScene.setEnterAction(
() -> {
FrameLayout.LayoutParams emptySceneLayoutParams =
(FrameLayout.LayoutParams) mEmptyView.getLayoutParams();
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mCurrentSceneView.getLayoutParams();
emptySceneLayoutParams.topMargin = mCurrentSceneView.getTop();
emptySceneLayoutParams.setMarginStart(lp.getMarginStart());
emptySceneLayoutParams.height = mCurrentSceneView.getHeight();
emptySceneLayoutParams.width = mCurrentSceneView.getWidth();
mEmptyView.setLayoutParams(emptySceneLayoutParams);
setCurrentScene(mEmptyScene, mEmptyView);
});
mEmptyScene.setExitAction(this::removeAllViewsFromOverlay);
mChannelBannerScene = buildScene(mSceneContainer, mChannelBannerView);
mInputBannerScene = buildScene(mSceneContainer, mInputBannerView);
mKeypadChannelSwitchScene = buildScene(mSceneContainer, mKeypadChannelSwitchView);
mSelectInputScene = buildScene(mSceneContainer, mSelectInputView);
mCurrentScene = mEmptyScene;
// Enter transitions
TransitionSet enter =
new TransitionSet()
.addTransition(new SceneTransition(SceneTransition.ENTER))
.addTransition(new Fade(Fade.IN));
setTransition(mEmptyScene, mChannelBannerScene, enter);
setTransition(mEmptyScene, mInputBannerScene, enter);
setTransition(mEmptyScene, mKeypadChannelSwitchScene, enter);
setTransition(mEmptyScene, mSelectInputScene, enter);
// Exit transitions
TransitionSet exit =
new TransitionSet()
.addTransition(new SceneTransition(SceneTransition.EXIT))
.addTransition(new Fade(Fade.OUT));
setTransition(mChannelBannerScene, mEmptyScene, exit);
setTransition(mInputBannerScene, mEmptyScene, exit);
setTransition(mKeypadChannelSwitchScene, mEmptyScene, exit);
setTransition(mSelectInputScene, mEmptyScene, exit);
// All other possible transitions between scenes
TransitionInflater ti = TransitionInflater.from(mMainActivity);
Transition transition = ti.inflateTransition(R.transition.transition_between_scenes);
setTransition(mChannelBannerScene, mKeypadChannelSwitchScene, transition);
setTransition(mChannelBannerScene, mSelectInputScene, transition);
setTransition(mInputBannerScene, mSelectInputScene, transition);
setTransition(mKeypadChannelSwitchScene, mChannelBannerScene, transition);
setTransition(mKeypadChannelSwitchScene, mSelectInputScene, transition);
setTransition(mSelectInputScene, mChannelBannerScene, transition);
setTransition(mSelectInputScene, mInputBannerScene, transition);
mInitialized = true;
}
/** Returns the type of the given scene. */
@SceneType
public int getSceneType(Scene scene) {
if (scene == mChannelBannerScene) {
return SCENE_TYPE_CHANNEL_BANNER;
} else if (scene == mInputBannerScene) {
return SCENE_TYPE_INPUT_BANNER;
} else if (scene == mKeypadChannelSwitchScene) {
return SCENE_TYPE_KEYPAD_CHANNEL_SWITCH;
} else if (scene == mSelectInputScene) {
return SCENE_TYPE_SELECT_INPUT;
}
return SCENE_TYPE_EMPTY;
}
private void setCurrentScene(Scene scene, ViewGroup sceneView) {
if (mListener != null) {
mListener.onSceneChanged(getSceneType(mCurrentScene), getSceneType(scene));
}
mCurrentScene = scene;
mCurrentSceneView = sceneView;
// TODO: Is this a still valid call?
mMainActivity.updateKeyInputFocus();
}
public interface TransitionLayout {
// TODO: remove the parameter fromEmptyScene once a bug regarding transition alpha
// is fixed. The bug is that the transition alpha is not reset after the transition is
// canceled.
void onEnterAction(boolean fromEmptyScene);
void onExitAction();
}
private Scene buildScene(ViewGroup sceneRoot, final TransitionLayout layout) {
final Scene scene = new Scene(sceneRoot, (View) layout);
scene.setEnterAction(
() -> {
boolean wasEmptyScene = (mCurrentScene == mEmptyScene);
setCurrentScene(scene, (ViewGroup) layout);
layout.onEnterAction(wasEmptyScene);
});
scene.setExitAction(
() -> {
removeAllViewsFromOverlay();
layout.onExitAction();
});
return scene;
}
private void removeAllViewsFromOverlay() {
// Clean up all the animations which can be still running.
mSceneContainer.getOverlay().remove(mChannelBannerView);
mSceneContainer.getOverlay().remove(mInputBannerView);
mSceneContainer.getOverlay().remove(mKeypadChannelSwitchView);
mSceneContainer.getOverlay().remove(mSelectInputView);
}
private class SceneTransition extends Transition {
static final int ENTER = 0;
static final int EXIT = 1;
private final Animator mAnimator;
SceneTransition(int mode) {
mAnimator = mode == ENTER ? mEnterAnimator : mExitAnimator;
}
@Override
public void captureStartValues(TransitionValues transitionValues) {}
@Override
public void captureEndValues(TransitionValues transitionValues) {}
@Override
public Animator createAnimator(
ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
Animator animator = mAnimator.clone();
animator.setTarget(sceneRoot);
animator.addListener(new HardwareLayerAnimatorListenerAdapter(sceneRoot));
return animator;
}
}
/** An interface for notification of the scene transition. */
public interface Listener {
/**
* Called when the scene changes. This method is called just before the scene transition.
*/
void onSceneChanged(@SceneType int fromSceneType, @SceneType int toSceneType);
}
}