1 /* 2 * Copyright (C) 2016 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.dvr.ui.list; 18 19 import android.animation.ValueAnimator; 20 import android.content.Context; 21 import android.graphics.drawable.Drawable; 22 import android.support.v17.leanback.widget.RowPresenter; 23 import android.view.LayoutInflater; 24 import android.view.View; 25 import android.view.View.OnClickListener; 26 import android.view.View.OnFocusChangeListener; 27 import android.view.ViewGroup; 28 import android.view.animation.DecelerateInterpolator; 29 import android.widget.TextView; 30 import com.android.tv.R; 31 import com.android.tv.TvSingletons; 32 import com.android.tv.dvr.data.SeriesRecording; 33 import com.android.tv.dvr.ui.DvrUiHelper; 34 import com.android.tv.dvr.ui.list.SchedulesHeaderRow.SeriesRecordingHeaderRow; 35 36 /** A base class for RowPresenter for {@link SchedulesHeaderRow} */ 37 abstract class SchedulesHeaderRowPresenter extends RowPresenter { 38 private Context mContext; 39 40 public SchedulesHeaderRowPresenter(Context context) { 41 setHeaderPresenter(null); 42 setSelectEffectEnabled(false); 43 mContext = context; 44 } 45 46 /** Returns the context. */ 47 Context getContext() { 48 return mContext; 49 } 50 51 /** A ViewHolder for {@link SchedulesHeaderRow}. */ 52 public static class SchedulesHeaderRowViewHolder extends RowPresenter.ViewHolder { 53 private TextView mTitle; 54 private TextView mDescription; 55 56 public SchedulesHeaderRowViewHolder(Context context, ViewGroup parent) { 57 super( 58 LayoutInflater.from(context) 59 .inflate(R.layout.dvr_schedules_header, parent, false)); 60 mTitle = (TextView) view.findViewById(R.id.header_title); 61 mDescription = (TextView) view.findViewById(R.id.header_description); 62 } 63 } 64 65 @Override 66 protected void onBindRowViewHolder(RowPresenter.ViewHolder viewHolder, Object item) { 67 super.onBindRowViewHolder(viewHolder, item); 68 SchedulesHeaderRowViewHolder headerViewHolder = (SchedulesHeaderRowViewHolder) viewHolder; 69 SchedulesHeaderRow header = (SchedulesHeaderRow) item; 70 headerViewHolder.mTitle.setText(header.getTitle()); 71 headerViewHolder.mDescription.setText(header.getDescription()); 72 } 73 74 /** A presenter for {@link SchedulesHeaderRow.DateHeaderRow}. */ 75 public static class DateHeaderRowPresenter extends SchedulesHeaderRowPresenter { 76 public DateHeaderRowPresenter(Context context) { 77 super(context); 78 } 79 80 @Override 81 protected ViewHolder createRowViewHolder(ViewGroup parent) { 82 return new DateHeaderRowViewHolder(getContext(), parent); 83 } 84 85 /** A ViewHolder for {@link SchedulesHeaderRow.DateHeaderRow}. */ 86 public static class DateHeaderRowViewHolder extends SchedulesHeaderRowViewHolder { 87 public DateHeaderRowViewHolder(Context context, ViewGroup parent) { 88 super(context, parent); 89 } 90 } 91 } 92 93 /** A presenter for {@link SeriesRecordingHeaderRow}. */ 94 public static class SeriesRecordingHeaderRowPresenter extends SchedulesHeaderRowPresenter { 95 private final boolean mLtr; 96 private final Drawable mSettingsDrawable; 97 private final Drawable mCancelDrawable; 98 private final Drawable mResumeDrawable; 99 100 private final String mSettingsInfo; 101 private final String mCancelAllInfo; 102 private final String mResumeInfo; 103 104 public SeriesRecordingHeaderRowPresenter(Context context) { 105 super(context); 106 mLtr = 107 context.getResources().getConfiguration().getLayoutDirection() 108 == View.LAYOUT_DIRECTION_LTR; 109 mSettingsDrawable = context.getDrawable(R.drawable.ic_settings); 110 mCancelDrawable = context.getDrawable(R.drawable.ic_dvr_cancel_large); 111 mResumeDrawable = context.getDrawable(R.drawable.ic_record_start); 112 mSettingsInfo = context.getString(R.string.dvr_series_schedules_settings); 113 mCancelAllInfo = context.getString(R.string.dvr_series_schedules_stop); 114 mResumeInfo = context.getString(R.string.dvr_series_schedules_start); 115 } 116 117 @Override 118 protected ViewHolder createRowViewHolder(ViewGroup parent) { 119 return new SeriesHeaderRowViewHolder(getContext(), parent); 120 } 121 122 @Override 123 protected void onBindRowViewHolder(RowPresenter.ViewHolder viewHolder, Object item) { 124 super.onBindRowViewHolder(viewHolder, item); 125 SeriesHeaderRowViewHolder headerViewHolder = (SeriesHeaderRowViewHolder) viewHolder; 126 SeriesRecordingHeaderRow header = (SeriesRecordingHeaderRow) item; 127 headerViewHolder.mSeriesSettingsButton.setVisibility( 128 header.getSeriesRecording().isStopped() ? View.INVISIBLE : View.VISIBLE); 129 headerViewHolder.mSeriesSettingsButton.setText(mSettingsInfo); 130 setTextDrawable(headerViewHolder.mSeriesSettingsButton, mSettingsDrawable); 131 if (header.getSeriesRecording().isStopped()) { 132 headerViewHolder.mToggleStartStopButton.setText(mResumeInfo); 133 setTextDrawable(headerViewHolder.mToggleStartStopButton, mResumeDrawable); 134 } else { 135 headerViewHolder.mToggleStartStopButton.setText(mCancelAllInfo); 136 setTextDrawable(headerViewHolder.mToggleStartStopButton, mCancelDrawable); 137 } 138 headerViewHolder.mSeriesSettingsButton.setOnClickListener( 139 new OnClickListener() { 140 @Override 141 public void onClick(View view) { 142 DvrUiHelper.startSeriesSettingsActivity( 143 getContext(), 144 header.getSeriesRecording().getId(), 145 header.getPrograms(), 146 false, 147 false, 148 false, 149 null); 150 } 151 }); 152 headerViewHolder.mToggleStartStopButton.setOnClickListener( 153 new OnClickListener() { 154 @Override 155 public void onClick(View view) { 156 if (header.getSeriesRecording().isStopped()) { 157 // Reset priority to the highest. 158 SeriesRecording seriesRecording = 159 SeriesRecording.buildFrom(header.getSeriesRecording()) 160 .setPriority( 161 TvSingletons.getSingletons(getContext()) 162 .getDvrScheduleManager() 163 .suggestNewSeriesPriority()) 164 .build(); 165 TvSingletons.getSingletons(getContext()) 166 .getDvrManager() 167 .updateSeriesRecording(seriesRecording); 168 DvrUiHelper.startSeriesSettingsActivity( 169 getContext(), 170 header.getSeriesRecording().getId(), 171 header.getPrograms(), 172 false, 173 false, 174 false, 175 null); 176 } else { 177 DvrUiHelper.showCancelAllSeriesRecordingDialog( 178 (DvrSchedulesActivity) view.getContext(), 179 header.getSeriesRecording()); 180 } 181 } 182 }); 183 } 184 185 private void setTextDrawable(TextView textView, Drawable drawableStart) { 186 textView.setCompoundDrawablesRelativeWithIntrinsicBounds( 187 drawableStart, null, null, null); 188 } 189 190 /** A ViewHolder for {@link SeriesRecordingHeaderRow}. */ 191 public static class SeriesHeaderRowViewHolder extends SchedulesHeaderRowViewHolder { 192 private final TextView mSeriesSettingsButton; 193 private final TextView mToggleStartStopButton; 194 private final boolean mLtr; 195 196 private final View mSelector; 197 198 private View mLastFocusedView; 199 200 public SeriesHeaderRowViewHolder(Context context, ViewGroup parent) { 201 super(context, parent); 202 mLtr = 203 context.getResources().getConfiguration().getLayoutDirection() 204 == View.LAYOUT_DIRECTION_LTR; 205 view.findViewById(R.id.button_container).setVisibility(View.VISIBLE); 206 mSeriesSettingsButton = (TextView) view.findViewById(R.id.series_settings); 207 mToggleStartStopButton = 208 (TextView) view.findViewById(R.id.series_toggle_start_stop); 209 mSelector = view.findViewById(R.id.selector); 210 OnFocusChangeListener onFocusChangeListener = 211 new View.OnFocusChangeListener() { 212 @Override 213 public void onFocusChange(View view, boolean focused) { 214 view.post( 215 new Runnable() { 216 @Override 217 public void run() { 218 updateSelector(view); 219 } 220 }); 221 } 222 }; 223 mSeriesSettingsButton.setOnFocusChangeListener(onFocusChangeListener); 224 mToggleStartStopButton.setOnFocusChangeListener(onFocusChangeListener); 225 } 226 227 private void updateSelector(View focusedView) { 228 int animationDuration = 229 mSelector 230 .getContext() 231 .getResources() 232 .getInteger(android.R.integer.config_shortAnimTime); 233 DecelerateInterpolator interpolator = new DecelerateInterpolator(); 234 235 if (focusedView.hasFocus()) { 236 ViewGroup.LayoutParams lp = mSelector.getLayoutParams(); 237 final int targetWidth = focusedView.getWidth(); 238 float targetTranslationX; 239 if (mLtr) { 240 targetTranslationX = focusedView.getLeft() - mSelector.getLeft(); 241 } else { 242 targetTranslationX = focusedView.getRight() - mSelector.getRight(); 243 } 244 245 // if the selector is invisible, set the width and translation X directly - 246 // don't animate. 247 if (mSelector.getAlpha() == 0) { 248 mSelector.setTranslationX(targetTranslationX); 249 lp.width = targetWidth; 250 mSelector.requestLayout(); 251 } 252 253 // animate the selector in and to the proper width and translation X. 254 final float deltaWidth = lp.width - targetWidth; 255 mSelector.animate().cancel(); 256 mSelector 257 .animate() 258 .translationX(targetTranslationX) 259 .alpha(1f) 260 .setUpdateListener( 261 new ValueAnimator.AnimatorUpdateListener() { 262 @Override 263 public void onAnimationUpdate(ValueAnimator animation) { 264 // Set width to the proper width for this animation 265 // step. 266 lp.width = 267 targetWidth 268 + Math.round( 269 deltaWidth 270 * (1f 271 - animation 272 .getAnimatedFraction())); 273 mSelector.requestLayout(); 274 } 275 }) 276 .setDuration(animationDuration) 277 .setInterpolator(interpolator) 278 .start(); 279 mLastFocusedView = focusedView; 280 } else if (mLastFocusedView == focusedView) { 281 mSelector.animate().setUpdateListener(null).cancel(); 282 mSelector 283 .animate() 284 .alpha(0f) 285 .setDuration(animationDuration) 286 .setInterpolator(interpolator) 287 .start(); 288 mLastFocusedView = null; 289 } 290 } 291 } 292 } 293 } 294