Implement playback state management

This commit is contained in:
Vasiliy 2019-04-13 10:31:32 +03:00
parent 416e0fb609
commit 4e1423d224
No known key found for this signature in database
GPG key ID: 9F74C4D2874D7523
33 changed files with 978 additions and 582 deletions

View file

@ -574,7 +574,7 @@ public class RouterActivity extends AppCompatActivity {
playQueue = new SinglePlayQueue((StreamInfo) info); playQueue = new SinglePlayQueue((StreamInfo) info);
if (playerChoice.equals(videoPlayerKey)) { if (playerChoice.equals(videoPlayerKey)) {
NavigationHelper.playOnMainPlayer(this, playQueue); NavigationHelper.playOnMainPlayer(this, playQueue, true);
} else if (playerChoice.equals(backgroundPlayerKey)) { } else if (playerChoice.equals(backgroundPlayerKey)) {
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true); NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
} else if (playerChoice.equals(popupPlayerKey)) { } else if (playerChoice.equals(popupPlayerKey)) {
@ -587,11 +587,11 @@ public class RouterActivity extends AppCompatActivity {
playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info); playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info);
if (playerChoice.equals(videoPlayerKey)) { if (playerChoice.equals(videoPlayerKey)) {
NavigationHelper.playOnMainPlayer(this, playQueue); NavigationHelper.playOnMainPlayer(this, playQueue, true);
} else if (playerChoice.equals(backgroundPlayerKey)) { } else if (playerChoice.equals(backgroundPlayerKey)) {
NavigationHelper.playOnBackgroundPlayer(this, playQueue); NavigationHelper.playOnBackgroundPlayer(this, playQueue, true);
} else if (playerChoice.equals(popupPlayerKey)) { } else if (playerChoice.equals(popupPlayerKey)) {
NavigationHelper.playOnPopupPlayer(this, playQueue); NavigationHelper.playOnPopupPlayer(this, playQueue, true);
} }
} }
}; };

View file

@ -50,6 +50,11 @@ public abstract class StreamHistoryDAO implements HistoryDAO<StreamHistoryEntity
" ORDER BY " + STREAM_ACCESS_DATE + " DESC") " ORDER BY " + STREAM_ACCESS_DATE + " DESC")
public abstract Flowable<List<StreamHistoryEntry>> getHistory(); public abstract Flowable<List<StreamHistoryEntry>> getHistory();
@Query("SELECT * FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID +
" = :streamId ORDER BY " + STREAM_ACCESS_DATE + " DESC LIMIT 1")
@Nullable
public abstract StreamHistoryEntity getLatestEntry(final long streamId);
@Query("DELETE FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId") @Query("DELETE FROM " + STREAM_HISTORY_TABLE + " WHERE " + JOIN_STREAM_ID + " = :streamId")
public abstract int deleteStreamHistory(final long streamId); public abstract int deleteStreamHistory(final long streamId);

View file

@ -5,6 +5,8 @@ import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity; import android.arch.persistence.room.Entity;
import android.arch.persistence.room.ForeignKey; import android.arch.persistence.room.ForeignKey;
import java.util.concurrent.TimeUnit;
import static android.arch.persistence.room.ForeignKey.CASCADE; import static android.arch.persistence.room.ForeignKey.CASCADE;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID; import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
@ -22,6 +24,12 @@ public class StreamStateEntity {
final public static String JOIN_STREAM_ID = "stream_id"; final public static String JOIN_STREAM_ID = "stream_id";
final public static String STREAM_PROGRESS_TIME = "progress_time"; final public static String STREAM_PROGRESS_TIME = "progress_time";
/** Playback state will not be saved, if playback time less than this threshold */
private static final int PLAYBACK_SAVE_THRESHOLD_START_SECONDS = 5;
/** Playback state will not be saved, if time left less than this threshold */
private static final int PLAYBACK_SAVE_THRESHOLD_END_SECONDS = 10;
@ColumnInfo(name = JOIN_STREAM_ID) @ColumnInfo(name = JOIN_STREAM_ID)
private long streamUid; private long streamUid;
@ -48,4 +56,10 @@ public class StreamStateEntity {
public void setProgressTime(long progressTime) { public void setProgressTime(long progressTime) {
this.progressTime = progressTime; this.progressTime = progressTime;
} }
public boolean isValid(int durationInSeconds) {
final int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(progressTime);
return seconds > PLAYBACK_SAVE_THRESHOLD_START_SECONDS
&& seconds < durationInSeconds - PLAYBACK_SAVE_THRESHOLD_END_SECONDS;
}
} }

View file

@ -89,12 +89,14 @@ import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.views.AnimatedProgressBar;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import icepick.State; import icepick.State;
import io.reactivex.Single; import io.reactivex.Single;
@ -118,7 +120,7 @@ public class VideoDetailFragment
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1; private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2; private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4; private static final int TOOLBAR_ITEMS_UPDATE_FLAG = 0x4;
private static final int COMMENTS_UPDATE_FLAG = 0x4; private static final int COMMENTS_UPDATE_FLAG = 0x8;
private boolean autoPlayEnabled; private boolean autoPlayEnabled;
private boolean showRelatedStreams; private boolean showRelatedStreams;
@ -136,6 +138,8 @@ public class VideoDetailFragment
private Disposable currentWorker; private Disposable currentWorker;
@NonNull @NonNull
private CompositeDisposable disposables = new CompositeDisposable(); private CompositeDisposable disposables = new CompositeDisposable();
@Nullable
private Disposable positionSubscriber = null;
private List<VideoStream> sortedVideoStreams; private List<VideoStream> sortedVideoStreams;
private int selectedVideoStreamIndex = -1; private int selectedVideoStreamIndex = -1;
@ -153,6 +157,7 @@ public class VideoDetailFragment
private View thumbnailBackgroundButton; private View thumbnailBackgroundButton;
private ImageView thumbnailImageView; private ImageView thumbnailImageView;
private ImageView thumbnailPlayButton; private ImageView thumbnailPlayButton;
private AnimatedProgressBar positionView;
private View videoTitleRoot; private View videoTitleRoot;
private TextView videoTitleTextView; private TextView videoTitleTextView;
@ -165,6 +170,7 @@ public class VideoDetailFragment
private TextView detailControlsDownload; private TextView detailControlsDownload;
private TextView appendControlsDetail; private TextView appendControlsDetail;
private TextView detailDurationView; private TextView detailDurationView;
private TextView detailPositionView;
private LinearLayout videoDescriptionRootLayout; private LinearLayout videoDescriptionRootLayout;
private TextView videoUploadDateView; private TextView videoUploadDateView;
@ -259,6 +265,8 @@ public class VideoDetailFragment
// Check if it was loading when the fragment was stopped/paused, // Check if it was loading when the fragment was stopped/paused,
if (wasLoading.getAndSet(false)) { if (wasLoading.getAndSet(false)) {
selectAndLoadVideo(serviceId, url, name); selectAndLoadVideo(serviceId, url, name);
} else if (currentInfo != null) {
updateProgressInfo(currentInfo);
} }
} }
@ -268,8 +276,10 @@ public class VideoDetailFragment
PreferenceManager.getDefaultSharedPreferences(activity) PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this); .unregisterOnSharedPreferenceChangeListener(this);
if (positionSubscriber != null) positionSubscriber.dispose();
if (currentWorker != null) currentWorker.dispose(); if (currentWorker != null) currentWorker.dispose();
if (disposables != null) disposables.clear(); if (disposables != null) disposables.clear();
positionSubscriber = null;
currentWorker = null; currentWorker = null;
disposables = null; disposables = null;
} }
@ -462,6 +472,7 @@ public class VideoDetailFragment
videoTitleTextView = rootView.findViewById(R.id.detail_video_title_view); videoTitleTextView = rootView.findViewById(R.id.detail_video_title_view);
videoTitleToggleArrow = rootView.findViewById(R.id.detail_toggle_description_view); videoTitleToggleArrow = rootView.findViewById(R.id.detail_toggle_description_view);
videoCountView = rootView.findViewById(R.id.detail_view_count_view); videoCountView = rootView.findViewById(R.id.detail_view_count_view);
positionView = rootView.findViewById(R.id.position_view);
detailControlsBackground = rootView.findViewById(R.id.detail_controls_background); detailControlsBackground = rootView.findViewById(R.id.detail_controls_background);
detailControlsPopup = rootView.findViewById(R.id.detail_controls_popup); detailControlsPopup = rootView.findViewById(R.id.detail_controls_popup);
@ -469,6 +480,7 @@ public class VideoDetailFragment
detailControlsDownload = rootView.findViewById(R.id.detail_controls_download); detailControlsDownload = rootView.findViewById(R.id.detail_controls_download);
appendControlsDetail = rootView.findViewById(R.id.touch_append_detail); appendControlsDetail = rootView.findViewById(R.id.touch_append_detail);
detailDurationView = rootView.findViewById(R.id.detail_duration_view); detailDurationView = rootView.findViewById(R.id.detail_duration_view);
detailPositionView = rootView.findViewById(R.id.detail_position_view);
videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout); videoDescriptionRootLayout = rootView.findViewById(R.id.detail_description_root_layout);
videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view); videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view);
@ -536,10 +548,10 @@ public class VideoDetailFragment
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> { final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
switch (i) { switch (i) {
case 0: case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), true);
break; break;
case 1: case 1:
NavigationHelper.enqueueOnPopupPlayer(getActivity(), new SinglePlayQueue(item)); NavigationHelper.enqueueOnPopupPlayer(getActivity(), new SinglePlayQueue(item), true);
break; break;
case 2: case 2:
if (getFragmentManager() != null) { if (getFragmentManager() != null) {
@ -890,11 +902,11 @@ public class VideoDetailFragment
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo); final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
if (append) { if (append) {
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue); NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue, false);
} else { } else {
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
final Intent intent = NavigationHelper.getPlayerIntent( final Intent intent = NavigationHelper.getPlayerIntent(
activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution, true
); );
activity.startService(intent); activity.startService(intent);
} }
@ -914,9 +926,9 @@ public class VideoDetailFragment
private void openNormalBackgroundPlayer(final boolean append) { private void openNormalBackgroundPlayer(final boolean append) {
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo); final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
if (append) { if (append) {
NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue); NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue, false);
} else { } else {
NavigationHelper.playOnBackgroundPlayer(activity, itemQueue); NavigationHelper.playOnBackgroundPlayer(activity, itemQueue, true);
} }
} }
@ -926,7 +938,7 @@ public class VideoDetailFragment
mIntent = NavigationHelper.getPlayerIntent(activity, mIntent = NavigationHelper.getPlayerIntent(activity,
MainVideoPlayer.class, MainVideoPlayer.class,
playQueue, playQueue,
getSelectedVideoStream().getResolution()); getSelectedVideoStream().getResolution(), true);
startActivity(mIntent); startActivity(mIntent);
} }
@ -1032,6 +1044,8 @@ public class VideoDetailFragment
animateView(spinnerToolbar, false, 200); animateView(spinnerToolbar, false, 200);
animateView(thumbnailPlayButton, false, 50); animateView(thumbnailPlayButton, false, 50);
animateView(detailDurationView, false, 100); animateView(detailDurationView, false, 100);
animateView(detailPositionView, false, 100);
animateView(positionView, false, 50);
videoTitleTextView.setText(name != null ? name : ""); videoTitleTextView.setText(name != null ? name : "");
videoTitleTextView.setMaxLines(1); videoTitleTextView.setMaxLines(1);
@ -1146,6 +1160,7 @@ public class VideoDetailFragment
videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate())); videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
} }
prepareDescription(info.getDescription()); prepareDescription(info.getDescription());
updateProgressInfo(info);
animateView(spinnerToolbar, true, 500); animateView(spinnerToolbar, true, 500);
setupActionBar(info); setupActionBar(info);
@ -1250,4 +1265,37 @@ public class VideoDetailFragment
showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema); showError(getString(R.string.blocked_by_gema), false, R.drawable.gruese_die_gema);
} }
private void updateProgressInfo(@NonNull final StreamInfo info) {
if (positionSubscriber != null) {
positionSubscriber.dispose();
}
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
final boolean playbackResumeEnabled =
prefs.getBoolean(activity.getString(R.string.enable_watch_history_key), true)
&& prefs.getBoolean(activity.getString(R.string.enable_playback_resume_key), true);
if (!playbackResumeEnabled || info.getDuration() <= 0) {
positionView.setVisibility(View.INVISIBLE);
detailPositionView.setVisibility(View.GONE);
return;
}
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
positionSubscriber = recordManager.loadStreamState(info)
.subscribeOn(Schedulers.io())
.onErrorComplete()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(state -> {
final int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime());
positionView.setMax((int) info.getDuration());
positionView.setProgress(seconds);
detailPositionView.setText(Localization.getDurationString(seconds));
animateView(positionView, true, 500);
animateView(detailPositionView, true, 500);
}, e -> {
if (DEBUG) e.printStackTrace();
}, () -> {
animateView(positionView, false, 500);
animateView(detailPositionView, false, 500);
});
}
} }

