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);
if (playerChoice.equals(videoPlayerKey)) {
NavigationHelper.playOnMainPlayer(this, playQueue);
NavigationHelper.playOnMainPlayer(this, playQueue, true);
} else if (playerChoice.equals(backgroundPlayerKey)) {
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
} 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);
if (playerChoice.equals(videoPlayerKey)) {
NavigationHelper.playOnMainPlayer(this, playQueue);
NavigationHelper.playOnMainPlayer(this, playQueue, true);
} else if (playerChoice.equals(backgroundPlayerKey)) {
NavigationHelper.playOnBackgroundPlayer(this, playQueue);
NavigationHelper.playOnBackgroundPlayer(this, playQueue, true);
} 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")
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")
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.ForeignKey;
import java.util.concurrent.TimeUnit;
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.STREAM_STATE_TABLE;
@ -22,6 +24,12 @@ public class StreamStateEntity {
final public static String JOIN_STREAM_ID = "stream_id";
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)
private long streamUid;
@ -48,4 +56,10 @@ public class StreamStateEntity {
public void setProgressTime(long 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.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.views.AnimatedProgressBar;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import icepick.State;
import io.reactivex.Single;
@ -118,7 +120,7 @@ public class VideoDetailFragment
private static final int RELATED_STREAMS_UPDATE_FLAG = 0x1;
private static final int RESOLUTIONS_MENU_UPDATE_FLAG = 0x2;
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 showRelatedStreams;
@ -136,6 +138,8 @@ public class VideoDetailFragment
private Disposable currentWorker;
@NonNull
private CompositeDisposable disposables = new CompositeDisposable();
@Nullable
private Disposable positionSubscriber = null;
private List<VideoStream> sortedVideoStreams;
private int selectedVideoStreamIndex = -1;
@ -153,6 +157,7 @@ public class VideoDetailFragment
private View thumbnailBackgroundButton;
private ImageView thumbnailImageView;
private ImageView thumbnailPlayButton;
private AnimatedProgressBar positionView;
private View videoTitleRoot;
private TextView videoTitleTextView;
@ -165,6 +170,7 @@ public class VideoDetailFragment
private TextView detailControlsDownload;
private TextView appendControlsDetail;
private TextView detailDurationView;
private TextView detailPositionView;
private LinearLayout videoDescriptionRootLayout;
private TextView videoUploadDateView;
@ -259,6 +265,8 @@ public class VideoDetailFragment
// Check if it was loading when the fragment was stopped/paused,
if (wasLoading.getAndSet(false)) {
selectAndLoadVideo(serviceId, url, name);
} else if (currentInfo != null) {
updateProgressInfo(currentInfo);
}
}
@ -268,8 +276,10 @@ public class VideoDetailFragment
PreferenceManager.getDefaultSharedPreferences(activity)
.unregisterOnSharedPreferenceChangeListener(this);
if (positionSubscriber != null) positionSubscriber.dispose();
if (currentWorker != null) currentWorker.dispose();
if (disposables != null) disposables.clear();
positionSubscriber = null;
currentWorker = null;
disposables = null;
}
@ -462,6 +472,7 @@ public class VideoDetailFragment
videoTitleTextView = rootView.findViewById(R.id.detail_video_title_view);
videoTitleToggleArrow = rootView.findViewById(R.id.detail_toggle_description_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);
detailControlsPopup = rootView.findViewById(R.id.detail_controls_popup);
@ -469,6 +480,7 @@ public class VideoDetailFragment
detailControlsDownload = rootView.findViewById(R.id.detail_controls_download);
appendControlsDetail = rootView.findViewById(R.id.touch_append_detail);
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);
videoUploadDateView = rootView.findViewById(R.id.detail_upload_date_view);
@ -536,10 +548,10 @@ public class VideoDetailFragment
final DialogInterface.OnClickListener actions = (DialogInterface dialogInterface, int i) -> {
switch (i) {
case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), true);
break;
case 1:
NavigationHelper.enqueueOnPopupPlayer(getActivity(), new SinglePlayQueue(item));
NavigationHelper.enqueueOnPopupPlayer(getActivity(), new SinglePlayQueue(item), true);
break;
case 2:
if (getFragmentManager() != null) {
@ -890,11 +902,11 @@ public class VideoDetailFragment
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
if (append) {
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue);
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue, false);
} else {
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
final Intent intent = NavigationHelper.getPlayerIntent(
activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution
activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution, true
);
activity.startService(intent);
}
@ -914,9 +926,9 @@ public class VideoDetailFragment
private void openNormalBackgroundPlayer(final boolean append) {
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
if (append) {
NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue);
NavigationHelper.enqueueOnBackgroundPlayer(activity, itemQueue, false);
} else {
NavigationHelper.playOnBackgroundPlayer(activity, itemQueue);
NavigationHelper.playOnBackgroundPlayer(activity, itemQueue, true);
}
}
@ -926,7 +938,7 @@ public class VideoDetailFragment
mIntent = NavigationHelper.getPlayerIntent(activity,
MainVideoPlayer.class,
playQueue,
getSelectedVideoStream().getResolution());
getSelectedVideoStream().getResolution(), true);
startActivity(mIntent);
}
@ -1032,6 +1044,8 @@ public class VideoDetailFragment
animateView(spinnerToolbar, false, 200);
animateView(thumbnailPlayButton, false, 50);
animateView(detailDurationView, false, 100);
animateView(detailPositionView, false, 100);
animateView(positionView, false, 50);
videoTitleTextView.setText(name != null ? name : "");
videoTitleTextView.setMaxLines(1);
@ -1146,6 +1160,7 @@ public class VideoDetailFragment
videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate()));
}
prepareDescription(info.getDescription());
updateProgressInfo(info);
animateView(spinnerToolbar, true, 500);
setupActionBar(info);
@ -1250,4 +1265,37 @@ public class VideoDetailFragment
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) -> {
switch (i) {
case 0:
NavigationHelper.playOnBackgroundPlayer(context, new SinglePlayQueue(item));
NavigationHelper.playOnBackgroundPlayer(context, new SinglePlayQueue(item), true);
break;
case 1:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), true);
break;
case 2:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item), true);
break;
case 3:
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);
switch (i) {
case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), false);
break;
case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item), false);
break;
case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index), true);
break;
case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index), true);
break;
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index), true);
break;
case 5:
if (getFragmentManager() != null) {
@ -440,11 +440,11 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
monitorSubscription(result);
headerPlayAllButton.setOnClickListener(
view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnClickListener(
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(
view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
}
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);
switch (i) {
case 0:
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item));
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item), false);
break;
case 1:
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item));
NavigationHelper.enqueueOnPopupPlayer(activity, new SinglePlayQueue(item), false);
break;
case 2:
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index));
NavigationHelper.playOnMainPlayer(context, getPlayQueue(index), true);
break;
case 3:
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index));
NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index), true);
break;
case 4:
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index));
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(index), true);
break;
case 5:
ShareUtils.shareUrl(this.getContext(), item.getName(), item.getUrl());
ShareUtils.shareUrl(requireContext(), item.getName(), item.getUrl());
break;
default:
break;
@ -301,19 +301,19 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
.subscribe(getPlaylistBookmarkSubscriber());
headerPlayAllButton.setOnClickListener(view ->
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnClickListener(view ->
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
headerBackgroundButton.setOnClickListener(view ->
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
headerPopupButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue());
NavigationHelper.enqueueOnPopupPlayer(activity, getPlayQueue(), true);
return true;
});
headerBackgroundButton.setOnLongClickListener(view -> {
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue());
NavigationHelper.enqueueOnBackgroundPlayer(activity, getPlayQueue(), 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.StreamStateEntity;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import io.reactivex.Completable;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.Single;
@ -80,9 +82,9 @@ public class HistoryRecordManager {
final Date currentTime = new Date();
return Maybe.fromCallable(() -> database.runInTransaction(() -> {
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);
latestEntry.setAccessDate(currentTime);
latestEntry.setRepeatCount(latestEntry.getRepeatCount() + 1);
@ -99,7 +101,7 @@ public class HistoryRecordManager {
}
public Single<Integer> deleteWholeStreamHistory() {
return Single.fromCallable(() -> streamHistoryTable.deleteAll())
return Single.fromCallable(streamHistoryTable::deleteAll)
.subscribeOn(Schedulers.io());
}
@ -160,7 +162,7 @@ public class HistoryRecordManager {
}
public Single<Integer> deleteWholeSearchHistory() {
return Single.fromCallable(() -> searchHistoryTable.deleteAll())
return Single.fromCallable(searchHistoryTable::deleteAll)
.subscribeOn(Schedulers.io());
}
@ -180,18 +182,41 @@ public class HistoryRecordManager {
// Stream State History
///////////////////////////////////////////////////////
@SuppressWarnings("unused")
public Maybe<StreamStateEntity> loadStreamState(final StreamInfo info) {
return Maybe.fromCallable(() -> streamTable.upsert(new StreamEntity(info)))
.flatMap(streamId -> streamStateTable.getState(streamId).firstElement())
.flatMap(states -> states.isEmpty() ? Maybe.empty() : Maybe.just(states.get(0)))
public Maybe<StreamHistoryEntity> getStreamHistory(final StreamInfo info) {
return Maybe.fromCallable(() -> {
final long streamId = streamTable.upsert(new StreamEntity(info));
return streamHistoryTable.getLatestEntry(streamId);
}).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());
}
public Maybe<Long> saveStreamState(@NonNull final StreamInfo info, final long progressTime) {
return Maybe.fromCallable(() -> database.runInTransaction(() -> {
public Maybe<StreamStateEntity> loadStreamState(final StreamInfo info) {
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));
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());
}

View File

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

View File

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

View File

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

View File

@ -23,9 +23,11 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
@ -145,6 +147,8 @@ public abstract class BasePlayer implements
@NonNull
public static final String APPEND_ONLY = "append_only";
@NonNull
public static final String RESUME_PLAYBACK = "resume_playback";
@NonNull
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());
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...
initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence,
/*playOnInit=*/true);
@ -615,6 +634,9 @@ public abstract class BasePlayer implements
break;
case Player.STATE_ENDED: // 4
changeState(STATE_COMPLETED);
if (currentMetadata != null) {
resetPlaybackState(currentMetadata.getMetadata());
}
isPrepared = false;
break;
}
@ -721,6 +743,7 @@ public abstract class BasePlayer implements
case DISCONTINUITY_REASON_SEEK_ADJUSTMENT:
case DISCONTINUITY_REASON_INTERNAL:
if (playQueue.getIndex() != newWindowIndex) {
resetPlaybackState(playQueue.getItem());
playQueue.setIndex(newWindowIndex);
}
break;
@ -750,6 +773,9 @@ public abstract class BasePlayer implements
@Override
public void onSeekProcessed() {
if (DEBUG) Log.d(TAG, "ExoPlayer - onSeekProcessed() called");
if (isPrepared) {
savePlaybackState();
}
}
/*//////////////////////////////////////////////////////////////////////////
// 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 (DEBUG) Log.d(TAG, "savePlaybackState() called");
final Disposable stateSaver = recordManager.saveStreamState(info, progress)
.observeOn(AndroidSchedulers.mainThread())
.doOnError((e) -> {
if (DEBUG) e.printStackTrace();
})
.onErrorComplete()
.subscribe(
ignored -> {/* successful */},
error -> Log.e(TAG, "savePlaybackState() failure: ", error)
);
.subscribe();
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;
final StreamInfo currentInfo = currentMetadata.getMetadata();
if (simpleExoPlayer.getCurrentPosition() > RECOVERY_SKIP_THRESHOLD_MILLIS &&
simpleExoPlayer.getCurrentPosition() <
simpleExoPlayer.getDuration() - RECOVERY_SKIP_THRESHOLD_MILLIS) {
savePlaybackState(currentInfo, simpleExoPlayer.getCurrentPosition());
}
savePlaybackState(currentInfo, simpleExoPlayer.getCurrentPosition());
}
private void maybeUpdateCurrentMetadata() {
@ -1225,4 +1264,10 @@ public abstract class BasePlayer implements
public boolean gotDestroyed() {
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));
}
@Override
protected void onPause() {
playerImpl.savePlaybackState();
super.onPause();
}
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@ -579,7 +585,8 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
this.getPlaybackQuality(),
false
);
context.startService(intent);
@ -601,7 +608,8 @@ public final class MainVideoPlayer extends AppCompatActivity
this.getPlaybackSpeed(),
this.getPlaybackPitch(),
this.getPlaybackSkipSilence(),
this.getPlaybackQuality()
this.getPlaybackQuality(),
false
);
context.startService(intent);

