v0.11.6-beta

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQEcBAABCAAGBQJad0WzAAoJEBaPG0uMwegjobcH/RTigyyvFobt5UfeC/YeBdei
 EjnVQKNMqb9HG4ndQpCkV8riXJz20zcoIliU1gre56SF9H7S4v+EknPze18LM7HP
 DdRMA9ZEab+4zkRY8ITs0KV22nyxhW+UX1ThK6iB8yTV15V+Ra5w1LC1e9gdH/po
 lJg9SdWUcSYMUxCknVEszdHq05aRlVGs1nfOHv6WYp5Sfh5PF7SOt8J0rYBRO7MK
 /LgYM+jKBInytYErzWOXSeTHHZD74ggNbAbuYhSKEHb80eJ8jkd0omH9CRDj4XYr
 mBamRTCuS/740XmQABMv9jJJTj0qjyqVbYdlyyrvI1kTWSAJNDr3u0/HqTCCohA=
 =vcQv
 -----END PGP SIGNATURE-----

Merge tag 'v0.11.6-beta'

v0.11.6-beta
This commit is contained in:
Christian Schabesberger 2018-02-21 11:38:48 +01:00
commit 7d4c45c4c0
93 changed files with 2091 additions and 779 deletions

View file

@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 27
versionCode 46
versionName "0.11.5"
versionCode 47
versionName "0.11.6"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -55,7 +55,7 @@ dependencies {
exclude module: 'support-annotations'
}
implementation 'com.github.TeamNewPipe:NewPipeExtractor:1c97da8b51b3610'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:7fd21ec08581d'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:1.10.19'

View file

@ -16,7 +16,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher"
android:theme="@style/DarkTheme"
android:theme="@style/OpeningTheme"
tools:ignore="AllowBackup">
<activity
android:name=".MainActivity"
@ -31,7 +31,7 @@
<activity
android:name=".player.old.PlayVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/VideoPlayerTheme"
android:theme="@style/OldVideoPlayerTheme"
tools:ignore="UnusedAttribute"/>
<service
@ -56,8 +56,7 @@
android:name=".player.MainVideoPlayer"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask"
android:theme="@style/PlayerTheme"/>
android:launchMode="singleTask"/>
<activity
android:name=".settings.SettingsActivity"
@ -124,7 +123,7 @@
<activity
android:name=".RouterActivity"
android:taskAffinity=""
android:theme="@android:style/Theme.NoDisplay">
android:theme="@style/RouterActivityThemeDark">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
@ -175,17 +174,21 @@
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
<service
android:name=".RouterPlayerActivity$FetcherService"
android:exported="false"/>
<activity
android:name=".RouterPopupActivity"
android:label="@string/popup_mode_share_menu_title"
android:name=".RouterPlayerActivity"
android:excludeFromRecents="true"
android:label="@string/preferred_player_share_menu_title"
android:taskAffinity=""
android:theme="@style/PopupPermissionsTheme">
android:theme="@style/RouterActivityThemeDark">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH"/>
@ -204,6 +207,11 @@
<data android:pathPrefix="/embed/"/>
<data android:pathPrefix="/watch"/>
<data android:pathPrefix="/attribution_link"/>
<!-- channel prefix -->
<data android:pathPrefix="/channel/"/>
<data android:pathPrefix="/user/"/>
<!-- playlist prefix -->
<data android:pathPrefix="/playlist"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>

View file

@ -1,9 +1,12 @@
package org.schabi.newpipe;
import android.app.AlarmManager;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
@ -116,7 +119,6 @@ public class App extends Application {
});
}
private void initACRA() {
try {
final ACRAConfiguration acraConfig = new ConfigurationBuilder(this)
@ -149,4 +151,5 @@ public class App extends Application {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel);
}
}

View file

@ -77,17 +77,6 @@ public abstract class BaseFragment extends Fragment {
protected void initListeners() {
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
protected final int resolveResourceIdFromAttr(@AttrRes int attr) {
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{attr});
int attributeResourceId = a.getResourceId(0, 0);
a.recycle();
return attributeResourceId;
}
/*//////////////////////////////////////////////////////////////////////////
// DisplayImageOptions default configurations
//////////////////////////////////////////////////////////////////////////*/

View file

@ -30,7 +30,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
@ -50,17 +49,18 @@ import org.schabi.newpipe.database.history.dao.WatchHistoryDAO;
import org.schabi.newpipe.database.history.model.HistoryEntry;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import org.schabi.newpipe.database.history.model.WatchHistoryEntry;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.history.HistoryListener;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper;
@ -73,7 +73,8 @@ import io.reactivex.subjects.PublishSubject;
public class MainActivity extends AppCompatActivity implements HistoryListener {
private static final String TAG = "MainActivity";
public static final boolean DEBUG = false;
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private SharedPreferences sharedPreferences;
private ActionBarDrawerToggle toggle = null;
@ -83,9 +84,11 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (DEBUG)
Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
ThemeHelper.setTheme(this);
if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this));
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@ -93,62 +96,51 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
initFragments();
}
setSupportActionBar(findViewById(R.id.toolbar));
setupDrawer();
initHistory();
}
private void setupDrawer() {
final Toolbar toolbar = findViewById(R.id.toolbar);
final DrawerLayout drawer = findViewById(R.id.drawer_layout);
final NavigationView drawerItems = findViewById(R.id.navigation);
setSupportActionBar(toolbar);
drawerItems.getMenu().getItem(NewPipe.getIdOfService(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("service", "YouTube"))).setChecked(true);
//drawerItems.setItemIconTintList(null); // Set null to use the original icon
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
if (!BuildConfig.BUILD_TYPE.equals("release")) {
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
toggle.syncState();
drawer.addDrawerListener(toggle);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
private int lastService;
drawerItems.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
drawerItems.getMenu().getItem(NewPipe.getIdOfService(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("service", "YouTube"))).setChecked(false);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit();
editor.putString("service", item.getTitle().toString());
editor.apply();
drawer.closeDrawers();
drawerItems.getMenu().getItem(NewPipe.getIdOfService(PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("service", "YouTube"))).setChecked(true);
return true;
@Override
public void onDrawerOpened(View drawerView) {
lastService = ServiceHelper.getSelectedServiceId(MainActivity.this);
}
@Override
public void onDrawerClosed(View drawerView) {
if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) {
new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate);
}
}
});
drawerItems.setNavigationItemSelectedListener(item -> {
if (item.getGroupId() == R.id.menu_services_group) {
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getTitle().toString());
drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
}
drawer.closeDrawers();
return true;
});
} else {
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
if (toggle != null) {
toggle.syncState();
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawer.openDrawer(GravityCompat.START);
}
});
}
}
}
});
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
initHistory();
}
@Override
@ -171,20 +163,14 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
sharedPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, false).apply();
// https://stackoverflow.com/questions/10844112/runtimeexception-performing-pause-of-activity-that-is-not-resumed
// Briefly, let the activity resume properly posting the recreate call to end of the message queue
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
MainActivity.this.recreate();
}
});
new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate);
}
if(sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) {
if (sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) {
if (DEBUG) Log.d(TAG, "main page has changed, recreating main fragment...");
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false).apply();
NavigationHelper.openMainActivity(this);
}
}
@Override
@ -218,6 +204,38 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
} else super.onBackPressed();
}
/**
* Implement the following diagram behavior for the up button:
* <pre>
* +---------------+
* | Main Screen +----+
* +-------+-------+ |
* | |
* Up | Search Button
* | |
* +----+-----+ |
* +------------+ Search |-----+
* | +----+-----+
* | Open |
* | something Up
* | |
* | +------------+-------------+
* | | |
* | | Video <-> Channel |
* +---| Channel <-> Playlist |
* | Video <-> .... |
* | |
* +--------------------------+
* </pre>
*/
private void onHomeButtonPressed() {
// If search fragment wasn't found in the backstack...
if (!NavigationHelper.tryGotoSearchFragment(getSupportFragmentManager())) {
// ...go to the main fragment
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
}
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@ -243,6 +261,9 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(false);
}
updateDrawerNavigation();
return true;
}
@ -253,7 +274,7 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
switch (id) {
case android.R.id.home:
NavigationHelper.gotoMainFragment(getSupportFragmentManager());
onHomeButtonPressed();
return true;
case R.id.action_settings:
NavigationHelper.openSettings(this);
@ -287,6 +308,27 @@ public class MainActivity extends AppCompatActivity implements HistoryListener {
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void updateDrawerNavigation() {
if (getSupportActionBar() == null) return;
final Toolbar toolbar = findViewById(R.id.toolbar);
final DrawerLayout drawer = findViewById(R.id.drawer_layout);
final Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
if (fragment instanceof MainFragment) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
if (toggle != null) {
toggle.syncState();
toolbar.setNavigationOnClickListener(v -> drawer.openDrawer(GravityCompat.START));
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED);
}
} else {
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(v -> onHomeButtonPressed());
}
}
private void handleIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");

View file

@ -3,14 +3,25 @@ package org.schabi.newpipe;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.widget.Toast;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.NavigationHelper;
import java.util.Collection;
import java.util.HashSet;
/**
import icepick.Icepick;
import icepick.State;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
/*
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
* RouterActivity.java is part of NewPipe.
*
@ -34,23 +45,71 @@ import java.util.HashSet;
*/
public class RouterActivity extends AppCompatActivity {
@State
protected String currentUrl;
protected CompositeDisposable disposables = new CompositeDisposable();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Icepick.restoreInstanceState(this, savedInstanceState);
String videoUrl = getUrl(getIntent());
handleUrl(videoUrl);
if (TextUtils.isEmpty(currentUrl)) {
currentUrl = getUrl(getIntent());
if (TextUtils.isEmpty(currentUrl)) {
Toast.makeText(this, R.string.invalid_url_toast, Toast.LENGTH_LONG).show();
finish();
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}
@Override
protected void onStart() {
super.onStart();
handleUrl(currentUrl);
}
protected void handleUrl(String url) {
boolean success = NavigationHelper.openByLink(this, url);
if (!success) {
disposables.add(Observable
.fromCallable(() -> NavigationHelper.getIntentByLink(this, url))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(intent -> {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
}, this::handleError)
);
}
protected void handleError(Throwable error) {
error.printStackTrace();
if (error instanceof ExtractionException) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
} else {
ExtractorHelper.handleGeneralException(this, -1, null, error, UserAction.SOMETHING_ELSE, null);
}
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
disposables.clear();
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
@ -71,7 +130,8 @@ public class RouterActivity extends AppCompatActivity {
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
//this means that vidoe was called through share menu
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
videoUrl = getUris(extraText)[0];
final String[] uris = getUris(extraText);
videoUrl = uris.length > 0 ? uris[0] : null;
}
return videoUrl;

View file

@ -0,0 +1,413 @@
package org.schabi.newpipe;
import android.app.IntentService;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.StreamingService.LinkType;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.playlist.ChannelPlayQueue;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.Serializable;
import java.util.Arrays;
import icepick.State;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
/**
* Get the url from the intent and open it in the chosen preferred player
*/
public class RouterPlayerActivity extends RouterActivity {
@State
protected int currentServiceId = -1;
private StreamingService currentService;
@State
protected LinkType currentLinkType;
@State
protected int selectedRadioPosition = -1;
protected int selectedPreviously = -1;
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setTheme(ThemeHelper.isLightThemeSelected(this) ? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
}
@Override
protected void handleUrl(String url) {
disposables.add(Observable
.fromCallable(() -> {
if (currentServiceId == -1) {
currentService = NewPipe.getServiceByUrl(url);
currentServiceId = currentService.getServiceId();
currentLinkType = currentService.getLinkTypeByUrl(url);
currentUrl = NavigationHelper.getCleanUrl(currentService, url, currentLinkType);
} else {
currentService = NewPipe.getService(currentServiceId);
}
return currentLinkType != LinkType.NONE;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result) {
onSuccess();
} else {
onError();
}
}, this::handleError));
}
protected void onError() {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
finish();
}
protected void onSuccess() {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
if ((isExtAudioEnabled || isExtVideoEnabled) && currentLinkType != LinkType.STREAM) {
Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
finish();
return;
}
// TODO: Add some sort of "capabilities" field to services (audio only, video and audio, etc.)
if (currentService == ServiceList.SoundCloud.getService()) {
handleChoice(getString(R.string.background_player_key));
return;
}
final String playerChoiceKey = preferences.getString(getString(R.string.preferred_player_key), getString(R.string.preferred_player_default));
final String alwaysAskKey = getString(R.string.always_ask_player_key);
if (playerChoiceKey.equals(alwaysAskKey)) {
showDialog();
} else {
handleChoice(playerChoiceKey);
}
}
private void showDialog() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(this,
ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
LayoutInflater inflater = LayoutInflater.from(themeWrapper);
final LinearLayout rootLayout = (LinearLayout) inflater.inflate(R.layout.preferred_player_dialog_view, null, false);
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
final AdapterChoiceItem[] choices = {
new AdapterChoiceItem(getString(R.string.video_player_key), getString(R.string.video_player),
resolveResourceIdFromAttr(themeWrapper, R.attr.play)),
new AdapterChoiceItem(getString(R.string.background_player_key), getString(R.string.background_player),
resolveResourceIdFromAttr(themeWrapper, R.attr.audio)),
new AdapterChoiceItem(getString(R.string.popup_player_key), getString(R.string.popup_player),
resolveResourceIdFromAttr(themeWrapper, R.attr.popup))
};
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
final int indexOfChild = radioGroup.indexOfChild(radioGroup.findViewById(radioGroup.getCheckedRadioButtonId()));
final AdapterChoiceItem choice = choices[indexOfChild];
handleChoice(choice.key);
if (which == DialogInterface.BUTTON_POSITIVE) {
preferences.edit().putString(getString(R.string.preferred_player_key), choice.key).apply();
}
};
final AlertDialog alertDialog = new AlertDialog.Builder(themeWrapper)
.setTitle(R.string.preferred_player_share_menu_title)
.setView(radioGroup)
.setCancelable(true)
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
.setPositiveButton(R.string.always, dialogButtonsClickListener)
.setOnDismissListener((dialog) -> finish())
.create();
alertDialog.setOnShowListener(dialog -> {
setDialogButtonsState(alertDialog, radioGroup.getCheckedRadioButtonId() != -1);
});
radioGroup.setOnCheckedChangeListener((group, checkedId) -> setDialogButtonsState(alertDialog, true));
final View.OnClickListener radioButtonsClickListener = v -> {
final int indexOfChild = radioGroup.indexOfChild(v);
if (indexOfChild == -1) return;
selectedPreviously = selectedRadioPosition;
selectedRadioPosition = indexOfChild;
if (selectedPreviously == selectedRadioPosition) {
handleChoice(choices[selectedRadioPosition].key);
}
};
int id = 12345;
for (AdapterChoiceItem item : choices) {
final RadioButton radioButton = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
radioButton.setText(item.description);
radioButton.setCompoundDrawablesWithIntrinsicBounds(item.icon, 0, 0, 0);
radioButton.setChecked(false);
radioButton.setId(id++);
radioButton.setLayoutParams(new RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
radioButton.setOnClickListener(radioButtonsClickListener);
radioGroup.addView(radioButton);
}
if (selectedRadioPosition == -1) {
final String lastSelectedPlayer = preferences.getString(getString(R.string.preferred_player_last_selected_key), null);
if (!TextUtils.isEmpty(lastSelectedPlayer)) {
for (int i = 0; i < choices.length; i++) {
AdapterChoiceItem c = choices[i];
if (lastSelectedPlayer.equals(c.key)) {
selectedRadioPosition = i;
break;
}
}
}
}
selectedRadioPosition = Math.min(Math.max(-1, selectedRadioPosition), choices.length - 1);
if (selectedRadioPosition != -1) {
((RadioButton) radioGroup.getChildAt(selectedRadioPosition)).setChecked(true);
}
selectedPreviously = selectedRadioPosition;
alertDialog.show();
}
private void setDialogButtonsState(AlertDialog dialog, boolean state) {
final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
if (negativeButton == null || positiveButton == null) return;
negativeButton.setEnabled(state);
positiveButton.setEnabled(state);
}
private void handleChoice(final String playerChoiceKey) {
if (Arrays.asList(getResources().getStringArray(R.array.preferred_player_values_list)).contains(playerChoiceKey)) {
PreferenceManager.getDefaultSharedPreferences(this).edit()
.putString(getString(R.string.preferred_player_last_selected_key), playerChoiceKey).apply();
}
if (playerChoiceKey.equals(getString(R.string.popup_player_key)) && !PermissionHelper.isPopupEnabled(this)) {
PermissionHelper.showPopupEnablementToast(this);
finish();
return;
}
final Intent intent = new Intent(this, FetcherService.class);
intent.putExtra(FetcherService.KEY_CHOICE, new Choice(currentService.getServiceId(), currentLinkType, currentUrl, playerChoiceKey));
startService(intent);
finish();
}
private static class AdapterChoiceItem {
final String description, key;
@DrawableRes
final int icon;
AdapterChoiceItem(String key, String description, int icon) {
this.description = description;
this.key = key;
this.icon = icon;
}
}
private static class Choice implements Serializable {
final int serviceId;
final String url, playerChoice;
final LinkType linkType;
Choice(int serviceId, LinkType linkType, String url, String playerChoice) {
this.serviceId = serviceId;
this.linkType = linkType;
this.url = url;
this.playerChoice = playerChoice;
}
@Override
public String toString() {
return serviceId + ":" + url + " > " + linkType + " ::: " + playerChoice;
}
}
/*//////////////////////////////////////////////////////////////////////////
// Service Fetcher
//////////////////////////////////////////////////////////////////////////*/
public static class FetcherService extends IntentService {
private static final int ID = 456;
public static final String KEY_CHOICE = "key_choice";
private Disposable fetcher;
public FetcherService() {
super(FetcherService.class.getSimpleName());
}
@Override
public void onCreate() {
super.onCreate();
startForeground(ID, createNotification().build());
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent == null) return;
final Serializable serializable = intent.getSerializableExtra(KEY_CHOICE);
if (!(serializable instanceof Choice)) return;
Choice playerChoice = (Choice) serializable;
handleChoice(playerChoice);
}
public void handleChoice(Choice choice) {
Single<? extends Info> single = null;
UserAction userAction = UserAction.SOMETHING_ELSE;
switch (choice.linkType) {
case STREAM:
single = ExtractorHelper.getStreamInfo(choice.serviceId, choice.url, false);
userAction = UserAction.REQUESTED_STREAM;
break;
case CHANNEL:
single = ExtractorHelper.getChannelInfo(choice.serviceId, choice.url, false);
userAction = UserAction.REQUESTED_CHANNEL;
break;
case PLAYLIST:
single = ExtractorHelper.getPlaylistInfo(choice.serviceId, choice.url, false);
userAction = UserAction.REQUESTED_PLAYLIST;
break;
}
if (single != null) {
final UserAction finalUserAction = userAction;
final Consumer<Info> resultHandler = getResultHandler(choice);
fetcher = single
.observeOn(AndroidSchedulers.mainThread())
.subscribe(info -> {
resultHandler.accept(info);
if (fetcher != null) fetcher.dispose();
}, throwable -> ExtractorHelper.handleGeneralException(this,
choice.serviceId, choice.url, throwable, finalUserAction, ", opened with " + choice.playerChoice));
}
}
public Consumer<Info> getResultHandler(Choice choice) {
return info -> {
final String videoPlayerKey = getString(R.string.video_player_key);
final String backgroundPlayerKey = getString(R.string.background_player_key);
final String popupPlayerKey = getString(R.string.popup_player_key);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
boolean useOldVideoPlayer = PlayerHelper.isUsingOldPlayer(this);
PlayQueue playQueue;
String playerChoice = choice.playerChoice;
if (info instanceof StreamInfo) {
if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) {
NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info);
} else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) {
NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info);
} else if (playerChoice.equals(videoPlayerKey) && useOldVideoPlayer) {
NavigationHelper.playOnOldVideoPlayer(this, (StreamInfo) info);
} else {
playQueue = new SinglePlayQueue((StreamInfo) info);
if (playerChoice.equals(videoPlayerKey)) {
NavigationHelper.playOnMainPlayer(this, playQueue);
} else if (playerChoice.equals(backgroundPlayerKey)) {
NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
} else if (playerChoice.equals(popupPlayerKey)) {
NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true);
}
}
}
if (info instanceof ChannelInfo || info instanceof PlaylistInfo) {
playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info);
if (playerChoice.equals(videoPlayerKey)) {
NavigationHelper.playOnMainPlayer(this, playQueue);
} else if (playerChoice.equals(backgroundPlayerKey)) {
NavigationHelper.playOnBackgroundPlayer(this, playQueue);
} else if (playerChoice.equals(popupPlayerKey)) {
NavigationHelper.playOnPopupPlayer(this, playQueue);
}
}
};
}
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
if (fetcher != null) fetcher.dispose();
}
private NotificationCompat.Builder createNotification() {
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
.setOngoing(true)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentTitle(getString(R.string.preferred_player_fetcher_notification_title))
.setContentText(getString(R.string.preferred_player_fetcher_notification_message));
}
}
}