View file

@ -266,13 +266,13 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
final DialogInterface.OnClickListener actions = (dialogInterface, i) -> { final DialogInterface.OnClickListener actions = (dialogInterface, i) -> {
switch (i) { switch (i) {
case 0: case 0:
NavigationHelper.playOnBackgroundPlayer(context, new SinglePlayQueue(item)); NavigationHelper.playOnBackgroundPlayer(context, new SinglePlayQueue(item), true);
break; break;
case 1: case 1:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), true);
break; break;
case 2: case 2:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item)); NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item), true);
break; break;
case 3: case 3:
if (getFragmentManager() != null) { if (getFragmentManager() != null) {

View file

@ -170,19 +170,19 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0); final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
switch (i) { switch (i) {
case 0: case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), false);
break; break;
case 1: case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item)); NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item), false);
break; break;
case 2: case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index)); NavigationHelper.playOnMainPlayer(context, getPlayQueue(index), true);
break; break;
case 3: case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index)); NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index), true);
break; break;
case 4: case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index), true);
break; break;
case 5: case 5:
if (getFragmentManager() != null) { if (getFragmentManager() != null) {
@ -440,11 +440,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
monitorSubscription(result); monitorSubscription(result);
headerPlayAllButton.setOnClickListener( headerPlayAllButton.setOnClickListener(
view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnClickListener( headerPopupButton.setOnClickListener(
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue())); view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener( headerBackgroundButton.setOnClickListener(
view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue())); view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
} }
private PlayQueue getPlayQueue() { private PlayQueue getPlayQueue() {

View file

@ -154,22 +154,22 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0); final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0);
switch (i) { switch (i) {
case 0: case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), false);
break; break;
case 1: case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item)); NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item), false);
break; break;
case 2: case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index)); NavigationHelper.playOnMainPlayer(context, getPlayQueue(index), true);
break; break;
case 3: case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index)); NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index), true);
break; break;
case 4: case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index), true);
break; break;
case 5: case 5:
ShareUtils.shareUrl(this.getContext(), item.getName(), item.getUrl()); ShareUtils.shareUrl(requireContext(), item.getName(), item.getUrl());
break; break;
default: default:
break; break;
@ -301,19 +301,19 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
.subscribe(getPlaylistBookmarkSubscriber()); .subscribe(getPlaylistBookmarkSubscriber());
headerPlayAllButton.setOnClickListener(view -> headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnClickListener(view -> headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue())); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> headerBackgroundButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue())); NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnLongClickListener(view -> { headerPopupButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue()); NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
headerBackgroundButton.setOnLongClickListener(view -> { headerBackgroundButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue()); NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), true);
return true; return true;
}); });
} }

View file

@ -37,12 +37,14 @@ import org.schabi.newpipe.database.stream.dao.StreamStateDAO;
import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import io.reactivex.Completable;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Maybe; import io.reactivex.Maybe;
import io.reactivex.Single; import io.reactivex.Single;
@ -80,9 +82,9 @@ public class HistoryRecordManager {
final Date currentTime = new Date(); final Date currentTime = new Date();
return Maybe.fromCallable(() -> database.runInTransaction(() -> { return Maybe.fromCallable(() -> database.runInTransaction(() -> {
final long streamId = streamTable.upsert(new StreamEntity(info)); final long streamId = streamTable.upsert(new StreamEntity(info));
StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(); StreamHistoryEntity latestEntry = streamHistoryTable.getLatestEntry(streamId);
if (latestEntry != null && latestEntry.getStreamUid() == streamId) { if (latestEntry != null) {
streamHistoryTable.delete(latestEntry); streamHistoryTable.delete(latestEntry);
latestEntry.setAccessDate(currentTime); latestEntry.setAccessDate(currentTime);
latestEntry.setRepeatCount(latestEntry.getRepeatCount() + 1); latestEntry.setRepeatCount(latestEntry.getRepeatCount() + 1);
@ -99,7 +101,7 @@ public class HistoryRecordManager {
} }
public Single<Integer> deleteWholeStreamHistory() { public Single<Integer> deleteWholeStreamHistory() {
return Single.fromCallable(() -> streamHistoryTable.deleteAll()) return Single.fromCallable(streamHistoryTable::deleteAll)
.subscribeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
@ -160,7 +162,7 @@ public class HistoryRecordManager {
} }
public Single<Integer> deleteWholeSearchHistory() { public Single<Integer> deleteWholeSearchHistory() {
return Single.fromCallable(() -> searchHistoryTable.deleteAll()) return Single.fromCallable(searchHistoryTable::deleteAll)
.subscribeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
@ -180,18 +182,41 @@ public class HistoryRecordManager {
// Stream State History // Stream State History
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
@SuppressWarnings("unused") public Maybe<StreamHistoryEntity> getStreamHistory(final StreamInfo info) {
public Maybe<StreamStateEntity> loadStreamState(final StreamInfo info) { return Maybe.fromCallable(() -> {
return Maybe.fromCallable(() -> streamTable.upsert(new StreamEntity(info))) final long streamId = streamTable.upsert(new StreamEntity(info));
.flatMap(streamId -> streamStateTable.getState(streamId).firstElement()) return streamHistoryTable.getLatestEntry(streamId);
.flatMap(states -> states.isEmpty() ? Maybe.empty() : Maybe.just(states.get(0))) }).subscribeOn(Schedulers.io());
}
public Maybe<StreamStateEntity> loadStreamState(final PlayQueueItem queueItem) {
return queueItem.getStream()
.map((info) -> streamTable.upsert(new StreamEntity(info)))
.flatMapPublisher(streamStateTable::getState)
.firstElement()
.flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0)))
.filter(state -> state.isValid((int) queueItem.getDuration()))
.subscribeOn(Schedulers.io()); .subscribeOn(Schedulers.io());
} }
public Maybe<Long> saveStreamState(@NonNull final StreamInfo info, final long progressTime) { public Maybe<StreamStateEntity> loadStreamState(final StreamInfo info) {
return Maybe.fromCallable(() -> database.runInTransaction(() -> { return Single.fromCallable(() -> streamTable.upsert(new StreamEntity(info)))
.flatMapPublisher(streamStateTable::getState)
.firstElement()
.flatMap(list -> list.isEmpty() ? Maybe.empty() : Maybe.just(list.get(0)))
.filter(state -> state.isValid((int) info.getDuration()))
.subscribeOn(Schedulers.io());
}
public Completable saveStreamState(@NonNull final StreamInfo info, final long progressTime) {
return Completable.fromAction(() -> database.runInTransaction(() -> {
final long streamId = streamTable.upsert(new StreamEntity(info)); final long streamId = streamTable.upsert(new StreamEntity(info));
return streamStateTable.upsert(new StreamStateEntity(streamId, progressTime)); final StreamStateEntity state = new StreamStateEntity(streamId, progressTime);
if (state.isValid((int) info.getDuration())) {
streamStateTable.upsert(state);
} else {
streamStateTable.deleteState(streamId);
}
})).subscribeOn(Schedulers.io()); })).subscribeOn(Schedulers.io());
} }

View file

@ -310,11 +310,11 @@ public class StatisticsPlaylistFragment
} }
headerPlayAllButton.setOnClickListener(view -> headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnClickListener(view -> headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue())); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> headerBackgroundButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue())); NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
sortButton.setOnClickListener(view -> toggleSortMode()); sortButton.setOnClickListener(view -> toggleSortMode());
hideLoading(); hideLoading();
@ -377,19 +377,19 @@ public class StatisticsPlaylistFragment
final int index = Math.max(itemListAdapter.getItemsList().indexOf(item), 0); final int index = Math.max(itemListAdapter.getItemsList().indexOf(item), 0);
switch (i) { switch (i) {
case 0: case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(infoItem)); NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(infoItem), false);
break; break;
case 1: case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(infoItem)); NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(infoItem), false);
break; break;
case 2: case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index)); NavigationHelper.playOnMainPlayer(context, getPlayQueue(index), true);
break; break;
case 3: case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index)); NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index), true);
break; break;
case 4: case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index), true);
break; break;
case 5: case 5:
deleteEntry(index); deleteEntry(index);

