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 */ 14package android.support.v17.leanback.widget; 15 16import android.animation.ValueAnimator; 17import android.content.Context; 18import android.content.res.Resources; 19import android.graphics.drawable.ClipDrawable; 20import android.graphics.drawable.ColorDrawable; 21import android.graphics.drawable.Drawable; 22import android.graphics.drawable.LayerDrawable; 23import android.support.annotation.ColorInt; 24import android.support.v17.leanback.R; 25import android.view.Gravity; 26import android.view.LayoutInflater; 27import android.view.View; 28import android.view.ViewGroup; 29import android.view.ViewGroup.MarginLayoutParams; 30import android.view.animation.LinearInterpolator; 31import android.widget.FrameLayout; 32import android.widget.ProgressBar; 33import android.widget.TextView; 34 35/** 36 * A presenter for a control bar that supports "more actions", 37 * and toggling the set of controls between primary and secondary 38 * sets of {@link Actions}. 39 */ 40class PlaybackControlsPresenter extends ControlBarPresenter { 41 42 /** 43 * The data type expected by this presenter. 44 */ 45 static class BoundData extends ControlBarPresenter.BoundData { 46 /** 47 * The adapter containing secondary actions. 48 */ 49 ObjectAdapter secondaryActionsAdapter; 50 } 51 52 class ViewHolder extends ControlBarPresenter.ViewHolder { 53 ObjectAdapter mMoreActionsAdapter; 54 ObjectAdapter.DataObserver mMoreActionsObserver; 55 final FrameLayout mMoreActionsDock; 56 Presenter.ViewHolder mMoreActionsViewHolder; 57 boolean mMoreActionsShowing; 58 final TextView mCurrentTime; 59 final TextView mTotalTime; 60 final ProgressBar mProgressBar; 61 int mCurrentTimeInSeconds = -1; 62 StringBuilder mTotalTimeStringBuilder = new StringBuilder(); 63 StringBuilder mCurrentTimeStringBuilder = new StringBuilder(); 64 int mCurrentTimeMarginStart; 65 int mTotalTimeMarginEnd; 66 final PersistentFocusWrapper mControlsFocusWrapper; 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 mControlsFocusWrapper = (PersistentFocusWrapper) mControlBar.getParent(); 95 } 96 97 void showMoreActions(boolean show) { 98 if (show) { 99 if (mMoreActionsViewHolder == null) { 100 Action action = new PlaybackControlsRow.MoreActions(mMoreActionsDock.getContext()); 101 mMoreActionsViewHolder = mPresenter.onCreateViewHolder(mMoreActionsDock); 102 mPresenter.onBindViewHolder(mMoreActionsViewHolder, action); 103 mPresenter.setOnClickListener(mMoreActionsViewHolder, new View.OnClickListener() { 104 @Override 105 public void onClick(View v) { 106 toggleMoreActions(); 107 } 108 }); 109 } 110 if (mMoreActionsViewHolder.view.getParent() == null) { 111 mMoreActionsDock.addView(mMoreActionsViewHolder.view); 112 } 113 } else if (mMoreActionsViewHolder != null && 114 mMoreActionsViewHolder.view.getParent() != null) { 115 mMoreActionsDock.removeView(mMoreActionsViewHolder.view); 116 } 117 } 118 119 void toggleMoreActions() { 120 mMoreActionsShowing = !mMoreActionsShowing; 121 showControls(mPresenter); 122 } 123 124 @Override 125 ObjectAdapter getDisplayedAdapter() { 126 return mMoreActionsShowing ? mMoreActionsAdapter : mAdapter; 127 } 128 129 @Override 130 int getChildMarginFromCenter(Context context, int numControls) { 131 int margin = getControlIconWidth(context); 132 if (numControls < 4) { 133 margin += getChildMarginBiggest(context); 134 } else if (numControls < 6) { 135 margin += getChildMarginBigger(context); 136 } else { 137 margin += getChildMarginDefault(context); 138 } 139 return margin; 140 } 141 142 void setTotalTime(int totalTimeMs) { 143 if (totalTimeMs <= 0) { 144 mTotalTime.setVisibility(View.GONE); 145 mProgressBar.setVisibility(View.GONE); 146 } else { 147 mTotalTime.setVisibility(View.VISIBLE); 148 mProgressBar.setVisibility(View.VISIBLE); 149 formatTime(totalTimeMs / 1000, mTotalTimeStringBuilder); 150 mTotalTime.setText(mTotalTimeStringBuilder.toString()); 151 mProgressBar.setMax(totalTimeMs); 152 } 153 } 154 155 int getTotalTime() { 156 return mProgressBar.getMax(); 157 } 158 159 void setCurrentTime(int currentTimeMs) { 160 int seconds = currentTimeMs / 1000; 161 if (seconds != mCurrentTimeInSeconds) { 162 mCurrentTimeInSeconds = seconds; 163 formatTime(mCurrentTimeInSeconds, mCurrentTimeStringBuilder); 164 mCurrentTime.setText(mCurrentTimeStringBuilder.toString()); 165 } 166 mProgressBar.setProgress(currentTimeMs); 167 } 168 169 int getCurrentTime() { 170 return mProgressBar.getProgress(); 171 } 172 173 void setSecondaryProgress(int progressMs) { 174 mProgressBar.setSecondaryProgress(progressMs); 175 } 176 177 int getSecondaryProgress() { 178 return mProgressBar.getSecondaryProgress(); 179 } 180 } 181 182 private static void formatTime(int seconds, StringBuilder sb) { 183 int minutes = seconds / 60; 184 int hours = minutes / 60; 185 seconds -= minutes * 60; 186 minutes -= hours * 60; 187 188 sb.setLength(0); 189 if (hours > 0) { 190 sb.append(hours).append(':'); 191 if (minutes < 10) { 192 sb.append('0'); 193 } 194 } 195 sb.append(minutes).append(':'); 196 if (seconds < 10) { 197 sb.append('0'); 198 } 199 sb.append(seconds); 200 } 201 202 private boolean mMoreActionsEnabled = true; 203 private static int sChildMarginBigger; 204 private static int sChildMarginBiggest; 205 206 /** 207 * Constructor for a PlaybackControlsRowPresenter. 208 * 209 * @param layoutResourceId The resource id of the layout for this presenter. 210 */ 211 public PlaybackControlsPresenter(int layoutResourceId) { 212 super(layoutResourceId); 213 } 214 215 /** 216 * Enables the display of secondary actions. 217 * A "more actions" button will be displayed. When "more actions" is selected, 218 * the primary actions are replaced with the secondary actions. 219 */ 220 public void enableSecondaryActions(boolean enable) { 221 mMoreActionsEnabled = enable; 222 } 223 224 /** 225 * Returns true if secondary actions are enabled. 226 */ 227 public boolean areMoreActionsEnabled() { 228 return mMoreActionsEnabled; 229 } 230 231 public void setProgressColor(ViewHolder vh, @ColorInt int color) { 232 Drawable drawable = new ClipDrawable(new ColorDrawable(color), 233 Gravity.LEFT, ClipDrawable.HORIZONTAL); 234 ((LayerDrawable) vh.mProgressBar.getProgressDrawable()) 235 .setDrawableByLayerId(android.R.id.progress, drawable); 236 } 237 238 public void setTotalTime(ViewHolder vh, int ms) { 239 vh.setTotalTime(ms); 240 } 241 242 public int getTotalTime(ViewHolder vh) { 243 return vh.getTotalTime(); 244 } 245 246 public void setCurrentTime(ViewHolder vh, int ms) { 247 vh.setCurrentTime(ms); 248 } 249 250 public int getCurrentTime(ViewHolder vh) { 251 return vh.getCurrentTime(); 252 } 253 254 public void setSecondaryProgress(ViewHolder vh, int progressMs) { 255 vh.setSecondaryProgress(progressMs); 256 } 257 258 public int getSecondaryProgress(ViewHolder vh) { 259 return vh.getSecondaryProgress(); 260 } 261 262 public void showPrimaryActions(ViewHolder vh) { 263 if (vh.mMoreActionsShowing) { 264 vh.toggleMoreActions(); 265 } 266 } 267 268 public void resetFocus(ViewHolder vh) { 269 vh.mControlsFocusWrapper.clearSelection(); 270 vh.mControlBar.requestFocus(); 271 } 272 273 public void enableTimeMargins(ViewHolder vh, boolean enable) { 274 MarginLayoutParams lp; 275 lp = (MarginLayoutParams) vh.mCurrentTime.getLayoutParams(); 276 lp.setMarginStart(enable ? vh.mCurrentTimeMarginStart : 0); 277 vh.mCurrentTime.setLayoutParams(lp); 278 279 lp = (MarginLayoutParams) vh.mTotalTime.getLayoutParams(); 280 lp.setMarginEnd(enable ? vh.mTotalTimeMarginEnd : 0); 281 vh.mTotalTime.setLayoutParams(lp); 282 } 283 284 @Override 285 public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { 286 View v = LayoutInflater.from(parent.getContext()) 287 .inflate(getLayoutResourceId(), parent, false); 288 return new ViewHolder(v); 289 } 290 291 @Override 292 public void onBindViewHolder(Presenter.ViewHolder holder, Object item) { 293 ViewHolder vh = (ViewHolder) holder; 294 BoundData data = (BoundData) item; 295 296 // If binding to a new adapter, display primary actions. 297 if (vh.mMoreActionsAdapter != data.secondaryActionsAdapter) { 298 vh.mMoreActionsAdapter = data.secondaryActionsAdapter; 299 vh.mMoreActionsAdapter.registerObserver(vh.mMoreActionsObserver); 300 vh.mMoreActionsShowing = false; 301 } 302 303 super.onBindViewHolder(holder, item); 304 vh.showMoreActions(mMoreActionsEnabled); 305 } 306 307 @Override 308 public void onUnbindViewHolder(Presenter.ViewHolder holder) { 309 super.onUnbindViewHolder(holder); 310 ViewHolder vh = (ViewHolder) holder; 311 if (vh.mMoreActionsAdapter != null) { 312 vh.mMoreActionsAdapter.unregisterObserver(vh.mMoreActionsObserver); 313 vh.mMoreActionsAdapter = null; 314 } 315 } 316 317 int getChildMarginBigger(Context context) { 318 if (sChildMarginBigger == 0) { 319 sChildMarginBigger = context.getResources().getDimensionPixelSize( 320 R.dimen.lb_playback_controls_child_margin_bigger); 321 } 322 return sChildMarginBigger; 323 } 324 325 int getChildMarginBiggest(Context context) { 326 if (sChildMarginBiggest == 0) { 327 sChildMarginBiggest = context.getResources().getDimensionPixelSize( 328 R.dimen.lb_playback_controls_child_margin_biggest); 329 } 330 return sChildMarginBiggest; 331 } 332} 333