View file

@ -1,49 +0,0 @@
package org.schabi.newpipe;
import android.content.Intent;
import android.os.Build;
import android.widget.Toast;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.PermissionHelper;
/**
* Get the url from the intent and open a popup player
*/
public class RouterPopupActivity extends RouterActivity {
@Override
protected void handleUrl(String url) {
if (!PermissionHelper.isPopupEnabled(this)) {
PermissionHelper.showPopupEnablementToast(this);
finish();
return;
}
StreamingService service;
try {
service = NewPipe.getServiceByUrl(url);
} catch (ExtractionException e) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
return;
}
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
switch (service.getLinkTypeByUrl(url)) {
case STREAM:
break;
default:
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
return;
}
callIntent.putExtra(Constants.KEY_URL, url);
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
startService(callIntent);
finish();
}
}

View file

@ -19,7 +19,6 @@ import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
@ -247,6 +246,12 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
// Utils
//////////////////////////////////////////////////////////////////////////*/
public void setTitle(String title) {
if (DEBUG) Log.d(TAG, "setTitle() called with: title = [" + title + "]");
if (activity != null && activity.getSupportActionBar() != null) {
activity.getSupportActionBar().setTitle(title);
}
}
protected void openUrlInBrowser(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));

View file

@ -23,6 +23,7 @@ import android.view.ViewGroup;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.kiosk.KioskList;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
@ -33,23 +34,20 @@ import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
private ViewPager viewPager;
private boolean showBlankTab = false;
public int currentServiceId = -1;
private ViewPager viewPager;
/*//////////////////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////////////////*/
private static final int FALLBACK_SERVICE_ID = 0; // Youtube
private static final String FALLBACK_CHANNEL_URL =
"https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
private static final int FALLBACK_SERVICE_ID = ServiceList.YouTube.getId();
private static final String FALLBACK_CHANNEL_URL = "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ";
private static final String FALLBACK_CHANNEL_NAME = "Music";
private static final String FALLBACK_KIOSK_ID = "Trending";
private static final int KIOSK_MENU_OFFSET = 2000;
@ -66,8 +64,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
currentServiceId = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(getString(R.string.current_service_key), "0"));
currentServiceId = ServiceHelper.getSelectedServiceId(activity);
return inflater.inflate(R.layout.fragment_main, container, false);
}
@ -85,22 +82,10 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
tabLayout.setupWithViewPager(viewPager);
int channelIcon;
int whatsHotIcon;
int channelIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_channel);
int whatsHotIcon = ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.ic_hot);
if (ThemeHelper.isLightThemeSelected(getActivity())) {
tabLayout.setBackgroundColor(getResources().getColor(R.color.light_youtube_primary_color));
channelIcon = R.drawable.ic_channel_black_24dp;
whatsHotIcon = R.drawable.ic_whatshot_black_24dp;
} else {
channelIcon = R.drawable.ic_channel_white_24dp;
whatsHotIcon = R.drawable.ic_whatshot_white_24dp;
}
if (PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key))) {
if (isSubscriptionsPageOnlySelected()) {
tabLayout.getTabAt(0).setIcon(channelIcon);
} else {
tabLayout.getTabAt(0).setIcon(whatsHotIcon);
@ -138,7 +123,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
NavigationHelper.openSearchFragment(getFragmentManager(), NewPipe.getIdOfService(PreferenceManager.getDefaultSharedPreferences(getActivity()).getString("service", "YouTube")), "");
NavigationHelper.openSearchFragment(getFragmentManager(), ServiceHelper.getSelectedServiceId(activity), "");
return true;
}
return super.onOptionsItemSelected(item);
@ -163,11 +148,6 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
private class PagerAdapter extends FragmentPagerAdapter {
private int[] tabTitles = new int[]{
R.string.tab_main,
R.string.tab_subscriptions
};
PagerAdapter(FragmentManager fm) {
super(fm);
}
@ -176,13 +156,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
public Fragment getItem(int position) {
switch (position) {
case 0:
if(PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key))) {
return new SubscriptionFragment();
} else {
return getMainPageFragment();
}
return isSubscriptionsPageOnlySelected() ? new SubscriptionFragment() : getMainPageFragment();
case 1:
return new SubscriptionFragment();
default:
@ -198,13 +172,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
@Override
public int getCount() {
if(PreferenceManager.getDefaultSharedPreferences(getActivity())
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key))) {
return 1;
} else {
return 2;
}
return isSubscriptionsPageOnlySelected() ? 1 : 2;
}
}
@ -212,28 +180,33 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
// Main page content
//////////////////////////////////////////////////////////////////////////*/
private boolean isSubscriptionsPageOnlySelected() {
return PreferenceManager.getDefaultSharedPreferences(activity)
.getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key))
.equals(getString(R.string.subscription_page_key));
}
private Fragment getMainPageFragment() {
try {
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(getActivity());
final String setMainPage = preferences.getString(getString(R.string.main_page_content_key),
getString(R.string.main_page_selectd_kiosk_id));
if(setMainPage.equals(getString(R.string.blank_page_key))) {
getString(R.string.main_page_selectd_kiosk_id));
if (setMainPage.equals(getString(R.string.blank_page_key))) {
return new BlankFragment();
} else if(setMainPage.equals(getString(R.string.kiosk_page_key))) {
} else if (setMainPage.equals(getString(R.string.kiosk_page_key))) {
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
FALLBACK_SERVICE_ID);
String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id),
FALLBACK_KIOSK_ID);
KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId
);
KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId);
fragment.useAsFrontPage(true);
return fragment;
} else if(setMainPage.equals(getString(R.string.feed_page_key))) {
} else if (setMainPage.equals(getString(R.string.feed_page_key))) {
FeedFragment fragment = new FeedFragment();
fragment.useAsFrontPage(true);
return fragment;
} else if(setMainPage.equals(getString(R.string.channel_page_key))) {
} else if (setMainPage.equals(getString(R.string.channel_page_key))) {
int serviceId = preferences.getInt(getString(R.string.main_page_selected_service),
FALLBACK_SERVICE_ID);
String url = preferences.getString(getString(R.string.main_page_selected_channel_url),
@ -266,7 +239,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
StreamingService service = NewPipe.getService(currentServiceId);
KioskList kl = service.getKioskList();
int i = 0;
for(final String ks : kl.getAvailableKiosks()) {
for (final String ks : kl.getAvailableKiosks()) {
menu.add(0, KIOSK_MENU_OFFSET + i, Menu.NONE,
KioskTranslator.getTranslatedKioskName(ks, getContext()))
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {

View file

@ -13,7 +13,6 @@ import android.support.annotation.DrawableRes;
import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.text.TextUtilsCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
@ -25,7 +24,6 @@ import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -37,7 +35,6 @@ import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
@ -51,7 +48,6 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
@ -80,6 +76,7 @@ import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.Serializable;
import java.util.ArrayList;
@ -394,7 +391,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
if (relatedStreamsView.getChildCount() > initialCount) {
relatedStreamsView.removeViews(initialCount, relatedStreamsView.getChildCount() - (initialCount));
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
return;
}
@ -404,7 +401,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
//Log.d(TAG, "i = " + i);
relatedStreamsView.addView(infoItemBuilder.buildView(relatedStreamsView, item));
}
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.collapse)));
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.collapse)));
}
/*//////////////////////////////////////////////////////////////////////////
@ -579,7 +576,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
relatedStreamRootLayout.setVisibility(View.VISIBLE);
relatedStreamExpandButton.setVisibility(View.VISIBLE);
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, resolveResourceIdFromAttr(R.attr.expand)));
relatedStreamExpandButton.setImageDrawable(ContextCompat.getDrawable(activity, ThemeHelper.resolveResourceIdFromAttr(activity, R.attr.expand)));
} else {
if (info.getNextVideo() == null) relatedStreamRootLayout.setVisibility(View.GONE);
relatedStreamExpandButton.setVisibility(View.GONE);
@ -807,7 +804,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
openNormalBackgroundPlayer(append);
} else {
openExternalBackgroundPlayer(audioStream);
NavigationHelper.playOnExternalPlayer(activity, currentInfo.getName(), currentInfo.getUploaderName(), audioStream);
}
}
@ -841,13 +838,12 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
}
if (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(this.getString(R.string.use_external_video_player_key), false)) {
openExternalVideoPlayer(selectedVideoStream);
NavigationHelper.playOnExternalPlayer(activity, currentInfo.getName(), currentInfo.getUploaderName(), selectedVideoStream);
} else {
openNormalPlayer(selectedVideoStream);
}
}
private void openNormalBackgroundPlayer(final boolean append) {
final PlayQueue itemQueue = new SinglePlayQueue(currentInfo);
if (append) {
@ -857,40 +853,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
}
}
private void openExternalBackgroundPlayer(AudioStream audioStream) {
Intent intent;
intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(audioStream.getUrl()), audioStream.getFormat().getMimeType());
intent.putExtra(Intent.EXTRA_TITLE, currentInfo.getName());
intent.putExtra("title", currentInfo.getName());
activity.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(activity.getString(R.string.fdroid_vlc_url)));
activity.startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.i(TAG, "You unlocked a secret unicorn.");
}
});
builder.create().show();
Log.e(TAG, "Either no Streaming player for audio was installed, or something important crashed:");
e.printStackTrace();
}
}
private void openNormalPlayer(VideoStream selectedVideoStream) {
Intent mIntent;
boolean useOldPlayer = PlayerHelper.isUsingOldPlayer(activity) || (Build.VERSION.SDK_INT < 16);
@ -909,33 +871,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
startActivity(mIntent);
}
private void openExternalVideoPlayer(VideoStream selectedVideoStream) {
// External Player
Intent intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse(selectedVideoStream.getUrl()), selectedVideoStream.getFormat().getMimeType())
.putExtra(Intent.EXTRA_TITLE, currentInfo.getName())
.putExtra("title", currentInfo.getName());
this.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent()
.setAction(Intent.ACTION_VIEW)
.setData(Uri.parse(getString(R.string.fdroid_vlc_url)));
startActivity(intent);
}
})
.setNegativeButton(R.string.cancel, null);
builder.create().show();
}
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/

View file

@ -84,17 +84,6 @@ public abstract class BaseListInfoFragment<I extends ListInfo> extends BaseListF
currentNextItemsUrl = (String) savedObjects.poll();
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
public void setTitle(String title) {
Log.d(TAG, "setTitle() called with: title = [" + title + "]");
if (activity.getSupportActionBar() != null) {
activity.getSupportActionBar().setTitle(title);
}
}
/*//////////////////////////////////////////////////////////////////////////
// Load and handle
//////////////////////////////////////////////////////////////////////////*/

View file

@ -43,7 +43,6 @@ import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -102,11 +101,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
if(activity != null
&& useAsFrontPage
&& isVisibleToUser) {
try {
activity.getSupportActionBar().setTitle(currentInfo.getName());
} catch (Exception e) {
onError(e);
}
setTitle(currentInfo != null ? currentInfo.getName() : name);
}
}

View file

