Merge remote-tracking branch 'origin/master'

This commit is contained in:
Weblate 2017-05-15 14:42:35 +02:00
commit fcbb5536eb
22 changed files with 316 additions and 332 deletions

View file

@ -1,113 +1,135 @@
package org.schabi.newpipe.download; package org.schabi.newpipe.download;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.content.ContextCompat;
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.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
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.SeekBar; import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import org.schabi.newpipe.App; 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_info.AudioStream;
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
import org.schabi.newpipe.extractor.stream_info.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.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.Utils;
import java.io.File; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import us.shandian.giga.service.DownloadManagerService; import us.shandian.giga.service.DownloadManagerService;
public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
private static final String TAG = "DialogFragment";
private static final boolean DEBUG = MainActivity.DEBUG;
/** private static final String INFO_KEY = "info_key";
* Created by Christian Schabesberger on 21.09.15. private static final String SORTED_VIDEOS_LIST_KEY = "sorted_videos_list_key";
* <p> private static final String SELECTED_VIDEO_KEY = "selected_video_key";
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org> private static final String SELECTED_AUDIO_KEY = "selected_audio_key";
* DownloadDialog.java is part of NewPipe.
* <p>
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class DownloadDialog extends DialogFragment { private StreamInfo currentInfo;
private static final String TAG = DialogFragment.class.getName(); private ArrayList<VideoStream> sortedStreamVideosList;
private int selectedVideoIndex;
private int selectedAudioIndex;
public static final String TITLE = "name"; private EditText nameEditText;
public static final String FILE_SUFFIX_AUDIO = "file_suffix_audio"; private Spinner streamsSpinner;
public static final String FILE_SUFFIX_VIDEO = "file_suffix_video"; private RadioGroup radioVideoAudioGroup;
public static final String AUDIO_URL = "audio_url"; private TextView threadsCountTextView;
public static final String VIDEO_URL = "video_url"; private SeekBar threadsSeekBar;
public DownloadDialog() { public static DownloadDialog newInstance(StreamInfo info, ArrayList<VideoStream> sortedStreamVideosList, int selectedVideoIndex) {
}
public static DownloadDialog newInstance(Bundle args) {
DownloadDialog dialog = new DownloadDialog(); DownloadDialog dialog = new DownloadDialog();
dialog.setArguments(args); dialog.setInfo(info, sortedStreamVideosList, selectedVideoIndex);
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0); dialog.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
return dialog; return dialog;
} }
private void setInfo(StreamInfo info, ArrayList<VideoStream> sortedStreamVideosList, int selectedVideoIndex) {
this.currentInfo = info;
this.selectedVideoIndex = selectedVideoIndex;
this.sortedStreamVideosList = sortedStreamVideosList;
}
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public void onCreate(@Nullable Bundle savedInstanceState) {
Bundle savedInstanceState) { super.onCreate(savedInstanceState);
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
if (!PermissionHelper.checkStoragePermissions(getActivity())) {
getDialog().dismiss();
return;
}
if(ContextCompat.checkSelfPermission(this.getContext(),Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) if (savedInstanceState != null) {
ActivityCompat.requestPermissions(getActivity(),new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},0); Serializable serial = savedInstanceState.getSerializable(INFO_KEY);
if (serial instanceof StreamInfo) currentInfo = (StreamInfo) serial;
serial = savedInstanceState.getSerializable(SORTED_VIDEOS_LIST_KEY);
if (serial instanceof ArrayList) { //noinspection unchecked
sortedStreamVideosList = (ArrayList<VideoStream>) serial;
}
selectedVideoIndex = savedInstanceState.getInt(SELECTED_VIDEO_KEY, 0);
selectedAudioIndex = savedInstanceState.getInt(SELECTED_AUDIO_KEY, 0);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle 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.dialog_url, container);
} }
@Override @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
nameEditText = ((EditText) view.findViewById(R.id.file_name));
nameEditText.setText(createFileName(currentInfo.title));
selectedAudioIndex = Utils.getPreferredAudioFormat(getContext(), currentInfo.audio_streams);
Bundle arguments = getArguments(); streamsSpinner = (Spinner) view.findViewById(R.id.quality_spinner);
final Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); streamsSpinner.setOnItemSelectedListener(this);
final EditText name = (EditText) view.findViewById(R.id.file_name);
final TextView tCount = (TextView) view.findViewById(R.id.threads_count);
final SeekBar threads = (SeekBar) view.findViewById(R.id.threads);
toolbar.setTitle(R.string.download_dialog_title); threadsCountTextView = (TextView) view.findViewById(R.id.threads_count);
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(getActivity()) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp); threadsSeekBar = (SeekBar) view.findViewById(R.id.threads);
toolbar.inflateMenu(R.menu.dialog_url); radioVideoAudioGroup = (RadioGroup) view.findViewById(R.id.video_audio_group);
toolbar.setNavigationOnClickListener(new View.OnClickListener() { radioVideoAudioGroup.setOnCheckedChangeListener(this);
@Override
public void onClick(View v) {
getDialog().dismiss();
}
});
threads.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { initToolbar((Toolbar) view.findViewById(R.id.toolbar));
checkDownloadOptions(view);
setupVideoSpinner(sortedStreamVideosList, streamsSpinner);
int def = 3;
threadsCountTextView.setText(String.valueOf(def));
threadsSeekBar.setProgress(def - 1);
threadsSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override @Override
public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) { public void onProgressChanged(SeekBar seekbar, int progress, boolean fromUser) {
tCount.setText(String.valueOf(progress + 1)); threadsCountTextView.setText(String.valueOf(progress + 1));
} }
@Override @Override
@ -120,41 +142,111 @@ public class DownloadDialog extends DialogFragment {
} }
}); });
}
checkDownloadOptions(); @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(INFO_KEY, currentInfo);
outState.putSerializable(SORTED_VIDEOS_LIST_KEY, sortedStreamVideosList);
outState.putInt(SELECTED_VIDEO_KEY, selectedVideoIndex);
outState.putInt(SELECTED_AUDIO_KEY, selectedAudioIndex);
}
//int def = mPrefs.getInt("threads", 4); /*//////////////////////////////////////////////////////////////////////////
int def = 3; // Inits
threads.setProgress(def - 1); //////////////////////////////////////////////////////////////////////////*/
tCount.setText(String.valueOf(def));
name.setText(createFileName(arguments.getString(TITLE)));
private void initToolbar(Toolbar toolbar) {
if (DEBUG) Log.d(TAG, "initToolbar() called with: toolbar = [" + toolbar + "]");
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.inflateMenu(R.menu.dialog_url);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getDialog().dismiss();
}
});
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override @Override
public boolean onMenuItemClick(MenuItem item) { public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.okay) { if (item.getItemId() == R.id.okay) {
download(); downloadSelected();
return true; return true;
} else { } else return false;
return false;
}
} }
}); });
} }
protected void checkDownloadOptions() { public void setupAudioSpinner(final List<AudioStream> audioStreams, Spinner spinner) {
View view = getView(); String[] items = new String[audioStreams.size()];
Bundle arguments = getArguments(); for (int i = 0; i < audioStreams.size(); i++) {
AudioStream audioStream = audioStreams.get(i);
items[i] = MediaFormat.getNameById(audioStream.format) + " " + audioStream.avgBitrate + "kbps";
}
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, items);
spinner.setAdapter(itemAdapter);
spinner.setSelection(selectedAudioIndex);
}
public void setupVideoSpinner(final List<VideoStream> videoStreams, Spinner spinner) {
spinner.setAdapter(new SpinnerToolbarAdapter(getContext(), videoStreams, true));
spinner.setSelection(selectedVideoIndex);
}
/*//////////////////////////////////////////////////////////////////////////
// Radio group Video&Audio options - Listener
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
if (DEBUG) Log.d(TAG, "onCheckedChanged() called with: group = [" + group + "], checkedId = [" + checkedId + "]");
switch (checkedId) {
case R.id.audio_button:
setupAudioSpinner(currentInfo.audio_streams, streamsSpinner);
break;
case R.id.video_button:
setupVideoSpinner(sortedStreamVideosList, streamsSpinner);
break;
}
}
/*//////////////////////////////////////////////////////////////////////////
// Streams Spinner Listener
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (DEBUG) Log.d(TAG, "onItemSelected() called with: parent = [" + parent + "], view = [" + view + "], position = [" + position + "], id = [" + id + "]");
switch (radioVideoAudioGroup.getCheckedRadioButtonId()) {
case R.id.audio_button:
selectedAudioIndex = position;
break;
case R.id.video_button:
selectedVideoIndex = position;
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
protected void checkDownloadOptions(View view) {
RadioButton audioButton = (RadioButton) view.findViewById(R.id.audio_button); RadioButton audioButton = (RadioButton) view.findViewById(R.id.audio_button);
RadioButton videoButton = (RadioButton) view.findViewById(R.id.video_button); RadioButton videoButton = (RadioButton) view.findViewById(R.id.video_button);
if (arguments.getString(AUDIO_URL) == null) { if (currentInfo.audio_streams == null || currentInfo.audio_streams.size() == 0) {
audioButton.setVisibility(View.GONE); audioButton.setVisibility(View.GONE);
videoButton.setChecked(true); videoButton.setChecked(true);
} else if (arguments.getString(VIDEO_URL) == null) { } else if (sortedStreamVideosList == null || sortedStreamVideosList.size() == 0) {
videoButton.setVisibility(View.GONE); videoButton.setVisibility(View.GONE);
audioButton.setChecked(true); audioButton.setChecked(true);
} }
@ -164,14 +256,14 @@ public class DownloadDialog extends DialogFragment {
* #143 #44 #42 #22: make shure that the filename does not contain illegal chars. * #143 #44 #42 #22: make shure that the filename does not contain illegal chars.
* This should fix some of the "cannot download" problems. * This should fix some of the "cannot download" problems.
*/ */
private String createFileName(String fName) { private String createFileName(String fileName) {
// from http://eng-przemelek.blogspot.de/2009/07/how-to-create-valid-file-name.html // from http://eng-przemelek.blogspot.de/2009/07/how-to-create-valid-file-name.html
List<String> forbiddenCharsPatterns = new ArrayList<>(); List<String> forbiddenCharsPatterns = new ArrayList<>();
forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP
forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows
forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits
String nameToTest = fName; String nameToTest = fileName;
for (String pattern : forbiddenCharsPatterns) { for (String pattern : forbiddenCharsPatterns) {
nameToTest = nameToTest.replaceAll(pattern, "_"); nameToTest = nameToTest.replaceAll(pattern, "_");
} }
@ -179,55 +271,20 @@ public class DownloadDialog extends DialogFragment {
} }
//download audio, video or both? private void downloadSelected() {
private void download() { String url, location;
View view = getView();
Bundle arguments = getArguments();
final EditText name = (EditText) view.findViewById(R.id.file_name);
final SeekBar threads = (SeekBar) view.findViewById(R.id.threads);
RadioButton audioButton = (RadioButton) view.findViewById(R.id.audio_button);
RadioButton videoButton = (RadioButton) view.findViewById(R.id.video_button);
String fName = name.getText().toString().trim(); String fileName = nameEditText.getText().toString().trim();
if (fileName.isEmpty()) fileName = createFileName(currentInfo.title);
boolean isAudio = audioButton.isChecked(); boolean isAudio = radioVideoAudioGroup.getCheckedRadioButtonId() == R.id.audio_button;
String url, location, filename; url = isAudio ? currentInfo.audio_streams.get(selectedAudioIndex).url : sortedStreamVideosList.get(selectedVideoIndex).url;
if (isAudio) { location = isAudio ? NewPipeSettings.getAudioDownloadPath(getContext()) : NewPipeSettings.getVideoDownloadPath(getContext());
url = arguments.getString(AUDIO_URL);
location = NewPipeSettings.getAudioDownloadPath(getContext());
filename = fName + arguments.getString(FILE_SUFFIX_AUDIO);
} else {
url = arguments.getString(VIDEO_URL);
location = NewPipeSettings.getVideoDownloadPath(getContext());
filename = fName + arguments.getString(FILE_SUFFIX_VIDEO);
}
DownloadManagerService.startMission(getContext(), url, location, filename, isAudio, if (isAudio) fileName += "." + MediaFormat.getSuffixById(currentInfo.audio_streams.get(selectedAudioIndex).format);
threads.getProgress() + 1); else fileName += "." + MediaFormat.getSuffixById(sortedStreamVideosList.get(selectedVideoIndex).format);
DownloadManagerService.startMission(getContext(), url, location, fileName, isAudio, threadsSeekBar.getProgress() + 1);
getDialog().dismiss(); getDialog().dismiss();
} }
private void download(String url, String title,
String fileSuffix, File downloadDir, Context context) {
File saveFilePath = new File(downloadDir, createFileName(title) + fileSuffix);
long id = 0;
Log.i(TAG, "Started downloading '" + url +
"' => '" + saveFilePath + "' #" + id);
if (App.isUsingTor()) {
//if using Tor, do not use DownloadManager because the proxy cannot be set
//we'll see later
FileDownloader.downloadFile(getContext(), url, saveFilePath, title);
} else {
Intent intent = new Intent(getContext(), DownloadActivity.class);
intent.setAction(DownloadActivity.INTENT_DOWNLOAD);
intent.setData(Uri.parse(url));
intent.putExtra("fileName", createFileName(title) + fileSuffix);
startActivity(intent);
}
}
} }

View file

@ -1,165 +0,0 @@
package org.schabi.newpipe.download;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import org.schabi.newpipe.R;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import javax.net.ssl.HttpsURLConnection;
import info.guardianproject.netcipher.NetCipher;
/**
* Created by Christian Schabesberger on 14.08.15.
*
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* FileDownloader.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
// TODO: FOR HEAVEN SAKE !!! DO NOT SIMPLY USE ASYNCTASK. MAKE THIS A PROPER SERVICE !!!
public class FileDownloader extends AsyncTask<Void, Integer, Void> {
public static final String TAG = "FileDownloader";
private NotificationManager nm;
private NotificationCompat.Builder builder;
private int notifyId = 0x1234;
private int fileSize = 0xffffffff;
private final Context context;
private final String fileURL;
private final File saveFilePath;
private final String title;
private final String debugContext;
public FileDownloader(Context context, String fileURL, File saveFilePath, String title) {
this.context = context;
this.fileURL = fileURL;
this.saveFilePath = saveFilePath;
this.title = title;
this.debugContext = "'" + fileURL +
"' => '" + saveFilePath + "'";
}
/**
* Downloads a file from a URL in the background using an {@link AsyncTask}.
*
* @param fileURL HTTP URL of the file to be downloaded
* @param saveFilePath path of the directory to save the file
* @param title
* @throws IOException
*/
public static void downloadFile(final Context context, final String fileURL, final File saveFilePath, String title) {
new FileDownloader(context, fileURL, saveFilePath, title).execute();
}
/** AsyncTask impl: executed in gui thread */
@Override
protected void onPreExecute() {
super.onPreExecute();
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Drawable icon = context.getResources().getDrawable(R.mipmap.ic_launcher);
builder = new NotificationCompat.Builder(context)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
.setContentTitle(saveFilePath.getName())
.setContentText(saveFilePath.getAbsolutePath())
.setProgress(fileSize, 0, false);
nm.notify(notifyId, builder.build());
}
/** AsyncTask impl: executed in background thread does the download */
@Override
protected Void doInBackground(Void... voids) {
HttpsURLConnection con = null;
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
con = NetCipher.getHttpsURLConnection(fileURL);
int responseCode = con.getResponseCode();
// always check HTTP response code first
if (responseCode == HttpURLConnection.HTTP_OK) {
fileSize = con.getContentLength();
inputStream = new BufferedInputStream(con.getInputStream());
outputStream = new FileOutputStream(saveFilePath);
int bufferSize = 8192;
int downloaded = 0;
int bytesRead = -1;
byte[] buffer = new byte[bufferSize];
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
downloaded += bytesRead;
if (downloaded % 50000 < bufferSize) {
publishProgress(downloaded);
}
}
publishProgress(bufferSize);
} else {
Log.i(TAG, "No file to download. Server replied HTTP code: " + responseCode);
}
} catch (IOException e) {
Log.e(TAG, "No file to download. Server replied HTTP code: ", e);
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (con != null) {
con.disconnect();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... progress) {
builder.setProgress(fileSize, progress[0], false);
nm.notify(notifyId, builder.build());
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
nm.cancel(notifyId);
}
}

