fix conflict
This commit is contained in:
commit
665b9087b1
9 changed files with 462 additions and 224 deletions
|
@ -73,6 +73,31 @@ public class Downloader implements org.schabi.newpipe.extractor.Downloader {
|
||||||
mCookies = cookies;
|
mCookies = cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the content that the url is pointing by firing a HEAD request.
|
||||||
|
*
|
||||||
|
* @param url an url pointing to the content
|
||||||
|
* @return the size of the content, in bytes
|
||||||
|
*/
|
||||||
|
public long getContentLength(String url) throws IOException {
|
||||||
|
Response response = null;
|
||||||
|
try {
|
||||||
|
final Request request = new Request.Builder()
|
||||||
|
.head().url(url)
|
||||||
|
.addHeader("User-Agent", USER_AGENT)
|
||||||
|
.build();
|
||||||
|
response = client.newCall(request).execute();
|
||||||
|
|
||||||
|
return Long.parseLong(response.header("Content-Length"));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IOException("Invalid content length", e);
|
||||||
|
} finally {
|
||||||
|
if (response != null) {
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download the text file at the supplied URL as in download(String),
|
* Download the text file at the supplied URL as in download(String),
|
||||||
* but set the HTTP header field "Accept-Language" to the supplied string.
|
* but set the HTTP header field "Accept-Language" to the supplied string.
|
||||||
|
|
|
@ -1,56 +1,61 @@
|
||||||
package org.schabi.newpipe.download;
|
package org.schabi.newpipe.download;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.IdRes;
|
import android.support.annotation.IdRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.RadioButton;
|
import android.widget.RadioButton;
|
||||||
import android.widget.RadioGroup;
|
import android.widget.RadioGroup;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
|
||||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||||
|
import org.schabi.newpipe.extractor.stream.Stream;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.fragments.detail.SpinnerToolbarAdapter;
|
|
||||||
import org.schabi.newpipe.settings.NewPipeSettings;
|
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||||
import org.schabi.newpipe.util.FilenameUtils;
|
import org.schabi.newpipe.util.FilenameUtils;
|
||||||
import org.schabi.newpipe.util.ListHelper;
|
import org.schabi.newpipe.util.ListHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
import org.schabi.newpipe.util.StreamItemAdapter;
|
||||||
|
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import icepick.Icepick;
|
||||||
|
import icepick.State;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
|
|
||||||
public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
|
public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
|
||||||
private static final String TAG = "DialogFragment";
|
private static final String TAG = "DialogFragment";
|
||||||
private static final boolean DEBUG = MainActivity.DEBUG;
|
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||||
|
|
||||||
private static final String INFO_KEY = "info_key";
|
@State protected StreamInfo currentInfo;
|
||||||
private static final String SORTED_VIDEOS_LIST_KEY = "sorted_videos_list_key";
|
@State protected StreamSizeWrapper<AudioStream> wrappedAudioStreams = StreamSizeWrapper.empty();
|
||||||
private static final String SELECTED_VIDEO_KEY = "selected_video_key";
|
@State protected StreamSizeWrapper<VideoStream> wrappedVideoStreams = StreamSizeWrapper.empty();
|
||||||
private static final String SELECTED_AUDIO_KEY = "selected_audio_key";
|
@State protected int selectedVideoIndex = 0;
|
||||||
|
@State protected int selectedAudioIndex = 0;
|
||||||
|
|
||||||
private StreamInfo currentInfo;
|
private StreamItemAdapter<AudioStream> audioStreamsAdapter;
|
||||||
private ArrayList<VideoStream> sortedStreamVideosList;
|
private StreamItemAdapter<VideoStream> videoStreamsAdapter;
|
||||||
private int selectedVideoIndex;
|
|
||||||
private int selectedAudioIndex;
|
private CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
|
||||||
private EditText nameEditText;
|
private EditText nameEditText;
|
||||||
private Spinner streamsSpinner;
|
private Spinner streamsSpinner;
|
||||||
|
@ -58,17 +63,50 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
private TextView threadsCountTextView;
|
private TextView threadsCountTextView;
|
||||||
private SeekBar threadsSeekBar;
|
private SeekBar threadsSeekBar;
|
||||||
|
|
||||||
public static DownloadDialog newInstance(StreamInfo info, ArrayList<VideoStream> sortedStreamVideosList, int selectedVideoIndex) {
|
public static DownloadDialog newInstance(StreamInfo info) {
|
||||||
DownloadDialog dialog = new DownloadDialog();
|
DownloadDialog dialog = new DownloadDialog();
|
||||||
dialog.setInfo(info, sortedStreamVideosList, selectedVideoIndex);
|
dialog.setInfo(info);
|
||||||
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
|
|
||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInfo(StreamInfo info, ArrayList<VideoStream> sortedStreamVideosList, int selectedVideoIndex) {
|
public static DownloadDialog newInstance(Context context, StreamInfo info) {
|
||||||
|
final ArrayList<VideoStream> streamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context,
|
||||||
|
info.getVideoStreams(), info.getVideoOnlyStreams(), false));
|
||||||
|
final int selectedStreamIndex = ListHelper.getDefaultResolutionIndex(context, streamsList);
|
||||||
|
|
||||||
|
final DownloadDialog instance = newInstance(info);
|
||||||
|
instance.setVideoStreams(streamsList);
|
||||||
|
instance.setSelectedVideoStream(selectedStreamIndex);
|
||||||
|
instance.setAudioStreams(info.getAudioStreams());
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInfo(StreamInfo info) {
|
||||||
this.currentInfo = info;
|
this.currentInfo = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAudioStreams(List<AudioStream> audioStreams) {
|
||||||
|
setAudioStreams(new StreamSizeWrapper<>(audioStreams));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAudioStreams(StreamSizeWrapper<AudioStream> wrappedAudioStreams) {
|
||||||
|
this.wrappedAudioStreams = wrappedAudioStreams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoStreams(List<VideoStream> videoStreams) {
|
||||||
|
setVideoStreams(new StreamSizeWrapper<>(videoStreams));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoStreams(StreamSizeWrapper<VideoStream> wrappedVideoStreams) {
|
||||||
|
this.wrappedVideoStreams = wrappedVideoStreams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedVideoStream(int selectedVideoIndex) {
|
||||||
this.selectedVideoIndex = selectedVideoIndex;
|
this.selectedVideoIndex = selectedVideoIndex;
|
||||||
this.sortedStreamVideosList = sortedStreamVideosList;
|
}
|
||||||
|
|
||||||
|
public void setSelectedAudioStream(int selectedAudioIndex) {
|
||||||
|
this.selectedAudioIndex = selectedAudioIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -84,28 +122,21 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(getContext()));
|
||||||
Serializable serial = savedInstanceState.getSerializable(INFO_KEY);
|
Icepick.restoreInstanceState(this, savedInstanceState);
|
||||||
if (serial instanceof StreamInfo) currentInfo = (StreamInfo) serial;
|
|
||||||
|
|
||||||
serial = savedInstanceState.getSerializable(SORTED_VIDEOS_LIST_KEY);
|
this.videoStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedVideoStreams, true);
|
||||||
if (serial instanceof ArrayList) { //noinspection unchecked
|
this.audioStreamsAdapter = new StreamItemAdapter<>(getContext(), wrappedAudioStreams);
|
||||||
sortedStreamVideosList = (ArrayList<VideoStream>) serial;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedVideoIndex = savedInstanceState.getInt(SELECTED_VIDEO_KEY, 0);
|
|
||||||
selectedAudioIndex = savedInstanceState.getInt(SELECTED_AUDIO_KEY, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]");
|
||||||
return inflater.inflate(R.layout.dialog_url, container);
|
return inflater.inflate(R.layout.download_dialog, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
nameEditText = view.findViewById(R.id.file_name);
|
nameEditText = view.findViewById(R.id.file_name);
|
||||||
nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName()));
|
nameEditText.setText(FilenameUtils.createFilename(getContext(), currentInfo.getName()));
|
||||||
|
@ -116,12 +147,12 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
|
|
||||||
threadsCountTextView = view.findViewById(R.id.threads_count);
|
threadsCountTextView = view.findViewById(R.id.threads_count);
|
||||||
threadsSeekBar = view.findViewById(R.id.threads);
|
threadsSeekBar = view.findViewById(R.id.threads);
|
||||||
|
|
||||||
radioVideoAudioGroup = view.findViewById(R.id.video_audio_group);
|
radioVideoAudioGroup = view.findViewById(R.id.video_audio_group);
|
||||||
radioVideoAudioGroup.setOnCheckedChangeListener(this);
|
radioVideoAudioGroup.setOnCheckedChangeListener(this);
|
||||||
|
|
||||||
initToolbar(view.<Toolbar>findViewById(R.id.toolbar));
|
initToolbar(view.findViewById(R.id.toolbar));
|
||||||
checkDownloadOptions(view);
|
setupDownloadOptions();
|
||||||
setupVideoSpinner(sortedStreamVideosList, streamsSpinner);
|
|
||||||
|
|
||||||
int def = 3;
|
int def = 3;
|
||||||
threadsCountTextView.setText(String.valueOf(def));
|
threadsCountTextView.setText(String.valueOf(def));
|
||||||
|
@ -141,15 +172,35 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
public void onStopTrackingTouch(SeekBar p1) {
|
public void onStopTrackingTouch(SeekBar p1) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fetchStreamsSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchStreamsSize() {
|
||||||
|
disposables.clear();
|
||||||
|
|
||||||
|
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedVideoStreams).subscribe(result -> {
|
||||||
|
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.video_button) {
|
||||||
|
setupVideoSpinner();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
disposables.add(StreamSizeWrapper.fetchSizeForWrapper(wrappedAudioStreams).subscribe(result -> {
|
||||||
|
if (radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button) {
|
||||||
|
setupAudioSpinner();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
disposables.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putSerializable(INFO_KEY, currentInfo);
|
Icepick.saveInstanceState(this, outState);
|
||||||
outState.putSerializable(SORTED_VIDEOS_LIST_KEY, sortedStreamVideosList);
|
|
||||||
outState.putInt(SELECTED_VIDEO_KEY, selectedVideoIndex);
|
|
||||||
outState.putInt(SELECTED_AUDIO_KEY, selectedAudioIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -161,39 +212,31 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
toolbar.setTitle(R.string.download_dialog_title);
|
toolbar.setTitle(R.string.download_dialog_title);
|
||||||
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(getActivity()) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(getActivity()) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
||||||
toolbar.inflateMenu(R.menu.dialog_url);
|
toolbar.inflateMenu(R.menu.dialog_url);
|
||||||
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
toolbar.setNavigationOnClickListener(v -> getDialog().dismiss());
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
getDialog().dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
|
toolbar.setOnMenuItemClickListener(item -> {
|
||||||
@Override
|
if (item.getItemId() == R.id.okay) {
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
downloadSelected();
|
||||||
if (item.getItemId() == R.id.okay) {
|
return true;
|
||||||
downloadSelected();
|
|
||||||
return true;
|
|
||||||
} else return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupAudioSpinner(final List<AudioStream> audioStreams, Spinner spinner) {
|
private void setupAudioSpinner() {
|
||||||
String[] items = new String[audioStreams.size()];
|
if (getContext() == null) return;
|
||||||
for (int i = 0; i < audioStreams.size(); i++) {
|
|
||||||
AudioStream audioStream = audioStreams.get(i);
|
|
||||||
items[i] = audioStream.getFormat().getName() + " " + audioStream.getAverageBitrate() + "kbps";
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, items);
|
streamsSpinner.setAdapter(audioStreamsAdapter);
|
||||||
spinner.setAdapter(itemAdapter);
|
streamsSpinner.setSelection(selectedAudioIndex);
|
||||||
spinner.setSelection(selectedAudioIndex);
|
setRadioButtonsState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setupVideoSpinner(final List<VideoStream> videoStreams, Spinner spinner) {
|
private void setupVideoSpinner() {
|
||||||
spinner.setAdapter(new SpinnerToolbarAdapter(getContext(), videoStreams, true));
|
if (getContext() == null) return;
|
||||||
spinner.setSelection(selectedVideoIndex);
|
|
||||||
|
streamsSpinner.setAdapter(videoStreamsAdapter);
|
||||||
|
streamsSpinner.setSelection(selectedVideoIndex);
|
||||||
|
setRadioButtonsState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -205,10 +248,10 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
if (DEBUG) Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]");
|
if (DEBUG) Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]");
|
||||||
switch (checkedId) {
|
switch (checkedId) {
|
||||||
case R.id.audio_button:
|
case R.id.audio_button:
|
||||||
setupAudioSpinner(currentInfo.getAudioStreams(), streamsSpinner);
|
setupAudioSpinner();
|
||||||
break;
|
break;
|
||||||
case R.id.video_button:
|
case R.id.video_button:
|
||||||
setupVideoSpinner(sortedStreamVideosList, streamsSpinner);
|
setupVideoSpinner();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,37 +281,53 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
protected void checkDownloadOptions(View view) {
|
protected void setupDownloadOptions() {
|
||||||
RadioButton audioButton = view.findViewById(R.id.audio_button);
|
setRadioButtonsState(false);
|
||||||
RadioButton videoButton = view.findViewById(R.id.video_button);
|
|
||||||
|
|
||||||
if (currentInfo.getAudioStreams() == null || currentInfo.getAudioStreams().size() == 0) {
|
final RadioButton audioButton = radioVideoAudioGroup.findViewById(R.id.audio_button);
|
||||||
audioButton.setVisibility(View.GONE);
|
final RadioButton videoButton = radioVideoAudioGroup.findViewById(R.id.video_button);
|
||||||
|
final boolean isVideoStreamsAvailable = videoStreamsAdapter.getCount() > 0;
|
||||||
|
final boolean isAudioStreamsAvailable = audioStreamsAdapter.getCount() > 0;
|
||||||
|
|
||||||
|
audioButton.setVisibility(isAudioStreamsAvailable ? View.VISIBLE : View.GONE);
|
||||||
|
videoButton.setVisibility(isVideoStreamsAvailable ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
if (isVideoStreamsAvailable) {
|
||||||
videoButton.setChecked(true);
|
videoButton.setChecked(true);
|
||||||
} else if (sortedStreamVideosList == null || sortedStreamVideosList.size() == 0) {
|
setupVideoSpinner();
|
||||||
videoButton.setVisibility(View.GONE);
|
} else if (isAudioStreamsAvailable) {
|
||||||
audioButton.setChecked(true);
|
audioButton.setChecked(true);
|
||||||
|
setupAudioSpinner();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), R.string.no_streams_available_download, Toast.LENGTH_SHORT).show();
|
||||||
|
getDialog().dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setRadioButtonsState(boolean enabled) {
|
||||||
|
radioVideoAudioGroup.findViewById(R.id.audio_button).setEnabled(enabled);
|
||||||
|
radioVideoAudioGroup.findViewById(R.id.video_button).setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
private void downloadSelected() {
|
private void downloadSelected() {
|
||||||
String url, location;
|
Stream stream;
|
||||||
|
String location;
|
||||||
|
|
||||||
String fileName = nameEditText.getText().toString().trim();
|
String fileName = nameEditText.getText().toString().trim();
|
||||||
if (fileName.isEmpty()) fileName = FilenameUtils.createFilename(getContext(), currentInfo.getName());
|
if (fileName.isEmpty()) fileName = FilenameUtils.createFilename(getContext(), currentInfo.getName());
|
||||||
|
|
||||||
boolean isAudio = radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button;
|
boolean isAudio = radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button;
|
||||||
if (isAudio) {
|
if (isAudio) {
|
||||||
url = currentInfo.getAudioStreams().get(selectedAudioIndex).getUrl();
|
stream = audioStreamsAdapter.getItem(selectedAudioIndex);
|
||||||
location = NewPipeSettings.getAudioDownloadPath(getContext());
|
location = NewPipeSettings.getAudioDownloadPath(getContext());
|
||||||
fileName += "." + currentInfo.getAudioStreams().get(selectedAudioIndex).getFormat().getSuffix();
|
|
||||||
} else {
|
} else {
|
||||||
url = sortedStreamVideosList.get(selectedVideoIndex).getUrl();
|
stream = videoStreamsAdapter.getItem(selectedVideoIndex);
|
||||||
location = NewPipeSettings.getVideoDownloadPath(getContext());
|
location = NewPipeSettings.getVideoDownloadPath(getContext());
|
||||||
fileName += "." + sortedStreamVideosList.get(selectedVideoIndex).getFormat().getSuffix();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String url = stream.getUrl();
|
||||||
|
fileName += "." + stream.getFormat().getSuffix();
|
||||||
|
|
||||||
DownloadManagerService.startMission(getContext(), url, location, fileName, isAudio, threadsSeekBar.getProgress() + 1);
|
DownloadManagerService.startMission(getContext(), url, location, fileName, isAudio, threadsSeekBar.getProgress() + 1);
|
||||||
getDialog().dismiss();
|
getDialog().dismiss();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
package org.schabi.newpipe.fragments.detail;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SpinnerToolbarAdapter extends BaseAdapter {
|
|
||||||
private final List<VideoStream> videoStreams;
|
|
||||||
private final boolean showIconNoAudio;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public SpinnerToolbarAdapter(Context context, List<VideoStream> videoStreams, boolean showIconNoAudio) {
|
|
||||||
this.context = context;
|
|
||||||
this.videoStreams = videoStreams;
|
|
||||||
this.showIconNoAudio = showIconNoAudio;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return videoStreams.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getItem(int position) {
|
|
||||||
return videoStreams.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
|
||||||
return getCustomView(position, convertView, parent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
return getCustomView(((Spinner) parent).getSelectedItemPosition(), convertView, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private View getCustomView(int position, View convertView, ViewGroup parent, boolean isDropdownItem) {
|
|
||||||
if (convertView == null) {
|
|
||||||
convertView = LayoutInflater.from(context).inflate(R.layout.resolutions_spinner_item, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageView woSoundIcon = convertView.findViewById(R.id.wo_sound_icon);
|
|
||||||
TextView text = convertView.findViewById(android.R.id.text1);
|
|
||||||
VideoStream item = (VideoStream) getItem(position);
|
|
||||||
text.setText(item.getFormat().getName() + " " + item.getResolution());
|
|
||||||
|
|
||||||
int visibility = !showIconNoAudio ? View.GONE
|
|
||||||
: item.isVideoOnly ? View.VISIBLE
|
|
||||||
: isDropdownItem ? View.INVISIBLE
|
|
||||||
: View.GONE;
|
|
||||||
woSoundIcon.setVisibility(visibility);
|
|
||||||
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.DrawableRes;
|
import android.support.annotation.DrawableRes;
|
||||||
import android.support.annotation.FloatRange;
|
import android.support.annotation.FloatRange;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
import android.support.v4.view.animation.FastOutSlowInInterpolator;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
|
@ -61,6 +62,8 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
import org.schabi.newpipe.fragments.BackPressable;
|
import org.schabi.newpipe.fragments.BackPressable;
|
||||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||||
|
import org.schabi.newpipe.util.StreamItemAdapter;
|
||||||
|
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
|
||||||
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
|
import org.schabi.newpipe.fragments.local.dialog.PlaylistAppendDialog;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||||
|
@ -83,9 +86,9 @@ import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
@ -107,8 +110,6 @@ public class VideoDetailFragment
|
||||||
// Amount of videos to show on start
|
// Amount of videos to show on start
|
||||||
private static final int INITIAL_RELATED_VIDEOS = 8;
|
private static final int INITIAL_RELATED_VIDEOS = 8;
|
||||||
|
|
||||||
private ArrayList<VideoStream> sortedStreamVideosList;
|
|
||||||
|
|
||||||
private InfoItemBuilder infoItemBuilder = null;
|
private InfoItemBuilder infoItemBuilder = null;
|
||||||
|
|
||||||
private int updateFlags = 0;
|
private int updateFlags = 0;
|
||||||
|
@ -120,18 +121,16 @@ public class VideoDetailFragment
|
||||||
private boolean showRelatedStreams;
|
private boolean showRelatedStreams;
|
||||||
private boolean wasRelatedStreamsExpanded = false;
|
private boolean wasRelatedStreamsExpanded = false;
|
||||||
|
|
||||||
@State
|
@State protected int serviceId = Constants.NO_SERVICE_ID;
|
||||||
protected int serviceId = Constants.NO_SERVICE_ID;
|
@State protected String name;
|
||||||
@State
|
@State protected String url;
|
||||||
protected String name;
|
|
||||||
@State
|
|
||||||
protected String url;
|
|
||||||
|
|
||||||
private StreamInfo currentInfo;
|
private StreamInfo currentInfo;
|
||||||
private Disposable currentWorker;
|
private Disposable currentWorker;
|
||||||
private CompositeDisposable disposables = new CompositeDisposable();
|
private CompositeDisposable disposables = new CompositeDisposable();
|
||||||
|
|
||||||
private int selectedVideoStream = -1;
|
private List<VideoStream> sortedVideoStreams;
|
||||||
|
private int selectedVideoStreamIndex = -1;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Views
|
// Views
|
||||||
|
@ -708,27 +707,25 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
private void setupActionBar(final StreamInfo info) {
|
private void setupActionBar(final StreamInfo info) {
|
||||||
if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
|
if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
|
||||||
sortedStreamVideosList = new ArrayList<>(ListHelper.getSortedStreamVideosList(
|
|
||||||
activity, info.getVideoStreams(), info.getVideoOnlyStreams(), false));
|
|
||||||
|
|
||||||
selectedVideoStream = ListHelper.getDefaultResolutionIndex(activity, sortedStreamVideosList);
|
|
||||||
|
|
||||||
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity)
|
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.getBoolean(activity.getString(R.string.use_external_video_player_key), false);
|
.getBoolean(activity.getString(R.string.use_external_video_player_key), false);
|
||||||
spinnerToolbar.setAdapter(new SpinnerToolbarAdapter(activity, sortedStreamVideosList,
|
|
||||||
isExternalPlayerEnabled));
|
sortedVideoStreams = ListHelper.getSortedStreamVideosList(activity, info.getVideoStreams(), info.getVideoOnlyStreams(), false);
|
||||||
spinnerToolbar.setSelection(selectedVideoStream);
|
selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(activity, sortedVideoStreams);
|
||||||
|
|
||||||
|
final StreamItemAdapter<VideoStream> streamsAdapter = new StreamItemAdapter<>(activity, new StreamSizeWrapper<>(sortedVideoStreams), isExternalPlayerEnabled);
|
||||||
|
spinnerToolbar.setAdapter(streamsAdapter);
|
||||||
|
spinnerToolbar.setSelection(selectedVideoStreamIndex);
|
||||||
spinnerToolbar.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
spinnerToolbar.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
selectedVideoStream = position;
|
selectedVideoStreamIndex = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -940,8 +937,9 @@ public class VideoDetailFragment
|
||||||
this.autoPlayEnabled = autoplay;
|
this.autoPlayEnabled = autoplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private VideoStream getSelectedVideoStream() {
|
private VideoStream getSelectedVideoStream() {
|
||||||
return sortedStreamVideosList.get(selectedVideoStream);
|
return sortedVideoStreams != null ? sortedVideoStreams.get(selectedVideoStreamIndex) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareDescription(final String descriptionHtml) {
|
private void prepareDescription(final String descriptionHtml) {
|
||||||
|
@ -1216,18 +1214,19 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
|
|
||||||
public void openDownloadDialog() {
|
public void openDownloadDialog() {
|
||||||
try {
|
try {
|
||||||
DownloadDialog downloadDialog =
|
DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
|
||||||
DownloadDialog.newInstance(currentInfo,
|
downloadDialog.setVideoStreams(sortedVideoStreams);
|
||||||
sortedStreamVideosList,
|
downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
|
||||||
selectedVideoStream);
|
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
|
||||||
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
|
||||||
} catch (Exception e) {
|
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
|
||||||
Toast.makeText(activity,
|
} catch (Exception e) {
|
||||||
R.string.could_not_setup_download_menu,
|
Toast.makeText(activity,
|
||||||
Toast.LENGTH_LONG).show();
|
R.string.could_not_setup_download_menu,
|
||||||
e.printStackTrace();
|
Toast.LENGTH_LONG).show();
|
||||||
}
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
196
app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java
Normal file
196
app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.Downloader;
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||||
|
import org.schabi.newpipe.extractor.stream.Stream;
|
||||||
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import us.shandian.giga.util.Utility;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list adapter for a list of {@link Stream streams}, currently supporting {@link VideoStream} and {@link AudioStream}.
|
||||||
|
*/
|
||||||
|
public class StreamItemAdapter<T extends Stream> extends BaseAdapter {
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
private StreamSizeWrapper<T> streamsWrapper;
|
||||||
|
private final boolean showIconNoAudio;
|
||||||
|
|
||||||
|
public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper, boolean showIconNoAudio) {
|
||||||
|
this.context = context;
|
||||||
|
this.streamsWrapper = streamsWrapper;
|
||||||
|
this.showIconNoAudio = showIconNoAudio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamItemAdapter(Context context, StreamSizeWrapper<T> streamsWrapper) {
|
||||||
|
this(context, streamsWrapper, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getAll() {
|
||||||
|
return streamsWrapper.getStreamsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return streamsWrapper.getStreamsList().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getItem(int position) {
|
||||||
|
return streamsWrapper.getStreamsList().get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||||
|
return getCustomView(position, convertView, parent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
return getCustomView(((Spinner) parent).getSelectedItemPosition(), convertView, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private View getCustomView(int position, View convertView, ViewGroup parent, boolean isDropdownItem) {
|
||||||
|
if (convertView == null) {
|
||||||
|
convertView = LayoutInflater.from(context).inflate(R.layout.stream_quality_item, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ImageView woSoundIconView = convertView.findViewById(R.id.wo_sound_icon);
|
||||||
|
final TextView formatNameView = convertView.findViewById(R.id.stream_format_name);
|
||||||
|
final TextView qualityView = convertView.findViewById(R.id.stream_quality);
|
||||||
|
final TextView sizeView = convertView.findViewById(R.id.stream_size);
|
||||||
|
|
||||||
|
final T stream = getItem(position);
|
||||||
|
|
||||||
|
int woSoundIconVisibility = View.GONE;
|
||||||
|
String qualityString;
|
||||||
|
|
||||||
|
if (stream instanceof VideoStream) {
|
||||||
|
qualityString = ((VideoStream) stream).getResolution();
|
||||||
|
|
||||||
|
if (!showIconNoAudio) {
|
||||||
|
woSoundIconVisibility = View.GONE;
|
||||||
|
} else if (((VideoStream) stream).isVideoOnly()) {
|
||||||
|
woSoundIconVisibility = View.VISIBLE;
|
||||||
|
} else if (isDropdownItem) {
|
||||||
|
woSoundIconVisibility = View.INVISIBLE;
|
||||||
|
}
|
||||||
|
} else if (stream instanceof AudioStream) {
|
||||||
|
qualityString = ((AudioStream) stream).getAverageBitrate() + "kbps";
|
||||||
|
} else {
|
||||||
|
qualityString = stream.getFormat().getSuffix();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streamsWrapper.getSizeInBytes(position) > 0) {
|
||||||
|
sizeView.setText(streamsWrapper.getFormattedSize(position));
|
||||||
|
sizeView.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
sizeView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
formatNameView.setText(stream.getFormat().getName());
|
||||||
|
qualityView.setText(qualityString);
|
||||||
|
woSoundIconView.setVisibility(woSoundIconVisibility);
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper class that includes a way of storing the stream sizes.
|
||||||
|
*/
|
||||||
|
public static class StreamSizeWrapper<T extends Stream> implements Serializable {
|
||||||
|
private static final StreamSizeWrapper<Stream> EMPTY = new StreamSizeWrapper<>(Collections.emptyList());
|
||||||
|
private final List<T> streamsList;
|
||||||
|
private long[] streamSizes;
|
||||||
|
|
||||||
|
public StreamSizeWrapper(List<T> streamsList) {
|
||||||
|
this.streamsList = streamsList;
|
||||||
|
this.streamSizes = new long[streamsList.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < streamSizes.length; i++) streamSizes[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to fetch the sizes of all the streams in a wrapper.
|
||||||
|
*
|
||||||
|
* @param streamsWrapper the wrapper
|
||||||
|
* @return a {@link Single} that returns a boolean indicating if any elements were changed
|
||||||
|
*/
|
||||||
|
public static <X extends Stream> Single<Boolean> fetchSizeForWrapper(StreamSizeWrapper<X> streamsWrapper) {
|
||||||
|
final Callable<Boolean> fetchAndSet = () -> {
|
||||||
|
boolean hasChanged = false;
|
||||||
|
for (X stream : streamsWrapper.getStreamsList()) {
|
||||||
|
if (streamsWrapper.getSizeInBytes(stream) > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long contentLength = Downloader.getInstance().getContentLength(stream.getUrl());
|
||||||
|
streamsWrapper.setSize(stream, contentLength);
|
||||||
|
hasChanged = true;
|
||||||
|
}
|
||||||
|
return hasChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Single.fromCallable(fetchAndSet)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.onErrorReturnItem(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getStreamsList() {
|
||||||
|
return streamsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSizeInBytes(int streamIndex) {
|
||||||
|
return streamSizes[streamIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSizeInBytes(T stream) {
|
||||||
|
return streamSizes[streamsList.indexOf(stream)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormattedSize(int streamIndex) {
|
||||||
|
return Utility.formatBytes(getSizeInBytes(streamIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormattedSize(T stream) {
|
||||||
|
return Utility.formatBytes(getSizeInBytes(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(int streamIndex, long sizeInBytes) {
|
||||||
|
streamSizes[streamIndex] = sizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(T stream, long sizeInBytes) {
|
||||||
|
streamSizes[streamsList.indexOf(stream)] = sizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <X extends Stream> StreamSizeWrapper<X> empty() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (StreamSizeWrapper<X>) EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,8 +35,8 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/file_name"
|
android:layout_below="@+id/file_name"
|
||||||
android:layout_marginLeft="20dp"
|
|
||||||
android:layout_marginBottom="6dp"
|
android:layout_marginBottom="6dp"
|
||||||
|
android:layout_marginLeft="20dp"
|
||||||
android:gravity="left"
|
android:gravity="left"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
tools:ignore="RtlHardcoded">
|
tools:ignore="RtlHardcoded">
|
||||||
|
@ -59,12 +59,12 @@
|
||||||
android:id="@+id/quality_spinner"
|
android:id="@+id/quality_spinner"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minWidth="150dp"
|
|
||||||
android:layout_below="@+id/video_audio_group"
|
android:layout_below="@+id/video_audio_group"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
android:layout_marginLeft="20dp"
|
android:layout_marginLeft="20dp"
|
||||||
android:layout_marginRight="20dp"
|
android:layout_marginRight="20dp"
|
||||||
tools:listitem="@layout/resolutions_spinner_item"/>
|
android:minWidth="150dp"
|
||||||
|
tools:listitem="@layout/stream_quality_item"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/threads_text_view"
|
android:id="@+id/threads_text_view"
|
||||||
|
@ -82,8 +82,8 @@
|
||||||
android:layout_below="@+id/threads_text_view"
|
android:layout_below="@+id/threads_text_view"
|
||||||
android:layout_marginLeft="24dp"
|
android:layout_marginLeft="24dp"
|
||||||
android:layout_marginRight="24dp"
|
android:layout_marginRight="24dp"
|
||||||
android:paddingBottom="12dp"
|
android:orientation="horizontal"
|
||||||
android:orientation="horizontal">
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/threads_count"
|
android:id="@+id/threads_count"
|
|
@ -1,34 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="48dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/wo_sound_icon"
|
|
||||||
android:layout_width="20dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_alignParentLeft="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginLeft="4dp"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:src="?attr/volume_off"
|
|
||||||
tools:ignore="ContentDescription,RtlHardcoded"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/text1"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:layout_toRightOf="@+id/wo_sound_icon"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="left"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
|
||||||
android:textSize="14sp"
|
|
||||||
tools:ignore="RtlHardcoded"
|
|
||||||
tools:text="MPEG-4 1080p60"/>
|
|
||||||
</RelativeLayout>
|
|
66
app/src/main/res/layout/stream_quality_item.xml
Normal file
66
app/src/main/res/layout/stream_quality_item.xml
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/wo_sound_icon"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginLeft="6dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="?attr/volume_off"
|
||||||
|
tools:ignore="ContentDescription,RtlHardcoded"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/stream_format_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="22dp"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_toRightOf="@+id/wo_sound_icon"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="left|bottom"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingLeft="12dp"
|
||||||
|
android:paddingRight="18dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||||
|
android:textSize="12sp"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:text="MPEG-4"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/stream_quality"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="26dp"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_toRightOf="@+id/wo_sound_icon"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="left|top"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingLeft="12dp"
|
||||||
|
android:paddingRight="18dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:text="1080p60"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/stream_size"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:gravity="right|center_vertical"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:ignore="RtlHardcoded"
|
||||||
|
tools:text="123.4 MB"
|
||||||
|
tools:visibility="visible"/>
|
||||||
|
</RelativeLayout>
|
|
@ -180,6 +180,7 @@
|
||||||
<string name="invalid_file">File doesn\'t exist or insufficient permission to read or write to it</string>
|
<string name="invalid_file">File doesn\'t exist or insufficient permission to read or write to it</string>
|
||||||
<string name="file_name_empty_error">File name cannot be empty</string>
|
<string name="file_name_empty_error">File name cannot be empty</string>
|
||||||
<string name="error_occurred_detail">An error occurred: %1$s</string>
|
<string name="error_occurred_detail">An error occurred: %1$s</string>
|
||||||
|
<string name="no_streams_available_download">No streams available to download</string>
|
||||||
|
|
||||||
<!-- error activity -->
|
<!-- error activity -->
|
||||||
<string name="sorry_string">Sorry, that should not have happened.</string>
|
<string name="sorry_string">Sorry, that should not have happened.</string>
|
||||||
|
|
Loading…
Reference in a new issue