@ -57,6 +57,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
@State
protected String kioskId = "";
protected String kioskTranslatedName;
/*//////////////////////////////////////////////////////////////////////////
// Views
@ -87,16 +88,11 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
try {
activity.getSupportActionBar()
.setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
} catch (Exception e) {
onUnrecoverableError(e, UserAction.UI_ERROR,
"none",
"none", R.string.app_ui_crash);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
kioskTranslatedName = KioskTranslator.getTranslatedKioskName(kioskId, activity);
name = kioskTranslatedName;
}
@Override
@ -104,8 +100,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
super.setUserVisibleHint(isVisibleToUser);
if(useAsFrontPage && isVisibleToUser && activity != null) {
try {
activity.getSupportActionBar()
.setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
setTitle(kioskTranslatedName);
} catch (Exception e) {
onUnrecoverableError(e, UserAction.UI_ERROR,
"none",
@ -115,11 +110,8 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_kiosk, container, false);
activity.getSupportActionBar()
.setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity()));
return view;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_kiosk, container, false);
}
/*//////////////////////////////////////////////////////////////////////////
@ -171,9 +163,8 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
public void handleResult(@NonNull final KioskInfo result) {
super.handleResult(result);
String title = KioskTranslator.getTranslatedKioskName(result.id, getActivity());
ActionBar supportActionBar = activity.getSupportActionBar();
supportActionBar.setTitle(title);
name = kioskTranslatedName;
setTitle(kioskTranslatedName);
if (!result.getErrors().isEmpty()) {
showSnackBarError(result.getErrors(),

View file

@ -4,6 +4,7 @@ import android.content.Intent;
import android.view.MenuItem;
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.PermissionHelper;
import static org.schabi.newpipe.player.BackgroundPlayer.ACTION_CLOSE;
@ -48,6 +49,12 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity {
@Override
public boolean onPlayerOptionSelected(MenuItem item) {
if (item.getItemId() == R.id.action_switch_popup) {
if (!PermissionHelper.isPopupEnabled(this)) {
PermissionHelper.showPopupEnablementToast(this);
return true;
}
this.player.setRecovery();
getApplicationContext().sendBroadcast(getPlayerShutdownIntent());
getApplicationContext().startService(getSwitchIntent(PopupVideoPlayer.class));

View file

@ -111,6 +111,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
public static final String PLAYBACK_QUALITY = "playback_quality";
public static final String PLAY_QUEUE = "play_queue";
public static final String APPEND_ONLY = "append_only";
public static final String SELECT_ON_APPEND = "select_on_append";
/*//////////////////////////////////////////////////////////////////////////
// Playback
@ -218,7 +219,13 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
// Resolve append intents
if (intent.getBooleanExtra(APPEND_ONLY, false) && playQueue != null) {
int sizeBeforeAppend = playQueue.size();
playQueue.append(queue.getStreams());
if (intent.getBooleanExtra(SELECT_ON_APPEND, false) && queue.getStreams().size() > 0) {
playQueue.setIndex(sizeBeforeAppend);
}
return;
}

View file

@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.media.AudioManager;
import android.os.Build;
@ -65,8 +64,6 @@ import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.PopupMenuIconHacker;
import org.schabi.newpipe.util.ThemeHelper;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -400,9 +397,8 @@ public final class MainVideoPlayer extends Activity {
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
if (simpleExoPlayer == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !PermissionHelper.checkSystemAlertWindowPermission(MainVideoPlayer.this)) {
Toast.makeText(MainVideoPlayer.this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
if (!PermissionHelper.isPopupEnabled(context)) {
PermissionHelper.showPopupEnablementToast(context);
return;
}

View file

@ -31,7 +31,6 @@ import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
@ -49,20 +48,12 @@ import android.widget.PopupMenu;
import android.widget.RemoteViews;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.event.PlayerEventListener;
@ -70,22 +61,12 @@ import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.IOException;
import java.util.List;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import static org.schabi.newpipe.player.helper.PlayerHelper.isUsingOldPlayer;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
@ -125,8 +106,8 @@ public final class PopupVideoPlayer extends Service {
private RemoteViews notRemoteView;
private VideoPlayerImpl playerImpl;
private Disposable currentWorker;
private LockManager lockManager;
/*//////////////////////////////////////////////////////////////////////////
// Service-Activity Binder
//////////////////////////////////////////////////////////////////////////*/
@ -157,21 +138,8 @@ public final class PopupVideoPlayer extends Service {
if (playerImpl.getPlayer() == null) initPopup();
if (!playerImpl.isPlaying()) playerImpl.getPlayer().setPlayWhenReady(true);
if (intent != null && intent.getStringExtra(Constants.KEY_URL) != null) {
final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
final String url = intent.getStringExtra(Constants.KEY_URL);
playerImpl.handleIntent(intent);
playerImpl.setStartedFromNewPipe(false);
final FetcherHandler fetcherRunnable = new FetcherHandler(this, serviceId, url);
currentWorker = ExtractorHelper.getStreamInfo(serviceId,url,false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(fetcherRunnable::onReceive, fetcherRunnable::onError);
} else {
playerImpl.setStartedFromNewPipe(true);
playerImpl.handleIntent(intent);
}
return START_NOT_STICKY;
}
@ -302,7 +270,6 @@ public final class PopupVideoPlayer extends Service {
}
if (lockManager != null) lockManager.releaseWifiAndCpu();
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
if (currentWorker != null) currentWorker.dispose();
mBinder = null;
playerImpl = null;
@ -452,7 +419,6 @@ public final class PopupVideoPlayer extends Service {
this.getPlaybackPitch(),
this.getPlaybackQuality()
);
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else {
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
@ -862,63 +828,4 @@ public final class PopupVideoPlayer extends Service {
return true;
}
}
/**
* Fetcher handler used if open by a link out of NewPipe
*/
private class FetcherHandler {
private final int serviceId;
private final String url;
private final Context context;
private final Handler mainHandler;
private FetcherHandler(Context context, int serviceId, String url) {
this.mainHandler = new Handler(PopupVideoPlayer.this.getMainLooper());
this.context = context;
this.url = url;
this.serviceId = serviceId;
}
private void onReceive(final StreamInfo info) {
mainHandler.post(() -> {
final Intent intent = NavigationHelper.getPlayerIntent(getApplicationContext(),
PopupVideoPlayer.class, new SinglePlayQueue(info));
playerImpl.handleIntent(intent);
});
}
private void onError(final Throwable exception) {
if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]");
exception.printStackTrace();
mainHandler.post(() -> {
if (exception instanceof ReCaptchaException) {
onReCaptchaException();
} else if (exception instanceof IOException) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
Toast.makeText(context, R.string.live_streams_not_supported, Toast.LENGTH_LONG).show();
} else if (exception instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else {
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error :
exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
ErrorActivity.reportError(mainHandler, context, exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(serviceId), url, errorId));
}
});
stopSelf();
}
private void onReCaptchaException() {
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
Intent intent = new Intent(context, ReCaptchaActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
stopSelf();
}
}
}

View file

@ -183,7 +183,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
this.player.getPlaybackSpeed(),
this.player.getPlaybackPitch(),
null
);
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
////////////////////////////////////////////////////////////////////////////
// Service Connection

View file

@ -39,7 +39,6 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
@ -85,12 +84,6 @@ public abstract class VideoPlayer extends BasePlayer
public static final boolean DEBUG = BasePlayer.DEBUG;
public final String TAG;
/*//////////////////////////////////////////////////////////////////////////
// Intent
//////////////////////////////////////////////////////////////////////////*/
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
/*//////////////////////////////////////////////////////////////////////////
// Player
//////////////////////////////////////////////////////////////////////////*/
@ -102,7 +95,6 @@ public abstract class VideoPlayer extends BasePlayer
protected String playbackQuality;
private boolean startedFromNewPipe = true;
protected boolean wasPlaying = false;
/*//////////////////////////////////////////////////////////////////////////
@ -695,14 +687,6 @@ public abstract class VideoPlayer extends BasePlayer
return availableStreams.get(selectedStreamIndex);
}
public boolean isStartedFromNewPipe() {
return startedFromNewPipe;
}
public void setStartedFromNewPipe(boolean startedFromNewPipe) {
this.startedFromNewPipe = startedFromNewPipe;
}
public Handler getControlsVisibilityHandler() {
return controlsVisibilityHandler;
}

View file

@ -15,6 +15,10 @@ public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, C
super(item);
}
public ChannelPlayQueue(final ChannelInfo info) {
this(info.getServiceId(), info.getUrl(), info.getNextStreamsUrl(), info.getRelatedStreams(), 0);
}
public ChannelPlayQueue(final int serviceId,
final String url,
final String nextPageUrl,

View file

@ -15,6 +15,10 @@ public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo,
super(item);
}
public PlaylistPlayQueue(final PlaylistInfo info) {
this(info.getServiceId(), info.getUrl(), info.getNextStreamsUrl(), info.getRelatedStreams(), 0);
}
public PlaylistPlayQueue(final int serviceId,
final String url,
final String nextPageUrl,

View file

@ -44,6 +44,7 @@ import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Vector;
@ -144,12 +145,7 @@ public class ErrorActivity extends AppCompatActivity {
// async call
public static void reportError(Handler handler, final Context context, final List<Throwable> el,
final Class returnActivity, final View rootView, final ErrorInfo errorInfo) {
handler.post(new Runnable() {
@Override
public void run() {
reportError(context, el, returnActivity, rootView, errorInfo);
}
});
handler.post(() -> reportError(context, el, returnActivity, rootView, errorInfo));
}
public static void reportError(final Context context, final CrashReportData report, final ErrorInfo errorInfo) {
@ -218,17 +214,13 @@ public class ErrorActivity extends AppCompatActivity {
addGuruMeditaion();
currentTimeStamp = getCurrentTimeStamp();
reportButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
reportButton.setOnClickListener((View v) -> {
Intent i = new Intent(Intent.ACTION_SENDTO);
i.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
.putExtra(Intent.EXTRA_TEXT, buildJson());
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS))
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
.putExtra(Intent.EXTRA_TEXT, buildJson());
startActivity(Intent.createChooser(intent, "Send Email"));
}
startActivity(Intent.createChooser(i, "Send Email"));
});
reportButton.setEnabled(false);

View file

@ -1,9 +1,14 @@
package org.schabi.newpipe.settings;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.util.Log;
import android.widget.Toast;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
@ -12,91 +17,208 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ZipHelper;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.annotation.Nonnull;
public class ContentSettingsFragment extends BasePreferenceFragment {
private static final int REQUEST_IMPORT_PATH = 8945;
private static final int REQUEST_EXPORT_PATH = 30945;
private String homeDir;
private File databasesDir;
private File newpipe_db;
private File newpipe_db_journal;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
homeDir = getActivity().getApplicationInfo().dataDir;
databasesDir = new File(homeDir + "/databases");
newpipe_db = new File(homeDir + "/databases/newpipe.db");
newpipe_db_journal = new File(homeDir + "/databases/newpipe.db-journal");
addPreferencesFromResource(R.xml.content_settings);
final ListPreference mainPageContentPref = (ListPreference) findPreference(getString(R.string.main_page_content_key));
mainPageContentPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValueO) {
final String newValue = newValueO.toString();
mainPageContentPref.setOnPreferenceChangeListener((Preference preference, Object newValueO) -> {
final String newValue = newValueO.toString();
final String mainPrefOldValue =
defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page");
final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref);
final String mainPrefOldValue =
defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page");
final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref);
if(newValue.equals(getString(R.string.kiosk_page_key))) {
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
selectKioskFragment.setOnSelectedLisener(new SelectKioskFragment.OnSelectedLisener() {
@Override
public void onKioskSelected(String kioskId, int service_id) {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service_id).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
String serviceName = "";
try {
serviceName = NewPipe.getService(service_id).getServiceInfo().name;
} catch (ExtractionException e) {
onError(e);
}
String kioskName = KioskTranslator.getTranslatedKioskName(kioskId,
getContext());
String summary =
String.format(getString(R.string.service_kiosk_string),
serviceName,
kioskName);
mainPageContentPref.setSummary(summary);
}
});
selectKioskFragment.setOnCancelListener(new SelectKioskFragment.OnCancelListener() {
@Override
public void onCancel() {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
}
});
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
} else if(newValue.equals(getString(R.string.channel_page_key))) {
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
selectChannelFragment.setOnSelectedLisener(new SelectChannelFragment.OnSelectedLisener() {
@Override
public void onChannelSelected(String url, String name, int service) {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_url), url).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_name), name).apply();
mainPageContentPref.setSummary(name);
}
});
selectChannelFragment.setOnCancelListener(new SelectChannelFragment.OnCancelListener() {
@Override
public void onCancel() {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
}
});
selectChannelFragment.show(getFragmentManager(), "select_channel");
} else {
mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue));
}
defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
return true;
if(newValue.equals(getString(R.string.kiosk_page_key))) {
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service_id).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply();
String serviceName = "";
try {
serviceName = NewPipe.getService(service_id).getServiceInfo().name;
} catch (ExtractionException e) {
onError(e);
}
String kioskName = KioskTranslator.getTranslatedKioskName(kioskId,
getContext());
String summary =
String.format(getString(R.string.service_kiosk_string),
serviceName,
kioskName);
mainPageContentPref.setSummary(summary);
});
selectKioskFragment.setOnCancelListener(() -> {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
});
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
} else if(newValue.equals(getString(R.string.channel_page_key))) {
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
defaultPreferences.edit()
.putInt(getString(R.string.main_page_selected_service), service).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_url), url).apply();
defaultPreferences.edit()
.putString(getString(R.string.main_page_selected_channel_name), name).apply();
mainPageContentPref.setSummary(name);
});
selectChannelFragment.setOnCancelListener(() -> {
mainPageContentPref.setSummary(mainPrefOldSummary);
mainPageContentPref.setValue(mainPrefOldValue);
});
selectChannelFragment.show(getFragmentManager(), "select_channel");
} else {
mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue));
}
defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
return true;
});
Preference importDataPreference = findPreference(getString(R.string.import_data));
importDataPreference.setOnPreferenceClickListener((Preference p) -> {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, false)
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_FILE);
startActivityForResult(i, REQUEST_IMPORT_PATH);
return true;
});
Preference exportDataPreference = findPreference(getString(R.string.export_data));
exportDataPreference.setOnPreferenceClickListener((Preference p) -> {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)
.putExtra(FilePickerActivityHelper.EXTRA_MODE, FilePickerActivityHelper.MODE_DIR);
startActivityForResult(i, REQUEST_EXPORT_PATH);
return true;
});
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nonnull Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (DEBUG) {
Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]");
}
if ((requestCode == REQUEST_IMPORT_PATH || requestCode == REQUEST_EXPORT_PATH)
&& resultCode == Activity.RESULT_OK) {
String path = data.getData().getPath();
if (requestCode == REQUEST_EXPORT_PATH) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
exportDatabase(path + "/NewPipeData-" + sdf.format(new Date()) + ".zip");
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.override_current_data)
.setPositiveButton(android.R.string.ok,
(DialogInterface d, int id) -> importDatabase(path))
.setNegativeButton(android.R.string.cancel,
(DialogInterface d, int id) -> d.cancel());
builder.create().show();
}
}
}
private void exportDatabase(String path) {
try {
ZipOutputStream outZip = new ZipOutputStream(
new BufferedOutputStream(
new FileOutputStream(path)));
ZipHelper.addFileToZip(outZip, newpipe_db.getPath(), "newpipe.db");
ZipHelper.addFileToZip(outZip, newpipe_db_journal.getPath(), "newpipe.db-journal");
outZip.close();
Toast.makeText(getContext(), R.string.export_complete_toast, Toast.LENGTH_SHORT)
.show();
} catch (Exception e) {
onError(e);
}
}
private void importDatabase(String filePath) {
// check if file is supported
ZipFile zipFile = null;
try {
zipFile = new ZipFile(filePath);
} catch (IOException ioe) {
Toast.makeText(getContext(), R.string.no_valid_zip_file, Toast.LENGTH_SHORT)
.show();
return;
} finally {
try {
zipFile.close();
} catch (Exception e){}
}
try {
ZipInputStream zipIn = new ZipInputStream(
new BufferedInputStream(
new FileInputStream(filePath)));
if (!databasesDir.exists() && !databasesDir.mkdir()) {
throw new Exception("Could not create databases dir");
}
if(!(ZipHelper.extractFileFromZip(zipIn, newpipe_db.getPath(), "newpipe.db")
&& ZipHelper.extractFileFromZip(zipIn, newpipe_db_journal.getPath(), "newpipe.db-journal"))) {
Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG)
.show();
}
zipIn.close();
// restart app to properly load db
//App.restart(getContext());
System.exit(0);
} catch (Exception e) {
onError(e);
}
}
@Override
@ -117,8 +239,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
getString(R.string.main_page_selected_service), 0));
String kioskName = KioskTranslator.getTranslatedKioskName(
defaultPreferences.getString(
getString(R.string.main_page_selectd_kiosk_id), "Trending"),
defaultPreferences.getString(
getString(R.string.main_page_selectd_kiosk_id), "Trending"),
getContext());
String summary =

View file

@ -46,7 +46,8 @@ public class DownloadSettingsFragment extends BasePreferenceFragment {
Log.d(TAG, "onPreferenceTreeClick() called with: preference = [" + preference + "]");
}
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE) || preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
if (preference.getKey().equals(DOWNLOAD_PATH_PREFERENCE)
|| preference.getKey().equals(DOWNLOAD_PATH_AUDIO_PREFERENCE)) {
Intent i = new Intent(getActivity(), FilePickerActivityHelper.class)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false)
.putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true)

View file

@ -14,21 +14,17 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.fragments.subscription.SubscriptionService;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ServiceIconMapper;
import org.schabi.newpipe.util.ServiceHelper;
import java.util.List;
import java.util.Vector;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* Created by Christian Schabesberger on 09.10.17.
* SelectKioskFragment.java is part of NewPipe.
@ -125,13 +121,15 @@ public class SelectKioskFragment extends DialogFragment {
throws Exception {
for(StreamingService service : NewPipe.getServices()) {
//TODO: Multi-service support
if (service.getServiceId() != ServiceList.YouTube.getId()) continue;
for(String kioskId : service.getKioskList().getAvailableKiosks()) {
String name = String.format(getString(R.string.service_kiosk_string),
service.getServiceInfo().name,
KioskTranslator.getTranslatedKioskName(kioskId, getContext()));
kioskList.add(new Entry(
//ServiceIconMapper.getIconResource(service.getServiceId()),
ServiceIconMapper.getIconResource(-1),
ServiceHelper.getIcon(service.getServiceId()),
service.getServiceId(),
kioskId,
name));
@ -140,9 +138,7 @@ public class SelectKioskFragment extends DialogFragment {
}
public int getItemCount() {
//todo: uncommend this line on multyservice support
//return kioskList.size();
return 1;
return kioskList.size();
}
public SelectKioskItemHolder onCreateViewHolder(ViewGroup parent, int type) {

View file

@ -43,7 +43,8 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
@Override
protected void onCreate(Bundle savedInstanceBundle) {
ThemeHelper.setTheme(this);
setTheme(ThemeHelper.getSettingsThemeStyle(this));
super.onCreate(savedInstanceBundle);
setContentView(R.layout.settings_layout);
@ -72,7 +73,9 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
finish();
} else getSupportFragmentManager().popBackStack();
}
return true;
}

View file

@ -19,29 +19,38 @@
package org.schabi.newpipe.util;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.Info;
import org.schabi.newpipe.extractor.ListExtractor.NextItemsResult;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.search.SearchEngine;
import org.schabi.newpipe.extractor.search.SearchResult;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.List;
import java.util.concurrent.Callable;
import io.reactivex.Maybe;
import io.reactivex.MaybeSource;
import io.reactivex.Single;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
public final class ExtractorHelper {
private static final String TAG = ExtractorHelper.class.getSimpleName();
@ -198,6 +207,37 @@ public final class ExtractorHelper {
});
}
/**
* A simple and general error handler that show a Toast for known exceptions, and for others, opens the report error activity with the (optional) error message.
*/
public static void handleGeneralException(Context context, int serviceId, String url, Throwable exception, UserAction userAction, String optionalErrorMessage) {
final Handler handler = new Handler(context.getMainLooper());
handler.post(() -> {
if (exception instanceof ReCaptchaException) {
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
Intent intent = new Intent(context, ReCaptchaActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} else if (exception instanceof IOException) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
} else if (exception instanceof YoutubeStreamExtractor.GemaException) {
Toast.makeText(context, R.string.blocked_by_gema, Toast.LENGTH_LONG).show();
} else if (exception instanceof YoutubeStreamExtractor.LiveStreamException) {
Toast.makeText(context, R.string.live_streams_not_supported, Toast.LENGTH_LONG).show();
} else if (exception instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else {
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error :
exception instanceof ParsingException ? R.string.parsing_error : R.string.general_error;
ErrorActivity.reportError(handler, context, exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(userAction,
serviceId == -1 ? "none" : NewPipe.getNameOfService(serviceId), url + (optionalErrorMessage == null ? "" : optionalErrorMessage), errorId));
}
});
}
/**
* Check if throwable have the cause that can be assignable from the causes to check.
*
@ -263,17 +303,4 @@ public final class ExtractorHelper {
InterruptedIOException.class,
InterruptedException.class);
}
public static String toUpperCase(String value) {
StringBuilder sb = new StringBuilder(value);
for (int index = 0; index < sb.length(); index++) {
char c = sb.charAt(index);
if (Character.isLowerCase(c)) {
sb.setCharAt(index, Character.toUpperCase(c));
} else {
sb.setCharAt(index, Character.toLowerCase(c));
}
}
return sb.toString();
}
}

View file

@ -1,4 +1,4 @@
/**
/*
* Copyright 2017 Mauricio Colli <mauriciocolli@outlook.com>
* InfoCache.java is part of NewPipe
*

View file

@ -9,6 +9,8 @@ import android.os.Build;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.ImageLoader;
@ -21,6 +23,10 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
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.VideoStream;
import org.schabi.newpipe.fragments.MainFragment;
import org.schabi.newpipe.fragments.detail.VideoDetailFragment;
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
@ -36,16 +42,21 @@ import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayerActivity;
import org.schabi.newpipe.player.VideoPlayer;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.settings.SettingsActivity;
import java.util.ArrayList;
@SuppressWarnings({"unused", "WeakerAccess"})
public class NavigationHelper {
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
/*//////////////////////////////////////////////////////////////////////////
// Players
//////////////////////////////////////////////////////////////////////////*/
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
@ -65,9 +76,11 @@ public class NavigationHelper {
public static Intent getPlayerEnqueueIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue) {
final PlayQueue playQueue,
final boolean selectOnAppend) {
return getPlayerIntent(context, targetClazz, playQueue)
.putExtra(BasePlayer.APPEND_ONLY, true);
.putExtra(BasePlayer.APPEND_ONLY, true)
.putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend);
}
public static Intent getPlayerIntent(final Context context,
@ -84,16 +97,39 @@ public class NavigationHelper {
}
public static void playOnMainPlayer(final Context context, final PlayQueue queue) {
context.startActivity(getPlayerIntent(context, MainVideoPlayer.class, queue));
final Intent playerIntent = getPlayerIntent(context, MainVideoPlayer.class, queue);
playerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(playerIntent);
}
public static void playOnPopupPlayer(final Activity activity, final PlayQueue queue) {
if (!PermissionHelper.isPopupEnabled(activity)) {
PermissionHelper.showPopupEnablementToast(activity);
public static void playOnOldVideoPlayer(Context context, StreamInfo info) {
ArrayList<VideoStream> videoStreamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.getVideoStreams(), null, false));
int index = ListHelper.getDefaultResolutionIndex(context, videoStreamsList);
if (index == -1) {
Toast.makeText(context, R.string.video_streams_empty, Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
activity.startService(getPlayerIntent(activity, PopupVideoPlayer.class, queue));
VideoStream videoStream = videoStreamsList.get(index);
Intent intent = new Intent(context, PlayVideoActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(PlayVideoActivity.VIDEO_TITLE, info.getName())
.putExtra(PlayVideoActivity.STREAM_URL, videoStream.getUrl())
.putExtra(PlayVideoActivity.VIDEO_URL, info.getUrl())
.putExtra(PlayVideoActivity.START_POSITION, info.getStartPosition());
context.startActivity(intent);
}
public static void playOnPopupPlayer(final Context context, final PlayQueue queue) {
if (!PermissionHelper.isPopupEnabled(context)) {
PermissionHelper.showPopupEnablementToast(context);
return;
}
Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
context.startService(getPlayerIntent(context, PopupVideoPlayer.class, queue));
}
public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue) {
@ -101,19 +137,92 @@ public class NavigationHelper {
context.startService(getPlayerIntent(context, BackgroundPlayer.class, queue));
}
public static void enqueueOnPopupPlayer(final Activity activity, final PlayQueue queue) {
if (!PermissionHelper.isPopupEnabled(activity)) {
PermissionHelper.showPopupEnablementToast(activity);
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue) {
enqueueOnPopupPlayer(context, queue, false);
}
public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) {
if (!PermissionHelper.isPopupEnabled(context)) {
PermissionHelper.showPopupEnablementToast(context);
return;
}
Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
activity.startService(getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, queue));
Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
context.startService(getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend));
}
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue) {
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue));
enqueueOnBackgroundPlayer(context, queue, false);
}
public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend) {
Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show();
context.startService(getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend));
}
/*//////////////////////////////////////////////////////////////////////////
// External Players
//////////////////////////////////////////////////////////////////////////*/
public static void playOnExternalAudioPlayer(Context context, StreamInfo info) {
final int index = ListHelper.getDefaultAudioFormat(context, info.getAudioStreams());
if (index == -1) {
Toast.makeText(context, R.string.audio_streams_empty, Toast.LENGTH_SHORT).show();
return;
}
AudioStream audioStream = info.getAudioStreams().get(index);
playOnExternalPlayer(context, info.getName(), info.getUploaderName(), audioStream);
}
public static void playOnExternalVideoPlayer(Context context, StreamInfo info) {
ArrayList<VideoStream> videoStreamsList = new ArrayList<>(ListHelper.getSortedStreamVideosList(context, info.getVideoStreams(), null, false));
int index = ListHelper.getDefaultResolutionIndex(context, videoStreamsList);
if (index == -1) {
Toast.makeText(context, R.string.video_streams_empty, Toast.LENGTH_SHORT).show();
return;
}
VideoStream videoStream = videoStreamsList.get(index);
playOnExternalPlayer(context, info.getName(), info.getUploaderName(), videoStream);
}
public static void playOnExternalPlayer(Context context, String name, String artist, Stream stream) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(stream.getUrl()), stream.getFormat().getMimeType());
intent.putExtra(Intent.EXTRA_TITLE, name);
intent.putExtra("title", name);
intent.putExtra("artist", artist);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
resolveActivityOrAskToInstall(context, intent);
}
public static void resolveActivityOrAskToInstall(Context context, Intent intent) {
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
} else {
if (context instanceof Activity) {
new AlertDialog.Builder(context)
.setMessage(R.string.no_player_found)
.setPositiveButton(R.string.install, (dialog, which) -> {
Intent i = new Intent();
i.setAction(Intent.ACTION_VIEW);
i.setData(Uri.parse(context.getString(R.string.fdroid_vlc_url)));
context.startActivity(i);
})
.setNegativeButton(R.string.cancel, (dialog, which) -> Log.i("NavigationHelper", "You unlocked a secret unicorn."))
.show();
//Log.e("NavigationHelper", "Either no Streaming player for audio was installed, or something important crashed:");
} else {
Toast.makeText(context, R.string.no_player_found_toast, Toast.LENGTH_LONG).show();
}
}
}
/*//////////////////////////////////////////////////////////////////////////
// Through FragmentManager
//////////////////////////////////////////////////////////////////////////*/
@ -136,11 +245,21 @@ public class NavigationHelper {
.commit();
}
public static boolean tryGotoSearchFragment(FragmentManager fragmentManager) {
if (MainActivity.DEBUG) {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
Log.d("NavigationHelper", "tryGoToSearchFragment() [" + i + "] = [" + fragmentManager.getBackStackEntryAt(i) + "]");
}
}
return fragmentManager.popBackStackImmediate(SEARCH_FRAGMENT_TAG, 0);
}
public static void openSearchFragment(FragmentManager fragmentManager, int serviceId, String query) {
fragmentManager.beginTransaction()
.setCustomAnimations(R.animator.custom_fade_in, R.animator.custom_fade_out, R.animator.custom_fade_in, R.animator.custom_fade_out)
.replace(R.id.fragment_holder, SearchFragment.getInstance(serviceId, query))
.addToBackStack(null)
.addToBackStack(SEARCH_FRAGMENT_TAG)
.commit();
}
@ -287,19 +406,6 @@ public class NavigationHelper {
// Link handling
//////////////////////////////////////////////////////////////////////////*/
public static boolean openByLink(Context context, String url) {
Intent intentByLink;
try {
intentByLink = getIntentByLink(context, url);
} catch (ExtractionException e) {
return false;
}
intentByLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentByLink.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentByLink);
return true;
}
private static Intent getOpenIntent(Context context, String url, int serviceId, StreamingService.LinkType type) {
Intent mIntent = new Intent(context, MainActivity.class);
mIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
@ -317,15 +423,14 @@ public class NavigationHelper {
throw new ExtractionException("Service not supported at the moment");
}
int serviceId = service.getServiceId();
StreamingService.LinkType linkType = service.getLinkTypeByUrl(url);
if (linkType == StreamingService.LinkType.NONE) {
throw new ExtractionException("Url not known to service. service=" + serviceId + " url=" + url);
throw new ExtractionException("Url not known to service. service=" + service + " url=" + url);
}
url = getCleanUrl(service, url, linkType);
Intent rIntent = getOpenIntent(context, url, serviceId, linkType);
Intent rIntent = getOpenIntent(context, url, service.getServiceId(), linkType);
switch (linkType) {
case STREAM:
@ -337,7 +442,7 @@ public class NavigationHelper {
return rIntent;
}
private static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
public static String getCleanUrl(StreamingService service, String dirtyUrl, StreamingService.LinkType linkType) throws ExtractionException {
switch (linkType) {
case STREAM:
return service.getStreamUrlIdHandler().cleanUrl(dirtyUrl);
@ -351,7 +456,6 @@ public class NavigationHelper {
return null;
}
private static Uri openMarketUrl(String packageName) {
return Uri.parse("market://details")
.buildUpon()

View file

@ -20,7 +20,6 @@ import org.schabi.newpipe.R;
public class PermissionHelper {
public static final int PERMISSION_WRITE_STORAGE = 778;
public static final int PERMISSION_READ_STORAGE = 777;
public static final int PERMISSION_SYSTEM_ALERT_WINDOW = 779;
public static boolean checkStoragePermissions(Activity activity) {
@ -80,27 +79,25 @@ public class PermissionHelper {
* In order to be able to draw over other apps, the permission android.permission.SYSTEM_ALERT_WINDOW have to be granted.
* <p>
* On < API 23 (MarshMallow) the permission was granted when the user installed the application (via AndroidManifest),
* on > 23, however, it have to start a activity asking the user if he agree.
* on > 23, however, it have to start a activity asking the user if he agrees.
* <p>
* This method just return if canDraw over other apps, if it doesn't, try to get the permission,
* it does not get the result of the startActivityForResult, if the user accept, the next time that he tries to open
* it will return true.
* This method just return if the app has permission to draw over other apps, and if it doesn't, it will try to get the permission.
*
* @param activity context to startActivityForResult
* @return returns {@link Settings#canDrawOverlays(Context)}
**/
@RequiresApi(api = Build.VERSION_CODES.M)
public static boolean checkSystemAlertWindowPermission(Activity activity) {
if (!Settings.canDrawOverlays(activity)) {
Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + activity.getPackageName()));
activity.startActivityForResult(i, PERMISSION_SYSTEM_ALERT_WINDOW);
public static boolean checkSystemAlertWindowPermission(Context context) {
if (!Settings.canDrawOverlays(context)) {
Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName()));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
return false;
}else return true;
}
public static boolean isPopupEnabled(Activity activity) {
public static boolean isPopupEnabled(Context context) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
PermissionHelper.checkSystemAlertWindowPermission(activity);
PermissionHelper.checkSystemAlertWindowPermission(context);
}
public static void showPopupEnablementToast(Context context) {

View file

@ -0,0 +1,67 @@
package org.schabi.newpipe.util;
import android.content.Context;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
public class ServiceHelper {
private static final StreamingService DEFAULT_FALLBACK_SERVICE = ServiceList.YouTube.getService();
@DrawableRes
public static int getIcon(int serviceId) {
switch (serviceId) {
case 0:
return R.drawable.place_holder_youtube;
case 1:
return R.drawable.place_holder_circle;
default:
return R.drawable.service;
}
}
public static int getSelectedServiceId(Context context) {
if (BuildConfig.BUILD_TYPE.equals("release")) return DEFAULT_FALLBACK_SERVICE.getServiceId();
final String serviceName = PreferenceManager.getDefaultSharedPreferences(context)
.getString(context.getString(R.string.current_service_key), context.getString(R.string.default_service_value));
int serviceId;
try {
serviceId = NewPipe.getService(serviceName).getServiceId();
} catch (ExtractionException e) {
serviceId = DEFAULT_FALLBACK_SERVICE.getServiceId();
}
return serviceId;
}
public static void setSelectedServiceId(Context context, int serviceId) {
String serviceName;
try {
serviceName = NewPipe.getService(serviceId).getServiceInfo().name;
} catch (ExtractionException e) {
serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().name;
}
setSelectedServicePreferences(context, serviceName);
}
public static void setSelectedServiceId(Context context, String serviceName) {
int serviceId = NewPipe.getIdOfService(serviceName);
if (serviceId == -1) serviceName = DEFAULT_FALLBACK_SERVICE.getServiceInfo().name;
setSelectedServicePreferences(context, serviceName);
}
private static void setSelectedServicePreferences(Context context, String serviceName) {
PreferenceManager.getDefaultSharedPreferences(context).edit().
putString(context.getString(R.string.current_service_key), serviceName).apply();
}
}

View file

@ -1,35 +0,0 @@
package org.schabi.newpipe.util;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
/**
* Created by Chrsitian Schabesberger on 09.10.17.
* ServiceIconMapper.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/>.
*/
public class ServiceIconMapper {
public static int getIconResource(int service_id) {
switch(service_id) {
case 0:
return R.drawable.youtube;
case 1:
return R.drawable.soud_cloud;
default:
return R.drawable.service;
}
}
}

View file

@ -1,29 +1,38 @@
package org.schabi.newpipe.util;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.PreferenceManager;
import android.support.annotation.AttrRes;
import android.support.annotation.StyleRes;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
public class ThemeHelper {
/**
* Apply the selected theme (on NewPipe settings) in the context
* with the default style (see {@link #setTheme(Context, int)}).
*
* @param context context that the theme will be applied
*/
public static void setTheme(Context context) {
String lightTheme = context.getResources().getString(R.string.light_theme_key);
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
String blackTheme = context.getResources().getString(R.string.black_theme_key);
setTheme(context, -1);
}
String selectedTheme = getSelectedTheme(context);
if (selectedTheme.equals(lightTheme)) context.setTheme(R.style.LightTheme);
else if (selectedTheme.equals(blackTheme)) context.setTheme(R.style.BlackTheme);
else if (selectedTheme.equals(darkTheme)) context.setTheme(R.style.DarkTheme);
// Fallback
else context.setTheme(R.style.DarkTheme);
/**
* Apply the selected theme (on NewPipe settings) in the context,
* themed according with the styles defined for the service .
*
* @param context context that the theme will be applied
* @param serviceId the theme will be styled to the service with this id,
* pass -1 to get the default style
*/
public static void setTheme(Context context, int serviceId) {
context.setTheme(getThemeForService(context, serviceId));
}
/**
@ -35,9 +44,73 @@ public class ThemeHelper {
return getSelectedTheme(context).equals(context.getResources().getString(R.string.light_theme_key));
}
@StyleRes
public static int getThemeForService(Context context, int serviceId) {
String lightTheme = context.getResources().getString(R.string.light_theme_key);
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
String blackTheme = context.getResources().getString(R.string.black_theme_key);
String selectedTheme = getSelectedTheme(context);
int defaultTheme = R.style.DarkTheme;
if (selectedTheme.equals(lightTheme)) defaultTheme = R.style.LightTheme;
else if (selectedTheme.equals(blackTheme)) defaultTheme = R.style.BlackTheme;
else if (selectedTheme.equals(darkTheme)) defaultTheme = R.style.DarkTheme;
if (serviceId <= -1) {
return defaultTheme;
}
final StreamingService service;
try {
service = NewPipe.getService(serviceId);
} catch (ExtractionException ignored) {
return defaultTheme;
}
String themeName = "DarkTheme";
if (selectedTheme.equals(lightTheme)) themeName = "LightTheme";
else if (selectedTheme.equals(blackTheme)) themeName = "BlackTheme";
else if (selectedTheme.equals(darkTheme)) themeName = "DarkTheme";
themeName += "." + service.getServiceInfo().name;
int resourceId = context.getResources().getIdentifier(themeName, "style", context.getPackageName());
if (resourceId > 0) {
return resourceId;
}
return defaultTheme;
}
public static String getSelectedTheme(Context context) {
String themeKey = context.getString(R.string.theme_key);
String defaultTheme = context.getResources().getString(R.string.default_theme_value);
return PreferenceManager.getDefaultSharedPreferences(context).getString(themeKey, defaultTheme);
}
@StyleRes
public static int getSettingsThemeStyle(Context context) {
String lightTheme = context.getResources().getString(R.string.light_theme_key);
String darkTheme = context.getResources().getString(R.string.dark_theme_key);
String blackTheme = context.getResources().getString(R.string.black_theme_key);
String selectedTheme = getSelectedTheme(context);
if (selectedTheme.equals(lightTheme)) return R.style.LightSettingsTheme;
else if (selectedTheme.equals(blackTheme)) return R.style.BlackSettingsTheme;
else if (selectedTheme.equals(darkTheme)) return R.style.DarkSettingsTheme;
// Fallback
else return R.style.DarkSettingsTheme;
}
/**
* Get a resource id from a resource styled according to the the context's theme.
*/
public static int resolveResourceIdFromAttr(Context context, @AttrRes int attr) {
TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
int attributeResourceId = a.getResourceId(0, 0);
a.recycle();
return attributeResourceId;
}
}

View file

@ -0,0 +1,94 @@
package org.schabi.newpipe.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* Created by Christian Schabesberger on 28.01.18.
* Copyright 2018 Christian Schabesberger <chris.schabesberger@mailbox.org>
* ZipHelper.java is part of NewPipe
*
* License: GPL-3.0+
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class ZipHelper {
private static final int BUFFER_SIZE = 2048;
/**
* This function helps to create zip files.
* Caution this will override the original file.
* @param outZip The ZipOutputStream where the data should be stored in
* @param file The path of the file that should be added to zip.
* @param name The path of the file inside the zip.
* @throws Exception
*/
public static void addFileToZip(ZipOutputStream outZip, String file, String name) throws Exception {
byte data[] = new byte[BUFFER_SIZE];
FileInputStream fi = new FileInputStream(file);
BufferedInputStream inputStream = new BufferedInputStream(fi, BUFFER_SIZE);
ZipEntry entry = new ZipEntry(name);
outZip.putNextEntry(entry);
int count;
while((count = inputStream.read(data, 0, BUFFER_SIZE)) != -1) {
outZip.write(data, 0, count);
}
inputStream.close();
}
/**
* This will extract data from Zipfiles.
* Caution this will override the original file.
* @param inZip The ZipOutputStream where the data is stored in
* @param file The path of the file on the disk where the data should be extracted to.
* @param name The path of the file inside the zip.
* @return will return true if the file was found within the zip file
* @throws Exception
*/
public static boolean extractFileFromZip(ZipInputStream inZip, String file, String name) throws Exception {
byte data[] = new byte[BUFFER_SIZE];
boolean found = false;
ZipEntry ze;
while((ze = inZip.getNextEntry()) != null) {
if(ze.getName().equals(name)) {
found = true;
// delete old file first
File oldFile = new File(file);
if(oldFile.exists()) {
if(!oldFile.delete()) {
throw new Exception("Could not delete " + file);
}
}
FileOutputStream outFile = new FileOutputStream(file);
int count = 0;
while((count = inZip.read(data)) != -1) {
outFile.write(data, 0, count);
}
outFile.close();
inZip.closeEntry();
}
}
return true;
}
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/decelerate_quint">
<alpha
android:duration="150"
android:fromAlpha="0.00"
android:toAlpha="1.0"/>
</set>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_quint">
<alpha
android:duration="350"
android:fromAlpha="1.0"
android:toAlpha="0.00"/>
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="200" android:exitFadeDuration="200">
<item android:drawable="@color/dark_ripple_color" android:state_checked="true" android:state_pressed="true" />
<item android:drawable="@color/dark_ripple_color" android:state_pressed="true" />
<item android:drawable="@color/dark_selected_color" android:state_checked="true" />
<item android:drawable="@android:color/transparent" />
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="200" android:exitFadeDuration="200">
<item android:drawable="@color/light_ripple_color" android:state_checked="true" android:state_pressed="true" />
<item android:drawable="@color/light_ripple_color" android:state_pressed="true" />
<item android:drawable="@color/light_selected_color" android:state_checked="true" />
<item android:drawable="@android:color/transparent" />
</selector>

View file

@ -11,7 +11,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="@color/dark_youtube_primary_color"
android:background="?attr/colorPrimary"
app:tabGravity="fill"/>
<android.support.v4.view.ViewPager

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:button="@null"
android:drawablePadding="24dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:maxLines="2"
android:minHeight="?attr/listPreferredItemHeightSmall"
android:paddingEnd="?attr/listPreferredItemPaddingRight"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?attr/listPreferredItemPaddingRight"
android:paddingStart="?attr/listPreferredItemPaddingLeft"
android:background="?attr/checked_selector_drawable"
android:textColor="?attr/textColorAlertDialogListItem"
tools:drawableLeft="?attr/play"
tools:text="Lorem ipsum dolor sit amet" />

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="?attr/listPreferredItemPaddingLeft" />

View file

@ -1,5 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/youtube" />
<item android:title="@string/soundcloud" />
<group
android:id="@+id/menu_services_group">
<item
android:id="@+id/menu_service_youtube"
android:icon="@drawable/place_holder_youtube"
android:title="@string/youtube"/>
<item
android:id="@+id/menu_service_soundcloud"
android:icon="@drawable/place_holder_circle"
android:title="@string/soundcloud"/>
</group>
</menu>

View file

@ -176,13 +176,13 @@
<string name="no_subscribers">صفر لا تقم با الإختيار (في بعض اللغات) لأنها ليست \"حالة خاصة\" للأندرويد</string>
<plurals name="subscribers">
<item quantity="zero">صفر</item>
<item quantity="one">واحد</item>
<item quantity="two">اثنان</item>
<item quantity="few">قليل</item>
<item quantity="many">كثير</item>
<item quantity="other">أخرى</item>
</plurals>
<item quantity="zero">صفر</item>
<item quantity="one">%s مشترك</item>
<item quantity="two">اثنان</item>
<item quantity="few">قليل</item>
<item quantity="many">كثير</item>
<item quantity="other">%s مشتركون</item>
</plurals>
<string name="no_views">لاتوجد مشاهدات</string>
<string name="no_videos">لاتوجد فديوهات</string>
@ -226,13 +226,13 @@
<string name="tab_licenses">التراخيص</string>
<string name="app_description">واجهة أمامية لليوتوب مجانية مفتوحة المصدر و خفيفة الوزن لنظام التشغيل أندرويد.</string>
<string name="contribution_title">ساهم</string>
<string name="contribution_encouragement">إذا كان لديك أفكار؛ او ترجمة، او تغييرات على التصميم، او تنظيف وتحسين الكود البرمجي ، أو تغييرات ثقيلة على الكود البرمجي، مساعدتك دائما موضع ترحيب. وكلما تم ذلك كلما كان ذلك أفضل!</string>
<string name="contribution_encouragement">إذا كانت لديك أفكار؛ أو ترجمة، أو تغييرات تخص التصميم، أو تنظيف و تحسين الشفرة البرمجية ، أو تعديلات عميقة عليها، فتذكر أنّ مساعدتك دائما موضع ترحيب. وكلما أتممنا شيئا كلما كان ذلك أفضل !</string>
<string name="view_on_github">عرض على GitHub</string>
<string name="donation_title">تبرع</string>
<string name="donation_encouragement">يتم تطوير NewPipe من قبل المتطوعين الذين يقضون وقت فراغهم لتقديم أفضل تجربة لك. الآن حان الوقت لإعطاء مرة أخرى للتأكد من المطورين لدينا يمكن أن تجعل NewPipe أكثر و أفضل بينما نتمتع بكوب من جافا!</string>
<string name="give_back">تبرع</string>
<string name="website_title">الموقع</string>
<string name="website_encouragement">للحصول على مزيد من المعلومات وآخر الأخبار حول NewPipe الرجاء زيارة موقعنا على الانترنت.</string>
<string name="website_encouragement">للحصول على مزيد من المعلومات و آخر الأخبار حول NewPipe الرجاء زيارة موقعنا على الانترنت.</string>
<string name="app_license_title">تراخيص NewPipe</string>
<string name="read_full_license">قراءة الترخيص</string>
@ -266,20 +266,20 @@
<string name="hold_to_append">اضغط للإدراج بقائمة الانتظار</string>
<plurals name="views">
<item quantity="zero">صفر</item>
<item quantity="one">واحد</item>
<item quantity="one">%s مشاهدة</item>
<item quantity="two">اثنان</item>
<item quantity="few">قليل</item>
<item quantity="many">كثير</item>
<item quantity="other">أخرى</item>
<item quantity="other">%s مشاهدات</item>
</plurals>
<plurals name="videos">
<item quantity="zero">صفر</item>
<item quantity="one">واحد</item>
<item quantity="one">%s فيديو</item>
<item quantity="two">اثنان</item>
<item quantity="few">قليل</item>
<item quantity="many">كثير</item>
<item quantity="other">أخرى</item>
<item quantity="other">%s فيديوهات</item>
</plurals>
<string name="recaptcha_request_toast">إعادة طلب كلمة التحقق</string>
@ -292,4 +292,18 @@
<string name="enqueue_on_background">إدراج بقائمة الانتظار على خلفية</string>
<string name="enqueue_on_popup">إدراج بقائمة الانتظار على المنبثقة</string>
<string name="start_here_on_background">ابدأ هنا على خلفية المصدر</string>
</resources>
<string name="default_content_country_title">المحتوى الإفتراضي حسب البلد</string>
<string name="toggle_orientation">تغيير الإتجاه</string>
<string name="switch_to_background">الإنتقال إلى الخلفية</string>
<string name="switch_to_popup">الإنتقال إلى نافذة منبثقة</string>
<string name="switch_to_main">التحول إلى الرئيسية</string>
<string name="service_title">الخدمة</string>
<string name="drawer_open">فتح الدرج</string>
<string name="drawer_close">إغلاق الدرج</string>
<string name="always">دائمًا</string>
<string name="just_once">مرة واحدة فقط</string>
<string name="invalid_url_toast">العنوان خاطئ</string>
<string name="preferred_player_share_menu_title">\@string/preferred_player_settings_title</string>
</resources>

View file

@ -172,7 +172,7 @@ otevření ve vyskakovacím okně</string>
<string name="show_search_suggestions_summary">Zobrazovat návrhy při vyhledávání</string>
<string name="enable_search_history_title">Historie vyhledávání</string>
<string name="enable_search_history_summary">Hledané výrazy lokálně uchovávat</string>
<string name="enable_watch_history_title">Historie</string>
<string name="enable_watch_history_title">Historie sledování</string>
<string name="enable_watch_history_summary">Evidovat zhlédnutá videa</string>
<string name="resume_on_audio_focus_gain_title">Přehrávat po přechodu do popředí</string>
<string name="resume_on_audio_focus_gain_summary">Pokračovat v přehrávání po přerušení (např. hovor)</string>
@ -298,4 +298,13 @@ otevření ve vyskakovacím okně</string>
<string name="give_back">Daruj</string>
<string name="website_title">Webová stránka</string>
<string name="website_encouragement">Pro další informace a poslední novinky o NewPipe navštivte naši stránku.</string>
<string name="default_content_country_title">Země výchozího obsahu</string>
<string name="service_title">Služba</string>
<string name="toggle_orientation">Změna orientaci</string>
<string name="switch_to_background">Na pozadí</string>
<string name="switch_to_popup">Do okna</string>
<string name="switch_to_main">Přepnout na hlavní</string>
<string name="drawer_open">Otevřít Drawer</string>
<string name="drawer_close">Zavřít Drawer</string>
</resources>

View file

@ -285,9 +285,9 @@
<string name="kiosk">Kiosk</string>
<string name="show_hold_to_append_summary">Tipp anzeigen, wenn der Hintergrundwiedergabe- oder Pop-up-Button auf der Videodetailseite gedrückt gehalten wird</string>
<string name="background_player_append">In der Warteschlange der Hintergrundwiedergabe</string>
<string name="new_and_hot">Neu und brandheiß</string>
<string name="new_and_hot">Neu &amp; Heiß</string>
<string name="hold_to_append">Halten zum Hinzufügen zur Warteschleife</string>
<string name="show_hold_to_append_title">Gedrückt halten, um Tipp hinzuzufügen anzeigen</string>
<string name="show_hold_to_append_title">\"Gedrückt halten, um Tipp hinzuzufügen\" anzeigen</string>
<string name="unknown_content">[Unbekannt]</string>
<string name="enqueue_on_background">In Warteschlange für Hintergrundwiedergabe</string>
@ -300,4 +300,32 @@
<string name="website_title">Website</string>
<string name="website_encouragement">Um mehr Informationen und die aktuellsten Nachrichten über NewPipe zu bekommen, besuche unsere Website.</string>
<string name="donation_encouragement">NewPipe wird von Freiwilligen entwickelt, die ihre Freizeit damit verbringen, dir das beste Erlebnis zu bieten. Jetzt ist es an der Zeit, etwas zurückzugeben, um sicherzustellen, dass unsere Entwickler NewPipe noch besser machen können, während sie eine Tasse Java genießen!</string>
</resources>
<string name="service_title">Service</string>
<string name="no_player_found_toast">Kein Streamplayer gefunden (Du kannst VLC installieren, um ihn abzuspielen)</string>
<string name="default_content_country_title">Standard-Land des Inhalts</string>
<string name="always">Immer</string>
<string name="just_once">Nur einmal</string>
<string name="toggle_orientation">Ausrichtung umschalten</string>
<string name="switch_to_background">In den Hintergrund wechseln</string>
<string name="switch_to_popup">Zu Popup wechseln</string>
<string name="switch_to_main">Zur Hauptseite wechseln</string>
<string name="external_player_unsupported_link_type">Externe Player unterstützen diese Art von Links nicht</string>
<string name="invalid_url_toast">Ungültige URL</string>
<string name="video_streams_empty">Keine Video-Streams gefunden</string>
<string name="audio_streams_empty">Keine Audio-Streams gefunden</string>
<string name="drawer_open">Navigationsleiste öffnen</string>
<string name="drawer_close">Navigationsleiste schließen</string>
<string name="preferred_player_share_menu_dialog_title">Mit bevorzugtem Player öffnen</string>
<string name="preferred_player_settings_title">Bevorzugter Player</string>
<string name="video_player">Video-Player</string>
<string name="background_player">Hintergrund-Player</string>
<string name="popup_player">Popup-Player</string>
<string name="always_ask_player">Immer fragen</string>
<string name="preferred_player_fetcher_notification_title">Informationen werden abgerufen…</string>
<string name="preferred_player_fetcher_notification_message">Der angeforderte Inhalt wird geladen</string>
</resources>

View file

@ -59,7 +59,7 @@
<string name="youtube_signature_decryption_error">No se pudo descifrar la URL del vídeo</string>
<string name="parsing_error">No se pudo analizar el sitio web</string>
<string name="show_next_and_similar_title">Mostrar vídeos siguientes y similares</string>
<string name="search_language_title">Idioma por defecto del contenido</string>
<string name="search_language_title">Idioma del contenido por defecto</string>
<string name="list_thumbnail_view_description">Vista previa del vídeo</string>
<string name="detail_thumbnail_view_description">Vista previa del vídeo</string>
<string name="detail_likes_img_view_description">Me gusta</string>
@ -284,7 +284,7 @@ abrir en modo popup</string>
<string name="title_activity_background_player">Reproductor de fondo</string>
<string name="title_activity_popup_player">Reproductor popup</string>
<string name="play_queue_remove">Remover</string>
<string name="play_queue_remove">Quitar</string>
<string name="play_queue_stream_detail">Detalles</string>
<string name="play_queue_audio_settings">Ajustes de audio</string>
<string name="unknown_content">[Desconocido]</string>
@ -294,9 +294,9 @@ abrir en modo popup</string>
<string name="start_here_on_main">Comenzar a reproducir aquí</string>
<string name="start_here_on_background">Comenzar aquí en segundo plano</string>
<string name="start_here_on_popup">Comenzar aquí en popup</string>
<string name="show_hold_to_append_title">Desplegar consejo \"Mantener para poner en la fila\"</string>
<string name="show_hold_to_append_title">Mostrar consejo \"Mantener para poner en la cola\"</string>
<string name="new_and_hot">Nuevo y popular</string>
<string name="hold_to_append">Mantener para poner en la fila</string>
<string name="hold_to_append">Mantener para poner en la cola</string>
<string name="donation_title">Donar</string>
<string name="donation_encouragement">NewPipe es desarrollado por voluntarios que emplean su tiempo libre en mejorar tu experiencia. ¡ Ahora puedes devolver el favor para asegurar que los desarrolladores puedan crear un NewPipe aún mejor mientras saborean una taza de Java !</string>
<string name="give_back">Donar</string>
@ -308,4 +308,27 @@ abrir en modo popup</string>
<string name="switch_to_popup">Cambiar a popup</string>
<string name="switch_to_main">Cambiar a principal</string>
</resources>
<string name="service_title">Servicio</string>
<string name="drawer_open">Abrir cajón</string>
<string name="drawer_close">Cerrar cajón</string>
<string name="no_player_found_toast">No se ha encontrado ningún reproductor de streaming (puede instalar VLC para reproducirlo)</string>
<string name="always">Siempre</string>
<string name="just_once">Sólo una vez</string>
<string name="external_player_unsupported_link_type">Los reproductores externos no soportan este tipo de enlaces</string>
<string name="invalid_url_toast">URL no válida</string>
<string name="video_streams_empty">No se encontraron transmisiones de vídeo</string>
<string name="audio_streams_empty">No se encontraron transmisiones de audio</string>
<string name="preferred_player_share_menu_title">\@string/preferred_player_settings_title</string>
<string name="preferred_player_share_menu_dialog_title">Abrir con el reproductor preferido</string>
<string name="preferred_player_settings_title">Reproductor preferido</string>
<string name="video_player">Reproductor de vídeo</string>
<string name="background_player">Reproductor de fondo</string>
<string name="popup_player">Reproductor de popup</string>
<string name="always_ask_player">Preguntar siempre</string>
<string name="preferred_player_fetcher_notification_title">Obteniendo información…</string>
<string name="preferred_player_fetcher_notification_message">El contenido solicitado se está cargando</string>
</resources>

View file

@ -139,7 +139,7 @@
<string name="start">Hasi</string>
<string name="pause">Pausatu</string>
<string name="view">Ikusi</string>
<string name="view">Jo</string>
<string name="delete">Ezabatu</string>
<string name="checksum">Egiaztaketa-batura</string>
@ -179,7 +179,7 @@
<string name="app_license_title">NewPipe Lizentzia</string>
<string name="contribution_encouragement">Ideiak, itzulpenak, diseinu aldaketak, kode garbiketak, kode aldaketa sakonak badituzu, laguntza beti da ongi etorria. Eginaz hobetzen da!</string>
<string name="read_full_license">Irakurri lizentzia</string>
<string name="contribution_title">Parte hartzea</string>
<string name="contribution_title">Hartu parte</string>
<string name="subscribe_button_title">Harpidetu</string>
<string name="subscribed_button_title">Harpidetuta</string>
<string name="channel_unsubscribed">Kanaletik harpidetza kenduta</string>
@ -244,4 +244,77 @@
<string name="history_empty">Historiala hutsik dago</string>
<string name="history_cleared">Historiala garbitu da</string>
<string name="item_deleted">Elementua ezabatuta</string>
<string name="show_hold_to_append_title">Erakutsi \"Mantendu eransteko\" aholkua</string>
<string name="show_hold_to_append_summary">Erakutsi aholkua bigarren planoko eta laster-leihoko botoia sakatzean bideoaren xehetasunen orrian</string>
<string name="default_content_country_title">Lehenetsitako edukiaren herrialdea</string>
<string name="service_title">Zerbitzua</string>
<string name="background_player_append">Bigarren planoko erreproduzigailuaren ilaran</string>
<string name="popup_playing_append">Laster-leiho erreproduzigailuaren ilaran</string>
<string name="play_all">Jo denak</string>
<string name="unknown_content">[Ezezaguna]</string>
<string name="toggle_orientation">Txandakatu orientazioa</string>
<string name="switch_to_background">Aldatu bigarren planora</string>
<string name="switch_to_popup">Aldatu laster-leihora</string>
<string name="switch_to_main">Aldatu nagusira</string>
<string name="player_stream_failure">Huts egin du jario hau erreproduzitzean</string>
<string name="player_unrecoverable_failure">Erreproduzigailuaren errore berreskuraezina gertatu da</string>
<string name="player_recoverable_failure">Erreproduzigailuaren erroretik berreskuratzen</string>
<string name="donation_title">Dohaintza</string>
<string name="donation_encouragement">NewPipe bere denbora librea zuri esperientziarik onena ekartzeko ematen duten boluntarioek garatzen dute. Orain zerbait atzera emateko unea da, garatzaileek NewPipe hobetu ahal izan dezaten Javako kafe bat hartzen duten bitartean!</string>
<string name="give_back">Egin dohaintza</string>
<string name="website_title">Webgunea</string>
<string name="website_encouragement">NewPipe aplikazioari buruzko informazio gehiago eta azken berriak jasotzeko bisitatu gure webgunea.</string>
<string name="delete_item_search_history">Elementu hau bilaketen historialetik ezabatu nahi duzu?</string>
<string name="main_page_content">Orri nagusiko edukia</string>
<string name="blank_page_summary">Orri hutsa</string>
<string name="kiosk_page_summary">Kioskoaren orria</string>
<string name="subscription_page_summary">Harpidetza orria</string>
<string name="feed_page_summary">Jario-orria</string>
<string name="channel_page_summary">Kanal-orria</string>
<string name="select_a_channel">Hautatu kanal bat</string>
<string name="no_channel_subscribed_yet">Ez zara inolako kanalera harpidetu oraindik</string>
<string name="select_a_kiosk">Hautatu kiosko bat</string>
<string name="kiosk">Kioskoa</string>
<string name="trending">Joerak</string>
<string name="top_50">Lehen 50ak</string>
<string name="new_and_hot">Berria eta arrakastatsua</string>
<string name="title_activity_background_player">Bigarren planoko erreproduzigailua</string>
<string name="title_activity_popup_player">Laster-leiho erreproduzigailua</string>
<string name="play_queue_remove">Kendu</string>
<string name="play_queue_stream_detail">Xehetasunak</string>
<string name="play_queue_audio_settings">Audio ezarpenak</string>
<string name="hold_to_append">Mantendu ilaran jartzeko</string>
<string name="enqueue_on_background">Jarri ilaran bigarren planoan</string>
<string name="enqueue_on_popup">Jarri ilaran laster-leihoan</string>
<string name="start_here_on_main">Hasi hemen erreproduzitzen</string>
<string name="start_here_on_background">Hasi hemen bigarren planoan</string>
<string name="start_here_on_popup">Hasi hemen laster-leihoan</string>
<string name="drawer_open">"Ireki tiradera "</string>
<string name="drawer_close">Itxi tiradera</string>
<string name="no_player_found_toast">Ez da jarioen erreproduzigailurik aurkitu (VLC instalatu dezakezu)</string>
<string name="always">Beti</string>
<string name="just_once">Behin besterik ez</string>
<string name="external_player_unsupported_link_type">Kanpo erreproduzigailuek ez dituzte mota honetako estekak onartzen</string>
<string name="invalid_url_toast">URL baliogabea</string>
<string name="video_streams_empty">Ez da bideo jariorik aurkitu</string>
<string name="audio_streams_empty">Ez da audio jariorik aurkitu</string>
<string name="preferred_player_share_menu_dialog_title">Ireki gogoko erreproduzigailuarekin</string>
<string name="preferred_player_settings_title">Gogoko erreproduzigailua</string>
<string name="video_player">Bideo erreproduzigailua</string>
<string name="background_player">Bigarren planoko erreproduzigailua</string>
<string name="popup_player">Laster-leiho erreproduzigailua</string>
<string name="always_ask_player">Galdetu beti</string>
<string name="preferred_player_fetcher_notification_title">Informazioa eskuratzen…</string>
<string name="preferred_player_fetcher_notification_message">Eskatutako edukia kargatzen ari da</string>
</resources>

View file

@ -5,8 +5,8 @@
<string name="default_resolution_title">Résolution par défaut</string>
<string name="did_you_mean">Vouliez-vous dire : %1$s ?</string>
<string name="download">Télécharger</string>
<string name="download_path_title">Chemin de téléchargement</string>
<string name="download_path_dialog_title">Entrer le chemin de téléchargement des vidéos</string>
<string name="download_path_title">Chemin de téléchargement vidéo</string>
<string name="download_path_dialog_title">Entrez le chemin de téléchargement des vidéos</string>
<string name="download_path_summary">Chemin de stockage des vidéos téléchargées</string>
<string name="install">Installer</string>
<string name="kore_not_found">Lapplication Kore est introuvable. L\'installer ?</string>
@ -29,7 +29,7 @@
<string name="download_dialog_title">Télécharger</string>
<string name="next_video_title">Vidéo suivante</string>
<string name="show_next_and_similar_title">Afficher les vidéos suivantes et similaires</string>
<string name="url_not_supported_toast">URL non pris en charge</string>
<string name="url_not_supported_toast">Lien non pris en charge</string>
<string name="settings_category_video_audio_title">Vidéo et audio</string>
<string name="settings_category_other_title">Autre</string>
@ -37,7 +37,7 @@
<string name="detail_thumbnail_view_description">Miniature daperçu vidéo</string>
<string name="detail_dislikes_img_view_description">Je naime pas</string>
<string name="detail_likes_img_view_description">Jaime</string>
<string name="search_language_title">Langue par défaut du contenu</string>
<string name="search_language_title">Langue du contenu par défaut</string>
<string name="detail_uploader_thumbnail_view_description">Miniature de lavatar de lutilisateur</string>
<string name="use_external_video_player_title">Utiliser un lecteur vidéo externe</string>
<string name="use_external_audio_player_title">Utiliser un lecteur audio externe</string>
@ -53,7 +53,7 @@
<string name="settings_category_appearance_title">Apparence</string>
<string name="network_error">Erreur réseau</string>
<string name="download_path_audio_title">Chemin de téléchargement</string>
<string name="download_path_audio_title">Chemin de téléchargement audio</string>
<string name="download_path_audio_summary">Chemin de stockage des fichiers audio téléchargés</string>
<string name="download_path_audio_dialog_title">Entrez le chemin de téléchargement des fichiers audio</string>
@ -70,7 +70,7 @@
<string name="duration_live">Direct</string>
<string name="could_not_load_thumbnails">Impossible de charger toutes les miniatures</string>
<string name="youtube_signature_decryption_error">Erreur lors du décryptage du lien</string>
<string name="youtube_signature_decryption_error">Impossible de déchiffrer la signature du lien</string>
<string name="light_parsing_error">Impossible d\'analyser complètement le site web</string>
<string name="live_streams_not_supported">Il s\'agit d\'un direct, non supporté pour le moment.</string>
<string name="sorry_string">Désolé, une erreur inattendue s\'est produite.</string>
@ -109,15 +109,15 @@
<string name="finish">OK</string>
<string name="msg_name">Nom du fichier</string>
<string name="msg_threads">Discussions</string>
<string name="msg_threads">Threads</string>
<string name="msg_error">Erreur</string>
<string name="msg_server_unsupported">Serveur non supporté</string>
<string name="msg_exists">Fichier déjà existant</string>
<string name="msg_url_malform">URL malformée ou internet indisponible</string>
<string name="msg_url_malform">Lien malformé ou internet indisponible</string>
<string name="msg_running">Téléchargement NewPipe</string>
<string name="msg_running_detail">Appuyer pour plus de détails</string>
<string name="msg_wait">Veuillez patienter…</string>
<string name="msg_copied">Copié dans le presse-papier</string>
<string name="msg_copied">Copié dans le presse-papiers</string>
<string name="no_available_dir">Sélectionner un dossier de téléchargement disponible</string>
<string name="could_not_load_image">Impossible de charger l\'image</string>
@ -136,7 +136,7 @@
<string name="open_in_popup_mode">Ouvrir en mode fenêtré</string>
<string name="popup_mode_share_menu_title">Mode fenêtré NewPipe</string>
<string name="popup_playing_toast">Lire en mode fenêtré</string>
<string name="popup_playing_toast">Lecture en mode fenêtré</string>
<string name="yes">Oui</string>
<string name="later">Plus tard</string>
<string name="disabled">Désactivé</string>
@ -153,7 +153,7 @@
<string name="controls_background_title">Arrière-plan</string>
<string name="controls_popup_title">Fenêtre</string>
<string name="default_popup_resolution_title">Résolution par défaut de la fenêtre</string>
<string name="default_popup_resolution_title">Résolution de la fenêtre par défaut</string>
<string name="show_higher_resolutions_title">Afficher des résolutions plus élevées</string>
<string name="show_higher_resolutions_summary">Certains appareils uniquement supportent la lecture 2K/4K</string>
<string name="default_video_format_title">Format vidéo par défaut</string>
@ -212,7 +212,7 @@
<string name="settings_file_replacement_character_title">Caractère de remplacement</string>
<string name="enable_search_history_title">Historique de recherche</string>
<string name="enable_search_history_summary">Sauvegarder les recherches sur l\'appareil</string>
<string name="enable_search_history_summary">Conserver les recherches sur l\'appareil</string>
<string name="enable_watch_history_title">Historique</string>
<string name="title_activity_history">Historique</string>
<string name="title_history_search">Recherché</string>
@ -268,17 +268,17 @@
<string name="top_50">Top 50</string>
<string name="new_and_hot">Nouveau &amp; populaire</string>
<string name="background_player_append">Mis en file d\'attente du lecteur en arrière-plan</string>
<string name="popup_playing_append">Mis en file d\'attente du lecteur fenêtré</string>
<string name="popup_playing_append">Mis en file d\'attente du lecteur en fenêtré</string>
<string name="play_all">Tout lire</string>
<string name="player_stream_failure">Échec de la lecture de ce flux</string>
<string name="player_unrecoverable_failure">Une erreur irrécupérable du lecteur s\'est produite</string>
<string name="no_channel_subscribed_yet">Encore aucune chaîne souscrite</string>
<string name="title_activity_background_player">Lecteur en arrière-plan</string>
<string name="title_activity_popup_player">Lecteur fenêtré</string>
<string name="title_activity_popup_player">Lecteur en fenêtré</string>
<string name="play_queue_remove">Retirer</string>
<string name="play_queue_stream_detail">Détails</string>
<string name="play_queue_audio_settings">Réglages audio</string>
<string name="play_queue_audio_settings">Paramètres audio</string>
<string name="show_hold_to_append_title">Afficher l\'aide \"Appui long pour mettre en file d\'attente\"</string>
<string name="show_hold_to_append_summary">Afficher l\'aide en appuyant sur les boutons \"Arrière-plan\" et \"Fenêtre\" sur la page de détails d\'une vidéo</string>
<string name="unknown_content">[Inconnu]</string>
@ -291,7 +291,7 @@
<string name="kiosk">Kiosque</string>
<string name="hold_to_append">Appui long pour mettre en file d\'attente</string>
<string name="enqueue_on_background">Mettre en file d\'attente en arrière-plan</string>
<string name="enqueue_on_popup">Mettre en file d\'attente du lecteur fenêtré</string>
<string name="enqueue_on_popup">Mettre en file d\'attente en fenêtré</string>
<string name="start_here_on_main">Démarrer ici</string>
<string name="start_here_on_background">Démarrer ici en arrière-plan</string>
<string name="start_here_on_popup">Démarrer ici en fenêtré</string>
@ -301,9 +301,31 @@
<string name="website_encouragement">Pour obtenir plus d\'informations et les dernières nouvelles à propos de NewPipe, visitez notre site Internet.</string>
<string name="give_back">Donner en retour</string>
<string name="default_content_country_title">Pays du contenu par défaut</string>
<string name="toggle_orientation">Orientation</string>
<string name="toggle_orientation">Rotation</string>
<string name="switch_to_background">Basculer en arrière-plan</string>
<string name="switch_to_popup">Basculer en fenêtré</string>
<string name="switch_to_main">Basculer en normal</string>
<string name="service_title">Service</string>
<string name="drawer_open">Ouvrir le menu</string>
<string name="drawer_close">Fermer le menu</string>
<string name="no_player_found_toast">Aucun lecteur de flux trouvé (vous pouvez installer VLC pour le lire)</string>
<string name="always">Toujours</string>
<string name="just_once">Une seule fois</string>
<string name="external_player_unsupported_link_type">Les lecteurs externes ne supportent pas ces types de liens</string>
<string name="invalid_url_toast">Lien non valide</string>
<string name="video_streams_empty">Aucun flux vidéo trouvé</string>
<string name="audio_streams_empty">Aucun flux audio trouvé</string>
<string name="preferred_player_share_menu_dialog_title">Ouvrir avec le lecteur préféré</string>
<string name="preferred_player_settings_title">Lecteur préféré</string>
<string name="video_player">Lecteur vidéo</string>
<string name="background_player">Lecteur en arrière-plan</string>
<string name="popup_player">Lecteur en fenêtré</string>
<string name="always_ask_player">Toujours demander</string>
<string name="preferred_player_fetcher_notification_title">Obtention des infos…</string>
<string name="preferred_player_fetcher_notification_message">Le contenu demandé est en chargement</string>
</resources>

View file

@ -313,4 +313,27 @@
<string name="switch_to_popup">Cambia in visualizzazione a comparsa</string>
<string name="switch_to_main">Cambia al menù principale</string>
</resources>
<string name="service_title">Servizio</string>
<string name="drawer_open">"Apri il menù "</string>
<string name="drawer_close">Chiudi il menù</string>
<string name="no_player_found_toast">Nessun riproduttore multimediale trovato (puoi installare VLC per riprodurlo)</string>
<string name="always">Sempre</string>
<string name="just_once">Solo una volta</string>
<string name="external_player_unsupported_link_type">I riproduttori esterni non supportano questa tipologia di collegamenti</string>
<string name="invalid_url_toast">URL invalido</string>
<string name="video_streams_empty">Nessun flusso video trovato</string>
<string name="audio_streams_empty">Nessun flusso audio trovato</string>
<string name="preferred_player_share_menu_title">Riproduttore preferito</string>
<string name="preferred_player_share_menu_dialog_title">Apri con il riproduttore preferito</string>
<string name="preferred_player_settings_title">Riproduttore preferito</string>
<string name="video_player">Riproduttore video</string>
<string name="background_player">Riproduttore di fondo</string>
<string name="popup_player">Riproduttore a comparsa</string>
<string name="always_ask_player">Chiedi sempre</string>
<string name="preferred_player_fetcher_notification_title">Raccogliendo informazioni…</string>
<string name="preferred_player_fetcher_notification_message">Il contenuto richiesto sta caricando</string>
</resources>

View file

@ -1,17 +1,17 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="upload_date_text">%1$s に公開</string>
<string name="no_player_found">ストリームのプレイヤーが見つかりません。VLC を入手しますか。</string>
<string name="no_player_found">動画プレイヤーが見つかりません。VLC を入手しますか?</string>
<string name="install">入手</string>
<string name="cancel">取り消し</string>
<string name="open_in_browser">ブラウザーで開く</string>
<string name="open_in_browser">ブラウザで開く</string>
<string name="share">共有</string>
<string name="download">保存</string>
<string name="search">検索</string>
<string name="settings">設定</string>
<string name="did_you_mean">%1$s の間違いではありませんか</string>
<string name="share_dialog_title">共有</string>
<string name="choose_browser">ブラウザーを選択</string>
<string name="choose_browser">ブラウザを選択</string>
<string name="screen_rotation">回転</string>
<string name="download_path_title">動画を保存する場所</string>
<string name="download_path_summary">動画を保存する場所</string>
@ -35,8 +35,8 @@
<string name="list_thumbnail_view_description">動画 プレビュー サムネイル</string>
<string name="detail_thumbnail_view_description">動画 プレビュー サムネイル</string>
<string name="detail_uploader_thumbnail_view_description">アップローダー サムネイル</string>
<string name="detail_dislikes_img_view_description">不適切</string>
<string name="detail_likes_img_view_description">好ましい</string>
<string name="detail_dislikes_img_view_description">低評価</string>
<string name="detail_likes_img_view_description">高評価</string>
<string name="use_external_video_player_title">外部プレイヤーを使用する</string>
<string name="use_external_audio_player_title">外部プレイヤーを使用する</string>
<string name="background_player_playing_toast">バックグラウンドで再生中</string>
@ -59,7 +59,7 @@
<string name="err_dir_create">保存場所 \'%1$s\' を作成できません</string>
<string name="info_dir_created">保存場所 \'%1$s\' を作成しました</string>
<string name="general_error">異常</string>
<string name="general_error">エラー</string>
<string name="could_not_load_thumbnails">全てのサムネイルを読み込むことができません</string>
<string name="youtube_signature_decryption_error">動画のURL署名を復号できませんでした</string>
<string name="parsing_error">Webサイトを解析できませんでした</string>
@ -273,4 +273,9 @@
<string name="play_queue_remove">削除</string>
<string name="play_queue_stream_detail">詳細</string>
<string name="play_queue_audio_settings">音声の設定</string>
<string name="toggle_orientation">画面を回転</string>
<string name="switch_to_background">バックグラウンド再生に変更</string>
<string name="switch_to_popup">ポップアップ再生に変更</string>
<string name="switch_to_main">メイン再生に変更</string>
</resources>

View file

@ -126,12 +126,15 @@
<string name="use_old_player_summary">Senas įtaisytas media grotuvas</string>
<plurals name="subscribers">
<item quantity="one">%s prenumeratorius</item>
<item quantity="few">%s prenumeratoriai</item>
<item quantity="one">% prenumeratorius</item>
<item quantity="few">% prenumeratoriai</item>
<item quantity="other">% prenumeratorių</item>
</plurals>
<plurals name="videos">
<item quantity="one">Vaizdo įrašai</item>
<item quantity="one">% vaizdo įrašas</item>
<item quantity="few">% vaizdo įrašai</item>
<item quantity="other">% vaizdo įrašų</item>
</plurals>
<string name="start">Pradėti</string>
@ -208,7 +211,9 @@
<string name="no_subscribers">Nėra prenumeratorių</string>
<string name="no_views">Nėra peržiūrų</string>
<plurals name="views">
<item quantity="one">%a peržiūra</item>
<item quantity="one">% peržiūra</item>
<item quantity="few">% peržiūros</item>
<item quantity="other">% peržiūrų</item>
</plurals>
<string name="no_videos">Nėra vaizdo įrašų</string>

View file

@ -296,4 +296,32 @@
<string name="give_back">Bidra</string>
<string name="website_title">Nettside</string>
<string name="website_encouragement">Mer informasjon og siste nytt om NewPipe er å finne på vår nettside.</string>
</resources>
<string name="default_content_country_title">Forvalgt innholdsland</string>
<string name="service_title">Tjeneste</string>
<string name="toggle_orientation">Veksle skjermretning</string>
<string name="switch_to_background">Bytt til bakgrunnsmodus</string>
<string name="switch_to_popup">Bytt til oppsprettsmodus</string>
<string name="switch_to_main">Bytt til hovedmodus</string>
<string name="drawer_open">Åpne skuff</string>
<string name="drawer_close">Lukk skuff</string>
<string name="no_player_found_toast">Ingen strømmespiller installert (du kan installere VLC for å spille den)</string>
<string name="always">Alltid</string>
<string name="just_once">Kun én gang</string>
<string name="external_player_unsupported_link_type">Eksterne avspillere kan ikke spille lenker av disse typene</string>
<string name="invalid_url_toast">Ugyldig nettadresse</string>
<string name="video_streams_empty">Fant ingen videostrømmer</string>
<string name="audio_streams_empty">Fant ingen lydstrømmer</string>
<string name="preferred_player_share_menu_dialog_title">Åpne med foretrukket avspiller</string>
<string name="preferred_player_settings_title">Foretrukket avpsiller</string>
<string name="video_player">Videoavspiller</string>
<string name="background_player">Bakgrunnsavspiller</string>
<string name="popup_player">Oppsprettsavspiller</string>
<string name="always_ask_player">Spør alltid</string>
<string name="preferred_player_fetcher_notification_title">Henter informasjon…</string>
<string name="preferred_player_fetcher_notification_message">Forespurt innhold innlastes</string>
</resources>

View file

@ -303,4 +303,33 @@ te openen in pop-upmodus</string>
<string name="give_back">Teruggeven</string>
<string name="website_title">Website</string>
<string name="website_encouragement">Bezoek onze website voor meer informatie en het laatste nieuws over NewPipe.</string>
</resources>
<string name="default_content_country_title">Standaardinhoudsland</string>
<string name="service_title">Dienst</string>
<string name="toggle_orientation">Oriëntatie wijzigen</string>
<string name="switch_to_background">Verplaatsen naar achtergrond</string>
<string name="switch_to_popup">Verplaatsen naar pop-up</string>
<string name="switch_to_main">Verplaatsen naar normaal</string>
<string name="drawer_open">Menu openen</string>
<string name="drawer_close">Menu sluiten</string>
<string name="no_player_found_toast">Geen speler met streamondersteuning gevonden (je kan VLC installeren om het af te spelen)</string>
<string name="always">Altijd</string>
<string name="just_once">Eenmalig</string>
<string name="external_player_unsupported_link_type">Externe spelers ondersteunen deze soorten koppelingen niet</string>
<string name="invalid_url_toast">Ongeldige URL</string>
<string name="video_streams_empty">Geen videostreams gevonden</string>
<string name="audio_streams_empty">Geen audiostreams gevonden</string>
<string name="preferred_player_share_menu_title">Voorkeursspeler</string>
<string name="preferred_player_share_menu_dialog_title">Openen met voorkeursspeler</string>
<string name="preferred_player_settings_title">Voorkeursspeler</string>
<string name="video_player">Videospeler</string>
<string name="background_player">Achtergrondspeler</string>
<string name="popup_player">Pop-upspeler</string>
<string name="always_ask_player">Altijd vragen</string>
<string name="preferred_player_fetcher_notification_title">Info ophalen…</string>
<string name="preferred_player_fetcher_notification_message">De gevraagde inhoud is aan het laden</string>
</resources>

View file

@ -42,7 +42,7 @@
<string name="light_theme_title">Jasny</string>
<string name="download_dialog_title">Pobrane</string>
<string name="next_video_title">Następne video</string>
<string name="next_video_title">Następne wideo</string>
<string name="show_next_and_similar_title">Pokaż następne i podobne filmy</string>
<string name="url_not_supported_toast">URL nie obsługiwany</string>
<string name="search_language_title">Preferowany język zawartości</string>
@ -296,4 +296,27 @@
<string name="start_here_on_main">Zacznij Odtwarzanie Tutaj</string>
<string name="start_here_on_background">Zacznij Odwarzanie Tutaj — w Tle</string>
<string name="start_here_on_popup">Zacznij Odtwarzanie Tutaj — w Okienku</string>
<string name="no_player_found_toast">Nie znaleziono odtwarzacza strumieniowego (żeby odtworzyć możesz zainstalować VLC)</string>
<string name="default_content_country_title">Domyślny kraj zawartości</string>
<string name="service_title">Usługa</string>
<string name="always">Zawsze</string>
<string name="just_once">Tylko raz</string>
<string name="toggle_orientation">Przełącz orientację</string>
<string name="switch_to_background">Odtwarzaj w tle</string>
<string name="external_player_unsupported_link_type">Zewnętrzne odtwarzacze nie obsługują linków tego typu</string>
<string name="invalid_url_toast">Nieprawidłowy URL</string>
<string name="video_streams_empty">Nie znaleziono strumieni wideo</string>
<string name="audio_streams_empty">Nie znaleziono strumieni audio</string>
<string name="preferred_player_share_menu_title">Preferowany odtwarzacz</string>
<string name="preferred_player_share_menu_dialog_title">Otwórz za pomocą preferowanego odtwarzacza</string>
<string name="preferred_player_settings_title">Preferowany odtwarzacz</string>
<string name="video_player">Odtwarzacz wideo</string>
<string name="background_player">Odtwarzacz w tle</string>
<string name="always_ask_player">Pytaj za każdym razem</string>
<string name="preferred_player_fetcher_notification_title">Informacje…</string>
<string name="preferred_player_fetcher_notification_message">Ładuję żądaną zawartość</string>
</resources>

View file

@ -284,4 +284,30 @@ abrir em modo popup</string>
<string name="give_back">Retribuir</string>
<string name="website_title">Site oficial</string>
<string name="website_encouragement">Para obter mais informações e as últimas notícias sobre o NewPipe visite nosso Site.</string>
</resources>
<string name="no_player_found_toast">Nenhum reprodudor de stream encontrado (você pode instalar VLC para reproduzir isto)</string>
<string name="default_content_country_title">País do conteúdo padrão</string>
<string name="service_title">Serviço</string>
<string name="always">Sempre</string>
<string name="just_once">Apenas esta vez</string>
<string name="toggle_orientation">Alterar a orientação</string>
<string name="switch_to_background">Alterar para plano de fundo</string>
<string name="switch_to_popup">Alterar para Popup</string>
<string name="switch_to_main">Aletar para principal</string>
<string name="external_player_unsupported_link_type">Reprodutores externos não suportam estes tipos de links</string>
<string name="invalid_url_toast">URL inválida</string>
<string name="video_streams_empty">Nenhum stream de vídeo encontrado</string>
<string name="audio_streams_empty">Nenhum stream de áudio encontrado</string>
<string name="preferred_player_share_menu_dialog_title">Abrir com reprodutor padrão</string>
<string name="preferred_player_settings_title">Reprodutor padrão</string>
<string name="video_player">Reprodutor de vídeo</string>
<string name="background_player">Reprodutor em plano de fundo</string>
<string name="popup_player">Reprodutor popup</string>
<string name="always_ask_player">Sempre perguntar</string>
<string name="preferred_player_fetcher_notification_title">Obtendo informações…</string>
<string name="preferred_player_fetcher_notification_message">O conteúdo solicitado está carregando</string>
</resources>

View file

@ -76,7 +76,7 @@
<string name="main_bg_subtitle">Apăsați căutare pentru a începe</string>
<string name="autoplay_by_calling_app_title">Redă automat</string>
<string name="autoplay_by_calling_app_summary">Redă automat un videoclip atunci când NewPipe este deschis din altă aplicație</string>
<string name="duration_live">în direct</string>
<string name="duration_live">În direct</string>
<string name="downloads">Descărcări</string>
<string name="downloads_title">Descărcări</string>
<string name="error_report_title">Raport de erori</string>
@ -168,7 +168,7 @@ pentru a deschide în mod pop-up</string>
<string name="show_search_suggestions_title">Arată sugestii</string>
<string name="show_search_suggestions_summary">Arată sugestii în timpul căutării</string>
<string name="settings_category_popup_title">Pop-up</string>
<string name="settings_category_popup_title">Popup</string>
<string name="filter">Filtrează</string>
<string name="refresh">Reîmprospătare</string>
<string name="clear">Șterge</string>
@ -192,7 +192,7 @@ pentru a deschide în mod pop-up</string>
<string name="enable_watch_history_summary">Memorează videourile văzute</string>
<string name="resume_on_audio_focus_gain_title">Reia la câștigarea focalizării</string>
<string name="resume_on_audio_focus_gain_summary">Continuă redarea după întreruperi (ex. după apeluri)</string>
<string name="settings_category_player_title">Player</string>
<string name="settings_category_player_title">"Player "</string>
<string name="settings_category_player_behavior_title">Comportament</string>
<string name="settings_category_history_title">Istoric</string>
<string name="playlist">Playlist</string>
@ -273,4 +273,7 @@ pentru a deschide în mod pop-up</string>
<string name="trending">Trenduri</string>
<string name="top_50">Top 50</string>
<string name="new_and_hot">Tendințe</string>
</resources>
<string name="service_title">Serviciu</string>
<string name="background_player_append">Adăugaţi în playlist fundal</string>
<string name="popup_playing_append">Adăugaţi în playlist pop-up</string>
</resources>

View file

@ -270,15 +270,15 @@
<string name="main_page_content">Ana sayfanın içeriği</string>
<string name="blank_page_summary">Boş Sayfa</string>
<string name="kiosk_page_summary">Büfe Sayfası</string>
<string name="kiosk_page_summary">Köşk Sayfası</string>
<string name="subscription_page_summary">Abonelik Sayfası</string>
<string name="feed_page_summary">Besleme Sayfası</string>
<string name="channel_page_summary">Kanal Sayfası</string>
<string name="select_a_channel">Kanal seç</string>
<string name="no_channel_subscribed_yet">Henüz abone olunan kanal yok</string>
<string name="select_a_kiosk">Büfe seç</string>
<string name="select_a_kiosk">Köşk seç</string>
<string name="kiosk">Büfe</string>
<string name="kiosk">Köşk</string>
<string name="trending">Eğilimler</string>
<string name="top_50">En Üst 50</string>
<string name="new_and_hot">Yeni ve sıcak</string>
@ -291,11 +291,39 @@
<string name="enqueue_on_background">Arka Planda Kuyruğa Al</string>
<string name="enqueue_on_popup">ılır Pencerede Kuyruğa Al</string>
<string name="start_here_on_main">Burayı Oynatmaya Başla</string>
<string name="start_here_on_background">Arka Planda Burayı Başlat</string>
<string name="start_here_on_popup">ılır Pencerede Burayı Başlat</string>
<string name="start_here_on_background">Burayı Arka Planda Başlat</string>
<string name="start_here_on_popup">Burayı ılır Pencerede Başlat</string>
<string name="donation_title">Bağış yapın</string>
<string name="donation_encouragement">NewPipe, size en iyi deneyimi sunmak için boş zamanını harcayan gönüllüler tarafından geliştirilmektedir. Geliştiricilerimizin bir fincan kahvenin tadını çıkarırken NewPipe\'ı daha da çok iyileştirebilmelerinden emin olmak için karşılığını vermenin tam zamanı!</string>
<string name="give_back">Karşılığını ver</string>
<string name="website_title">Web sitesi</string>
<string name="website_encouragement">NewPipe hakkında daha çok bilgiyi ve son haberleri almak için web sitemize uğrayın.</string>
</resources>
<string name="default_content_country_title">Öntanımlı içerik ülkesi</string>
<string name="service_title">Hizmet</string>
<string name="toggle_orientation">Yönelimi Değiştir</string>
<string name="switch_to_background">Arka Plana Geç</string>
<string name="switch_to_popup">ılır Pencereye Geç</string>
<string name="switch_to_main">Ana Görünüme Geç</string>
<string name="drawer_open">Çekmeceyi Aç</string>
<string name="drawer_close">Çekmeceyi Kapat</string>
<string name="no_player_found_toast">Akış oynatıcı bulunamadı (oynatmak için VLC\'yi kurabilirsiniz)</string>
<string name="always">Her Zaman</string>
<string name="just_once">Yalnızca Bir Kez</string>
<string name="external_player_unsupported_link_type">Harici oynatıcılar bu türdeki bağlantıları desteklemiyor</string>
<string name="invalid_url_toast">Geçersiz URL</string>
<string name="video_streams_empty">Video akışı bulunamadı</string>
<string name="audio_streams_empty">Ses akışı bulunamadı</string>
<string name="preferred_player_share_menu_dialog_title">Yeğlenen oynatıcıyla aç</string>
<string name="preferred_player_settings_title">Yeğlenen oynatıcı</string>
<string name="video_player">Video oynatıcı</string>
<string name="background_player">Arka plan oynatıcı</string>
<string name="popup_player">ılır pencere oynatıcı</string>
<string name="always_ask_player">Her zaman sor</string>
<string name="preferred_player_fetcher_notification_title">Bilgi alınıyor…</string>
<string name="preferred_player_fetcher_notification_message">İstenen içerik yükleniyor</string>
</resources>

View file

@ -86,7 +86,7 @@
<string name="error_report_title">錯誤回報</string>
<string name="all">全部</string>
<string name="channel">頻道</string>
<string name="yes"></string>
<string name="yes"></string>
<string name="later">稍後</string>
<string name="disabled">已停用</string>
<string name="filter">篩選器</string>
@ -240,4 +240,24 @@
<string name="history_cleared">已清除歷史紀錄</string>
<string name="item_deleted">項目已刪除</string>
<string name="delete_item_search_history">確定要刪除此項搜尋紀錄嗎?</string>
</resources>
<string name="no_player_found_toast">沒有找到串流播放器(你可以安裝 VLC播放器 來播放)</string>
<string name="show_hold_to_append_title">顯示鎖定到附加指引上</string>
<string name="default_content_country_title">預設內容國家</string>
<string name="service_title">服務</string>
<string name="background_player_append">在背景播放器上等候</string>
<string name="popup_playing_append">在懸浮視窗播放器上等候</string>
<string name="play_all">全部播放</string>
<string name="always">總是</string>
<string name="just_once">僅一次</string>
<string name="unknown_content">[未知]</string>
<string name="toggle_orientation">切換方向</string>
<string name="switch_to_background">切換到背景</string>
<string name="switch_to_popup">切換到懸浮視窗</string>
<string name="switch_to_main">切換到主介面</string>
<string name="player_stream_failure">無法播放此串流</string>
<string name="player_unrecoverable_failure">發生無法復原的播放器錯誤</string>
<string name="player_recoverable_failure">從播放器錯誤中恢復</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--TODO: preffix these with "ic_"-->
<attr name="thumbs_up" format="reference"/>
<attr name="thumbs_down" format="reference"/>
<attr name="audio" format="reference"/>
@ -22,10 +23,14 @@
<attr name="selected" format="reference"/>
<attr name="search_add" format="reference"/>
<attr name="options" format="reference"/>
<attr name="play" format="reference"/>
<attr name="ic_hot" format="reference"/>
<attr name="ic_channel" format="reference"/>
<!-- Can't refer to colors directly into drawable's xml-->
<attr name="toolbar_shadow_drawable" format="reference"/>
<attr name="selector_drawable" format="reference"/>
<attr name="checked_selector_drawable" format="reference"/>
<attr name="separator_color" format="color"/>
<attr name="queue_background_color" format="color"/>

View file

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#CD201F</color>
<!-- Light Theme -->
<color name="light_background_color">#EEEEEE</color>
<color name="light_youtube_primary_color">#e53935</color>
<color name="light_youtube_dark_color">#d32f2f</color>
<color name="light_youtube_accent_color">#000000</color>
<color name="light_settings_accent_color">#e53935</color>
<color name="light_separator_color">#32000000</color>
<color name="light_ripple_color">#48868686</color>
<color name="light_selected_color">#2a868686</color>
<color name="light_contrast_background_color">#1fa6a6a6</color>
<color name="light_shadow_start_color">#5a000000</color>
<color name="light_license_background_color">#ffffff</color>
@ -16,11 +16,10 @@
<!-- Dark Theme -->
<color name="dark_background_color">#222222</color>
<color name="dark_youtube_primary_color">#CD322E</color>
<color name="dark_youtube_dark_color">#BC211D</color>
<color name="dark_youtube_accent_color">#FFFFFF</color>
<color name="dark_settings_accent_color">#ff5252</color>
<color name="dark_separator_color">#0affffff</color>
<color name="dark_ripple_color">#48ffffff</color>
<color name="dark_selected_color">#2affffff</color>
<color name="dark_contrast_background_color">#1f717171</color>
<color name="dark_shadow_start_color">#82000000</color>
<color name="dark_license_background_color">#424242</color>
@ -29,6 +28,7 @@
<!-- Black Theme -->
<color name="black_background_color">#000</color>
<color name="black_settings_accent_color">@color/dark_settings_accent_color</color>
<color name="black_separator_color">#1effffff</color>
<color name="black_contrast_background_color">#23454545</color>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- YouTube -->
<color name="light_youtube_primary_color">#e53935</color>
<color name="light_youtube_dark_color">#d32f2f</color>
<color name="light_youtube_accent_color">#000000</color>
<color name="dark_youtube_primary_color">#CD322E</color>
<color name="dark_youtube_dark_color">#BC211D</color>
<color name="dark_youtube_accent_color">#FFFFFF</color>
<!-- SoundCloud -->
<color name="light_soundcloud_primary_color">#f57c00</color>
<color name="light_soundcloud_dark_color">#ef6c00</color>
<color name="light_soundcloud_accent_color">#000000</color>
<color name="dark_soundcloud_primary_color">#f57c00</color>
<color name="dark_soundcloud_dark_color">#ef6c00</color>
<color name="dark_soundcloud_accent_color">#FFFFFF</color>
</resources>

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#CD201F</color>
</resources>

View file

@ -1,7 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources translatable="false">
<!-- Service -->
<string name="current_service_key" translatable="false">current_service</string>
<string-array name="service_list" translatable="false">
<item>@string/youtube</item>
<item>@string/soundcloud</item>
</string-array>
<string name="current_service_key" translatable="false">service</string>
<string name="default_service_value" translatable="false">@string/youtube</string>
<!-- Key values -->
<string name="download_path_key" translatable="false">download_path</string>
@ -46,12 +51,6 @@
<item>144p</item>
</string-array>
<string-array name="service_list" translatable="false">
<item>@string/youtube</item>
<item>@string/soundcloud</item>
</string-array>
<string name="service_key" translatable="false">service</string>
<string name="default_service_value" translatable="false">@string/youtube</string>
<string name="video_mp4_key" translatable="false">video_mp4</string>
<string name="video_webm_key" translatable="false">video_webm</string>
@ -131,6 +130,8 @@
<string name="main_page_selected_channel_name" translatable="false">main_page_selected_channel_name</string>
<string name="main_page_selected_channel_url" translatable="false">main_page_selected_channel_url</string>
<string name="main_page_selectd_kiosk_id" translatable="false">main_page_selectd_kiosk_id</string>
<string name="import_data">import_data</string>
<string name="export_data">export_data</string>
<!-- FileName Downloads -->
<string name="settings_file_charset_key" translatable="false">file_rename</string>
@ -149,6 +150,29 @@
<string name="default_file_charset_value" translatable="false">@string/charset_most_special_characters_value</string>
<!-- Preferred player -->
<string name="preferred_player_key" translatable="false">preferred_player_key</string>
<string name="preferred_player_default" translatable="false">@string/always_ask_player_key</string>
<string name="preferred_player_last_selected_key" translatable="false">preferred_player_last_selected</string>
<string name="video_player_key" translatable="false">video_player</string>
<string name="background_player_key" translatable="false">background_player</string>
<string name="popup_player_key" translatable="false">popup_player</string>
<string name="always_ask_player_key" translatable="false">always_ask_player</string>
<string-array name="preferred_player_description_list" translatable="false">
<item>@string/video_player</item>
<item>@string/background_player</item>
<item>@string/popup_player</item>
<item>@string/always_ask_player</item>
</string-array>
<string-array name="preferred_player_values_list" translatable="false">
<item>@string/video_player_key</item>
<item>@string/background_player_key</item>
<item>@string/popup_player_key</item>
<item>@string/always_ask_player_key</item>
</string-array>
<!-- alternatively, load these from some local android data store -->
<string-array name="language_codes" translatable="false">
<item>af</item>

View file

@ -5,6 +5,7 @@
<string name="view_count_text">%1$s views</string>
<string name="upload_date_text">Published on %1$s</string>
<string name="no_player_found">No stream player found. Do you want to install VLC?</string>
<string name="no_player_found_toast">No stream player found (you can install VLC to play it)</string>
<string name="install">Install</string>
<string name="cancel">Cancel</string>
<string name="fdroid_vlc_url" translatable="false">https://f-droid.org/repository/browse/?fdfilter=vlc&amp;fdid=org.videolan.vlc</string>
@ -119,6 +120,8 @@
<string name="best_resolution">Best resolution</string>
<string name="undo">Undo</string>
<string name="play_all">Play All</string>
<string name="always">Always</string>
<string name="just_once">Just Once</string>
<string name="notification_channel_id" translatable="false">newpipe</string>
<string name="notification_channel_name">NewPipe Notification</string>
@ -131,6 +134,10 @@
<string name="switch_to_popup">Switch to Popup</string>
<string name="switch_to_main">Switch to Main</string>
<string name="import_data_title">Import database</string>
<string name="export_data_title">Export database</string>
<string name="import_data_summary">Will override your current history and subscriptions</string>
<string name="export_data_summary">Export history, subscriptions and playlists.</string>
<!-- error strings -->
<string name="general_error">Error</string>
<string name="network_error">Network error</string>
@ -148,6 +155,10 @@
<string name="player_stream_failure">Failed to play this stream</string>
<string name="player_unrecoverable_failure">Unrecoverable player error occurred</string>
<string name="player_recoverable_failure">Recovering from player error</string>
<string name="external_player_unsupported_link_type">External players don\'t support these types of links</string>
<string name="invalid_url_toast">Invalid URL</string>
<string name="video_streams_empty">No video streams found</string>
<string name="audio_streams_empty">No audio streams found</string>
<!-- error activity -->
<string name="sorry_string">Sorry, that should not have happened.</string>
@ -310,6 +321,11 @@
<string name="select_a_channel">Select a channel</string>
<string name="no_channel_subscribed_yet">No channel subscribed yet</string>
<string name="select_a_kiosk">Select a kiosk</string>
<string name="export_complete_toast">Export complete</string>
<string name="import_complete_toast">Import complete</string>
<string name="no_valid_zip_file">No valid Zip file</string>
<string name="could_not_import_all_files">WARNING: Could not import all files.</string>
<string name="override_current_data">This will override your current setup.</string>
<!-- Kiosk Names -->
<string name="kiosk">Kiosk</string>
@ -336,4 +352,17 @@
<string name="drawer_close">Close Drawer</string>
<string name="youtube" translatable="false">YouTube</string>
<string name="soundcloud" translatable="false">SoundCloud</string>
<!-- Preferred player -->
<string name="preferred_player_share_menu_title" translatable="false">@string/preferred_player_settings_title</string>
<string name="preferred_player_share_menu_dialog_title">Open with preferred player</string>
<string name="preferred_player_settings_title">Preferred player</string>
<string name="video_player">Video player</string>
<string name="background_player">Background player</string>
<string name="popup_player">Popup player</string>
<string name="always_ask_player">Always ask</string>
<string name="preferred_player_fetcher_notification_title">Getting info…</string>
<string name="preferred_player_fetcher_notification_message">"The requested content is loading"</string>
</resources>

View file

@ -1,6 +1,15 @@
<resources>
<!-- Base application theme. -->
<style name="OpeningTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@android:color/transparent</item>
<item name="colorPrimaryDark">@android:color/transparent</item>
<item name="colorAccent">@android:color/transparent</item>
<item name="android:windowBackground">@color/dark_background_color</item>
</style>
<!-- Base themes -->
<style name="LightTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
@ -29,9 +38,13 @@
<item name="selected">@drawable/ic_fiber_manual_record_black_24dp</item>
<item name="search_add">@drawable/ic_arrow_top_left_black_24dp</item>
<item name="options">@drawable/ic_more_vert_black_24dp</item>
<item name="play">@drawable/ic_play_arrow_black_24dp</item>
<item name="ic_hot">@drawable/ic_whatshot_black_24dp</item>
<item name="ic_channel">@drawable/ic_channel_black_24dp</item>
<item name="separator_color">@color/light_separator_color</item>
<item name="contrast_background_color">@color/light_contrast_background_color</item>
<item name="checked_selector_drawable">@drawable/light_checked_selector</item>
<item name="queue_background_color">@color/light_queue_background_color</item>
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_light</item>
<item name="selector_drawable">@drawable/light_selector</item>
@ -40,7 +53,10 @@
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style>
<!-- Dark Theme-->
<style name="LightTheme.Switchable">
<item name="android:windowAnimationStyle">@style/SwitchAnimation</item>
</style>
<style name="DarkTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/dark_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/dark_youtube_dark_color</item>
@ -69,9 +85,13 @@
<item name="selected">@drawable/ic_fiber_manual_record_white_24dp</item>
<item name="search_add">@drawable/ic_arrow_top_left_white_24dp</item>
<item name="options">@drawable/ic_more_vert_white_24dp</item>
<item name="play">@drawable/ic_play_arrow_white_24dp</item>
<item name="ic_hot">@drawable/ic_whatshot_white_24dp</item>
<item name="ic_channel">@drawable/ic_channel_white_24dp</item>
<item name="separator_color">@color/dark_separator_color</item>
<item name="contrast_background_color">@color/dark_contrast_background_color</item>
<item name="checked_selector_drawable">@drawable/dark_checked_selector</item>
<item name="queue_background_color">@color/dark_queue_background_color</item>
<item name="toolbar_shadow_drawable">@drawable/toolbar_shadow_dark</item>
<item name="selector_drawable">@drawable/dark_selector</item>
@ -80,96 +100,83 @@
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style>
<style name="DarkTheme.Switchable">
<item name="android:windowAnimationStyle">@style/SwitchAnimation</item>
</style>
<style name="BlackTheme" parent="DarkTheme">
<item name="android:windowBackground">@color/black_background_color</item>
<item name="separator_color">@color/black_separator_color</item>
<item name="contrast_background_color">@color/black_contrast_background_color</item>
</style>
<style name="PlayerTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/black</item>
<style name="BlackTheme.Switchable">
<item name="android:windowAnimationStyle">@style/SwitchAnimation</item>
</style>
<!-- Settings -->
<style name="LightSettingsTheme" parent="LightTheme">
<item name="colorAccent">@color/light_settings_accent_color</item>
</style>
<style name="DarkSettingsTheme" parent="DarkTheme">
<item name="colorAccent">@color/dark_settings_accent_color</item>
</style>
<style name="BlackSettingsTheme" parent="BlackTheme">
<item name="colorAccent">@color/black_settings_accent_color</item>
</style>
<style name="SwitchAnimation" parent="@android:style/Animation.Activity">
<item name="android:windowEnterAnimation">@anim/switch_service_in</item>
<item name="android:windowExitAnimation">@anim/switch_service_out</item>
</style>
<style name="Toolbar.Title" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">18sp</item>
</style>
<style name="RedButton" parent="Widget.AppCompat.Button.Colored">
<item name="colorButtonNormal">@color/subscribe_background_color</item>
<item name="android:textColor">@color/subscribe_text_color</item>
<item name="colorControlHighlight">@color/dark_ripple_color</item>
</style>
<style name="VideoPlayerTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="OldVideoPlayerTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="windowActionBarOverlay">true</item>
<item name="android:actionBarStyle">@style/VideoPlayerActionBarTheme</item>
<item name="actionBarStyle">@style/VideoPlayerActionBarTheme</item>
<item name="android:actionBarStyle">@style/OldVideoPlayerActionBarTheme</item>
<item name="actionBarStyle">@style/OldVideoPlayerActionBarTheme</item>
<item name="android:windowBackground">@android:color/black</item>
</style>
<style name="VideoPlayerActionBarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse">
<style name="OldVideoPlayerActionBarTheme" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse">
<item name="android:displayOptions">showHome</item>
<item name="displayOptions">showHome</item>
<item name="android:background">@color/video_overlay_color</item>
<item name="background">@color/video_overlay_color</item>
</style>
<style name="FilePickerThemeLight" parent="NNF_BaseTheme.Light">
<item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item>
<item name="android:background">@color/light_background_color</item>
<item name="nnf_separator_color">@color/light_separator_color</item>
<style name="RouterActivityThemeLight" parent="LightTheme">
<item name="colorPrimary">@android:color/transparent</item>
<item name="colorPrimaryDark">@android:color/transparent</item>
<item name="colorAccent">@android:color/transparent</item>
<item name="alertDialogTheme">@style/FilePickerAlertDialogThemeLight</item>
<item name="nnf_toolbarTheme">@style/FilePickerToolbarLight</item>
</style>
<style name="FilePickerAlertDialogThemeLight" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item>
</style>
<style name="FilePickerToolbarLight" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">@color/light_youtube_primary_color</item>
</style>
<style name="FilePickerThemeDark" parent="FilePickerThemeLight">
<item name="colorPrimary">@color/dark_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/dark_youtube_dark_color</item>
<item name="colorAccent">@color/dark_youtube_accent_color</item>
<item name="android:background">@color/dark_background_color</item>
<item name="android:textColorPrimary">@color/dark_youtube_accent_color</item>
<item name="nnf_separator_color">@color/black_separator_color</item>
<item name="alertDialogTheme">@style/FilePickerAlertDialogThemeDark</item>
<item name="nnf_toolbarTheme">@style/FilePickerToolbarDark</item>
</style>
<style name="FilePickerAlertDialogThemeDark" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorPrimary">@color/dark_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/dark_youtube_dark_color</item>
<item name="colorAccent">@color/dark_youtube_accent_color</item>
</style>
<style name="FilePickerToolbarDark" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">@color/dark_youtube_primary_color</item>
</style>
<style name="PopupPermissionsTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:windowNoDisplay">true</item>
<item name="android:windowAnimationStyle">@null</item>
</style>
<style name="RouterActivityThemeDark" parent="DarkTheme">
<item name="colorPrimary">@android:color/transparent</item>
<item name="colorPrimaryDark">@android:color/transparent</item>
<item name="colorAccent">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@null</item>
</style>
</resources>

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="RedButton" parent="Widget.AppCompat.Button.Colored">
<item name="colorButtonNormal">@color/subscribe_background_color</item>
<item name="android:textColor">@color/subscribe_text_color</item>
<item name="colorControlHighlight">@color/dark_ripple_color</item>
</style>
<!--File picker styles-->
<style name="FilePickerThemeLight" parent="NNF_BaseTheme.Light">
<item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item>
<item name="android:background">@color/light_background_color</item>
<item name="nnf_separator_color">@color/light_separator_color</item>
<item name="alertDialogTheme">@style/FilePickerAlertDialogThemeLight</item>
<item name="nnf_toolbarTheme">@style/FilePickerToolbarLight</item>
</style>
<style name="FilePickerAlertDialogThemeLight" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorPrimary">@color/light_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/light_youtube_dark_color</item>
<item name="colorAccent">@color/light_youtube_accent_color</item>
</style>
<style name="FilePickerToolbarLight" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">@color/light_youtube_primary_color</item>
</style>
<style name="FilePickerThemeDark" parent="FilePickerThemeLight">
<item name="colorPrimary">@color/dark_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/dark_youtube_dark_color</item>
<item name="colorAccent">@color/dark_youtube_accent_color</item>
<item name="android:background">@color/dark_background_color</item>
<item name="android:textColorPrimary">@color/dark_youtube_accent_color</item>
<item name="nnf_separator_color">@color/black_separator_color</item>
<item name="alertDialogTheme">@style/FilePickerAlertDialogThemeDark</item>
<item name="nnf_toolbarTheme">@style/FilePickerToolbarDark</item>
</style>
<style name="FilePickerAlertDialogThemeDark" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorPrimary">@color/dark_youtube_primary_color</item>
<item name="colorPrimaryDark">@color/dark_youtube_dark_color</item>
<item name="colorAccent">@color/dark_youtube_accent_color</item>
</style>
<style name="FilePickerToolbarDark" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">@color/dark_youtube_primary_color</item>
</style>
</resources>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- YouTube -->
<style name="LightTheme.YouTube" parent="LightTheme.Switchable"/>
<style name="DarkTheme.YouTube" parent="DarkTheme.Switchable"/>
<style name="BlackTheme.YouTube" parent="BlackTheme.Switchable"/>
<!-- SoundCloud -->
<style name="LightTheme.SoundCloud" parent="LightTheme.Switchable">
<item name="colorPrimary">@color/light_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/light_soundcloud_dark_color</item>
<item name="colorAccent">@color/light_soundcloud_accent_color</item>
</style>
<style name="DarkTheme.SoundCloud" parent="DarkTheme.Switchable">
<item name="colorPrimary">@color/dark_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/dark_soundcloud_dark_color</item>
<item name="colorAccent">@color/dark_soundcloud_accent_color</item>
</style>
<style name="BlackTheme.SoundCloud" parent="BlackTheme.Switchable">
<item name="colorPrimary">@color/dark_soundcloud_primary_color</item>
<item name="colorPrimaryDark">@color/dark_soundcloud_dark_color</item>
<item name="colorAccent">@color/dark_soundcloud_accent_color</item>
</style>
</resources>

View file

@ -37,4 +37,14 @@
android:key="@string/main_page_content_key"
android:title="@string/main_page_content"
android:summary="%s"/>
<Preference
android:summary="@string/import_data_summary"
android:key="@string/import_data"
android:title="@string/import_data_title"/>
<Preference
android:title="@string/export_data_title"
android:key="@string/export_data"
android:summary="@string/export_data_summary"/>
</PreferenceScreen>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:title="@string/settings_category_video_audio_title">
<ListPreference
@ -81,6 +80,14 @@
android:summary="@string/autoplay_by_calling_app_summary"
android:title="@string/autoplay_by_calling_app_title"/>
<ListPreference
android:defaultValue="@string/preferred_player_default"
android:entries="@array/preferred_player_description_list"
android:entryValues="@array/preferred_player_values_list"
android:key="@string/preferred_player_key"
android:summary="%s"
android:title="@string/preferred_player_settings_title"/>
<SwitchPreference
android:defaultValue="false"
android:key="@string/resume_on_audio_focus_gain_key"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 207 KiB