View File

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

View File

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

View File

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

View File

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

View File

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

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"?>
<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:tools="http://schemas.android.com/tools"
android:id="@+id/video_item_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -19,6 +19,7 @@
android:id="@+id/appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:layout_behavior="android.support.design.widget.FlingBehavior">
@ -67,10 +68,10 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:paddingRight="30dp"
android:paddingBottom="10dp"
android:text="@string/hold_to_append"
android:textColor="@android:color/white"
android:textSize="20sp"
@ -84,17 +85,42 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginBottom="8dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="8dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:alpha=".6"
android:background="#23000000"
android:gravity="center"
android:paddingBottom="2dp"
android:paddingLeft="6dp"
android:paddingRight="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" />
<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"
@ -107,6 +133,19 @@
</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 -->
<RelativeLayout
android:id="@+id/detail_content_root_layout"
@ -133,8 +172,8 @@
android:layout_marginRight="20dp"
android:ellipsize="end"
android:maxLines="1"
android:paddingBottom="8dp"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_title_text_size"
tools:ignore="RtlHardcoded"
@ -179,9 +218,9 @@
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:paddingBottom="10dp"
android:visibility="gone"
tools:visibility="visible">
@ -191,8 +230,8 @@
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="6dp"
android:layout_marginRight="12dp"
android:baselineAligned="false"
android:orientation="horizontal">
@ -201,8 +240,8 @@
android:id="@+id/detail_uploader_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/details_panel"
android:layout_toStartOf="@id/details_panel"
android:layout_toLeftOf="@id/details_panel"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
@ -261,8 +300,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_views_text_size"
@ -354,8 +393,8 @@
android:drawableTop="?attr/ic_playlist_add"
android:focusable="true"
android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_add_to_playlist_title"
android:textSize="12sp" />
@ -371,8 +410,8 @@
android:drawableTop="?attr/audio"
android:focusable="true"
android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_background_title"
android:textSize="12sp" />
@ -388,8 +427,8 @@
android:drawableTop="?attr/popup"
android:focusable="true"
android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/controls_popup_title"
android:textSize="12sp" />
@ -405,8 +444,8 @@
android:drawableTop="?attr/download"
android:focusable="true"
android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:text="@string/download"
android:textSize="12sp" />
@ -444,10 +483,10 @@
android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="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"
@ -490,7 +529,7 @@
</android.support.design.widget.CoordinatorLayout>
<FrameLayout
android:id="@+id/relatedStreamsLayout"
android:id="@+id/relatedStreamsLayout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginTop="10dp"