View file

@ -9,11 +9,9 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner; import android.widget.Spinner;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream_info.VideoStream; import org.schabi.newpipe.extractor.stream_info.VideoStream;
import org.schabi.newpipe.util.Utils; import org.schabi.newpipe.util.Utils;
@ -71,16 +69,9 @@ class ActionBarHandler {
if (activity == null) return; if (activity == null) return;
selectedVideoStream = 0; selectedVideoStream = 0;
// this array will be shown in the dropdown menu for selecting the stream/resolution.
String[] itemArray = new String[videoStreams.size()];
for (int i = 0; i < videoStreams.size(); i++) {
VideoStream item = videoStreams.get(i);
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
}
int defaultResolutionIndex = Utils.getDefaultResolution(activity, videoStreams); int defaultResolutionIndex = Utils.getDefaultResolution(activity, videoStreams);
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(), android.R.layout.simple_spinner_dropdown_item, itemArray); boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(activity.getString(R.string.use_external_video_player_key), false);
toolbarSpinner.setAdapter(itemAdapter); toolbarSpinner.setAdapter(new SpinnerToolbarAdapter(activity, videoStreams, isExternalPlayerEnabled));
toolbarSpinner.setSelection(defaultResolutionIndex); toolbarSpinner.setSelection(defaultResolutionIndex);
toolbarSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { toolbarSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override

View file

@ -0,0 +1,74 @@
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_info.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 = (ImageView) convertView.findViewById(R.id.wo_sound_icon);
TextView text = (TextView) convertView.findViewById(android.R.id.text1);
VideoStream item = (VideoStream) getItem(position);
text.setText(MediaFormat.getNameById(item.format) + " " + item.resolution);
int visibility = !showIconNoAudio ? View.GONE
: item.isVideoOnly ? View.VISIBLE
: isDropdownItem ? View.INVISIBLE
: View.GONE;
woSoundIcon.setVisibility(visibility);
return convertView;
}
}

