Home | History | Annotate | Download | only in sidepanel
      1 /*
      2  * Copyright (C) 2015 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 
     17 package com.android.tv.ui.sidepanel;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorInflater;
     21 import android.animation.AnimatorListenerAdapter;
     22 import android.app.Activity;
     23 import android.app.FragmentManager;
     24 import android.app.FragmentTransaction;
     25 import android.view.View;
     26 
     27 import com.android.tv.R;
     28 
     29 public class SideFragmentManager {
     30     private static final String FIRST_BACKSTACK_RECORD_NAME = "0";
     31 
     32     private final Activity mActivity;
     33     private final FragmentManager mFragmentManager;
     34     private final Runnable mPreShowRunnable;
     35     private final Runnable mPostHideRunnable;
     36 
     37     // To get the count reliably while using popBackStack(),
     38     // instead of using getBackStackEntryCount() with popBackStackImmediate().
     39     private int mFragmentCount;
     40 
     41     private final View mPanel;
     42     private final Animator mShowAnimator;
     43     private final Animator mHideAnimator;
     44 
     45     private final Runnable mHideAllRunnable = new Runnable() {
     46         @Override
     47         public void run() {
     48             hideAll(true);
     49         }
     50     };
     51     private final long mShowDurationMillis;
     52 
     53     public SideFragmentManager(Activity activity, Runnable preShowRunnable,
     54             Runnable postHideRunnable) {
     55         mActivity = activity;
     56         mFragmentManager = mActivity.getFragmentManager();
     57         mPreShowRunnable = preShowRunnable;
     58         mPostHideRunnable = postHideRunnable;
     59 
     60         mPanel = mActivity.findViewById(R.id.side_panel);
     61         mShowAnimator = AnimatorInflater.loadAnimator(mActivity, R.animator.side_panel_enter);
     62         mShowAnimator.setTarget(mPanel);
     63         mHideAnimator = AnimatorInflater.loadAnimator(mActivity, R.animator.side_panel_exit);
     64         mHideAnimator.setTarget(mPanel);
     65         mHideAnimator.addListener(new AnimatorListenerAdapter() {
     66             @Override
     67             public void onAnimationEnd(Animator animation) {
     68                 // Animation is still in running state at this point.
     69                 hideAllInternal();
     70             }
     71         });
     72 
     73         mShowDurationMillis = mActivity.getResources().getInteger(
     74                 R.integer.side_panel_show_duration);
     75     }
     76 
     77     public int getCount() {
     78         return mFragmentCount;
     79     }
     80 
     81     public boolean isActive() {
     82         return mFragmentCount != 0 && !isHiding();
     83     }
     84 
     85     public boolean isHiding() {
     86         return mHideAnimator.isStarted();
     87     }
     88 
     89     /**
     90      * Shows the given {@link SideFragment}.
     91      */
     92     public void show(SideFragment sideFragment) {
     93         show(sideFragment, true);
     94     }
     95 
     96     /**
     97      * Shows the given {@link SideFragment}.
     98      */
     99     public void show(SideFragment sideFragment, boolean showEnterAnimation) {
    100         SideFragment.preloadRecycledViews(mActivity);
    101         if (isHiding()) {
    102             mHideAnimator.end();
    103         }
    104         boolean isFirst = (mFragmentCount == 0);
    105         if (isFirst) {
    106             if (mPreShowRunnable != null) {
    107                 mPreShowRunnable.run();
    108             }
    109         }
    110 
    111         FragmentTransaction ft = mFragmentManager.beginTransaction();
    112         if (!isFirst) {
    113             ft.setCustomAnimations(
    114                     showEnterAnimation ? R.animator.side_panel_fragment_enter : 0,
    115                     R.animator.side_panel_fragment_exit,
    116                     R.animator.side_panel_fragment_pop_enter,
    117                     R.animator.side_panel_fragment_pop_exit);
    118         }
    119         ft.replace(R.id.side_fragment_container, sideFragment)
    120                 .addToBackStack(Integer.toString(mFragmentCount)).commit();
    121         mFragmentCount++;
    122 
    123         if (isFirst) {
    124             mPanel.setVisibility(View.VISIBLE);
    125             mShowAnimator.start();
    126         }
    127         scheduleHideAll();
    128     }
    129 
    130     public void popSideFragment() {
    131         if (!isActive()) {
    132             return;
    133         } else if (mFragmentCount == 1) {
    134             // Show closing animation with the last fragment.
    135             hideAll(true);
    136             return;
    137         }
    138         mFragmentManager.popBackStack();
    139         mFragmentCount--;
    140     }
    141 
    142     public void hideAll(boolean withAnimation) {
    143         if (withAnimation) {
    144             if (!isHiding()) {
    145                 mHideAnimator.start();
    146             }
    147             return;
    148         }
    149         if (isHiding()) {
    150             mHideAnimator.end();
    151             return;
    152         }
    153         hideAllInternal();
    154     }
    155 
    156     private void hideAllInternal() {
    157         if (mFragmentCount == 0) {
    158             return;
    159         }
    160 
    161         mPanel.setVisibility(View.GONE);
    162         mFragmentManager.popBackStack(FIRST_BACKSTACK_RECORD_NAME,
    163                 FragmentManager.POP_BACK_STACK_INCLUSIVE);
    164         mFragmentCount = 0;
    165 
    166         if (mPostHideRunnable != null) {
    167             mPostHideRunnable.run();
    168         }
    169     }
    170 
    171     /**
    172      * Show the side panel with animation. If there are many entries in the fragment stack,
    173      * the animation look like that there's only one fragment.
    174      *
    175      * @param withAnimation specifies if animation should be shown.
    176      */
    177     public void showSidePanel(boolean withAnimation) {
    178         SideFragment.preloadRecycledViews(mActivity);
    179         if (mFragmentCount == 0) {
    180             return;
    181         }
    182 
    183         mPanel.setVisibility(View.VISIBLE);
    184         if (withAnimation) {
    185             mShowAnimator.start();
    186         }
    187         scheduleHideAll();
    188     }
    189 
    190     /**
    191      * Hide the side panel. This method just hide the panel and preserves the back
    192      * stack. If you want to empty the back stack, call {@link #hideAll}.
    193      */
    194     public void hideSidePanel(boolean withAnimation) {
    195         if (withAnimation) {
    196             mPanel.removeCallbacks(mHideAllRunnable);
    197             Animator hideAnimator =
    198                     AnimatorInflater.loadAnimator(mActivity, R.animator.side_panel_exit);
    199             hideAnimator.setTarget(mPanel);
    200             hideAnimator.start();
    201             hideAnimator.addListener(new AnimatorListenerAdapter() {
    202                 @Override
    203                 public void onAnimationEnd(Animator animation) {
    204                     mPanel.setVisibility(View.GONE);
    205                 }
    206             });
    207         } else {
    208             mPanel.setVisibility(View.GONE);
    209         }
    210     }
    211 
    212     public boolean isSidePanelVisible() {
    213         return mPanel.getVisibility() == View.VISIBLE;
    214     }
    215 
    216     public void scheduleHideAll() {
    217         mPanel.removeCallbacks(mHideAllRunnable);
    218         mPanel.postDelayed(mHideAllRunnable, mShowDurationMillis);
    219     }
    220 
    221     /**
    222      * Should {@code keyCode} hide the current panel.
    223      */
    224     public boolean isHideKeyForCurrentPanel(int keyCode) {
    225         if (isActive()) {
    226             SideFragment current = (SideFragment) mFragmentManager.findFragmentById(
    227                     R.id.side_fragment_container);
    228             return current != null && current.isHideKeyForThisPanel(keyCode);
    229         }
    230         return false;
    231     }
    232 }
    233