View file

@ -319,11 +319,11 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
setVideoCount(itemListAdapter.getItemsList().size()); setVideoCount(itemListAdapter.getItemsList().size());
headerPlayAllButton.setOnClickListener(view -> headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue())); NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnClickListener(view -> headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue())); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view -> headerBackgroundButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue())); NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
hideLoading(); hideLoading();
} }
@ -534,20 +534,20 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
switch (i) { switch (i) {
case 0: case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, NavigationHelper.enqueueOnBackgroundPlayer(context,
new SinglePlayQueue(infoItem)); new SinglePlayQueue(infoItem), false);
break; break;
case 1: case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new NavigationHelper.enqueueOnPopupPlayer(activity, new
SinglePlayQueue(infoItem)); SinglePlayQueue(infoItem), false);
break; break;
case 2: case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index)); NavigationHelper.playOnMainPlayer(context, getPlayQueue(index), true);
break; break;
case 3: case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index)); NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index), true);
break; break;
case 4: case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index)); NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index), true);
break; break;
case 5: case 5:
changeThumbnailUrl(item.thumbnailUrl); changeThumbnailUrl(item.thumbnailUrl);

View file

@ -150,6 +150,7 @@ public final class BackgroundPlayer extends Service {
lockManager.releaseWifiAndCpu(); lockManager.releaseWifiAndCpu();
} }
if (basePlayerImpl != null) { if (basePlayerImpl != null) {
basePlayerImpl.savePlaybackState();
basePlayerImpl.stopActivityBinding(); basePlayerImpl.stopActivityBinding();
basePlayerImpl.destroy(); basePlayerImpl.destroy();
} }

View file

@ -23,9 +23,11 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.AudioManager; import android.media.AudioManager;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
@ -145,6 +147,8 @@ public abstract class BasePlayer implements
@NonNull @NonNull
public static final String APPEND_ONLY = "append_only"; public static final String APPEND_ONLY = "append_only";
@NonNull @NonNull
public static final String RESUME_PLAYBACK = "resume_playback";
@NonNull
public static final String SELECT_ON_APPEND = "select_on_append"; public static final String SELECT_ON_APPEND = "select_on_append";
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -279,8 +283,23 @@ public abstract class BasePlayer implements
) { ) {
simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition()); simpleExoPlayer.seekTo(playQueue.getIndex(), queue.getItem().getRecoveryPosition());
return; return;
} else if (intent.getBooleanExtra(RESUME_PLAYBACK, false) && isPlaybackResumeEnabled()) {
final PlayQueueItem item = queue.getItem();
if (item != null && item.getRecoveryPosition() == PlayQueueItem.RECOVERY_UNSET && isPlaybackResumeEnabled()) {
final Disposable stateLoader = recordManager.loadStreamState(item)
.observeOn(AndroidSchedulers.mainThread())
.doFinally(() -> initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
/*playOnInit=*/true))
.subscribe(
state -> queue.setRecovery(queue.getIndex(), state.getProgressTime()),
error -> {
if (DEBUG) error.printStackTrace();
}
);
databaseUpdateReactor.add(stateLoader);
return;
}
} }
// Good to go... // Good to go...
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
/*playOnInit=*/true); /*playOnInit=*/true);
@ -615,6 +634,9 @@ public abstract class BasePlayer implements
break; break;
case Player.STATE_ENDED: // 4 case Player.STATE_ENDED: // 4
changeState(STATE_COMPLETED); changeState(STATE_COMPLETED);
if (currentMetadata != null) {
resetPlaybackState(currentMetadata.getMetadata());
}
isPrepared = false; isPrepared = false;
break; break;
} }
@ -721,6 +743,7 @@ public abstract class BasePlayer implements
case DISCONTINUITY_REASON_SEEK_ADJUSTMENT: case DISCONTINUITY_REASON_SEEK_ADJUSTMENT:
case DISCONTINUITY_REASON_INTERNAL: case DISCONTINUITY_REASON_INTERNAL:
if (playQueue.getIndex() != newWindowIndex) { if (playQueue.getIndex() != newWindowIndex) {
resetPlaybackState(playQueue.getItem());
playQueue.setIndex(newWindowIndex); playQueue.setIndex(newWindowIndex);
} }
break; break;
@ -750,6 +773,9 @@ public abstract class BasePlayer implements
@Override @Override
public void onSeekProcessed() { public void onSeekProcessed() {
if (DEBUG) Log.d(TAG, "ExoPlayer - onSeekProcessed() called"); if (DEBUG) Log.d(TAG, "ExoPlayer - onSeekProcessed() called");
if (isPrepared) {
savePlaybackState();
}
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Playback Listener // Playback Listener
@ -1017,27 +1043,40 @@ public abstract class BasePlayer implements
} }
} }
protected void savePlaybackState(final StreamInfo info, final long progress) { private void savePlaybackState(final StreamInfo info, final long progress) {
if (info == null) return; if (info == null) return;
if (DEBUG) Log.d(TAG, "savePlaybackState() called");
final Disposable stateSaver = recordManager.saveStreamState(info, progress) final Disposable stateSaver = recordManager.saveStreamState(info, progress)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnError((e) -> {
if (DEBUG) e.printStackTrace();
})
.onErrorComplete() .onErrorComplete()
.subscribe( .subscribe();
ignored -> {/* successful */},
error -> Log.e(TAG, "savePlaybackState() failure: ", error)
);
databaseUpdateReactor.add(stateSaver); databaseUpdateReactor.add(stateSaver);
} }
private void savePlaybackState() { private void resetPlaybackState(final PlayQueueItem queueItem) {
if (queueItem == null) return;
final Disposable stateSaver = queueItem.getStream()
.flatMapCompletable(info -> recordManager.saveStreamState(info, 0))
.observeOn(AndroidSchedulers.mainThread())
.doOnError((e) -> {
if (DEBUG) e.printStackTrace();
})
.onErrorComplete()
.subscribe();
databaseUpdateReactor.add(stateSaver);
}
public void resetPlaybackState(final StreamInfo info) {
savePlaybackState(info, 0);
}
public void savePlaybackState() {
if (simpleExoPlayer == null || currentMetadata == null) return; if (simpleExoPlayer == null || currentMetadata == null) return;
final StreamInfo currentInfo = currentMetadata.getMetadata(); final StreamInfo currentInfo = currentMetadata.getMetadata();
savePlaybackState(currentInfo, simpleExoPlayer.getCurrentPosition());
if (simpleExoPlayer.getCurrentPosition() > RECOVERY_SKIP_THRESHOLD_MILLIS &&
simpleExoPlayer.getCurrentPosition() <
simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD_MILLIS) {
savePlaybackState(currentInfo, simpleExoPlayer.getCurrentPosition());
}
} }
private void maybeUpdateCurrentMetadata() { private void maybeUpdateCurrentMetadata() {
@ -1225,4 +1264,10 @@ public abstract class BasePlayer implements
public boolean gotDestroyed() { public boolean gotDestroyed() {
return simpleExoPlayer == null; return simpleExoPlayer == null;
} }
private boolean isPlaybackResumeEnabled() {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(context.getString(R.string.enable_watch_history_key), true)
&& prefs.getBoolean(context.getString(R.string.enable_playback_resume_key), true);
}
} }

View file

@ -247,6 +247,12 @@ public final class MainVideoPlayer extends AppCompatActivity
super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(newBase)); super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(newBase));
} }
@Override
protected void onPause() {
playerImpl.savePlaybackState();
super.onPause();
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// State Saving // State Saving
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -579,7 +585,8 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getPlaybackSpeed(), this.getPlaybackSpeed(),
this.getPlaybackPitch(), this.getPlaybackPitch(),
this.getPlaybackSkipSilence(), this.getPlaybackSkipSilence(),
this.getPlaybackQuality() this.getPlaybackQuality(),
false
); );
context.startService(intent); context.startService(intent);
@ -601,7 +608,8 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getPlaybackSpeed(), this.getPlaybackSpeed(),
this.getPlaybackPitch(), this.getPlaybackPitch(),
this.getPlaybackSkipSilence(), this.getPlaybackSkipSilence(),
this.getPlaybackQuality() this.getPlaybackQuality(),
false
); );
context.startService(intent); context.startService(intent);