View file

@ -330,7 +330,8 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
updateFlags |= RELATED_STREAMS_UPDATE_FLAG; updateFlags |= RELATED_STREAMS_UPDATE_FLAG;
} else if (key.equals(getString(R.string.preferred_video_format_key)) } else if (key.equals(getString(R.string.preferred_video_format_key))
|| key.equals(getString(R.string.default_resolution_key)) || key.equals(getString(R.string.default_resolution_key))
|| key.equals(getString(R.string.show_higher_resolutions_key))) { || key.equals(getString(R.string.show_higher_resolutions_key))
|| key.equals(getString(R.string.use_external_video_player_key))) {
updateFlags |= RESOLUTIONS_MENU_UPDATE_FLAG; updateFlags |= RESOLUTIONS_MENU_UPDATE_FLAG;
} else if (key.equals(getString(R.string.show_play_with_kodi_key))) { } else if (key.equals(getString(R.string.show_play_with_kodi_key))) {
updateFlags |= TOOLBAR_ITEMS_UPDATE_FLAG; updateFlags |= TOOLBAR_ITEMS_UPDATE_FLAG;
@ -682,33 +683,10 @@ public class VideoDetailFragment extends BaseFragment implements StreamExtractor
} }
try { try {
Bundle args = new Bundle(); DownloadDialog downloadDialog = DownloadDialog.newInstance(info, sortedStreamVideosList, selectedStreamId);
// Sometimes it may be that some information is not available due to changes fo the
// website which was crawled. Then the ui has to understand this and act right.
if (info.audio_streams != null) {
AudioStream audioStream =
info.audio_streams.get(Utils.getPreferredAudioFormat(activity, info.audio_streams));
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
args.putString(DownloadDialog.AUDIO_URL, audioStream.url);
args.putString(DownloadDialog.FILE_SUFFIX_AUDIO, audioSuffix);
}
if (sortedStreamVideosList != null) {
VideoStream selectedStreamItem = sortedStreamVideosList.get(selectedStreamId);
String videoSuffix = "." + MediaFormat.getSuffixById(selectedStreamItem.format);
args.putString(DownloadDialog.FILE_SUFFIX_VIDEO, videoSuffix);
args.putString(DownloadDialog.VIDEO_URL, selectedStreamItem.url);
}
args.putString(DownloadDialog.TITLE, info.title);
DownloadDialog downloadDialog = DownloadDialog.newInstance(args);
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog"); downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) { } catch (Exception e) {
Toast.makeText(activity, Toast.makeText(activity, R.string.could_not_setup_download_menu, Toast.LENGTH_LONG).show();
R.string.could_not_setup_download_menu, Toast.LENGTH_LONG).show();
e.printStackTrace(); e.printStackTrace();
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,005 B

View file

@ -32,12 +32,11 @@
<RadioGroup <RadioGroup
android:id="@+id/video_audio_group" android:id="@+id/video_audio_group"
android:layout_width="match_parent" 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_marginBottom="12dp"
android:layout_marginLeft="20dp" android:layout_marginLeft="20dp"
android:layout_marginRight="20dp" android:layout_marginBottom="6dp"
android:gravity="left" android:gravity="left"
android:orientation="horizontal" android:orientation="horizontal"
tools:ignore="RtlHardcoded"> tools:ignore="RtlHardcoded">
@ -56,11 +55,22 @@
android:text="@string/audio"/> android:text="@string/audio"/>
</RadioGroup> </RadioGroup>
<Spinner
android:id="@+id/quality_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="150dp"
android:layout_below="@+id/video_audio_group"
android:layout_marginBottom="12dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
tools:listitem="@layout/resolutions_spinner_item"/>
<TextView <TextView
android:id="@+id/threads_text_view" android:id="@+id/threads_text_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/video_audio_group" android:layout_below="@+id/quality_spinner"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:layout_marginLeft="24dp" android:layout_marginLeft="24dp"
android:layout_marginRight="24dp" android:layout_marginRight="24dp"

View file

@ -0,0 +1,34 @@
<?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="16sp"
tools:ignore="RtlHardcoded"
tools:text="MPEG-4 1080p60 very long res"/>
</RelativeLayout>

View file

@ -13,5 +13,6 @@
<attr name="popup" format="reference"/> <attr name="popup" format="reference"/>
<attr name="expand" format="reference"/> <attr name="expand" format="reference"/>
<attr name="collapse" format="reference"/> <attr name="collapse" format="reference"/>
<attr name="volume_off" format="reference"/>
<attr name="separatorColor" format="color"/> <attr name="separatorColor" format="color"/>
</resources> </resources>

View file

@ -24,6 +24,7 @@
<string name="screen_rotation">rotation</string> <string name="screen_rotation">rotation</string>
<string name="settings_activity_title">Settings</string> <string name="settings_activity_title">Settings</string>
<string name="use_external_video_player_title">Use external video player</string> <string name="use_external_video_player_title">Use external video player</string>
<string name="use_external_video_player_summary">Some resolutions will NOT have audio when this option is enabled</string>
<string name="use_external_audio_player_title">Use external audio player</string> <string name="use_external_audio_player_title">Use external audio player</string>
<string name="popup_mode_share_menu_title">NewPipe Popup mode</string> <string name="popup_mode_share_menu_title">NewPipe Popup mode</string>
<string name="rss_button_title" translatable="false">RSS</string> <string name="rss_button_title" translatable="false">RSS</string>

View file

@ -26,6 +26,7 @@
<item name="popup">@drawable/ic_picture_in_picture_black_24dp</item> <item name="popup">@drawable/ic_picture_in_picture_black_24dp</item>
<item name="expand">@drawable/ic_expand_more_black_24dp</item> <item name="expand">@drawable/ic_expand_more_black_24dp</item>
<item name="collapse">@drawable/ic_expand_less_black_24dp</item> <item name="collapse">@drawable/ic_expand_less_black_24dp</item>
<item name="volume_off">@drawable/ic_volume_off_black_24dp</item>
<item name="separatorColor">@color/light_separator_color</item> <item name="separatorColor">@color/light_separator_color</item>
<item name="colorControlHighlight">@color/light_ripple_color</item> <item name="colorControlHighlight">@color/light_ripple_color</item>
</style> </style>
@ -50,6 +51,7 @@
<item name="popup">@drawable/ic_picture_in_picture_white_24dp</item> <item name="popup">@drawable/ic_picture_in_picture_white_24dp</item>
<item name="expand">@drawable/ic_expand_more_white_24dp</item> <item name="expand">@drawable/ic_expand_more_white_24dp</item>
<item name="collapse">@drawable/ic_expand_less_white_24dp</item> <item name="collapse">@drawable/ic_expand_less_white_24dp</item>
<item name="volume_off">@drawable/ic_volume_off_white_24dp</item>
<item name="separatorColor">@color/dark_separator_color</item> <item name="separatorColor">@color/dark_separator_color</item>
<item name="colorControlHighlight">@color/dark_ripple_color</item> <item name="colorControlHighlight">@color/dark_ripple_color</item>
</style> </style>

View file

@ -12,6 +12,7 @@
<CheckBoxPreference <CheckBoxPreference
android:key="@string/use_external_video_player_key" android:key="@string/use_external_video_player_key"
android:title="@string/use_external_video_player_title" android:title="@string/use_external_video_player_title"
android:summary="@string/use_external_video_player_summary"
android:defaultValue="false"/> android:defaultValue="false"/>
<CheckBoxPreference <CheckBoxPreference

View file

@ -5,7 +5,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.3.1' classpath 'com.android.tools.build:gradle:2.3.2'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files