Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * 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 License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 package androidx.leanback.widget;
     15 
     16 import android.content.Context;
     17 import android.graphics.drawable.ClipDrawable;
     18 import android.graphics.drawable.ColorDrawable;
     19 import android.graphics.drawable.Drawable;
     20 import android.graphics.drawable.LayerDrawable;
     21 import android.view.Gravity;
     22 import android.view.LayoutInflater;
     23 import android.view.View;
     24 import android.view.ViewGroup;
     25 import android.view.ViewGroup.MarginLayoutParams;
     26 import android.widget.FrameLayout;
     27 import android.widget.ProgressBar;
     28 import android.widget.TextView;
     29 
     30 import androidx.annotation.ColorInt;
     31 import androidx.leanback.R;
     32 import androidx.leanback.util.MathUtil;
     33 
     34 /**
     35  * A presenter for a control bar that supports "more actions",
     36  * and toggling the set of controls between primary and secondary
     37  * sets of {@link Actions}.
     38  */
     39 class PlaybackControlsPresenter extends ControlBarPresenter {
     40 
     41     /**
     42      * The data type expected by this presenter.
     43      */
     44     static class BoundData extends ControlBarPresenter.BoundData {
     45         /**
     46          * The adapter containing secondary actions.
     47          */
     48         ObjectAdapter secondaryActionsAdapter;
     49     }
     50 
     51     class ViewHolder extends ControlBarPresenter.ViewHolder {
     52         ObjectAdapter mMoreActionsAdapter;
     53         ObjectAdapter.DataObserver mMoreActionsObserver;
     54         final FrameLayout mMoreActionsDock;
     55         Presenter.ViewHolder mMoreActionsViewHolder;
     56         boolean mMoreActionsShowing;
     57         final TextView mCurrentTime;
     58         final TextView mTotalTime;
     59         final ProgressBar mProgressBar;
     60         long mCurrentTimeInMs = -1;         // Hold current time in milliseconds
     61         long mTotalTimeInMs = -1;           // Hold total time in milliseconds
     62         long mSecondaryProgressInMs = -1;   // Hold secondary progress in milliseconds
     63         StringBuilder mTotalTimeStringBuilder = new StringBuilder();
     64         StringBuilder mCurrentTimeStringBuilder = new StringBuilder();
     65         int mCurrentTimeMarginStart;
     66         int mTotalTimeMarginEnd;
     67 
     68         ViewHolder(View rootView) {
     69             super(rootView);
     70             mMoreActionsDock = (FrameLayout) rootView.findViewById(R.id.more_actions_dock);
     71             mCurrentTime = (TextView) rootView.findViewById(R.id.current_time);
     72             mTotalTime = (TextView) rootView.findViewById(R.id.total_time);
     73             mProgressBar = (ProgressBar) rootView.findViewById(R.id.playback_progress);
     74             mMoreActionsObserver = new ObjectAdapter.DataObserver() {
     75                 @Override
     76                 public void onChanged() {
     77                     if (mMoreActionsShowing) {
     78                         showControls(mPresenter);
     79                     }
     80                 }
     81                 @Override
     82                 public void onItemRangeChanged(int positionStart, int itemCount) {
     83                     if (mMoreActionsShowing) {
     84                         for (int i = 0; i < itemCount; i++) {
     85                             bindControlToAction(positionStart + i, mPresenter);
     86                         }
     87                     }
     88                 }
     89             };
     90             mCurrentTimeMarginStart =
     91                     ((MarginLayoutParams) mCurrentTime.getLayoutParams()).getMarginStart();
     92             mTotalTimeMarginEnd =
     93                     ((MarginLayoutParams) mTotalTime.getLayoutParams()).getMarginEnd();
     94         }
     95 
     96         void showMoreActions(boolean show) {
     97             if (show) {
     98                 if (mMoreActionsViewHolder == null) {
     99                     Action action = new PlaybackControlsRow.MoreActions(mMoreActionsDock.getContext());
    100                     mMoreActionsViewHolder = mPresenter.onCreateViewHolder(mMoreActionsDock);
    101                     mPresenter.onBindViewHolder(mMoreActionsViewHolder, action);
    102                     mPresenter.setOnClickListener(mMoreActionsViewHolder, new View.OnClickListener() {
    103                         @Override
    104                         public void onClick(View v) {
    105                             toggleMoreActions();
    106                         }
    107                     });
    108                 }
    109                 if (mMoreActionsViewHolder.view.getParent() == null) {
    110                     mMoreActionsDock.addView(mMoreActionsViewHolder.view);
    111                 }
    112             } else if (mMoreActionsViewHolder != null
    113                     && mMoreActionsViewHolder.view.getParent() != null) {
    114                 mMoreActionsDock.removeView(mMoreActionsViewHolder.view);
    115             }
    116         }
    117 
    118         void toggleMoreActions() {
    119             mMoreActionsShowing = !mMoreActionsShowing;
    120             showControls(mPresenter);
    121         }
    122 
    123         @Override
    124         ObjectAdapter getDisplayedAdapter() {
    125             return mMoreActionsShowing ? mMoreActionsAdapter : mAdapter;
    126         }
    127 
    128         @Override
    129         int getChildMarginFromCenter(Context context, int numControls) {
    130             int margin = getControlIconWidth(context);
    131             if (numControls < 4) {
    132                 margin += getChildMarginBiggest(context);
    133             } else if (numControls < 6) {
    134                 margin += getChildMarginBigger(context);
    135             } else {
    136                 margin += getChildMarginDefault(context);
    137             }
    138             return margin;
    139         }
    140 
    141         void setTotalTime(long totalTimeMs) {
    142             if (totalTimeMs <= 0) {
    143                 mTotalTime.setVisibility(View.GONE);
    144                 mProgressBar.setVisibility(View.GONE);
    145             } else {
    146                 mTotalTime.setVisibility(View.VISIBLE);
    147                 mProgressBar.setVisibility(View.VISIBLE);
    148                 mTotalTimeInMs = totalTimeMs;
    149                 formatTime(totalTimeMs / 1000, mTotalTimeStringBuilder);
    150                 mTotalTime.setText(mTotalTimeStringBuilder.toString());
    151                 mProgressBar.setMax(Integer.MAX_VALUE);//current progress will be a fraction of this
    152             }
    153         }
    154 
    155         long getTotalTime() {
    156             return mTotalTimeInMs;
    157         }
    158 
    159         void setCurrentTime(long currentTimeMs) {
    160             long seconds = currentTimeMs / 1000;
    161             if (currentTimeMs != mCurrentTimeInMs) {
    162                 mCurrentTimeInMs = currentTimeMs;
    163                 formatTime(seconds, mCurrentTimeStringBuilder);
    164                 mCurrentTime.setText(mCurrentTimeStringBuilder.toString());
    165             }
    166             // Use ratio to represent current progres
    167             double ratio = (double) mCurrentTimeInMs / mTotalTimeInMs;     // Range: [0, 1]
    168             double progressRatio = ratio * Integer.MAX_VALUE;   // Could safely cast to int
    169             mProgressBar.setProgress((int)progressRatio);
    170         }
    171 
    172         long getCurrentTime() {
    173             return mTotalTimeInMs;
    174         }
    175 
    176         void setSecondaryProgress(long progressMs) {
    177             mSecondaryProgressInMs = progressMs;
    178             // Solve the progress bar by using ratio
    179             double ratio = (double) progressMs / mTotalTimeInMs;           // Range: [0, 1]
    180             double progressRatio = ratio * Integer.MAX_VALUE;   // Could safely cast to int
    181             mProgressBar.setSecondaryProgress((int) progressRatio);
    182         }
    183 
    184         long getSecondaryProgress() {
    185             return mSecondaryProgressInMs;
    186         }
    187     }
    188 
    189     static void formatTime(long seconds, StringBuilder sb) {
    190         long minutes = seconds / 60;
    191         long hours = minutes / 60;
    192         seconds -= minutes * 60;
    193         minutes -= hours * 60;
    194 
    195         sb.setLength(0);
    196         if (hours > 0) {
    197             sb.append(hours).append(':');
    198             if (minutes < 10) {
    199                 sb.append('0');
    200             }
    201         }
    202         sb.append(minutes).append(':');
    203         if (seconds < 10) {
    204             sb.append('0');
    205         }
    206         sb.append(seconds);
    207     }
    208 
    209     private boolean mMoreActionsEnabled = true;
    210     private static int sChildMarginBigger;
    211     private static int sChildMarginBiggest;
    212 
    213     /**
    214      * Constructor for a PlaybackControlsRowPresenter.
    215      *
    216      * @param layoutResourceId The resource id of the layout for this presenter.
    217      */
    218     public PlaybackControlsPresenter(int layoutResourceId) {
    219         super(layoutResourceId);
    220     }
    221 
    222     /**
    223      * Enables the display of secondary actions.
    224      * A "more actions" button will be displayed.  When "more actions" is selected,
    225      * the primary actions are replaced with the secondary actions.
    226      */
    227     public void enableSecondaryActions(boolean enable) {
    228         mMoreActionsEnabled = enable;
    229     }
    230 
    231     /**
    232      * Returns true if secondary actions are enabled.
    233      */
    234     public boolean areMoreActionsEnabled() {
    235         return mMoreActionsEnabled;
    236     }
    237 
    238     public void setProgressColor(ViewHolder vh, @ColorInt int color) {
    239         Drawable drawable = new ClipDrawable(new ColorDrawable(color),
    240                 Gravity.LEFT, ClipDrawable.HORIZONTAL);
    241         ((LayerDrawable) vh.mProgressBar.getProgressDrawable())
    242                 .setDrawableByLayerId(android.R.id.progress, drawable);
    243     }
    244 
    245     public void setTotalTime(ViewHolder vh, int ms) {
    246         setTotalTimeLong(vh, (long) ms);
    247     }
    248 
    249     public void setTotalTimeLong(ViewHolder vh, long ms) {
    250         vh.setTotalTime(ms);
    251     }
    252 
    253     public int getTotalTime(ViewHolder vh) {
    254         return MathUtil.safeLongToInt(getTotalTimeLong(vh));
    255     }
    256 
    257     public long getTotalTimeLong(ViewHolder vh) {
    258         return vh.getTotalTime();
    259     }
    260 
    261     public void setCurrentTime(ViewHolder vh, int ms) {
    262         setCurrentTimeLong(vh, (long) ms);
    263     }
    264 
    265     public void setCurrentTimeLong(ViewHolder vh, long ms) {
    266         vh.setCurrentTime(ms);
    267     }
    268 
    269     public int getCurrentTime(ViewHolder vh) {
    270         return MathUtil.safeLongToInt(getCurrentTimeLong(vh));
    271     }
    272 
    273     public long getCurrentTimeLong(ViewHolder vh) {
    274         return vh.getCurrentTime();
    275     }
    276 
    277     public void setSecondaryProgress(ViewHolder vh, int progressMs) {
    278         setSecondaryProgressLong(vh, (long) progressMs);
    279     }
    280 
    281     public void setSecondaryProgressLong(ViewHolder vh, long progressMs) {
    282         vh.setSecondaryProgress(progressMs);
    283     }
    284 
    285     public int getSecondaryProgress(ViewHolder vh) {
    286         return MathUtil.safeLongToInt(getSecondaryProgressLong(vh));
    287     }
    288 
    289     public long getSecondaryProgressLong(ViewHolder vh) {
    290         return vh.getSecondaryProgress();
    291     }
    292 
    293     public void showPrimaryActions(ViewHolder vh) {
    294         if (vh.mMoreActionsShowing) {
    295             vh.toggleMoreActions();
    296         }
    297     }
    298 
    299     public void resetFocus(ViewHolder vh) {
    300         vh.mControlBar.requestFocus();
    301     }
    302 
    303     public void enableTimeMargins(ViewHolder vh, boolean enable) {
    304         MarginLayoutParams lp;
    305         lp = (MarginLayoutParams) vh.mCurrentTime.getLayoutParams();
    306         lp.setMarginStart(enable ? vh.mCurrentTimeMarginStart : 0);
    307         vh.mCurrentTime.setLayoutParams(lp);
    308 
    309         lp = (MarginLayoutParams) vh.mTotalTime.getLayoutParams();
    310         lp.setMarginEnd(enable ? vh.mTotalTimeMarginEnd : 0);
    311         vh.mTotalTime.setLayoutParams(lp);
    312     }
    313 
    314     @Override
    315     public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
    316         View v = LayoutInflater.from(parent.getContext())
    317             .inflate(getLayoutResourceId(), parent, false);
    318         return new ViewHolder(v);
    319     }
    320 
    321     @Override
    322     public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
    323         ViewHolder vh = (ViewHolder) holder;
    324         BoundData data = (BoundData) item;
    325 
    326         // If binding to a new adapter, display primary actions.
    327         if (vh.mMoreActionsAdapter != data.secondaryActionsAdapter) {
    328             vh.mMoreActionsAdapter = data.secondaryActionsAdapter;
    329             vh.mMoreActionsAdapter.registerObserver(vh.mMoreActionsObserver);
    330             vh.mMoreActionsShowing = false;
    331         }
    332 
    333         super.onBindViewHolder(holder, item);
    334         vh.showMoreActions(mMoreActionsEnabled);
    335     }
    336 
    337     @Override
    338     public void onUnbindViewHolder(Presenter.ViewHolder holder) {
    339         super.onUnbindViewHolder(holder);
    340         ViewHolder vh = (ViewHolder) holder;
    341         if (vh.mMoreActionsAdapter != null) {
    342             vh.mMoreActionsAdapter.unregisterObserver(vh.mMoreActionsObserver);
    343             vh.mMoreActionsAdapter = null;
    344         }
    345     }
    346 
    347     int getChildMarginBigger(Context context) {
    348         if (sChildMarginBigger == 0) {
    349             sChildMarginBigger = context.getResources().getDimensionPixelSize(
    350                     R.dimen.lb_playback_controls_child_margin_bigger);
    351         }
    352         return sChildMarginBigger;
    353     }
    354 
    355     int getChildMarginBiggest(Context context) {
    356         if (sChildMarginBiggest == 0) {
    357             sChildMarginBiggest = context.getResources().getDimensionPixelSize(
    358                     R.dimen.lb_playback_controls_child_margin_biggest);
    359         }
    360         return sChildMarginBiggest;
    361     }
    362 }
    363