View file

@ -325,6 +325,7 @@ public final class PopupVideoPlayer extends Service {
isPopupClosing = true; isPopupClosing = true;
if (playerImpl != null) { if (playerImpl != null) {
playerImpl.savePlaybackState();
if (playerImpl.getRootView() != null) { if (playerImpl.getRootView() != null) {
windowManager.removeView(playerImpl.getRootView()); windowManager.removeView(playerImpl.getRootView());
} }
@ -565,7 +566,8 @@ public final class PopupVideoPlayer extends Service {
this.getPlaybackSpeed(), this.getPlaybackSpeed(),
this.getPlaybackPitch(), this.getPlaybackPitch(),
this.getPlaybackSkipSilence(), this.getPlaybackSkipSilence(),
this.getPlaybackQuality() this.getPlaybackQuality(),
false
); );
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);

View file

@ -188,7 +188,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
this.player.getPlaybackSpeed(), this.player.getPlaybackSpeed(),
this.player.getPlaybackPitch(), this.player.getPlaybackPitch(),
this.player.getPlaybackSkipSilence(), this.player.getPlaybackSkipSilence(),
null null,
false
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} }

View file

@ -543,6 +543,11 @@ public abstract class VideoPlayer extends BasePlayer
playbackSpeedTextView.setText(formatSpeed(getPlaybackSpeed())); playbackSpeedTextView.setText(formatSpeed(getPlaybackSpeed()));
super.onPrepared(playWhenReady); super.onPrepared(playWhenReady);
if (simpleExoPlayer.getCurrentPosition() != 0 && !isControlsVisible()) {
controlsVisibilityHandler.removeCallbacksAndMessages(null);
controlsVisibilityHandler.postDelayed(this::showControlsThenHide, DEFAULT_CONTROLS_DURATION);
}
} }
@Override @Override

View file

@ -124,7 +124,7 @@ public class CommentTextOnTouchListener implements View.OnTouchListener {
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(info -> { .subscribe(info -> {
PlayQueue playQueue = new SinglePlayQueue((StreamInfo) info, seconds*1000); PlayQueue playQueue = new SinglePlayQueue((StreamInfo) info, seconds*1000);
NavigationHelper.playOnPopupPlayer(context, playQueue); NavigationHelper.playOnPopupPlayer(context, playQueue, false);
}); });
return true; return true;
} }

View file

