Merge pull request #2288 from nv95/playback_state_list
Playback positions in lists
This commit is contained in:
commit
61472a995f
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ package org.schabi.newpipe.database.stream.model;
|
|||
import android.arch.persistence.room.ColumnInfo;
|
||||
import android.arch.persistence.room.Entity;
|
||||
import android.arch.persistence.room.ForeignKey;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
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;
|
||||
|
@ -22,6 +25,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 +57,18 @@ 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj instanceof StreamStateEntity) {
|
||||
return ((StreamStateEntity) obj).streamUid == streamUid
|
||||
&& ((StreamStateEntity) obj).progressTime == progressTime;
|
||||
} else return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.setProgressAnimated(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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,12 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
infoListAdapter = new InfoListAdapter(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
infoListAdapter.dispose();
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -94,6 +100,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
}
|
||||
updateFlags = 0;
|
||||
}
|
||||
|
||||
itemsList.post(infoListAdapter::updateStates);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -266,13 +274,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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@ package org.schabi.newpipe.info_list;
|
|||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
|
@ -59,13 +61,14 @@ public class InfoItemBuilder {
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem) {
|
||||
return buildView(parent, infoItem, false);
|
||||
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem, @Nullable StreamStateEntity state) {
|
||||
return buildView(parent, infoItem, state, false);
|
||||
}
|
||||
|
||||
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem, boolean useMiniVariant) {
|
||||
public View buildView(@NonNull ViewGroup parent, @NonNull final InfoItem infoItem,
|
||||
@Nullable StreamStateEntity state, boolean useMiniVariant) {
|
||||
InfoItemHolder holder = holderFromInfoType(parent, infoItem.getInfoType(), useMiniVariant);
|
||||
holder.updateFromItem(infoItem);
|
||||
holder.updateFromItem(infoItem, state);
|
||||
return holder.itemView;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.PlaylistGridInfoItemHolder;
|
||||
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
|
||||
|
@ -50,7 +53,7 @@ import java.util.List;
|
|||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
public class InfoListAdapter extends StateObjectsListAdapter {
|
||||
private static final String TAG = InfoListAdapter.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
|
@ -87,6 +90,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||
}
|
||||
|
||||
public InfoListAdapter(Activity a) {
|
||||
super(a.getApplicationContext());
|
||||
infoItemBuilder = new InfoItemBuilder(a);
|
||||
infoItemList = new ArrayList<>();
|
||||
}
|
||||
|
@ -115,50 +119,64 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||
this.useGridVariant = useGridVariant;
|
||||
}
|
||||
|
||||
public void addInfoItemList(List<InfoItem> data) {
|
||||
public void addInfoItemList(@Nullable final List<InfoItem> data) {
|
||||
if (data != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItemList() before > infoItemList.size() = " + infoItemList.size() + ", data.size() = " + data.size());
|
||||
}
|
||||
|
||||
int offsetStart = sizeConsideringHeaderOffset();
|
||||
infoItemList.addAll(data);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItemList() after > offsetStart = " + offsetStart + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
||||
}
|
||||
|
||||
notifyItemRangeInserted(offsetStart, data.size());
|
||||
|
||||
if (footer != null && showFooter) {
|
||||
int footerNow = sizeConsideringHeaderOffset();
|
||||
notifyItemMoved(offsetStart, footerNow);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "addInfoItemList() footer from " + offsetStart + " to " + footerNow);
|
||||
}
|
||||
loadStates(data, infoItemList.size(), () -> addInfoItemListImpl(data));
|
||||
}
|
||||
}
|
||||
|
||||
public void addInfoItem(InfoItem data) {
|
||||
private void addInfoItemListImpl(@NonNull List<InfoItem> data) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItemList() before > infoItemList.size() = " + infoItemList.size() + ", data.size() = " + data.size());
|
||||
}
|
||||
|
||||
int offsetStart = sizeConsideringHeaderOffset();
|
||||
infoItemList.addAll(data);
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItemList() after > offsetStart = " + offsetStart + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
||||
}
|
||||
|
||||
notifyItemRangeInserted(offsetStart, data.size());
|
||||
|
||||
if (footer != null && showFooter) {
|
||||
int footerNow = sizeConsideringHeaderOffset();
|
||||
notifyItemMoved(offsetStart, footerNow);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "addInfoItemList() footer from " + offsetStart + " to " + footerNow);
|
||||
}
|
||||
}
|
||||
|
||||
public void addInfoItem(@Nullable InfoItem data) {
|
||||
if (data != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItem() before > infoItemList.size() = " + infoItemList.size() + ", thread = " + Thread.currentThread());
|
||||
}
|
||||
loadState(data, infoItemList.size(), () -> addInfoItemImpl(data));
|
||||
}
|
||||
}
|
||||
|
||||
int positionInserted = sizeConsideringHeaderOffset();
|
||||
infoItemList.add(data);
|
||||
private void addInfoItemImpl(@NonNull InfoItem data) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItem() before > infoItemList.size() = " + infoItemList.size() + ", thread = " + Thread.currentThread());
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItem() after > position = " + positionInserted + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
||||
}
|
||||
notifyItemInserted(positionInserted);
|
||||
int positionInserted = sizeConsideringHeaderOffset();
|
||||
infoItemList.add(data);
|
||||
|
||||
if (footer != null && showFooter) {
|
||||
int footerNow = sizeConsideringHeaderOffset();
|
||||
notifyItemMoved(positionInserted, footerNow);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addInfoItem() after > position = " + positionInserted + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
||||
}
|
||||
notifyItemInserted(positionInserted);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "addInfoItem() footer from " + positionInserted + " to " + footerNow);
|
||||
}
|
||||
if (footer != null && showFooter) {
|
||||
int footerNow = sizeConsideringHeaderOffset();
|
||||
notifyItemMoved(positionInserted, footerNow);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "addInfoItem() footer from " + positionInserted + " to " + footerNow);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateStates() {
|
||||
if (!infoItemList.isEmpty()) {
|
||||
updateAllStates(infoItemList);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,6 +185,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||
return;
|
||||
}
|
||||
infoItemList.clear();
|
||||
clearStates();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
@ -240,8 +259,9 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onCreateViewHolder() called with: parent = [" + parent + "], type = [" + type + "]");
|
||||
switch (type) {
|
||||
|
@ -278,13 +298,13 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
if (DEBUG) Log.d(TAG, "onBindViewHolder() called with: holder = [" + holder.getClass().getSimpleName() + "], position = [" + position + "]");
|
||||
if (holder instanceof InfoItemHolder) {
|
||||
// If header isn't null, offset the items by -1
|
||||
if (header != null) position--;
|
||||
|
||||
((InfoItemHolder) holder).updateFromItem(infoItemList.get(position));
|
||||
((InfoItemHolder) holder).updateFromItem(infoItemList.get(position), getState(position));
|
||||
} else if (holder instanceof HFHolder && position == 0 && header != null) {
|
||||
((HFHolder) holder).view = header;
|
||||
} else if (holder instanceof HFHolder && position == sizeConsideringHeaderOffset() && footer != null && showFooter) {
|
||||
|
@ -292,6 +312,28 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
|
||||
if (!payloads.isEmpty() && holder instanceof InfoItemHolder) {
|
||||
for (Object payload : payloads) {
|
||||
if (payload instanceof StreamStateEntity) {
|
||||
((InfoItemHolder) holder).updateState(infoItemList.get(header == null ? position : position - 1),
|
||||
(StreamStateEntity) payload);
|
||||
} else if (payload instanceof Boolean) {
|
||||
((InfoItemHolder) holder).updateState(infoItemList.get(header == null ? position : position - 1),
|
||||
null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onBindViewHolder(holder, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onItemStateChanged(int position, @Nullable StreamStateEntity state) {
|
||||
notifyItemChanged(header == null ? position : position + 1, state != null ? state : false);
|
||||
}
|
||||
|
||||
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
||||
return new GridLayoutManager.SpanSizeLookup() {
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
package org.schabi.newpipe.info_list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import org.schabi.newpipe.BuildConfig;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
||||
import org.schabi.newpipe.util.SparseArrayUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
public abstract class StateObjectsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private final SparseArray<StreamStateEntity> states;
|
||||
private final HistoryRecordManager recordManager;
|
||||
private final CompositeDisposable stateLoaders;
|
||||
private final Context context;
|
||||
|
||||
public StateObjectsListAdapter(Context context) {
|
||||
this.states = new SparseArray<>();
|
||||
this.recordManager = new HistoryRecordManager(context);
|
||||
this.context = context;
|
||||
this.stateLoaders = new CompositeDisposable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public StreamStateEntity getState(int position) {
|
||||
return states.get(position);
|
||||
}
|
||||
|
||||
protected void clearStates() {
|
||||
states.clear();
|
||||
}
|
||||
|
||||
private void appendStates(List<StreamStateEntity> statesEntities, int offset) {
|
||||
for (int i = 0; i < statesEntities.size(); i++) {
|
||||
final StreamStateEntity state = statesEntities.get(i);
|
||||
if (state != null) {
|
||||
states.append(offset + i, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void appendState(StreamStateEntity statesEntity, int offset) {
|
||||
if (statesEntity != null) {
|
||||
states.append(offset, statesEntity);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeState(int index) {
|
||||
states.remove(index);
|
||||
}
|
||||
|
||||
protected void moveState(int from, int to) {
|
||||
final StreamStateEntity item = states.get(from);
|
||||
if (from < to) {
|
||||
SparseArrayUtils.shiftItemsDown(states, from, to);
|
||||
} else {
|
||||
SparseArrayUtils.shiftItemsUp(states, to, from);
|
||||
}
|
||||
states.put(to, item);
|
||||
}
|
||||
|
||||
protected void loadStates(List<InfoItem> list, int offset, Runnable callback) {
|
||||
if (isPlaybackStatesVisible()) {
|
||||
stateLoaders.add(
|
||||
recordManager.loadStreamStateBatch(list)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(streamStateEntities -> {
|
||||
appendStates(streamStateEntities, offset);
|
||||
callback.run();
|
||||
}, throwable -> {
|
||||
if (BuildConfig.DEBUG) throwable.printStackTrace();
|
||||
callback.run();
|
||||
})
|
||||
);
|
||||
} else {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadState(InfoItem item, int offset, Runnable callback) {
|
||||
if (isPlaybackStatesVisible()) {
|
||||
stateLoaders.add(
|
||||
recordManager.loadStreamState(item)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(streamStateEntities -> {
|
||||
appendState(streamStateEntities[0], offset);
|
||||
callback.run();
|
||||
}, throwable -> {
|
||||
if (BuildConfig.DEBUG) throwable.printStackTrace();
|
||||
callback.run();
|
||||
})
|
||||
);
|
||||
} else {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadStatesForLocal(List<? extends LocalItem> list, int offset, Runnable callback) {
|
||||
if (isPlaybackStatesVisible()) {
|
||||
stateLoaders.add(
|
||||
recordManager.loadLocalStreamStateBatch(list)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(streamStateEntities -> {
|
||||
appendStates(streamStateEntities, offset);
|
||||
callback.run();
|
||||
}, throwable -> {
|
||||
if (BuildConfig.DEBUG) throwable.printStackTrace();
|
||||
callback.run();
|
||||
})
|
||||
);
|
||||
} else {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
private void processStatesUpdates(List<StreamStateEntity> streamStateEntities) {
|
||||
for (int i = 0; i < streamStateEntities.size(); i++) {
|
||||
final StreamStateEntity newState = streamStateEntities.get(i);
|
||||
if (!Objects.equals(states.get(i), newState)) {
|
||||
if (newState == null) {
|
||||
states.remove(i);
|
||||
} else {
|
||||
states.put(i, newState);
|
||||
}
|
||||
onItemStateChanged(i, newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateAllStates(List<InfoItem> list) {
|
||||
if (isPlaybackStatesVisible()) {
|
||||
stateLoaders.add(
|
||||
recordManager.loadStreamStateBatch(list)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::processStatesUpdates, throwable -> {
|
||||
if (BuildConfig.DEBUG) throwable.printStackTrace();
|
||||
})
|
||||
);
|
||||
} else {
|
||||
final int[] positions = SparseArrayUtils.getKeys(states);
|
||||
states.clear();
|
||||
for (int pos : positions) onItemStateChanged(pos, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateAllLocalStates(List<? extends LocalItem> list) {
|
||||
if (isPlaybackStatesVisible()) {
|
||||
stateLoaders.add(
|
||||
recordManager.loadLocalStreamStateBatch(list)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::processStatesUpdates, throwable -> {
|
||||
if (BuildConfig.DEBUG) throwable.printStackTrace();
|
||||
})
|
||||
);
|
||||
} else {
|
||||
final int[] positions = SparseArrayUtils.getKeys(states);
|
||||
states.clear();
|
||||
for (int pos : positions) onItemStateChanged(pos, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
stateLoaders.dispose();
|
||||
}
|
||||
|
||||
protected boolean isPlaybackStatesVisible() {
|
||||
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)
|
||||
&& prefs.getBoolean(context.getString(R.string.enable_playback_state_lists_key), true);
|
||||
}
|
||||
|
||||
protected abstract void onItemStateChanged(int position, @Nullable StreamStateEntity state);
|
||||
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
@ -38,8 +40,8 @@ public class ChannelInfoItemHolder extends ChannelMiniInfoItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
super.updateFromItem(infoItem);
|
||||
public void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
super.updateFromItem(infoItem, state);
|
||||
|
||||
if (!(infoItem instanceof ChannelInfoItem)) return;
|
||||
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
@ -30,7 +32,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
public void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
if (!(infoItem instanceof ChannelInfoItem)) return;
|
||||
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
|
||||
/*
|
||||
* Created by Christian Schabesberger on 12.02.17.
|
||||
|
@ -41,8 +41,8 @@ public class CommentsInfoItemHolder extends CommentsMiniInfoItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
super.updateFromItem(infoItem);
|
||||
public void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
super.updateFromItem(infoItem, state);
|
||||
|
||||
if (!(infoItem instanceof CommentsInfoItem)) return;
|
||||
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.View;
|
||||
|
@ -8,6 +9,7 @@ import android.widget.TextView;
|
|||
|
||||
import org.jsoup.helper.StringUtil;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
@ -65,7 +67,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
public void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
if (!(infoItem instanceof CommentsInfoItem)) return;
|
||||
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
||||
|
@ -35,5 +37,8 @@ public abstract class InfoItemHolder extends RecyclerView.ViewHolder {
|
|||
this.itemBuilder = infoItemBuilder;
|
||||
}
|
||||
|
||||
public abstract void updateFromItem(final InfoItem infoItem);
|
||||
public abstract void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state);
|
||||
|
||||
public void updateState(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
@ -30,7 +32,7 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
public void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
if (!(infoItem instanceof PlaylistInfoItem)) return;
|
||||
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
|
@ -40,8 +42,8 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
super.updateFromItem(infoItem);
|
||||
public void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
super.updateFromItem(infoItem, state);
|
||||
|
||||
if (!(infoItem instanceof StreamInfoItem)) return;
|
||||
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -7,12 +8,17 @@ import android.widget.ImageView;
|
|||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||
|
||||
|
@ -20,6 +26,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
|||
public final TextView itemVideoTitleView;
|
||||
public final TextView itemUploaderView;
|
||||
public final TextView itemDurationView;
|
||||
public final AnimatedProgressBar itemProgressView;
|
||||
|
||||
StreamMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
||||
super(infoItemBuilder, layoutId, parent);
|
||||
|
@ -28,6 +35,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
|||
itemVideoTitleView = itemView.findViewById(R.id.itemVideoTitleView);
|
||||
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
||||
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
||||
itemProgressView = itemView.findViewById(R.id.itemProgressView);
|
||||
}
|
||||
|
||||
public StreamMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
|
@ -35,7 +43,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final InfoItem infoItem) {
|
||||
public void updateFromItem(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
if (!(infoItem instanceof StreamInfoItem)) return;
|
||||
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
||||
|
||||
|
@ -47,13 +55,22 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
|||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||
R.color.duration_background_color));
|
||||
itemDurationView.setVisibility(View.VISIBLE);
|
||||
if (state != null) {
|
||||
itemProgressView.setVisibility(View.VISIBLE);
|
||||
itemProgressView.setMax((int) item.getDuration());
|
||||
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
} else {
|
||||
itemProgressView.setVisibility(View.GONE);
|
||||
}
|
||||
} else if (item.getStreamType() == StreamType.LIVE_STREAM) {
|
||||
itemDurationView.setText(R.string.duration_live);
|
||||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||
R.color.live_duration_background_color));
|
||||
itemDurationView.setVisibility(View.VISIBLE);
|
||||
itemProgressView.setVisibility(View.GONE);
|
||||
} else {
|
||||
itemDurationView.setVisibility(View.GONE);
|
||||
itemProgressView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Default thumbnail is shown on error, while loading and if the url is empty
|
||||
|
@ -83,6 +100,22 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(final InfoItem infoItem, @Nullable final StreamStateEntity state) {
|
||||
final StreamInfoItem item = (StreamInfoItem) infoItem;
|
||||
if (state != null && item.getDuration() > 0 && item.getStreamType() != StreamType.LIVE_STREAM) {
|
||||
itemProgressView.setMax((int) item.getDuration());
|
||||
if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
} else {
|
||||
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
AnimationUtils.animateView(itemProgressView, true, 500);
|
||||
}
|
||||
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||
AnimationUtils.animateView(itemProgressView, false, 500);
|
||||
}
|
||||
}
|
||||
|
||||
private void enableLongClick(final StreamInfoItem item) {
|
||||
itemView.setLongClickable(true);
|
||||
itemView.setOnLongClickListener(view -> {
|
||||
|
|
|
@ -76,6 +76,8 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
|
|||
}
|
||||
updateFlags = 0;
|
||||
}
|
||||
|
||||
itemsList.post(itemListAdapter::updateStates);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -150,6 +152,7 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
|
|||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
itemsList = null;
|
||||
itemListAdapter.dispose();
|
||||
itemListAdapter = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.schabi.newpipe.local;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
|
@ -8,6 +10,8 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.info_list.StateObjectsListAdapter;
|
||||
import org.schabi.newpipe.local.holder.LocalItemHolder;
|
||||
import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder;
|
||||
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
|
||||
|
@ -45,7 +49,7 @@ import java.util.List;
|
|||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
public class LocalItemListAdapter extends StateObjectsListAdapter {
|
||||
|
||||
private static final String TAG = LocalItemListAdapter.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
@ -72,6 +76,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
private View footer = null;
|
||||
|
||||
public LocalItemListAdapter(Activity activity) {
|
||||
super(activity.getApplicationContext());
|
||||
localItemBuilder = new LocalItemBuilder(activity);
|
||||
localItems = new ArrayList<>();
|
||||
dateFormat = DateFormat.getDateInstance(DateFormat.SHORT,
|
||||
|
@ -86,39 +91,49 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
localItemBuilder.setOnItemSelectedListener(null);
|
||||
}
|
||||
|
||||
public void addItems(List<? extends LocalItem> data) {
|
||||
public void addItems(@Nullable List<? extends LocalItem> data) {
|
||||
if (data != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addItems() before > localItems.size() = " +
|
||||
localItems.size() + ", data.size() = " + data.size());
|
||||
}
|
||||
loadStatesForLocal(data, localItems.size(), () -> addItemsImpl(data));
|
||||
}
|
||||
}
|
||||
|
||||
int offsetStart = sizeConsideringHeader();
|
||||
localItems.addAll(data);
|
||||
private void addItemsImpl(@NonNull List<? extends LocalItem> data) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addItems() before > localItems.size() = " +
|
||||
localItems.size() + ", data.size() = " + data.size());
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addItems() after > offsetStart = " + offsetStart +
|
||||
", localItems.size() = " + localItems.size() +
|
||||
", header = " + header + ", footer = " + footer +
|
||||
", showFooter = " + showFooter);
|
||||
}
|
||||
int offsetStart = sizeConsideringHeader();
|
||||
localItems.addAll(data);
|
||||
|
||||
notifyItemRangeInserted(offsetStart, data.size());
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addItems() after > offsetStart = " + offsetStart +
|
||||
", localItems.size() = " + localItems.size() +
|
||||
", header = " + header + ", footer = " + footer +
|
||||
", showFooter = " + showFooter);
|
||||
}
|
||||
|
||||
if (footer != null && showFooter) {
|
||||
int footerNow = sizeConsideringHeader();
|
||||
notifyItemMoved(offsetStart, footerNow);
|
||||
notifyItemRangeInserted(offsetStart, data.size());
|
||||
|
||||
if (DEBUG) Log.d(TAG, "addItems() footer from " + offsetStart +
|
||||
" to " + footerNow);
|
||||
}
|
||||
if (footer != null && showFooter) {
|
||||
int footerNow = sizeConsideringHeader();
|
||||
notifyItemMoved(offsetStart, footerNow);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "addItems() footer from " + offsetStart +
|
||||
" to " + footerNow);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateStates() {
|
||||
if (!localItems.isEmpty()) {
|
||||
updateAllLocalStates(localItems);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeItem(final LocalItem data) {
|
||||
final int index = localItems.indexOf(data);
|
||||
|
||||
localItems.remove(index);
|
||||
removeState(index);
|
||||
notifyItemRemoved(index + (header != null ? 1 : 0));
|
||||
}
|
||||
|
||||
|
@ -130,6 +145,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
if (actualFrom >= localItems.size() || actualTo >= localItems.size()) return false;
|
||||
|
||||
localItems.add(actualTo, localItems.remove(actualFrom));
|
||||
moveState(actualFrom, actualTo);
|
||||
notifyItemMoved(fromAdapterPosition, toAdapterPosition);
|
||||
return true;
|
||||
}
|
||||
|
@ -139,6 +155,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
return;
|
||||
}
|
||||
localItems.clear();
|
||||
clearStates();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
@ -259,7 +276,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
// If header isn't null, offset the items by -1
|
||||
if (header != null) position--;
|
||||
|
||||
((LocalItemHolder) holder).updateFromItem(localItems.get(position), dateFormat);
|
||||
((LocalItemHolder) holder).updateFromItem(localItems.get(position), getState(position), dateFormat);
|
||||
} else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) {
|
||||
((HeaderFooterHolder) holder).view = header;
|
||||
} else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader()
|
||||
|
@ -268,6 +285,28 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
|
||||
if (!payloads.isEmpty() && holder instanceof LocalItemHolder) {
|
||||
for (Object payload : payloads) {
|
||||
if (payload instanceof StreamStateEntity) {
|
||||
((LocalItemHolder) holder).updateState(localItems.get(header == null ? position : position - 1),
|
||||
(StreamStateEntity) payload);
|
||||
} else if (payload instanceof Boolean) {
|
||||
((LocalItemHolder) holder).updateState(localItems.get(header == null ? position : position - 1),
|
||||
null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onBindViewHolder(holder, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onItemStateChanged(int position, @Nullable StreamStateEntity state) {
|
||||
notifyItemChanged(header == null ? position : position + 1, state != null ? state : false);
|
||||
}
|
||||
|
||||
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
||||
return new GridLayoutManager.SpanSizeLookup() {
|
||||
@Override
|
||||
|
|
|
@ -112,7 +112,10 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (playlistReactor != null) playlistReactor.dispose();
|
||||
if (playlistAdapter != null) playlistAdapter.unsetSelectedListener();
|
||||
if (playlistAdapter != null) {
|
||||
playlistAdapter.dispose();
|
||||
playlistAdapter.unsetSelectedListener();
|
||||
}
|
||||
|
||||
playlistReactor = null;
|
||||
playlistRecyclerView = null;
|
||||
|
|
|
@ -26,23 +26,29 @@ import android.support.annotation.NonNull;
|
|||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.AppDatabase;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.history.dao.SearchHistoryDAO;
|
||||
import org.schabi.newpipe.database.history.dao.StreamHistoryDAO;
|
||||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
|
||||
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
|
||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||
import org.schabi.newpipe.database.stream.dao.StreamDAO;
|
||||
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.InfoItem;
|
||||
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 +86,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 +105,7 @@ public class HistoryRecordManager {
|
|||
}
|
||||
|
||||
public Single<Integer> deleteWholeStreamHistory() {
|
||||
return Single.fromCallable(() -> streamHistoryTable.deleteAll())
|
||||
return Single.fromCallable(streamHistoryTable::deleteAll)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
|
@ -160,7 +166,7 @@ public class HistoryRecordManager {
|
|||
}
|
||||
|
||||
public Single<Integer> deleteWholeSearchHistory() {
|
||||
return Single.fromCallable(() -> searchHistoryTable.deleteAll())
|
||||
return Single.fromCallable(searchHistoryTable::deleteAll)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
|
@ -180,21 +186,104 @@ 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());
|
||||
}
|
||||
|
||||
public Single<StreamStateEntity[]> loadStreamState(final InfoItem info) {
|
||||
return Single.fromCallable(() -> {
|
||||
final List<StreamEntity> entities = streamTable.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
|
||||
if (entities.isEmpty()) {
|
||||
return new StreamStateEntity[]{null};
|
||||
}
|
||||
final List<StreamStateEntity> states = streamStateTable.getState(entities.get(0).getUid()).blockingFirst();
|
||||
if (states.isEmpty()) {
|
||||
return new StreamStateEntity[]{null};
|
||||
}
|
||||
return new StreamStateEntity[]{states.get(0)};
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<List<StreamStateEntity>> loadStreamStateBatch(final List<InfoItem> infos) {
|
||||
return Single.fromCallable(() -> {
|
||||
final List<StreamStateEntity> result = new ArrayList<>(infos.size());
|
||||
for (InfoItem info : infos) {
|
||||
final List<StreamEntity> entities = streamTable.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
|
||||
if (entities.isEmpty()) {
|
||||
result.add(null);
|
||||
continue;
|
||||
}
|
||||
final List<StreamStateEntity> states = streamStateTable.getState(entities.get(0).getUid()).blockingFirst();
|
||||
if (states.isEmpty()) {
|
||||
result.add(null);
|
||||
continue;
|
||||
}
|
||||
result.add(states.get(0));
|
||||
}
|
||||
return result;
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Single<List<StreamStateEntity>> loadLocalStreamStateBatch(final List<? extends LocalItem> items) {
|
||||
return Single.fromCallable(() -> {
|
||||
final List<StreamStateEntity> result = new ArrayList<>(items.size());
|
||||
for (LocalItem item : items) {
|
||||
long streamId;
|
||||
if (item instanceof StreamStatisticsEntry) {
|
||||
streamId = ((StreamStatisticsEntry) item).streamId;
|
||||
} else if (item instanceof PlaylistStreamEntity) {
|
||||
streamId = ((PlaylistStreamEntity) item).getStreamUid();
|
||||
} else if (item instanceof PlaylistStreamEntry) {
|
||||
streamId = ((PlaylistStreamEntry) item).streamId;
|
||||
} else {
|
||||
result.add(null);
|
||||
continue;
|
||||
}
|
||||
final List<StreamStateEntity> states = streamStateTable.getState(streamId).blockingFirst();
|
||||
if (states.isEmpty()) {
|
||||
result.add(null);
|
||||
continue;
|
||||
}
|
||||
result.add(states.get(0));
|
||||
}
|
||||
return result;
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Utility
|
||||
///////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
@ -38,5 +40,8 @@ public abstract class LocalItemHolder extends RecyclerView.ViewHolder {
|
|||
this.itemBuilder = itemBuilder;
|
||||
}
|
||||
|
||||
public abstract void updateFromItem(final LocalItem item, final DateFormat dateFormat);
|
||||
public abstract void updateFromItem(final LocalItem item, @Nullable final StreamStateEntity state, final DateFormat dateFormat);
|
||||
|
||||
public void updateState(final LocalItem localItem, @Nullable final StreamStateEntity state) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
|
||||
|
@ -21,7 +23,7 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
||||
public void updateFromItem(final LocalItem localItem, @Nullable final StreamStateEntity state, final DateFormat dateFormat) {
|
||||
if (!(localItem instanceof PlaylistMetadataEntry)) return;
|
||||
final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem;
|
||||
|
||||
|
@ -32,6 +34,6 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
|
|||
itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView,
|
||||
ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS);
|
||||
|
||||
super.updateFromItem(localItem, dateFormat);
|
||||
super.updateFromItem(localItem, state, dateFormat);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
@ -10,12 +11,16 @@ import android.widget.TextView;
|
|||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
||||
|
||||
|
@ -24,6 +29,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
|||
public final TextView itemAdditionalDetailsView;
|
||||
public final TextView itemDurationView;
|
||||
public final View itemHandleView;
|
||||
public final AnimatedProgressBar itemProgressView;
|
||||
|
||||
LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
||||
super(infoItemBuilder, layoutId, parent);
|
||||
|
@ -33,6 +39,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
|||
itemAdditionalDetailsView = itemView.findViewById(R.id.itemAdditionalDetails);
|
||||
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
||||
itemHandleView = itemView.findViewById(R.id.itemHandle);
|
||||
itemProgressView = itemView.findViewById(R.id.itemProgressView);
|
||||
}
|
||||
|
||||
public LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
|
@ -40,7 +47,7 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
||||
public void updateFromItem(final LocalItem localItem, @Nullable final StreamStateEntity state, final DateFormat dateFormat) {
|
||||
if (!(localItem instanceof PlaylistStreamEntry)) return;
|
||||
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
|
||||
|
||||
|
@ -53,6 +60,13 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
|||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||
R.color.duration_background_color));
|
||||
itemDurationView.setVisibility(View.VISIBLE);
|
||||
if (state != null) {
|
||||
itemProgressView.setVisibility(View.VISIBLE);
|
||||
itemProgressView.setMax((int) item.duration);
|
||||
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
} else {
|
||||
itemProgressView.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
itemDurationView.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -79,6 +93,23 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
|
|||
itemHandleView.setOnTouchListener(getOnTouchListener(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(LocalItem localItem, @Nullable StreamStateEntity state) {
|
||||
if (!(localItem instanceof PlaylistStreamEntry)) return;
|
||||
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
|
||||
if (state != null && item.duration > 0) {
|
||||
itemProgressView.setMax((int) item.duration);
|
||||
if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
} else {
|
||||
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
AnimationUtils.animateView(itemProgressView, true, 500);
|
||||
}
|
||||
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||
AnimationUtils.animateView(itemProgressView, false, 500);
|
||||
}
|
||||
}
|
||||
|
||||
private View.OnTouchListener getOnTouchListener(final PlaylistStreamEntry item) {
|
||||
return (view, motionEvent) -> {
|
||||
view.performClick();
|
||||
|
|
|
@ -10,12 +10,16 @@ import android.widget.TextView;
|
|||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.views.AnimatedProgressBar;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/*
|
||||
* Created by Christian Schabesberger on 01.08.16.
|
||||
|
@ -45,6 +49,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
|||
public final TextView itemDurationView;
|
||||
@Nullable
|
||||
public final TextView itemAdditionalDetails;
|
||||
public final AnimatedProgressBar itemProgressView;
|
||||
|
||||
public LocalStatisticStreamItemHolder(LocalItemBuilder itemBuilder, ViewGroup parent) {
|
||||
this(itemBuilder, R.layout.list_stream_item, parent);
|
||||
|
@ -58,6 +63,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
|||
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
||||
itemDurationView = itemView.findViewById(R.id.itemDurationView);
|
||||
itemAdditionalDetails = itemView.findViewById(R.id.itemAdditionalDetails);
|
||||
itemProgressView = itemView.findViewById(R.id.itemProgressView);
|
||||
}
|
||||
|
||||
private String getStreamInfoDetailLine(final StreamStatisticsEntry entry,
|
||||
|
@ -70,7 +76,7 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
||||
public void updateFromItem(final LocalItem localItem, @Nullable final StreamStateEntity state, final DateFormat dateFormat) {
|
||||
if (!(localItem instanceof StreamStatisticsEntry)) return;
|
||||
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
|
||||
|
||||
|
@ -82,8 +88,16 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
|||
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
|
||||
R.color.duration_background_color));
|
||||
itemDurationView.setVisibility(View.VISIBLE);
|
||||
if (state != null) {
|
||||
itemProgressView.setVisibility(View.VISIBLE);
|
||||
itemProgressView.setMax((int) item.duration);
|
||||
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
} else {
|
||||
itemProgressView.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
itemDurationView.setVisibility(View.GONE);
|
||||
itemProgressView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (itemAdditionalDetails != null) {
|
||||
|
@ -108,4 +122,21 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
|
|||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(LocalItem localItem, @Nullable StreamStateEntity state) {
|
||||
if (!(localItem instanceof StreamStatisticsEntry)) return;
|
||||
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
|
||||
if (state != null && item.duration > 0) {
|
||||
itemProgressView.setMax((int) item.duration);
|
||||
if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
} else {
|
||||
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
|
||||
AnimationUtils.animateView(itemProgressView, true, 500);
|
||||
}
|
||||
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
|
||||
AnimationUtils.animateView(itemProgressView, false, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
|
||||
import java.text.DateFormat;
|
||||
|
@ -31,7 +33,7 @@ public abstract class PlaylistItemHolder extends LocalItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
||||
public void updateFromItem(final LocalItem localItem, @Nullable final StreamStateEntity state, final DateFormat dateFormat) {
|
||||
itemView.setOnClickListener(view -> {
|
||||
if (itemBuilder.getOnItemSelectedListener() != null) {
|
||||
itemBuilder.getOnItemSelectedListener().selected(localItem);
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package org.schabi.newpipe.local.holder;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
|
||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.local.LocalItemBuilder;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
|
@ -21,7 +23,7 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateFromItem(final LocalItem localItem, final DateFormat dateFormat) {
|
||||
public void updateFromItem(final LocalItem localItem, @Nullable final StreamStateEntity state, final DateFormat dateFormat) {
|
||||
if (!(localItem instanceof PlaylistRemoteEntity)) return;
|
||||
final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem;
|
||||
|
||||
|
@ -33,6 +35,6 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
|
|||
itemBuilder.displayImage(item.getThumbnailUrl(), itemThumbnailView,
|
||||
ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS);
|
||||
|
||||
super.updateFromItem(localItem, dateFormat);
|
||||
super.updateFromItem(localItem, state, dateFormat);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -23,7 +23,6 @@ import android.support.annotation.Nullable;
|
|||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
@ -48,10 +47,8 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|||
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.info_list.InfoListAdapter;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsExportService;
|
||||
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.FilePickerActivityHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
@ -131,6 +128,12 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
|||
subscriptionService = SubscriptionService.getInstance(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
infoListAdapter.dispose();
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||
|
@ -150,6 +153,8 @@ public class SubscriptionFragment extends BaseStateFragment<List<SubscriptionEnt
|
|||
}
|
||||
updateFlags = 0;
|
||||
}
|
||||
|
||||
itemsList.post(infoListAdapter::updateStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -150,6 +150,7 @@ public final class BackgroundPlayer extends Service {
|
|||
lockManager.releaseWifiAndCpu();
|
||||
}
|
||||
if (basePlayerImpl != null) {
|
||||
basePlayerImpl.savePlaybackState();
|
||||
basePlayerImpl.stopActivityBinding();
|
||||
basePlayerImpl.destroy();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,6 +248,12 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(newBase));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
playerImpl.savePlaybackState();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// State Saving
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
@ -583,7 +589,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
this.getPlaybackSpeed(),
|
||||
this.getPlaybackPitch(),
|
||||
this.getPlaybackSkipSilence(),
|
||||
this.getPlaybackQuality()
|
||||
this.getPlaybackQuality(),
|
||||
false
|
||||
);
|
||||
context.startService(intent);
|
||||
|
||||
|
@ -605,7 +612,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
this.getPlaybackSpeed(),
|
||||
this.getPlaybackPitch(),
|
||||
this.getPlaybackSkipSilence(),
|
||||
this.getPlaybackQuality()
|
||||
this.getPlaybackQuality(),
|
||||
false
|
||||
);
|
||||
context.startService(intent);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -542,6 +542,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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
public abstract class SparseArrayUtils {
|
||||
|
||||
public static <T> void shiftItemsDown(SparseArray<T> sparseArray, int lower, int upper) {
|
||||
for (int i = lower + 1; i <= upper; i++) {
|
||||
final T o = sparseArray.get(i);
|
||||
sparseArray.put(i - 1, o);
|
||||
sparseArray.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void shiftItemsUp(SparseArray<T> sparseArray, int lower, int upper) {
|
||||
for (int i = upper - 1; i >= lower; i--) {
|
||||
final T o = sparseArray.get(i);
|
||||
sparseArray.put(i + 1, o);
|
||||
sparseArray.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> int[] getKeys(SparseArray<T> sparseArray) {
|
||||
final int[] result = new int[sparseArray.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = sparseArray.keyAt(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
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);
|
||||
}
|
||||
|
||||
public synchronized void setProgressAnimated(int progress) {
|
||||
cancelAnimation();
|
||||
animation = new ProgressBarAnimation(this, getProgress(), progress);
|
||||
startAnimation(animation);
|
||||
}
|
||||
|
||||
private void cancelAnimation() {
|
||||
if (animation != null) {
|
||||
animation.cancel();
|
||||
animation = null;
|
||||
}
|
||||
clearAnimation();
|
||||
}
|
||||
|
||||
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.setProgress((int) value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,12 +1,14 @@
|
|||
<?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"
|
||||
android:focusableInTouchMode="true"
|
||||
android:orientation="horizontal">
|
||||
tools:ignore="RtlHardcoded"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
android:id="@+id/detail_main_content"
|
||||
|
@ -19,6 +21,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 +70,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 +87,17 @@
|
|||
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"
|
||||
|
@ -103,10 +106,48 @@
|
|||
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"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="12:38"
|
||||
tools:visibility="visible" />
|
||||
</FrameLayout>
|
||||
|
||||
</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"
|
||||
app:layout_scrollFlags="scroll"
|
||||
tools:max="100"
|
||||
tools:progress="40"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!-- CONTENT -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/detail_content_root_layout"
|
||||
|
@ -133,11 +174,10 @@
|
|||
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"
|
||||
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
|
||||
|
@ -147,7 +187,7 @@
|
|||
android:layout_gravity="center_vertical|right"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:src="@drawable/arrow_down"
|
||||
tools:ignore="ContentDescription,RtlHardcoded" />
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
@ -179,9 +219,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 +231,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 +241,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"
|
||||
|
@ -213,8 +253,7 @@
|
|||
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" />
|
||||
android:src="@drawable/buddy" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detail_uploader_text_view"
|
||||
|
@ -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"
|
||||
|
|
|
@ -1,489 +1,529 @@
|
|||
<?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"
|
||||
app:layout_scrollFlags="scroll"
|
||||
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>
|
||||
|
|
|
@ -66,4 +66,16 @@
|
|||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
tools:text="Uploader"/>
|
||||
|
||||
<org.schabi.newpipe.views.AnimatedProgressBar
|
||||
android:id="@+id/itemProgressView"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:progressDrawable="?progress_horizontal_drawable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:layout_alignStart="@id/itemThumbnailView"
|
||||
android:layout_alignEnd="@id/itemThumbnailView"
|
||||
android:layout_below="@id/itemThumbnailView"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -79,4 +79,16 @@
|
|||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
tools:text="2 years ago • 10M views"/>
|
||||
|
||||
<org.schabi.newpipe.views.AnimatedProgressBar
|
||||
android:id="@+id/itemProgressView"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:progressDrawable="?progress_horizontal_drawable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:layout_alignStart="@id/itemThumbnailView"
|
||||
android:layout_alignEnd="@id/itemThumbnailView"
|
||||
android:layout_below="@id/itemThumbnailView"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -69,4 +69,16 @@
|
|||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
tools:text="Uploader" />
|
||||
|
||||
<org.schabi.newpipe.views.AnimatedProgressBar
|
||||
android:id="@+id/itemProgressView"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:progressDrawable="?progress_horizontal_drawable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:layout_alignStart="@id/itemThumbnailView"
|
||||
android:layout_alignEnd="@id/itemThumbnailView"
|
||||
android:layout_below="@id/itemThumbnailView"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -81,4 +81,15 @@
|
|||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
tools:text="Uploader" />
|
||||
|
||||
<org.schabi.newpipe.views.AnimatedProgressBar
|
||||
android:id="@+id/itemProgressView"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:progressDrawable="?progress_horizontal_drawable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:layout_alignStart="@id/itemThumbnailView"
|
||||
android:layout_alignEnd="@id/itemThumbnailView"
|
||||
android:layout_below="@id/itemThumbnailView"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -83,4 +83,15 @@
|
|||
android:textSize="@dimen/video_item_search_uploader_text_size"
|
||||
tools:text="Uploader" />
|
||||
|
||||
<org.schabi.newpipe.views.AnimatedProgressBar
|
||||
android:id="@+id/itemProgressView"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:progressDrawable="?progress_horizontal_drawable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:layout_alignStart="@id/itemThumbnailView"
|
||||
android:layout_alignEnd="@id/itemThumbnailView"
|
||||
android:layout_below="@id/itemThumbnailView"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -14,7 +14,7 @@
|
|||
<dimen name="video_item_search_thumbnail_image_width">142dp</dimen>
|
||||
<dimen name="video_item_search_thumbnail_image_height">80dp</dimen>
|
||||
<!-- Calculated: 2*video_item_search_padding + video_item_search_thumbnail_image_height -->
|
||||
<dimen name="video_item_search_height">104dp</dimen>
|
||||
<dimen name="video_item_search_height">106dp</dimen>
|
||||
<!-- Paddings & Margins -->
|
||||
<dimen name="video_item_search_image_right_margin">10dp</dimen>
|
||||
<dimen name="video_item_search_duration_vertical_padding">1sp</dimen>
|
||||
|
|
|
@ -167,7 +167,12 @@
|
|||
<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="enable_playback_state_lists_title">Позиции в списках</string>
|
||||
<string name="enable_playback_state_lists_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>
|
||||
|
|
|
@ -135,8 +135,13 @@
|
|||
<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_summary">Запам\'ятовувати переглянуті відео</string>
|
||||
<string name="enable_playback_resume_title">Продовживати перегляд</string>
|
||||
<string name="enable_playback_resume_summary">Відновлювати останню позицію</string>
|
||||
<string name="enable_playback_state_lists_title">Позиції у списках</string>
|
||||
<string name="enable_playback_state_lists_summary">Відображати індикатори позицій переглядів у списках</string>
|
||||
<string name="settings_category_clear_data_title">Очистити дані</string>
|
||||
<string name="enable_watch_history_summary">Вести облік перегляду відеозаписів</string>
|
||||
<string name="enable_watch_history_title">Історія переглядів</string>
|
||||
<string name="resume_on_audio_focus_gain_title">Відновити програвання при поверненні з тла</string>
|
||||
<string name="resume_on_audio_focus_gain_summary">Продовжувати програвання після переривань (наприклад, телефонних дзвінків)</string>
|
||||
<string name="show_hold_to_append_title">Показати пораду \"Утримуй, щоб додати\"</string>
|
||||
|
|
|
@ -44,6 +44,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"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<dimen name="video_item_grid_thumbnail_image_width">164dp</dimen>
|
||||
<dimen name="video_item_grid_thumbnail_image_height">92dp</dimen>
|
||||
<!-- Calculated: 2*video_item_search_padding + video_item_search_thumbnail_image_height -->
|
||||
<dimen name="video_item_search_height">94dp</dimen>
|
||||
<dimen name="video_item_search_height">96dp</dimen>
|
||||
<!-- Paddings & Margins -->
|
||||
<dimen name="video_item_search_padding">12dp</dimen>
|
||||
<dimen name="video_item_search_image_right_margin">6dp</dimen>
|
||||
|
|
|
@ -150,6 +150,8 @@
|
|||
<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="enable_playback_state_lists_key" translatable="false">enable_playback_state_lists</string>
|
||||
|
||||
<string name="import_data">import_data</string>
|
||||
<string name="export_data">export_data</string>
|
||||
|
|
|
@ -89,7 +89,12 @@
|
|||
<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 & 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="enable_playback_state_lists_title">Positions in lists</string>
|
||||
<string name="enable_playback_state_lists_summary">Show playback position indicators in lists</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>
|
||||
|
|
|
@ -66,6 +66,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>
|
||||
|
@ -130,6 +131,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>
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -1,40 +1,62 @@
|
|||
<?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
|
||||
android:defaultValue="true"
|
||||
android:dependency="@string/enable_playback_resume_key"
|
||||
android:key="@string/enable_playback_state_lists_key"
|
||||
android:summary="@string/enable_playback_state_lists_summary"
|
||||
android:title="@string/enable_playback_state_lists_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>
|
Loading…
Reference in New Issue