View File

@ -1,489 +1,528 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/video_item_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/video_item_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/detail_main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/detail_main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:layout_behavior="android.support.design.widget.FlingBehavior">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:layout_behavior="android.support.design.widget.FlingBehavior">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll">
<!-- THUMBNAIL -->
<FrameLayout
android:id="@+id/detail_thumbnail_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:layout_collapseMode="parallax">
<!-- THUMBNAIL -->
<FrameLayout
android:id="@+id/detail_thumbnail_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:layout_collapseMode="parallax">
<ImageView
android:id="@+id/detail_thumbnail_image_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:contentDescription="@string/detail_thumbnail_view_description"
android:scaleType="fitCenter"
tools:ignore="RtlHardcoded"
tools:layout_height="200dp"
tools:src="@drawable/dummy_thumbnail" />
<ImageView
android:id="@+id/detail_thumbnail_image_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:contentDescription="@string/detail_thumbnail_view_description"
android:scaleType="fitCenter"
tools:ignore="RtlHardcoded"
tools:layout_height="200dp"
tools:src="@drawable/dummy_thumbnail" />
<ImageView
android:id="@+id/detail_thumbnail_play_button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:src="@drawable/new_play_arrow"
android:visibility="invisible"
tools:ignore="ContentDescription"
tools:visibility="visible" />
<ImageView
android:id="@+id/detail_thumbnail_play_button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:src="@drawable/new_play_arrow"
android:visibility="invisible"
tools:ignore="ContentDescription"
tools:visibility="visible" />
<TextView
android:id="@+id/touch_append_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#64000000"
android:paddingBottom="10dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="10dp"
android:text="@string/hold_to_append"
android:textColor="@android:color/white"
android:textSize="20sp"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible" />
<TextView
android:id="@+id/touch_append_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#64000000"
android:paddingLeft="30dp"
android:paddingTop="10dp"
android:paddingRight="30dp"
android:paddingBottom="10dp"
android:text="@string/hold_to_append"
android:textColor="@android:color/white"
android:textSize="20sp"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible" />
<TextView
android:id="@+id/detail_duration_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginBottom="8dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="8dp"
android:alpha=".6"
android:background="#23000000"
android:gravity="center"
android:paddingBottom="2dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="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" />
</FrameLayout>
<TextView
android:id="@+id/detail_duration_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginLeft="12dp"
android:layout_marginTop="8dp"
android:layout_marginRight="12dp"
android:layout_marginBottom="8dp"
android:alpha=".6"
android:background="#23000000"
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" />
</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 -->
<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">
</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">
</android.support.design.widget.CollapsingToolbarLayout>
<TextView
android:id="@+id/detail_video_title_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="20dp"
android:ellipsize="end"
android:maxLines="1"
android:paddingBottom="8dp"
android:paddingTop="12dp"
android:textAppearance="?android:attr/textAppearanceLarge"
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." />
<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:progressDrawable="?attr/progress_horizontal_drawable"
android:visibility="invisible"
tools:max="100"
tools:progress="40"
tools:visibility="visible" />
<ImageView
android:id="@+id/detail_toggle_description_view"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="center_vertical|right"
android:layout_marginLeft="5dp"
android:src="@drawable/arrow_down"
tools:ignore="ContentDescription,RtlHardcoded" />
<!-- CONTENT -->
<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">
</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-->
<ProgressBar
android:id="@+id/loading_progress_bar"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/detail_title_root_layout"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/detail_video_title_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="20dp"
android:ellipsize="end"
android:maxLines="1"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:textAppearance="?android:attr/textAppearanceLarge"
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-->
<include
android:id="@+id/error_panel"
layout="@layout/error_retry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/detail_title_root_layout"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:visibility="gone"
tools:visibility="visible" />
<ImageView
android:id="@+id/detail_toggle_description_view"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="center_vertical|right"
android:layout_marginLeft="5dp"
android:src="@drawable/arrow_down"
tools:ignore="ContentDescription,RtlHardcoded" />
<!--HIDING ROOT-->
<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">
</FrameLayout>
<!--DETAIL-->
<RelativeLayout
android:id="@+id/detail_root"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="6dp"
android:baselineAligned="false"
android:orientation="horizontal">
<!-- LOADING INDICATOR-->
<ProgressBar
android:id="@+id/loading_progress_bar"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/detail_title_root_layout"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible" />
<!--UPLOADER-->
<LinearLayout
android:id="@+id/detail_uploader_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/details_panel"
android:layout_toStartOf="@id/details_panel"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="6dp">
<!--ERROR PANEL-->
<include
android:id="@+id/error_panel"
layout="@layout/error_retry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/detail_title_root_layout"
android:layout_marginTop="@dimen/video_item_detail_error_panel_margin"
android:visibility="gone"
tools:visibility="visible" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/detail_uploader_thumbnail_view"
android:layout_width="@dimen/video_item_detail_uploader_image_size"
android:layout_height="@dimen/video_item_detail_uploader_image_size"
android:contentDescription="@string/detail_uploader_thumbnail_view_description"
android:src="@drawable/buddy"
tools:ignore="RtlHardcoded" />
<!--HIDING ROOT-->
<LinearLayout
android:id="@+id/detail_content_root_hiding"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/detail_title_root_layout"
android:orientation="vertical"
android:paddingBottom="10dp"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/detail_uploader_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_uploader_text_size"
android:textStyle="bold"
tools:ignore="RtlHardcoded"
tools:text="Uploader" />
<!--DETAIL-->
<RelativeLayout
android:id="@+id/detail_root"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginLeft="12dp"
android:layout_marginTop="6dp"
android:layout_marginRight="12dp"
android:baselineAligned="false"
android:orientation="horizontal">
<!--<Button
android:id="@+id/detail_uploader_subscribe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="12dp"
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>
<!--UPLOADER-->
<LinearLayout
android:id="@+id/detail_uploader_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toStartOf="@id/details_panel"
android:layout_toLeftOf="@id/details_panel"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="6dp">
<!-- VIEW & THUMBS -->
<RelativeLayout
android:id="@+id/details_panel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:paddingLeft="6dp"
android:paddingRight="6dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/detail_uploader_thumbnail_view"
android:layout_width="@dimen/video_item_detail_uploader_image_size"
android:layout_height="@dimen/video_item_detail_uploader_image_size"
android:contentDescription="@string/detail_uploader_thumbnail_view_description"
android:src="@drawable/buddy"
tools:ignore="RtlHardcoded" />
<TextView
android:id="@+id/detail_view_count_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_views_text_size"
tools:ignore="RtlHardcoded"
tools:text="2,816,821,505 views" />
<TextView
android:id="@+id/detail_uploader_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_uploader_text_size"
android:textStyle="bold"
tools:ignore="RtlHardcoded"
tools:text="Uploader" />
<ImageView
android:id="@+id/detail_thumbs_up_img_view"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:contentDescription="@string/detail_likes_img_view_description"
android:src="?attr/thumbs_up" />
<!--<Button
android:id="@+id/detail_uploader_subscribe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="12dp"
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
android:id="@+id/detail_thumbs_up_count_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="@dimen/video_item_detail_like_margin"
android:layout_toRightOf="@id/detail_thumbs_up_img_view"
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="12M" />
<!-- VIEW & THUMBS -->
<RelativeLayout
android:id="@+id/details_panel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:paddingLeft="6dp"
android:paddingRight="6dp">
<ImageView
android:id="@+id/detail_thumbs_down_img_view"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
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" />
<TextView
android:id="@+id/detail_view_count_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_views_text_size"
tools:ignore="RtlHardcoded"
tools:text="2,816,821,505 views" />
<TextView
android:id="@+id/detail_thumbs_down_count_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="@dimen/video_item_detail_like_margin"
android:layout_toRightOf="@id/detail_thumbs_down_img_view"
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" />
<ImageView
android:id="@+id/detail_thumbs_up_img_view"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:contentDescription="@string/detail_likes_img_view_description"
android:src="?attr/thumbs_up" />
<TextView
android:id="@+id/detail_thumbs_disabled_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="12dp"
android:layout_toRightOf="@id/detail_thumbs_down_img_view"
android:gravity="center_vertical"
android:text="@string/disabled"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_likes_text_size"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible" />
</RelativeLayout>
</RelativeLayout>
<TextView
android:id="@+id/detail_thumbs_up_count_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="@dimen/video_item_detail_like_margin"
android:layout_toRightOf="@id/detail_thumbs_up_img_view"
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="12M" />
<LinearLayout
android:id="@+id/detail_control_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="6dp">
<ImageView
android:id="@+id/detail_thumbs_down_img_view"
android:layout_width="@dimen/video_item_detail_like_image_width"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
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
android:id="@+id/detail_controls_playlist_append"
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/append_playlist"
android:drawableTop="?attr/ic_playlist_add"
android:focusable="true"
android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:text="@string/controls_add_to_playlist_title"
android:textSize="12sp" />
<TextView
android:id="@+id/detail_thumbs_down_count_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="@dimen/video_item_detail_like_margin"
android:layout_toRightOf="@id/detail_thumbs_down_img_view"
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
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:paddingBottom="6dp"
android:paddingTop="6dp"
android:text="@string/controls_background_title"
android:textSize="12sp" />
<TextView
android:id="@+id/detail_thumbs_disabled_view"
android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_detail_like_image_height"
android:layout_below="@id/detail_view_count_view"
android:layout_marginLeft="12dp"
android:layout_toRightOf="@id/detail_thumbs_down_img_view"
android:gravity="center_vertical"
android:text="@string/disabled"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_detail_likes_text_size"
android:textStyle="bold"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible" />
</RelativeLayout>
</RelativeLayout>
<TextView
android:id="@+id/detail_controls_popup"
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/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" />
<LinearLayout
android:id="@+id/detail_control_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="6dp">
<TextView
android:id="@+id/detail_controls_download"
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/controls_download_desc"
android:drawableTop="?attr/download"
android:focusable="true"
android:gravity="center"
android:paddingBottom="6dp"
android:paddingTop="6dp"
android:text="@string/download"
android:textSize="12sp" />
<!-- CONTROLS -->
<TextView
android:id="@+id/detail_controls_playlist_append"
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/append_playlist"
android:drawableTop="?attr/ic_playlist_add"
android:focusable="true"
android:gravity="center"
android:paddingTop="6dp"
android:paddingBottom="6dp"
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
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
<TextView
android:id="@+id/detail_controls_popup"
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/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-->
<LinearLayout
android:id="@+id/detail_description_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/detail_controls_download"
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/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
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
android:id="@+id/detail_description_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="12dp"
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
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?attr/separator_color" />
<!--DESCRIPTIONS-->
<LinearLayout
android:id="@+id/detail_description_root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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>
</RelativeLayout>
<TextView
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:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:background="@color/transparent_background_color"
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp">
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:background="@color/transparent_background_color"
app:tabBackground="@drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp">
</android.support.design.widget.TabLayout>
</android.support.design.widget.TabLayout>
</android.support.design.widget.CoordinatorLayout>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>

View File

@ -167,7 +167,10 @@
<string name="fragment_whats_new">Что нового</string>
<string name="enable_search_history_title">История поиска</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="resume_on_audio_focus_gain_title">Возобновить при фокусе</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="enable_search_history_title">Історія пошуків</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="resume_on_audio_focus_gain_title">Відновити відтворення</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_settings_update" format="reference"/>
<attr name="progress_horizontal_drawable" format="reference"/>
<!-- Can't refer to colors directly in drawable's xml-->
<attr name="toolbar_shadow_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_watch_history_key" translatable="false">enable_watch_history</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="export_data">export_data</string>

View File

@ -95,7 +95,10 @@
<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_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="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>

View File

@ -65,6 +65,7 @@
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_light</item>
<item name="selector_drawable">@drawable/light_selector</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>
</style>
@ -128,6 +129,7 @@
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_dark</item>
<item name="selector_drawable">@drawable/dark_selector</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>
</style>

View File

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

View File

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