@ -69,12 +69,14 @@ public class NavigationHelper {
public static Intent getPlayerIntent(@NonNull final Context context, public static Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class targetClazz, @NonNull final Class targetClazz,
@NonNull final PlayQueue playQueue, @NonNull final PlayQueue playQueue,
@Nullable final String quality) { @Nullable final String quality,
final boolean resumePlayback) {
Intent intent = new Intent(context, targetClazz); Intent intent = new Intent(context, targetClazz);
final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class); final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class);
if (cacheKey != null) intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey); if (cacheKey != null) intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey);
if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality); if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback);
return intent; return intent;
} }
@ -82,16 +84,18 @@ public class NavigationHelper {
@NonNull @NonNull
public static Intent getPlayerIntent(@NonNull final Context context, public static Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class targetClazz, @NonNull final Class targetClazz,
@NonNull final PlayQueue playQueue) { @NonNull final PlayQueue playQueue,
return getPlayerIntent(context, targetClazz, playQueue, null); final boolean resumePlayback) {
return getPlayerIntent(context, targetClazz, playQueue, null, resumePlayback);
} }
@NonNull @NonNull
public static Intent getPlayerEnqueueIntent(@NonNull final Context context, public static Intent getPlayerEnqueueIntent(@NonNull final Context context,
@NonNull final Class targetClazz, @NonNull final Class targetClazz,
@NonNull final PlayQueue playQueue, @NonNull final PlayQueue playQueue,
final boolean selectOnAppend) { final boolean selectOnAppend,
return getPlayerIntent(context, targetClazz, playQueue) final boolean resumePlayback) {
return getPlayerIntent(context, targetClazz, playQueue, resumePlayback)
.putExtra(BasePlayer.APPEND_ONLY, true) .putExtra(BasePlayer.APPEND_ONLY, true)
.putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend); .putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend);
} }
@ -104,40 +108,41 @@ public class NavigationHelper {
final float playbackSpeed, final float playbackSpeed,
final float playbackPitch, final float playbackPitch,
final boolean playbackSkipSilence, final boolean playbackSkipSilence,
@Nullable final String playbackQuality) { @Nullable final String playbackQuality,
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality) final boolean resumePlayback) {
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality, resumePlayback)
.putExtra(BasePlayer.REPEAT_MODE, repeatMode) .putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed) .putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch) .putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch)
.putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence); .putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence);
} }
public static void playOnMainPlayer(final Context context, final PlayQueue queue) { public static void playOnMainPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
final Intent playerIntent = getPlayerIntent(context, MainVideoPlayer.class, queue); final Intent playerIntent = getPlayerIntent(context, MainVideoPlayer.class, queue, resumePlayback);
playerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); playerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(playerIntent); context.startActivity(playerIntent);
} }
public static void playOnPopupPlayer(final Context context, final PlayQueue queue) { public static void playOnPopupPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
if (!PermissionHelper.isPopupEnabled(context)) { if (!PermissionHelper.isPopupEnabled(context)) {
PermissionHelper.showPopupEnablementToast(context); PermissionHelper.showPopupEnablementToast(context);
return; return;
} }
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
startService(context, getPlayerIntent(context, PopupVideoPlayer.class, queue)); startService(context, getPlayerIntent(context, PopupVideoPlayer.class, queue, resumePlayback));
} }
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue) { public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
startService(context, getPlayerIntent(context, BackgroundPlayer.class, queue)); startService(context, getPlayerIntent(context, BackgroundPlayer.class, queue, resumePlayback));
} }
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue) { public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
enqueueOnPopupPlayer(context, queue, false); enqueueOnPopupPlayer(context, queue, false, resumePlayback);
} }
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) { public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend, final boolean resumePlayback) {
if (!PermissionHelper.isPopupEnabled(context)) { if (!PermissionHelper.isPopupEnabled(context)) {
PermissionHelper.showPopupEnablementToast(context); PermissionHelper.showPopupEnablementToast(context);
return; return;
@ -145,17 +150,17 @@ public class NavigationHelper {
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
startService(context, startService(context,
getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend)); getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend, resumePlayback));
} }
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) { public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) {
enqueueOnBackgroundPlayer(context, queue, false); enqueueOnBackgroundPlayer(context, queue, false, resumePlayback);
} }
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) { public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend, final boolean resumePlayback) {
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
startService(context, startService(context,
getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend)); getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend, resumePlayback));
} }
public static void startService(@NonNull final Context context, @NonNull final Intent intent) { public static void startService(@NonNull final Context context, @NonNull final Intent intent) {

View file

@ -0,0 +1,69 @@
package org.schabi.newpipe.views;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.ProgressBar;
public final class AnimatedProgressBar extends ProgressBar {
@Nullable
private ProgressBarAnimation animation = null;
public AnimatedProgressBar(Context context) {
super(context);
}
public AnimatedProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AnimatedProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public synchronized void setProgress(int progress) {
cancelAnimation();
animation = new ProgressBarAnimation(this, getProgress(), progress);
startAnimation(animation);
}
private void cancelAnimation() {
if (animation != null) {
animation.cancel();
animation = null;
}
clearAnimation();
}
private void setProgressInternal(int progress) {
super.setProgress(progress);
}
private static class ProgressBarAnimation extends Animation {
private final AnimatedProgressBar progressBar;
private final float from;
private final float to;
ProgressBarAnimation(AnimatedProgressBar progressBar, float from, float to) {
super();
this.progressBar = progressBar;
this.from = from;
this.to = to;
setDuration(500);
setInterpolator(new AccelerateDecelerateInterpolator());
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
float value = from + (to - from) * interpolatedTime;
progressBar.setProgressInternal((int) value);
}
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="@color/dark_ripple_color" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/dark_soundcloud_primary_color" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="@color/light_ripple_color" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/light_soundcloud_primary_color" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="@color/dark_ripple_color" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/dark_youtube_primary_color" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="@color/light_ripple_color" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/light_youtube_primary_color" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/video_item_detail" android:id="@+id/video_item_detail"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -19,6 +19,7 @@
android:id="@+id/appbarlayout" android:id="@+id/appbarlayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
app:elevation="0dp" app:elevation="0dp"
app:layout_behavior="android.support.design.widget.FlingBehavior"> app:layout_behavior="android.support.design.widget.FlingBehavior">
@ -67,10 +68,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:background="#64000000" android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp" android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp" android:paddingTop="10dp"
android:paddingRight="30dp"
android:paddingBottom="10dp"
android:text="@string/hold_to_append" android:text="@string/hold_to_append"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="20sp" android:textSize="20sp"
@ -84,17 +85,42 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|right" android:layout_gravity="bottom|right"
android:layout_marginBottom="8dp"
android:layout_marginLeft="12dp" android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:alpha=".6" android:alpha=".6"
android:background="#23000000" android:background="#23000000"
android:gravity="center" android:gravity="center"
android:paddingBottom="2dp"
android:paddingLeft="6dp" android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="2dp" android:paddingTop="2dp"
android:paddingRight="6dp"
android:paddingBottom="2dp"
android:textAllCaps="true"
android:textColor="@android:color/white"
android:textSize="12sp"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="12:38"
tools:visibility="visible" />
<TextView
android:id="@+id/detail_position_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:layout_marginLeft="12dp"
android:layout_marginTop="8dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:alpha=".6"
android:background="?colorPrimary"
android:gravity="center"
android:paddingLeft="6dp"
android:paddingTop="2dp"
android:paddingRight="6dp"
android:paddingBottom="2dp"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="12sp" android:textSize="12sp"
@ -107,6 +133,19 @@
</android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.CollapsingToolbarLayout>
<org.schabi.newpipe.views.AnimatedProgressBar
android:id="@+id/position_view"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_marginTop="-2dp"
android:background="@android:color/transparent"
android:progressDrawable="?attr/progress_horizontal_drawable"
android:visibility="invisible"
tools:max="100"
tools:progress="40"
tools:visibility="visible" />
<!-- CONTENT --> <!-- CONTENT -->
<RelativeLayout <RelativeLayout
android:id="@+id/detail_content_root_layout" android:id="@+id/detail_content_root_layout"
@ -133,8 +172,8 @@
android:layout_marginRight="20dp" android:layout_marginRight="20dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:paddingBottom="8dp"
android:paddingTop="12dp" android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_title_text_size" android:textSize="@dimen/video_item_detail_title_text_size"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
@ -179,9 +218,9 @@
android:id="@+id/detail_content_root_hiding" android:id="@+id/detail_content_root_hiding"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="10dp"
android:layout_below="@+id/detail_title_root_layout" android:layout_below="@+id/detail_title_root_layout"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="10dp"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> tools:visibility="visible">
@ -191,8 +230,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="55dp" android:layout_height="55dp"
android:layout_marginLeft="12dp" android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginRight="12dp"
android:baselineAligned="false" android:baselineAligned="false"
android:orientation="horizontal"> android:orientation="horizontal">
@ -201,8 +240,8 @@
android:id="@+id/detail_uploader_root_layout" android:id="@+id/detail_uploader_root_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_toLeftOf="@id/details_panel"
android:layout_toStartOf="@id/details_panel" android:layout_toStartOf="@id/details_panel"
android:layout_toLeftOf="@id/details_panel"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
@ -261,8 +300,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:lines="1" android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_views_text_size" android:textSize="@dimen/video_item_detail_views_text_size"
@ -354,8 +393,8 @@
android:drawableTop="?attr/ic_playlist_add" android:drawableTop="?attr/ic_playlist_add"
android:focusable="true" android:focusable="true"
android:gravity="center" android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp" android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_add_to_playlist_title" android:text="@string/controls_add_to_playlist_title"
android:textSize="12sp" /> android:textSize="12sp" />
@ -371,8 +410,8 @@
android:drawableTop="?attr/audio" android:drawableTop="?attr/audio"
android:focusable="true" android:focusable="true"
android:gravity="center" android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp" android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_background_title" android:text="@string/controls_background_title"
android:textSize="12sp" /> android:textSize="12sp" />
@ -388,8 +427,8 @@
android:drawableTop="?attr/popup" android:drawableTop="?attr/popup"
android:focusable="true" android:focusable="true"
android:gravity="center" android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp" android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_popup_title" android:text="@string/controls_popup_title"
android:textSize="12sp" /> android:textSize="12sp" />
@ -405,8 +444,8 @@
android:drawableTop="?attr/download" android:drawableTop="?attr/download"
android:focusable="true" android:focusable="true"
android:gravity="center" android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp" android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/download" android:text="@string/download"
android:textSize="12sp" /> android:textSize="12sp" />
@ -444,10 +483,10 @@
android:id="@+id/detail_description_view" android:id="@+id/detail_description_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="12dp" android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true" android:textIsSelectable="true"
android:textSize="@dimen/video_item_detail_description_text_size" android:textSize="@dimen/video_item_detail_description_text_size"
@ -490,7 +529,7 @@
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>
<FrameLayout <FrameLayout
android:id="@+id/relatedStreamsLayout" android:id="@+id/relatedStreamsLayout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"

View file

@ -1,489 +1,528 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/video_item_detail" android:id="@+id/video_item_detail"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:focusableInTouchMode="true"> android:focusableInTouchMode="true">
<android.support.design.widget.CoordinatorLayout <android.support.design.widget.CoordinatorLayout
android:id="@+id/detail_main_content" android:id="@+id/detail_main_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout <android.support.design.widget.AppBarLayout
android:id="@+id/appbarlayout" android:id="@+id/appbarlayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true" android:background="@android:color/transparent"
app:elevation="0dp" android:fitsSystemWindows="true"
app:layout_behavior="android.support.design.widget.FlingBehavior"> app:elevation="0dp"
app:layout_behavior="android.support.design.widget.FlingBehavior">
<android.support.design.widget.CollapsingToolbarLayout <android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_scrollFlags="scroll"> app:layout_scrollFlags="scroll">
<!-- THUMBNAIL --> <!-- THUMBNAIL -->
<FrameLayout <FrameLayout
android:id="@+id/detail_thumbnail_root_layout" android:id="@+id/detail_thumbnail_root_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/black" android:background="@android:color/black"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackground" android:foreground="?attr/selectableItemBackground"
app:layout_collapseMode="parallax"> app:layout_collapseMode="parallax">
<ImageView <ImageView
android:id="@+id/detail_thumbnail_image_view" android:id="@+id/detail_thumbnail_image_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:contentDescription="@string/detail_thumbnail_view_description" android:contentDescription="@string/detail_thumbnail_view_description"
android:scaleType="fitCenter" android:scaleType="fitCenter"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
tools:layout_height="200dp" tools:layout_height="200dp"
tools:src="@drawable/dummy_thumbnail" /> tools:src="@drawable/dummy_thumbnail" />
<ImageView <ImageView
android:id="@+id/detail_thumbnail_play_button" android:id="@+id/detail_thumbnail_play_button"
android:layout_width="64dp" android:layout_width="64dp"
android:layout_height="64dp" android:layout_height="64dp"
android:layout_gravity="center" android:layout_gravity="center"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:src="@drawable/new_play_arrow" android:src="@drawable/new_play_arrow"
android:visibility="invisible" android:visibility="invisible"
tools:ignore="ContentDescription" tools:ignore="ContentDescription"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView <TextView
android:id="@+id/touch_append_detail" android:id="@+id/touch_append_detail"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:background="#64000000" android:background="#64000000"
android:paddingBottom="10dp" android:paddingLeft="30dp"
android:paddingLeft="30dp" android:paddingTop="10dp"
android:paddingRight="30dp" android:paddingRight="30dp"
android:paddingTop="10dp" android:paddingBottom="10dp"
android:text="@string/hold_to_append" android:text="@string/hold_to_append"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
android:visibility="gone" android:visibility="gone"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView <TextView
android:id="@+id/detail_duration_view" android:id="@+id/detail_duration_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|right" android:layout_gravity="bottom|right"
android:layout_marginBottom="8dp" android:layout_marginLeft="12dp"
android:layout_marginLeft="12dp" android:layout_marginTop="8dp"
android:layout_marginRight="12dp" android:layout_marginRight="12dp"
android:layout_marginTop="8dp" android:layout_marginBottom="8dp"
android:alpha=".6" android:alpha=".6"
android:background="#23000000" android:background="#23000000"
android:gravity="center" android:gravity="center"
android:paddingBottom="2dp" android:paddingLeft="6dp"
android:paddingLeft="6dp" android:paddingTop="2dp"
android:paddingRight="6dp" android:paddingRight="6dp"
android:paddingTop="2dp" android:paddingBottom="2dp"
android:textAllCaps="true" android:textAllCaps="true"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="12sp" android:textSize="12sp"
android:textStyle="bold" android:textStyle="bold"
android:visibility="gone" android:visibility="gone"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
tools:text="12:38" tools:text="12:38"
tools:visibility="visible" /> tools:visibility="visible" />
</FrameLayout>
</android.support.design.widget.CollapsingToolbarLayout> <TextView
android:id="@+id/detail_position_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:layout_marginLeft="12dp"
android:layout_marginTop="8dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:alpha=".6"
android:background="?colorPrimary"
android:gravity="center"
android:paddingLeft="6dp"
android:paddingTop="2dp"
android:paddingRight="6dp"
android:paddingBottom="2dp"
android:textAllCaps="true"
android:textColor="@android:color/white"
android:textSize="12sp"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:text="12:38"
tools:visibility="visible" />
<!-- CONTENT --> </FrameLayout>
<RelativeLayout
android:id="@+id/detail_content_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:windowBackground"
app:layout_scrollFlags="scroll">
<!-- TITLE --> </android.support.design.widget.CollapsingToolbarLayout>
<FrameLayout
android:id="@+id/detail_title_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:paddingLeft="12dp"
android:paddingRight="12dp">
<TextView <org.schabi.newpipe.views.AnimatedProgressBar
android:id="@+id/detail_video_title_view" android:id="@+id/position_view"
android:layout_width="match_parent" style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_height="match_parent" android:layout_width="match_parent"
android:layout_marginRight="20dp" android:layout_height="4dp"
android:ellipsize="end" android:layout_marginTop="-2dp"
android:maxLines="1" android:progressDrawable="?attr/progress_horizontal_drawable"
android:paddingBottom="8dp" android:visibility="invisible"
android:paddingTop="12dp" tools:max="100"
android:textAppearance="?android:attr/textAppearanceLarge" tools:progress="40"
android:textSize="@dimen/video_item_detail_title_text_size" tools:visibility="visible" />
tools:ignore="RtlHardcoded"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum. Nunc eleifend est quis ipsum porttitor egestas. Sed facilisis, nisl quis eleifend pellentesque, orci metus egestas dolor, at accumsan eros metus quis libero." />
<ImageView <!-- CONTENT -->
android:id="@+id/detail_toggle_description_view" <RelativeLayout
android:layout_width="15dp" android:id="@+id/detail_content_root_layout"
android:layout_height="15dp" android:layout_width="match_parent"
android:layout_gravity="center_vertical|right" android:layout_height="wrap_content"
android:layout_marginLeft="5dp" android:background="?android:windowBackground"
android:src="@drawable/arrow_down" app:layout_scrollFlags="scroll">
tools:ignore="ContentDescription,RtlHardcoded" />
</FrameLayout> <!-- TITLE -->
<FrameLayout
android:id="@+id/detail_title_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:paddingLeft="12dp"
android:paddingRight="12dp">
<!-- LOADING INDICATOR--> <TextView
<ProgressBar android:id="@+id/detail_video_title_view"
android:id="@+id/loading_progress_bar" android:layout_width="match_parent"
style="@style/Widget.AppCompat.ProgressBar" android:layout_height="match_parent"
android:layout_width="match_parent" android:layout_marginRight="20dp"
android:layout_height="wrap_content" android:ellipsize="end"
android:layout_below="@id/detail_title_root_layout" android:maxLines="1"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin" android:paddingTop="12dp"
android:indeterminate="true" android:paddingBottom="8dp"
android:visibility="gone" android:textAppearance="?android:attr/textAppearanceLarge"
tools:visibility="visible" /> android:textSize="@dimen/video_item_detail_title_text_size"
tools:ignore="RtlHardcoded"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum. Nunc eleifend est quis ipsum porttitor egestas. Sed facilisis, nisl quis eleifend pellentesque, orci metus egestas dolor, at accumsan eros metus quis libero." />
<!--ERROR PANEL--> <ImageView
<include android:id="@+id/detail_toggle_description_view"
android:id="@+id/error_panel" android:layout_width="15dp"
layout="@layout/error_retry" android:layout_height="15dp"
android:layout_width="match_parent" android:layout_gravity="center_vertical|right"
android:layout_height="wrap_content" android:layout_marginLeft="5dp"
android:layout_below="@id/detail_title_root_layout" android:src="@drawable/arrow_down"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin" tools:ignore="ContentDescription,RtlHardcoded" />
android:visibility="gone"
tools:visibility="visible" />
<!--HIDING ROOT--> </FrameLayout>
<LinearLayout
android:id="@+id/detail_content_root_hiding"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="10dp"
android:layout_below="@+id/detail_title_root_layout"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<!--DETAIL--> <!-- LOADING INDICATOR-->
<RelativeLayout <ProgressBar
android:id="@+id/detail_root" android:id="@+id/loading_progress_bar"
android:layout_width="match_parent" style="@style/Widget.AppCompat.ProgressBar"
android:layout_height="55dp" android:layout_width="match_parent"
android:layout_marginLeft="12dp" android:layout_height="wrap_content"
android:layout_marginRight="12dp" android:layout_below="@id/detail_title_root_layout"
android:layout_marginTop="6dp" android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:baselineAligned="false" android:indeterminate="true"
android:orientation="horizontal"> android:visibility="gone"
tools:visibility="visible" />
<!--UPLOADER--> <!--ERROR PANEL-->
<LinearLayout <include
android:id="@+id/detail_uploader_root_layout" android:id="@+id/error_panel"
android:layout_width="match_parent" layout="@layout/error_retry"
android:layout_height="match_parent" android:layout_width="match_parent"
android:layout_toLeftOf="@id/details_panel" android:layout_height="wrap_content"
android:layout_toStartOf="@id/details_panel" android:layout_below="@id/detail_title_root_layout"
android:background="?attr/selectableItemBackground" android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:gravity="center_vertical" android:visibility="gone"
android:orientation="horizontal" tools:visibility="visible" />
android:padding="6dp">
<de.hdodenhof.circleimageview.CircleImageView <!--HIDING ROOT-->
android:id="@+id/detail_uploader_thumbnail_view" <LinearLayout
android:layout_width="@dimen/video_item_detail_uploader_image_size" android:id="@+id/detail_content_root_hiding"
android:layout_height="@dimen/video_item_detail_uploader_image_size" android:layout_width="match_parent"
android:contentDescription="@string/detail_uploader_thumbnail_view_description" android:layout_height="match_parent"
android:src="@drawable/buddy" android:layout_below="@+id/detail_title_root_layout"
tools:ignore="RtlHardcoded" /> android:orientation="vertical"
android:paddingBottom="10dp"
android:visibility="gone"
tools:visibility="visible">
<TextView <!--DETAIL-->
android:id="@+id/detail_uploader_text_view" <RelativeLayout
android:layout_width="match_parent" android:id="@+id/detail_root"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_marginLeft="15dp" android:layout_height="55dp"
android:ellipsize="marquee" android:layout_marginLeft="12dp"
android:fadingEdge="horizontal" android:layout_marginTop="6dp"
android:marqueeRepeatLimit="marquee_forever" android:layout_marginRight="12dp"
android:scrollHorizontally="true" android:baselineAligned="false"
android:singleLine="true" android:orientation="horizontal">
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_uploader_text_size"
android:textStyle="bold"
tools:ignore="RtlHardcoded"
tools:text="Uploader" />
<!--<Button <!--UPLOADER-->
android:id="@+id/detail_uploader_subscribe" <LinearLayout
android:layout_width="wrap_content" android:id="@+id/detail_uploader_root_layout"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_gravity="center_vertical|right" android:layout_height="match_parent"
android:layout_marginRight="12dp" android:layout_toStartOf="@id/details_panel"
android:text="@string/rss_button_title" android:layout_toLeftOf="@id/details_panel"
android:textSize="12sp" android:background="?attr/selectableItemBackground"
android:theme="@style/RedButton" android:gravity="center_vertical"
android:drawableLeft="@drawable/ic_rss_feed_white_24dp" android:orientation="horizontal"
tools:ignore="RtlHardcoded" android:padding="6dp">
android:visibility="gone"/>-->
</LinearLayout>
<!-- VIEW & THUMBS --> <de.hdodenhof.circleimageview.CircleImageView
<RelativeLayout android:id="@+id/detail_uploader_thumbnail_view"
android:id="@+id/details_panel" android:layout_width="@dimen/video_item_detail_uploader_image_size"
android:layout_width="wrap_content" android:layout_height="@dimen/video_item_detail_uploader_image_size"
android:layout_height="match_parent" android:contentDescription="@string/detail_uploader_thumbnail_view_description"
android:layout_alignParentEnd="true" android:src="@drawable/buddy"
android:layout_alignParentRight="true" tools:ignore="RtlHardcoded" />
android:paddingLeft="6dp"
android:paddingRight="6dp">
<TextView <TextView
android:id="@+id/detail_view_count_view" android:id="@+id/detail_uploader_text_view"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" android:layout_marginLeft="15dp"
android:layout_marginBottom="6dp" android:ellipsize="marquee"
android:layout_marginTop="6dp" android:fadingEdge="horizontal"
android:lines="1" android:marqueeRepeatLimit="marquee_forever"
android:textAppearance="?android:attr/textAppearanceLarge" android:scrollHorizontally="true"
android:textSize="@dimen/video_item_detail_views_text_size" android:singleLine="true"
tools:ignore="RtlHardcoded" android:textAppearance="?android:attr/textAppearanceLarge"
tools:text="2,816,821,505 views" /> android:textSize="@dimen/video_item_detail_uploader_text_size"
android:textStyle="bold"
tools:ignore="RtlHardcoded"
tools:text="Uploader" />
<ImageView <!--<Button
android:id="@+id/detail_thumbs_up_img_view" android:id="@+id/detail_uploader_subscribe"
android:layout_width="@dimen/video_item_detail_like_image_width" android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height" android:layout_height="wrap_content"
android:layout_below="@id/detail_view_count_view" android:layout_gravity="center_vertical|right"
android:contentDescription="@string/detail_likes_img_view_description" android:layout_marginRight="12dp"
android:src="?attr/thumbs_up" /> android:text="@string/rss_button_title"
android:textSize="12sp"
android:theme="@style/RedButton"
android:drawableLeft="@drawable/ic_rss_feed_white_24dp"
tools:ignore="RtlHardcoded"
android:visibility="gone"/>-->
</LinearLayout>
<TextView <!-- VIEW & THUMBS -->
android:id="@+id/detail_thumbs_up_count_view" <RelativeLayout
android:layout_width="wrap_content" android:id="@+id/details_panel"
android:layout_height="@dimen/video_item_detail_like_image_height" android:layout_width="wrap_content"
android:layout_below="@id/detail_view_count_view" android:layout_height="match_parent"
android:layout_marginLeft="@dimen/video_item_detail_like_margin" android:layout_alignParentEnd="true"
android:layout_toRightOf="@id/detail_thumbs_up_img_view" android:layout_alignParentRight="true"
android:gravity="center_vertical" android:paddingLeft="6dp"
android:lines="1" android:paddingRight="6dp">
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_likes_text_size"
tools:ignore="RtlHardcoded"
tools:text="12M" />
<ImageView <TextView
android:id="@+id/detail_thumbs_down_img_view" android:id="@+id/detail_view_count_view"
android:layout_width="@dimen/video_item_detail_like_image_width" android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height" android:layout_height="wrap_content"
android:layout_below="@id/detail_view_count_view" android:layout_centerHorizontal="true"
android:layout_marginLeft="12dp" android:layout_marginTop="6dp"
android:layout_toRightOf="@id/detail_thumbs_up_count_view" android:layout_marginBottom="6dp"
android:contentDescription="@string/detail_dislikes_img_view_description" android:lines="1"
android:src="?attr/thumbs_down" android:textAppearance="?android:attr/textAppearanceLarge"
tools:ignore="RtlHardcoded" /> android:textSize="@dimen/video_item_detail_views_text_size"
tools:ignore="RtlHardcoded"
tools:text="2,816,821,505 views" />
<TextView <ImageView
android:id="@+id/detail_thumbs_down_count_view" android:id="@+id/detail_thumbs_up_img_view"
android:layout_width="wrap_content" android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height" android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view" android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="@dimen/video_item_detail_like_margin" android:contentDescription="@string/detail_likes_img_view_description"
android:layout_toRightOf="@id/detail_thumbs_down_img_view" android:src="?attr/thumbs_up" />
android:gravity="center_vertical"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_likes_text_size"
tools:ignore="RtlHardcoded"
tools:text="10K" />
<TextView <TextView
android:id="@+id/detail_thumbs_disabled_view" android:id="@+id/detail_thumbs_up_count_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height" android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view" android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="12dp" android:layout_marginLeft="@dimen/video_item_detail_like_margin"
android:layout_toRightOf="@id/detail_thumbs_down_img_view" android:layout_toRightOf="@id/detail_thumbs_up_img_view"
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@string/disabled" android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge" android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_likes_text_size" android:textSize="@dimen/video_item_detail_likes_text_size"
android:textStyle="bold" tools:ignore="RtlHardcoded"
android:visibility="gone" tools:text="12M" />
tools:ignore="RtlHardcoded"
tools:visibility="visible" />
</RelativeLayout>
</RelativeLayout>
<LinearLayout <ImageView
android:id="@+id/detail_control_panel" android:id="@+id/detail_thumbs_down_img_view"
android:layout_width="match_parent" android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="wrap_content" android:layout_height="@dimen/video_item_detail_like_image_height"
android:orientation="horizontal" android:layout_below="@id/detail_view_count_view"
android:padding="6dp"> android:layout_marginLeft="12dp"
android:layout_toRightOf="@id/detail_thumbs_up_count_view"
android:contentDescription="@string/detail_dislikes_img_view_description"
android:src="?attr/thumbs_down"
tools:ignore="RtlHardcoded" />
<!-- CONTROLS --> <TextView
<TextView android:id="@+id/detail_thumbs_down_count_view"
android:id="@+id/detail_controls_playlist_append" android:layout_width="wrap_content"
android:layout_width="80dp" android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_height="55dp" android:layout_below="@id/detail_view_count_view"
android:layout_gravity="center_vertical" android:layout_marginLeft="@dimen/video_item_detail_like_margin"
android:layout_weight="1" android:layout_toRightOf="@id/detail_thumbs_down_img_view"
android:background="?attr/selectableItemBackgroundBorderless" android:gravity="center_vertical"
android:clickable="true" android:lines="1"
android:contentDescription="@string/append_playlist" android:textAppearance="?android:attr/textAppearanceMedium"
android:drawableTop="?attr/ic_playlist_add" android:textSize="@dimen/video_item_detail_likes_text_size"
android:focusable="true" tools:ignore="RtlHardcoded"
android:gravity="center" tools:text="10K" />
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:text="@string/controls_add_to_playlist_title"
android:textSize="12sp" />
<TextView <TextView
android:id="@+id/detail_controls_background" android:id="@+id/detail_thumbs_disabled_view"
android:layout_width="80dp" android:layout_width="wrap_content"
android:layout_height="55dp" android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_gravity="center_vertical" android:layout_below="@id/detail_view_count_view"
android:layout_weight="1" android:layout_marginLeft="12dp"
android:background="?attr/selectableItemBackgroundBorderless" android:layout_toRightOf="@id/detail_thumbs_down_img_view"
android:clickable="true" android:gravity="center_vertical"
android:contentDescription="@string/play_audio" android:text="@string/disabled"
android:drawableTop="?attr/audio" android:textAppearance="?android:attr/textAppearanceLarge"
android:focusable="true" android:textSize="@dimen/video_item_detail_likes_text_size"
android:gravity="center" android:textStyle="bold"
android:paddingBottom="6dp" android:visibility="gone"
android:paddingTop="6dp" tools:ignore="RtlHardcoded"
android:text="@string/controls_background_title" tools:visibility="visible" />
android:textSize="12sp" /> </RelativeLayout>
</RelativeLayout>
<TextView <LinearLayout
android:id="@+id/detail_controls_popup" android:id="@+id/detail_control_panel"
android:layout_width="80dp" android:layout_width="match_parent"
android:layout_height="55dp" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:orientation="horizontal"
android:layout_weight="1" android:padding="6dp">
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/open_in_popup_mode"
android:drawableTop="?attr/popup"
android:focusable="true"
android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:text="@string/controls_popup_title"
android:textSize="12sp" />
<TextView <!-- CONTROLS -->
android:id="@+id/detail_controls_download" <TextView
android:layout_width="80dp" android:id="@+id/detail_controls_playlist_append"
android:layout_height="55dp" android:layout_width="80dp"
android:layout_gravity="center_vertical" android:layout_height="55dp"
android:layout_weight="1" android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackgroundBorderless" android:layout_weight="1"
android:clickable="true" android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/controls_download_desc" android:clickable="true"
android:drawableTop="?attr/download" android:contentDescription="@string/append_playlist"
android:focusable="true" android:drawableTop="?attr/ic_playlist_add"
android:gravity="center" android:focusable="true"
android:paddingBottom="6dp" android:gravity="center"
android:paddingTop="6dp" android:paddingTop="6dp"
android:text="@string/download" android:paddingBottom="6dp"
android:textSize="12sp" /> android:text="@string/controls_add_to_playlist_title"
android:textSize="12sp" />
</LinearLayout> <TextView
android:id="@+id/detail_controls_background"
android:layout_width="80dp"
android:layout_height="55dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/play_audio"
android:drawableTop="?attr/audio"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_background_title"
android:textSize="12sp" />
<View <TextView
android:layout_width="match_parent" android:id="@+id/detail_controls_popup"
android:layout_height="1px" android:layout_width="80dp"
android:layout_marginLeft="8dp" android:layout_height="55dp"
android:layout_marginRight="8dp" android:layout_gravity="center_vertical"
android:background="?attr/separator_color" /> android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/open_in_popup_mode"
android:drawableTop="?attr/popup"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_popup_title"
android:textSize="12sp" />
<!--DESCRIPTIONS--> <TextView
<LinearLayout android:id="@+id/detail_controls_download"
android:id="@+id/detail_description_root_layout" android:layout_width="80dp"
android:layout_width="match_parent" android:layout_height="55dp"
android:layout_height="wrap_content" android:layout_gravity="center_vertical"
android:layout_marginTop="5dp" android:layout_weight="1"
android:orientation="vertical" android:background="?attr/selectableItemBackgroundBorderless"
android:visibility="gone" android:clickable="true"
tools:visibility="visible"> android:contentDescription="@string/controls_download_desc"
android:drawableTop="?attr/download"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/download"
android:textSize="12sp" />
<TextView </LinearLayout>
android:id="@+id/detail_upload_date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textStyle="bold"
tools:text="Published on Oct 2, 2009" />
<TextView <View
android:id="@+id/detail_description_view" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="1px"
android:layout_height="wrap_content" android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp" android:layout_marginRight="8dp"
android:layout_marginLeft="12dp" android:background="?attr/separator_color" />
android:layout_marginRight="12dp"
android:layout_marginTop="3dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true"
android:textSize="@dimen/video_item_detail_description_text_size"
tools:text="Description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum." />
<View <!--DESCRIPTIONS-->
android:layout_width="match_parent" <LinearLayout
android:layout_height="1px" android:id="@+id/detail_description_root_layout"
android:layout_marginLeft="8dp" android:layout_width="match_parent"
android:layout_marginRight="8dp" android:layout_height="wrap_content"
android:background="?attr/separator_color" /> android:layout_marginTop="5dp"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
</LinearLayout> <TextView
android:id="@+id/detail_upload_date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="@dimen/video_item_detail_upload_date_text_size"
android:textStyle="bold"
tools:text="Published on Oct 2, 2009" />
</LinearLayout> <TextView
</RelativeLayout> android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginTop="3dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true"
android:textSize="@dimen/video_item_detail_description_text_size"
tools:text="Description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum." />
</android.support.design.widget.AppBarLayout> <View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager <android.support.v4.view.ViewPager
android:id="@+id/viewpager" android:id="@+id/viewpager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.TabLayout <android.support.design.widget.TabLayout
android:id="@+id/tablayout" android:id="@+id/tablayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|center" android:layout_gravity="bottom|center"
android:background="@color/transparent_background_color" android:background="@color/transparent_background_color"
app:tabBackground="@drawable/tab_selector" app:tabBackground="@drawable/tab_selector"
app:tabGravity="center" app:tabGravity="center"
app:tabIndicatorHeight="0dp"> app:tabIndicatorHeight="0dp">
</android.support.design.widget.TabLayout> </android.support.design.widget.TabLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>
</FrameLayout> </FrameLayout>

View file

@ -167,7 +167,10 @@
<string name="fragment_whats_new">Что нового</string> <string name="fragment_whats_new">Что нового</string>
<string name="enable_search_history_title">История поиска</string> <string name="enable_search_history_title">История поиска</string>
<string name="enable_search_history_summary">Хранить запросы поиска локально</string> <string name="enable_search_history_summary">Хранить запросы поиска локально</string>
<string name="enable_watch_history_title">История и кэш</string> <string name="enable_watch_history_title">История просмотров</string>
<string name="enable_playback_resume_title">Продолжать воспроизведение</string>
<string name="enable_playback_resume_summary">Восстанавливать с последней позиции</string>
<string name="settings_category_clear_data_title">Очистить данные</string>
<string name="enable_watch_history_summary">Запоминать воспроизведённые потоки</string> <string name="enable_watch_history_summary">Запоминать воспроизведённые потоки</string>
<string name="resume_on_audio_focus_gain_title">Возобновить при фокусе</string> <string name="resume_on_audio_focus_gain_title">Возобновить при фокусе</string>
<string name="resume_on_audio_focus_gain_summary">Возобновлять воспроизведение после перерывов (например, телефонных звонков)</string> <string name="resume_on_audio_focus_gain_summary">Возобновлять воспроизведение после перерывов (например, телефонных звонков)</string>

View file

@ -153,7 +153,10 @@
<string name="show_search_suggestions_summary">Показувати пропозиції під час пошуку</string> <string name="show_search_suggestions_summary">Показувати пропозиції під час пошуку</string>
<string name="enable_search_history_title">Історія пошуків</string> <string name="enable_search_history_title">Історія пошуків</string>
<string name="enable_search_history_summary">Зберігати пошукові запити локально</string> <string name="enable_search_history_summary">Зберігати пошукові запити локально</string>
<string name="enable_watch_history_title">Історія та кеш</string> <string name="enable_watch_history_title">Історія переглядiв</string>
<string name="enable_playback_resume_title">Продовживати перегляд</string>
<string name="enable_playback_resume_summary">Відновлювати останню позицію</string>
<string name="settings_category_clear_data_title">Очистити дані</string>
<string name="enable_watch_history_summary">Вести облік перегляду відеозаписів</string> <string name="enable_watch_history_summary">Вести облік перегляду відеозаписів</string>
<string name="resume_on_audio_focus_gain_title">Відновити відтворення</string> <string name="resume_on_audio_focus_gain_title">Відновити відтворення</string>
<string name="resume_on_audio_focus_gain_summary">Продовжувати відтворення опісля переривання (наприклад телефонного дзвінка)</string> <string name="resume_on_audio_focus_gain_summary">Продовжувати відтворення опісля переривання (наприклад телефонного дзвінка)</string>

View file

@ -43,6 +43,7 @@
<attr name="ic_delete" format="reference"/> <attr name="ic_delete" format="reference"/>
<attr name="ic_settings_update" format="reference"/> <attr name="ic_settings_update" format="reference"/>
<attr name="progress_horizontal_drawable" format="reference"/>
<!-- Can't refer to colors directly in drawable's xml--> <!-- Can't refer to colors directly in drawable's xml-->
<attr name="toolbar_shadow_drawable" format="reference"/> <attr name="toolbar_shadow_drawable" format="reference"/>
<attr name="selector_drawable" format="reference"/> <attr name="selector_drawable" format="reference"/>

View file

@ -150,6 +150,7 @@
<string name="enable_search_history_key" translatable="false">enable_search_history</string> <string name="enable_search_history_key" translatable="false">enable_search_history</string>
<string name="enable_watch_history_key" translatable="false">enable_watch_history</string> <string name="enable_watch_history_key" translatable="false">enable_watch_history</string>
<string name="main_page_content_key" translatable="false">main_page_content</string> <string name="main_page_content_key" translatable="false">main_page_content</string>
<string name="enable_playback_resume_key" translatable="false">enable_playback_resume</string>
<string name="import_data">import_data</string> <string name="import_data">import_data</string>
<string name="export_data">export_data</string> <string name="export_data">export_data</string>

View file

@ -95,7 +95,10 @@
<string name="show_search_suggestions_summary">Show suggestions when searching</string> <string name="show_search_suggestions_summary">Show suggestions when searching</string>
<string name="enable_search_history_title">Search history</string> <string name="enable_search_history_title">Search history</string>
<string name="enable_search_history_summary">Store search queries locally</string> <string name="enable_search_history_summary">Store search queries locally</string>
<string name="enable_watch_history_title">History &amp; Cache</string> <string name="enable_watch_history_title">Watch history</string>
<string name="enable_playback_resume_title">Resume playback</string>
<string name="enable_playback_resume_summary">Restore last playback position</string>
<string name="settings_category_clear_data_title">Clear data</string>
<string name="enable_watch_history_summary">Keep track of watched videos</string> <string name="enable_watch_history_summary">Keep track of watched videos</string>
<string name="resume_on_audio_focus_gain_title">Resume on focus gain</string> <string name="resume_on_audio_focus_gain_title">Resume on focus gain</string>
<string name="resume_on_audio_focus_gain_summary">Continue playing after interruptions (e.g. phone calls)</string> <string name="resume_on_audio_focus_gain_summary">Continue playing after interruptions (e.g. phone calls)</string>

View file

@ -65,6 +65,7 @@
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_light</item> <item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_light</item>
<item name="selector_drawable">@drawable/light_selector</item> <item name="selector_drawable">@drawable/light_selector</item>
<item name="colorControlHighlight">@color/light_ripple_color</item> <item name="colorControlHighlight">@color/light_ripple_color</item>
<item name="progress_horizontal_drawable">@drawable/progress_youtube_horizontal_light</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style> </style>
@ -128,6 +129,7 @@
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_dark</item> <item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_dark</item>
<item name="selector_drawable">@drawable/dark_selector</item> <item name="selector_drawable">@drawable/dark_selector</item>
<item name="colorControlHighlight">@color/dark_ripple_color</item> <item name="colorControlHighlight">@color/dark_ripple_color</item>
<item name="progress_horizontal_drawable">@drawable/progress_youtube_horizontal_dark</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style> </style>

View file

@ -15,18 +15,21 @@
<item name="colorPrimary">@color/light_soundcloud_primary_color</item> <item name="colorPrimary">@color/light_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/light_soundcloud_dark_color</item> <item name="colorPrimaryDark">@color/light_soundcloud_dark_color</item>
<item name="colorAccent">@color/light_soundcloud_accent_color</item> <item name="colorAccent">@color/light_soundcloud_accent_color</item>
<item name="progress_horizontal_drawable">@drawable/progress_soundcloud_horizontal_light</item>
</style> </style>
<style name="DarkTheme.SoundCloud" parent="DarkTheme.Switchable"> <style name="DarkTheme.SoundCloud" parent="DarkTheme.Switchable">
<item name="colorPrimary">@color/dark_soundcloud_primary_color</item> <item name="colorPrimary">@color/dark_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/dark_soundcloud_dark_color</item> <item name="colorPrimaryDark">@color/dark_soundcloud_dark_color</item>
<item name="colorAccent">@color/dark_soundcloud_accent_color</item> <item name="colorAccent">@color/dark_soundcloud_accent_color</item>
<item name="progress_horizontal_drawable">@drawable/progress_soundcloud_horizontal_dark</item>
</style> </style>
<style name="BlackTheme.SoundCloud" parent="BlackTheme.Switchable"> <style name="BlackTheme.SoundCloud" parent="BlackTheme.Switchable">
<item name="colorPrimary">@color/dark_soundcloud_primary_color</item> <item name="colorPrimary">@color/dark_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/dark_soundcloud_dark_color</item> <item name="colorPrimaryDark">@color/dark_soundcloud_dark_color</item>
<item name="colorAccent">@color/dark_soundcloud_accent_color</item> <item name="colorAccent">@color/dark_soundcloud_accent_color</item>
<item name="progress_horizontal_drawable">@drawable/progress_soundcloud_horizontal_dark</item>
</style> </style>
<!-- Media.ccc --> <!-- Media.ccc -->

View file

@ -1,40 +1,54 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:key="general_preferences" android:key="general_preferences"
android:title="@string/settings_category_history_title"> android:title="@string/settings_category_history_title">
<SwitchPreference <SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="true" android:defaultValue="true"
android:key="@string/enable_watch_history_key" android:key="@string/enable_watch_history_key"
android:summary="@string/enable_watch_history_summary" android:summary="@string/enable_watch_history_summary"
android:title="@string/enable_watch_history_title"/> android:title="@string/enable_watch_history_title"
app:iconSpaceReserved="false" />
<SwitchPreference
android:defaultValue="true"
android:dependency="@string/enable_watch_history_key"
android:key="@string/enable_playback_resume_key"
android:summary="@string/enable_playback_resume_summary"
android:title="@string/enable_playback_resume_title"
app:iconSpaceReserved="false" />
<SwitchPreference <SwitchPreference
app:iconSpaceReserved="false"
android:defaultValue="true" android:defaultValue="true"
android:key="@string/enable_search_history_key" android:key="@string/enable_search_history_key"
android:summary="@string/enable_search_history_summary" android:summary="@string/enable_search_history_summary"
android:title="@string/enable_search_history_title"/> android:title="@string/enable_search_history_title"
app:iconSpaceReserved="false" />
<Preference <PreferenceCategory
app:iconSpaceReserved="false" android:layout="@layout/settings_category_header_layout"
android:key="@string/metadata_cache_wipe_key" android:title="@string/settings_category_clear_data_title"
android:summary="@string/metadata_cache_wipe_summary" app:iconSpaceReserved="false">
android:title="@string/metadata_cache_wipe_title"/>
<Preference <Preference
app:iconSpaceReserved="false" android:key="@string/metadata_cache_wipe_key"
android:key="@string/clear_views_history_key" android:summary="@string/metadata_cache_wipe_summary"
android:title="@string/clear_views_history_title" android:title="@string/metadata_cache_wipe_title"
android:summary="@string/clear_views_history_summary"/> app:iconSpaceReserved="false" />
<Preference <Preference
app:iconSpaceReserved="false" android:key="@string/clear_views_history_key"
android:key="@string/clear_search_history_key" android:summary="@string/clear_views_history_summary"
android:title="@string/clear_search_history_title" android:title="@string/clear_views_history_title"
android:summary="@string/clear_search_history_summary"/> app:iconSpaceReserved="false" />
<Preference
android:key="@string/clear_search_history_key"
android:summary="@string/clear_search_history_summary"
android:title="@string/clear_search_history_title"
app:iconSpaceReserved="false" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>