mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-01-11 18:00:32 +00:00
Refactor adapter
This commit is contained in:
parent
c7cd9e86ac
commit
273f731dd5
@ -1,16 +1,14 @@
|
|||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.support.annotation.NonNull;
|
||||||
import android.content.SharedPreferences;
|
import android.support.annotation.Nullable;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||||
import org.schabi.newpipe.extractor.InfoItem;
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
@ -29,16 +27,11 @@ import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
|
|||||||
import org.schabi.newpipe.info_list.holder.StreamGridInfoItemHolder;
|
import org.schabi.newpipe.info_list.holder.StreamGridInfoItemHolder;
|
||||||
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
|
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
|
||||||
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
|
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
|
||||||
import org.schabi.newpipe.util.FallbackViewHolder;
|
import org.schabi.newpipe.util.FallbackViewHolder;
|
||||||
import org.schabi.newpipe.util.OnClickGesture;
|
import org.schabi.newpipe.util.OnClickGesture;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
@ -60,7 +53,7 @@ import io.reactivex.disposables.CompositeDisposable;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* 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 String TAG = InfoListAdapter.class.getSimpleName();
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
@ -80,10 +73,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
private static final int COMMENT_HOLDER_TYPE = 0x401;
|
private static final int COMMENT_HOLDER_TYPE = 0x401;
|
||||||
|
|
||||||
private final InfoItemBuilder infoItemBuilder;
|
private final InfoItemBuilder infoItemBuilder;
|
||||||
private final HistoryRecordManager historyRecordManager;
|
|
||||||
private final ArrayList<InfoItem> infoItemList;
|
private final ArrayList<InfoItem> infoItemList;
|
||||||
private final ArrayList<StreamStateEntity> states;
|
|
||||||
private final CompositeDisposable stateLoaders;
|
|
||||||
private boolean useMiniVariant = false;
|
private boolean useMiniVariant = false;
|
||||||
private boolean useGridVariant = false;
|
private boolean useGridVariant = false;
|
||||||
private boolean showFooter = false;
|
private boolean showFooter = false;
|
||||||
@ -100,11 +90,9 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
}
|
}
|
||||||
|
|
||||||
public InfoListAdapter(Activity a) {
|
public InfoListAdapter(Activity a) {
|
||||||
|
super(a.getApplicationContext());
|
||||||
infoItemBuilder = new InfoItemBuilder(a);
|
infoItemBuilder = new InfoItemBuilder(a);
|
||||||
historyRecordManager = new HistoryRecordManager(a);
|
|
||||||
infoItemList = new ArrayList<>();
|
infoItemList = new ArrayList<>();
|
||||||
states = new ArrayList<>();
|
|
||||||
stateLoaders = new CompositeDisposable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnStreamSelectedListener(OnClickGesture<StreamInfoItem> listener) {
|
public void setOnStreamSelectedListener(OnClickGesture<StreamInfoItem> listener) {
|
||||||
@ -131,107 +119,64 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
this.useGridVariant = useGridVariant;
|
this.useGridVariant = useGridVariant;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addInfoItemList(final List<InfoItem> data) {
|
public void addInfoItemList(@Nullable final List<InfoItem> data) {
|
||||||
if (isPlaybackStatesVisible()) {
|
|
||||||
stateLoaders.add(
|
|
||||||
historyRecordManager.loadStreamStateBatch(data)
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(streamStateEntities -> {
|
|
||||||
addInfoItemList(data, streamStateEntities);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
final ArrayList<StreamStateEntity> states = new ArrayList<>(data.size());
|
|
||||||
for (int i = data.size(); i > 0; i--) states.add(null);
|
|
||||||
addInfoItemList(data, states);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addInfoItemList(List<InfoItem> data, List<StreamStateEntity> statesEntities) {
|
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
if (DEBUG) {
|
loadStates(data, infoItemList.size(), () -> addInfoItemListImpl(data));
|
||||||
Log.d(TAG, "addInfoItemList() before > infoItemList.size() = " + infoItemList.size() + ", data.size() = " + data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
int offsetStart = sizeConsideringHeaderOffset();
|
|
||||||
infoItemList.addAll(data);
|
|
||||||
states.addAll(statesEntities);
|
|
||||||
|
|
||||||
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(InfoItem data) {
|
private void addInfoItemListImpl(@NonNull List<InfoItem> data) {
|
||||||
if (isPlaybackStatesVisible()) {
|
if (DEBUG) {
|
||||||
stateLoaders.add(
|
Log.d(TAG, "addInfoItemList() before > infoItemList.size() = " + infoItemList.size() + ", data.size() = " + data.size());
|
||||||
historyRecordManager.loadStreamState(data)
|
}
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(streamStateEntity -> {
|
int offsetStart = sizeConsideringHeaderOffset();
|
||||||
addInfoItem(data, streamStateEntity[0]);
|
infoItemList.addAll(data);
|
||||||
})
|
|
||||||
);
|
if (DEBUG) {
|
||||||
} else {
|
Log.d(TAG, "addInfoItemList() after > offsetStart = " + offsetStart + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
||||||
addInfoItem(data, null);
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addInfoItem(InfoItem data, StreamStateEntity state) {
|
public void addInfoItem(@Nullable InfoItem data) {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
if (DEBUG) {
|
loadState(data, infoItemList.size(), () -> addInfoItemImpl(data));
|
||||||
Log.d(TAG, "addInfoItem() before > infoItemList.size() = " + infoItemList.size() + ", thread = " + Thread.currentThread());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int positionInserted = sizeConsideringHeaderOffset();
|
private void addInfoItemImpl(@NonNull InfoItem data) {
|
||||||
infoItemList.add(data);
|
if (DEBUG) {
|
||||||
states.add(state);
|
Log.d(TAG, "addInfoItem() before > infoItemList.size() = " + infoItemList.size() + ", thread = " + Thread.currentThread());
|
||||||
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
int positionInserted = sizeConsideringHeaderOffset();
|
||||||
Log.d(TAG, "addInfoItem() after > position = " + positionInserted + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
infoItemList.add(data);
|
||||||
}
|
|
||||||
notifyItemInserted(positionInserted);
|
|
||||||
|
|
||||||
if (footer != null && showFooter) {
|
if (DEBUG) {
|
||||||
int footerNow = sizeConsideringHeaderOffset();
|
Log.d(TAG, "addInfoItem() after > position = " + positionInserted + ", infoItemList.size() = " + infoItemList.size() + ", header = " + header + ", footer = " + footer + ", showFooter = " + showFooter);
|
||||||
notifyItemMoved(positionInserted, footerNow);
|
}
|
||||||
|
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() {
|
public void updateStates() {
|
||||||
if (infoItemList.isEmpty()) {
|
if (!infoItemList.isEmpty()) {
|
||||||
return;
|
updateAllStates(infoItemList);
|
||||||
}
|
|
||||||
if (isPlaybackStatesVisible()) {
|
|
||||||
stateLoaders.add(
|
|
||||||
historyRecordManager.loadStreamStateBatch(infoItemList)
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe((streamStateEntities) -> {
|
|
||||||
if (streamStateEntities.size() == states.size()) {
|
|
||||||
for (int i = 0; i < states.size(); i++) {
|
|
||||||
final StreamStateEntity newState = streamStateEntities.get(i);
|
|
||||||
if (!Objects.equals(states.get(i), newState)) {
|
|
||||||
states.set(i, newState);
|
|
||||||
notifyItemChanged(header == null ? i : i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//oops, something is wrong
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +185,7 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
infoItemList.clear();
|
infoItemList.clear();
|
||||||
states.clear();
|
clearStates();
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,8 +259,9 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
Log.d(TAG, "onCreateViewHolder() called with: parent = [" + parent + "], type = [" + type + "]");
|
Log.d(TAG, "onCreateViewHolder() called with: parent = [" + parent + "], type = [" + type + "]");
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -352,13 +298,13 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 (DEBUG) Log.d(TAG, "onBindViewHolder() called with: holder = [" + holder.getClass().getSimpleName() + "], position = [" + position + "]");
|
||||||
if (holder instanceof InfoItemHolder) {
|
if (holder instanceof InfoItemHolder) {
|
||||||
// If header isn't null, offset the items by -1
|
// If header isn't null, offset the items by -1
|
||||||
if (header != null) position--;
|
if (header != null) position--;
|
||||||
|
|
||||||
((InfoItemHolder) holder).updateFromItem(infoItemList.get(position), states.get(position));
|
((InfoItemHolder) holder).updateFromItem(infoItemList.get(position), getState(position));
|
||||||
} else if (holder instanceof HFHolder && position == 0 && header != null) {
|
} else if (holder instanceof HFHolder && position == 0 && header != null) {
|
||||||
((HFHolder) holder).view = header;
|
((HFHolder) holder).view = header;
|
||||||
} else if (holder instanceof HFHolder && position == sizeConsideringHeaderOffset() && footer != null && showFooter) {
|
} else if (holder instanceof HFHolder && position == sizeConsideringHeaderOffset() && footer != null && showFooter) {
|
||||||
@ -366,6 +312,11 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onItemStateChanged(int position, @Nullable StreamStateEntity state) {
|
||||||
|
notifyItemChanged(header == null ? position : position + 1, state);
|
||||||
|
}
|
||||||
|
|
||||||
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
||||||
return new GridLayoutManager.SpanSizeLookup() {
|
return new GridLayoutManager.SpanSizeLookup() {
|
||||||
@Override
|
@Override
|
||||||
@ -375,16 +326,4 @@ public class InfoListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
|
||||||
stateLoaders.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPlaybackStatesVisible() {
|
|
||||||
final Context context = infoItemBuilder.getContext();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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,19 +1,17 @@
|
|||||||
package org.schabi.newpipe.local;
|
package org.schabi.newpipe.local;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.support.annotation.NonNull;
|
||||||
import android.content.SharedPreferences;
|
import android.support.annotation.Nullable;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.database.LocalItem;
|
import org.schabi.newpipe.database.LocalItem;
|
||||||
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
import org.schabi.newpipe.info_list.StateObjectsListAdapter;
|
||||||
import org.schabi.newpipe.local.holder.LocalItemHolder;
|
import org.schabi.newpipe.local.holder.LocalItemHolder;
|
||||||
import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder;
|
import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder;
|
||||||
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
|
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
|
||||||
@ -30,10 +28,6 @@ import org.schabi.newpipe.util.OnClickGesture;
|
|||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
@ -55,7 +49,7 @@ import io.reactivex.disposables.CompositeDisposable;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* 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 String TAG = LocalItemListAdapter.class.getSimpleName();
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
@ -73,10 +67,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
private static final int REMOTE_PLAYLIST_GRID_HOLDER_TYPE = 0x2004;
|
private static final int REMOTE_PLAYLIST_GRID_HOLDER_TYPE = 0x2004;
|
||||||
|
|
||||||
private final LocalItemBuilder localItemBuilder;
|
private final LocalItemBuilder localItemBuilder;
|
||||||
private final HistoryRecordManager historyRecordManager;
|
|
||||||
private final ArrayList<LocalItem> localItems;
|
private final ArrayList<LocalItem> localItems;
|
||||||
private final ArrayList<StreamStateEntity> states;
|
|
||||||
private final CompositeDisposable stateLoaders;
|
|
||||||
private final DateFormat dateFormat;
|
private final DateFormat dateFormat;
|
||||||
|
|
||||||
private boolean showFooter = false;
|
private boolean showFooter = false;
|
||||||
@ -85,13 +76,11 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
private View footer = null;
|
private View footer = null;
|
||||||
|
|
||||||
public LocalItemListAdapter(Activity activity) {
|
public LocalItemListAdapter(Activity activity) {
|
||||||
|
super(activity.getApplicationContext());
|
||||||
localItemBuilder = new LocalItemBuilder(activity);
|
localItemBuilder = new LocalItemBuilder(activity);
|
||||||
historyRecordManager = new HistoryRecordManager(activity);
|
|
||||||
localItems = new ArrayList<>();
|
localItems = new ArrayList<>();
|
||||||
dateFormat = DateFormat.getDateInstance(DateFormat.SHORT,
|
dateFormat = DateFormat.getDateInstance(DateFormat.SHORT,
|
||||||
Localization.getPreferredLocale(activity));
|
Localization.getPreferredLocale(activity));
|
||||||
states = new ArrayList<>();
|
|
||||||
stateLoaders = new CompositeDisposable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedListener(OnClickGesture<LocalItem> listener) {
|
public void setSelectedListener(OnClickGesture<LocalItem> listener) {
|
||||||
@ -102,76 +91,49 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
localItemBuilder.setOnItemSelectedListener(null);
|
localItemBuilder.setOnItemSelectedListener(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addItems(List<? extends LocalItem> data) {
|
public void addItems(@Nullable List<? extends LocalItem> data) {
|
||||||
if (isPlaybackStatesVisible()) {
|
if (data != null) {
|
||||||
stateLoaders.add(
|
loadStatesForLocal(data, localItems.size(), () -> addItemsImpl(data));
|
||||||
historyRecordManager.loadLocalStreamStateBatch(data)
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(streamStateEntities ->
|
|
||||||
addItems(data, streamStateEntities))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
final ArrayList<StreamStateEntity> states = new ArrayList<>(data.size());
|
|
||||||
for (int i = data.size(); i > 0; i--) states.add(null);
|
|
||||||
addItems(data, states);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addItems(List<? extends LocalItem> data, List<StreamStateEntity> streamStates) {
|
private void addItemsImpl(@NonNull List<? extends LocalItem> data) {
|
||||||
if (data != null) {
|
if (DEBUG) {
|
||||||
if (DEBUG) {
|
Log.d(TAG, "addItems() before > localItems.size() = " +
|
||||||
Log.d(TAG, "addItems() before > localItems.size() = " +
|
localItems.size() + ", data.size() = " + data.size());
|
||||||
localItems.size() + ", data.size() = " + data.size());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int offsetStart = sizeConsideringHeader();
|
int offsetStart = sizeConsideringHeader();
|
||||||
localItems.addAll(data);
|
localItems.addAll(data);
|
||||||
states.addAll(streamStates);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "addItems() after > offsetStart = " + offsetStart +
|
Log.d(TAG, "addItems() after > offsetStart = " + offsetStart +
|
||||||
", localItems.size() = " + localItems.size() +
|
", localItems.size() = " + localItems.size() +
|
||||||
", header = " + header + ", footer = " + footer +
|
", header = " + header + ", footer = " + footer +
|
||||||
", showFooter = " + showFooter);
|
", showFooter = " + showFooter);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyItemRangeInserted(offsetStart, data.size());
|
notifyItemRangeInserted(offsetStart, data.size());
|
||||||
|
|
||||||
if (footer != null && showFooter) {
|
if (footer != null && showFooter) {
|
||||||
int footerNow = sizeConsideringHeader();
|
int footerNow = sizeConsideringHeader();
|
||||||
notifyItemMoved(offsetStart, footerNow);
|
notifyItemMoved(offsetStart, footerNow);
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "addItems() footer from " + offsetStart +
|
if (DEBUG) Log.d(TAG, "addItems() footer from " + offsetStart +
|
||||||
" to " + footerNow);
|
" to " + footerNow);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateStates() {
|
public void updateStates() {
|
||||||
if (localItems.isEmpty() || !isPlaybackStatesVisible()) return;
|
if (!localItems.isEmpty()) {
|
||||||
stateLoaders.add(
|
updateAllLocalStates(localItems);
|
||||||
historyRecordManager.loadLocalStreamStateBatch(localItems)
|
}
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe((streamStateEntities) -> {
|
|
||||||
if (streamStateEntities.size() == states.size()) {
|
|
||||||
for (int i = 0; i < states.size(); i++) {
|
|
||||||
final StreamStateEntity newState = streamStateEntities.get(i);
|
|
||||||
if (!Objects.equals(states.get(i), newState)) {
|
|
||||||
states.set(i, newState);
|
|
||||||
notifyItemChanged(header == null ? i : i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//oops, something is wrong
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeItem(final LocalItem data) {
|
public void removeItem(final LocalItem data) {
|
||||||
final int index = localItems.indexOf(data);
|
final int index = localItems.indexOf(data);
|
||||||
|
|
||||||
localItems.remove(index);
|
localItems.remove(index);
|
||||||
|
removeState(index);
|
||||||
notifyItemRemoved(index + (header != null ? 1 : 0));
|
notifyItemRemoved(index + (header != null ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +145,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
if (actualFrom >= localItems.size() || actualTo >= localItems.size()) return false;
|
if (actualFrom >= localItems.size() || actualTo >= localItems.size()) return false;
|
||||||
|
|
||||||
localItems.add(actualTo, localItems.remove(actualFrom));
|
localItems.add(actualTo, localItems.remove(actualFrom));
|
||||||
states.add(actualTo, states.remove(actualFrom));
|
moveState(actualFrom, actualTo);
|
||||||
notifyItemMoved(fromAdapterPosition, toAdapterPosition);
|
notifyItemMoved(fromAdapterPosition, toAdapterPosition);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -193,6 +155,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
localItems.clear();
|
localItems.clear();
|
||||||
|
clearStates();
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +276,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
// If header isn't null, offset the items by -1
|
// If header isn't null, offset the items by -1
|
||||||
if (header != null) position--;
|
if (header != null) position--;
|
||||||
|
|
||||||
((LocalItemHolder) holder).updateFromItem(localItems.get(position), states.get(position), dateFormat);
|
((LocalItemHolder) holder).updateFromItem(localItems.get(position), getState(position), dateFormat);
|
||||||
} else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) {
|
} else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) {
|
||||||
((HeaderFooterHolder) holder).view = header;
|
((HeaderFooterHolder) holder).view = header;
|
||||||
} else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader()
|
} else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader()
|
||||||
@ -322,6 +285,11 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onItemStateChanged(int position, @Nullable StreamStateEntity state) {
|
||||||
|
notifyItemChanged(header == null ? position : position + 1, state);
|
||||||
|
}
|
||||||
|
|
||||||
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
|
||||||
return new GridLayoutManager.SpanSizeLookup() {
|
return new GridLayoutManager.SpanSizeLookup() {
|
||||||
@Override
|
@Override
|
||||||
@ -331,16 +299,4 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
|
||||||
stateLoaders.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isPlaybackStatesVisible() {
|
|
||||||
final Context context = localItemBuilder.getContext();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user