Merge branch 'dev' into playlist_peertube

This commit is contained in:
bopol 2020-05-31 12:26:46 +02:00 committed by GitHub
commit 596eb4a0f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
865 changed files with 2627 additions and 2137 deletions

8
.gitignore vendored
View File

@ -10,3 +10,11 @@
*~ *~
.weblate .weblate
*.class *.class
# vscode / eclipse files
*.classpath
*.project
*.settings
bin/
.vscode/
*.code-workspace

View File

@ -5,13 +5,13 @@ android:
components: components:
# The BuildTools version used by NewPipe # The BuildTools version used by NewPipe
- tools - tools
- build-tools-28.0.3 - build-tools-29.0.3
# The SDK version used to compile NewPipe # The SDK version used to compile NewPipe
- android-28 - android-29
before_install: before_install:
- yes | sdkmanager "platforms;android-28" - yes | sdkmanager "platforms;android-29"
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
licenses: licenses:

View File

@ -5,16 +5,16 @@ apply plugin: 'kotlin-kapt'
apply plugin: 'checkstyle' apply plugin: 'checkstyle'
android { android {
compileSdkVersion 28 compileSdkVersion 29
buildToolsVersion '28.0.3' buildToolsVersion '29.0.3'
defaultConfig { defaultConfig {
applicationId "org.schabi.newpipe" applicationId "org.schabi.newpipe"
resValue "string", "app_name", "NewPipe" resValue "string", "app_name", "NewPipe"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 28 targetSdkVersion 29
versionCode 930 versionCode 950
versionName "0.19.3" versionName "0.19.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -80,22 +80,22 @@ android {
} }
ext { ext {
androidxLibVersion = '1.0.0' icepickVersion = '3.2.0'
exoPlayerLibVersion = '2.10.8' checkstyleVersion = '8.32'
roomDbLibVersion = '2.1.0' stethoVersion = '1.5.1'
leakCanaryLibVersion = '1.5.4' //1.6.1 leakCanaryVersion = '2.2'
okHttpLibVersion = '3.12.6' exoPlayerVersion = '2.11.4'
icepickLibVersion = '3.2.0' androidxLifecycleVersion = '2.2.0'
stethoLibVersion = '1.5.0' androidxRoomVersion = '2.2.5'
markwonVersion = '4.2.1' groupieVersion = '2.8.0'
checkstyleVersion = '8.31' markwonVersion = '4.3.1'
} }
checkstyle { checkstyle {
configFile rootProject.file('checkstyle.xml') configFile rootProject.file('checkstyle.xml')
ignoreFailures false ignoreFailures false
showViolations true showViolations true
toolVersion = "${checkstyleVersion}" toolVersion = checkstyleVersion
} }
task runCheckstyle(type: Checkstyle) { task runCheckstyle(type: Checkstyle) {
@ -117,15 +117,6 @@ task runCheckstyle(type: Checkstyle) {
} }
} }
runCheckstyle.doLast {
reports.all { report ->
def outputFile = report.destination
if (outputFile.exists() && outputFile.text.contains("severity=\"error\"")) {
throw new GradleException("There were checkstyle errors! For more info check $outputFile")
}
}
}
configurations { configurations {
ktlint ktlint
} }
@ -149,71 +140,73 @@ afterEvaluate {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "frankiesardo:icepick:${icepickVersion}"
kapt "frankiesardo:icepick-processor:${icepickVersion}"
debugImplementation "com.puppycrawl.tools:checkstyle:${checkstyleVersion}" debugImplementation "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
ktlint "com.pinterest:ktlint:0.35.0" ktlint "com.pinterest:ktlint:0.35.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.1' debugImplementation "com.facebook.stetho:stetho:${stethoVersion}"
androidTestImplementation "android.arch.persistence.room:testing:1.1.1" debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoVersion}"
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}"
implementation "com.squareup.leakcanary:leakcanary-object-watcher-android:${leakCanaryVersion}"
debugImplementation "androidx.multidex:multidex:2.0.1"
testImplementation 'junit:junit:4.13'
testImplementation 'org.mockito:mockito-core:3.3.3'
androidTestImplementation "androidx.test.ext:junit:1.1.1"
androidTestImplementation "androidx.room:room-testing:${androidxRoomVersion}"
androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0", {
exclude module: 'support-annotations' exclude module: 'support-annotations'
}) }
implementation 'com.github.TeamNewpipe:NewPipeExtractor:b40ccb507539dd7489775cca0187d6756bcb203a' implementation 'com.github.TeamNewPipe:NewPipeExtractor:bda83fe6a5b9a8a0751669fbc444fa49d72d0d2f'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.23.0'
implementation 'androidx.appcompat:appcompat:1.1.0' implementation "com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751"
implementation "androidx.legacy:legacy-support-v4:${androidxLibVersion}" implementation "org.jsoup:jsoup:1.13.1"
implementation "com.google.android.material:material:${androidxLibVersion}"
implementation "androidx.recyclerview:recyclerview:${androidxLibVersion}"
implementation "androidx.legacy:legacy-preference-v14:${androidxLibVersion}"
implementation "androidx.cardview:cardview:${androidxLibVersion}"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.xwray:groupie:2.7.0' implementation "com.squareup.okhttp3:okhttp:3.12.11"
implementation 'com.xwray:groupie-kotlin-android-extensions:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata:2.0.0' implementation "com.google.android.exoplayer:exoplayer:${exoPlayerVersion}"
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.0.0' implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerVersion}"
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
// Originally in NewPipeExtractor implementation "com.google.android.material:material:1.1.0"
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'org.jsoup:jsoup:1.9.2'
implementation 'ch.acra:acra:4.9.2' //4.11 implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.preference:preference:1.1.1"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' implementation "androidx.lifecycle:lifecycle-livedata:${androidxLifecycleVersion}"
implementation 'de.hdodenhof:circleimageview:2.2.0' implementation "androidx.lifecycle:lifecycle-viewmodel:${androidxLifecycleVersion}"
implementation 'com.nononsenseapps:filepicker:4.2.1' implementation "androidx.lifecycle:lifecycle-extensions:${androidxLifecycleVersion}"
implementation "com.google.android.exoplayer:exoplayer:${exoPlayerLibVersion}" implementation "androidx.room:room-runtime:${androidxRoomVersion}"
implementation "com.google.android.exoplayer:extension-mediasession:${exoPlayerLibVersion}" implementation "androidx.room:room-rxjava2:${androidxRoomVersion}"
kapt "androidx.room:room-compiler:${androidxRoomVersion}"
debugImplementation "com.facebook.stetho:stetho:${stethoLibVersion}" implementation "com.xwray:groupie:${groupieVersion}"
debugImplementation "com.facebook.stetho:stetho-urlconnection:${stethoLibVersion}" implementation "com.xwray:groupie-kotlin-android-extensions:${groupieVersion}"
debugImplementation 'androidx.multidex:multidex:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.2' implementation "de.hdodenhof:circleimageview:3.1.0"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation "com.nostra13.universalimageloader:universal-image-loader:1.9.5"
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
implementation 'org.ocpsoft.prettytime:prettytime:4.0.3.Final'
implementation "androidx.room:room-runtime:${roomDbLibVersion}"
implementation "androidx.room:room-rxjava2:${roomDbLibVersion}"
kapt "androidx.room:room-compiler:${roomDbLibVersion}"
implementation "frankiesardo:icepick:${icepickLibVersion}"
kapt "frankiesardo:icepick-processor:${icepickLibVersion}"
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakCanaryLibVersion}"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryLibVersion}"
implementation "com.squareup.okhttp3:okhttp:${okHttpLibVersion}"
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stethoLibVersion}"
implementation "io.noties.markwon:core:${markwonVersion}" implementation "io.noties.markwon:core:${markwonVersion}"
implementation "io.noties.markwon:linkify:${markwonVersion}" implementation "io.noties.markwon:linkify:${markwonVersion}"
implementation "com.nononsenseapps:filepicker:4.2.1"
implementation "ch.acra:acra-core:5.5.0"
implementation "io.reactivex.rxjava2:rxjava:2.2.19"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "com.jakewharton.rxbinding2:rxbinding:2.2.0"
implementation "org.ocpsoft.prettytime:prettytime:4.0.5.Final"
} }
static String getGitWorkingBranch() { static String getGitWorkingBranch() {

View File

@ -1,107 +0,0 @@
package org.schabi.newpipe;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.annotation.NonNull;
import androidx.multidex.MultiDex;
import com.facebook.stetho.Stetho;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import com.squareup.leakcanary.AndroidHeapDumper;
import com.squareup.leakcanary.DefaultLeakDirectoryProvider;
import com.squareup.leakcanary.HeapDumper;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.LeakDirectoryProvider;
import com.squareup.leakcanary.RefWatcher;
import org.schabi.newpipe.extractor.downloader.Downloader;
import java.io.File;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
public class DebugApp extends App {
private static final String TAG = DebugApp.class.toString();
@Override
protected void attachBaseContext(final Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
@Override
public void onCreate() {
super.onCreate();
initStetho();
}
@Override
protected Downloader getDownloader() {
DownloaderImpl downloader = DownloaderImpl.init(new OkHttpClient.Builder()
.addNetworkInterceptor(new StethoInterceptor()));
setCookiesToDownloader(downloader);
return downloader;
}
private void initStetho() {
// Create an InitializerBuilder
Stetho.InitializerBuilder initializerBuilder =
Stetho.newInitializerBuilder(this);
// Enable Chrome DevTools
initializerBuilder.enableWebKitInspector(
Stetho.defaultInspectorModulesProvider(this)
);
// Enable command line interface
initializerBuilder.enableDumpapp(
Stetho.defaultDumperPluginsProvider(getApplicationContext())
);
// Use the InitializerBuilder to generate an Initializer
Stetho.Initializer initializer = initializerBuilder.build();
// Initialize Stetho with the Initializer
Stetho.initialize(initializer);
}
@Override
protected boolean isDisposedRxExceptionsReported() {
return PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(getString(R.string.allow_disposed_exceptions_key), false);
}
@Override
protected RefWatcher installLeakCanary() {
return LeakCanary.refWatcher(this)
.heapDumper(new ToggleableHeapDumper(this))
// give each object 10 seconds to be gc'ed, before leak canary gets nosy on it
.watchDelay(10, TimeUnit.SECONDS)
.buildAndInstall();
}
public static class ToggleableHeapDumper implements HeapDumper {
private final HeapDumper dumper;
private final SharedPreferences preferences;
private final String dumpingAllowanceKey;
ToggleableHeapDumper(@NonNull final Context context) {
LeakDirectoryProvider leakDirectoryProvider = new DefaultLeakDirectoryProvider(context);
this.dumper = new AndroidHeapDumper(context, leakDirectoryProvider);
this.preferences = PreferenceManager.getDefaultSharedPreferences(context);
this.dumpingAllowanceKey = context.getString(R.string.allow_heap_dumping_key);
}
private boolean isDumpingAllowed() {
return preferences.getBoolean(dumpingAllowanceKey, false);
}
@Override
public File dumpHeap() {
return isDumpingAllowed() ? dumper.dumpHeap() : HeapDumper.RETRY_LATER;
}
}
}

View File

@ -0,0 +1,59 @@
package org.schabi.newpipe
import android.content.Context
import androidx.multidex.MultiDex
import androidx.preference.PreferenceManager
import com.facebook.stetho.Stetho
import com.facebook.stetho.okhttp3.StethoInterceptor
import leakcanary.AppWatcher
import leakcanary.LeakCanary
import okhttp3.OkHttpClient
import org.schabi.newpipe.extractor.downloader.Downloader
class DebugApp : App() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}
override fun onCreate() {
super.onCreate()
initStetho()
// Give each object 10 seconds to be GC'ed, before LeakCanary gets nosy on it
AppWatcher.config = AppWatcher.config.copy(watchDurationMillis = 10000)
LeakCanary.config = LeakCanary.config.copy(dumpHeap = PreferenceManager
.getDefaultSharedPreferences(this).getBoolean(getString(
R.string.allow_heap_dumping_key), false))
}
override fun getDownloader(): Downloader {
val downloader = DownloaderImpl.init(OkHttpClient.Builder()
.addNetworkInterceptor(StethoInterceptor()))
setCookiesToDownloader(downloader)
return downloader
}
private fun initStetho() {
// Create an InitializerBuilder
val initializerBuilder = Stetho.newInitializerBuilder(this)
// Enable Chrome DevTools
initializerBuilder.enableWebKitInspector(Stetho.defaultInspectorModulesProvider(this))
// Enable command line interface
initializerBuilder.enableDumpapp(
Stetho.defaultDumperPluginsProvider(applicationContext))
// Use the InitializerBuilder to generate an Initializer
val initializer = initializerBuilder.build()
// Initialize Stetho with the Initializer
Stetho.initialize(initializer)
}
override fun isDisposedRxExceptionsReported(): Boolean {
return PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(getString(R.string.allow_disposed_exceptions_key), false)
}
}

View File

@ -21,6 +21,7 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:logo="@mipmap/ic_launcher" android:logo="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true"
android:theme="@style/OpeningTheme" android:theme="@style/OpeningTheme"
tools:ignore="AllowBackup"> tools:ignore="AllowBackup">
<activity <activity

View File

@ -9,19 +9,16 @@ import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache; import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import org.acra.ACRA; import org.acra.ACRA;
import org.acra.config.ACRAConfiguration;
import org.acra.config.ACRAConfigurationException; import org.acra.config.ACRAConfigurationException;
import org.acra.config.ConfigurationBuilder; import org.acra.config.CoreConfiguration;
import org.acra.config.CoreConfigurationBuilder;
import org.acra.sender.ReportSenderFactory; import org.acra.sender.ReportSenderFactory;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Downloader;
@ -72,13 +69,6 @@ public class App extends Application {
private static final Class<? extends ReportSenderFactory>[] private static final Class<? extends ReportSenderFactory>[]
REPORT_SENDER_FACTORY_CLASSES = new Class[]{AcraReportSenderFactory.class}; REPORT_SENDER_FACTORY_CLASSES = new Class[]{AcraReportSenderFactory.class};
private static App app; private static App app;
private RefWatcher refWatcher;
@Nullable
public static RefWatcher getRefWatcher(final Context context) {
final App application = (App) context.getApplicationContext();
return application.refWatcher;
}
public static App getApp() { public static App getApp() {
return app; return app;
@ -95,13 +85,6 @@ public class App extends Application {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
refWatcher = installLeakCanary();
app = this; app = this;
// Initialize settings first because others inits can use its values // Initialize settings first because others inits can use its values
@ -219,7 +202,7 @@ public class App extends Application {
private void initACRA() { private void initACRA() {
try { try {
final ACRAConfiguration acraConfig = new ConfigurationBuilder(this) final CoreConfiguration acraConfig = new CoreConfigurationBuilder(this)
.setReportSenderFactoryClasses(REPORT_SENDER_FACTORY_CLASSES) .setReportSenderFactoryClasses(REPORT_SENDER_FACTORY_CLASSES)
.setBuildConfigClass(BuildConfig.class) .setBuildConfigClass(BuildConfig.class)
.build(); .build();
@ -280,10 +263,6 @@ public class App extends Application {
appUpdateNotificationManager.createNotificationChannel(appUpdateChannel); appUpdateNotificationManager.createNotificationChannel(appUpdateChannel);
} }
protected RefWatcher installLeakCanary() {
return RefWatcher.DISABLED;
}
protected boolean isDisposedRxExceptionsReported() { protected boolean isDisposedRxExceptionsReported() {
return false; return false;
} }

View File

@ -11,10 +11,10 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.squareup.leakcanary.RefWatcher;
import icepick.Icepick; import icepick.Icepick;
import icepick.State; import icepick.State;
import leakcanary.AppWatcher;
public abstract class BaseFragment extends Fragment { public abstract class BaseFragment extends Fragment {
public static final ImageLoader IMAGE_LOADER = ImageLoader.getInstance(); public static final ImageLoader IMAGE_LOADER = ImageLoader.getInstance();
@ -78,16 +78,14 @@ public abstract class BaseFragment extends Fragment {
Icepick.saveInstanceState(this, outState); Icepick.saveInstanceState(this, outState);
} }
protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) { } protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
RefWatcher refWatcher = App.getRefWatcher(getActivity()); AppWatcher.INSTANCE.getObjectWatcher().watch(this);
if (refWatcher != null) {
refWatcher.watch(this);
}
} }
@Override @Override
@ -100,9 +98,11 @@ public abstract class BaseFragment extends Fragment {
// Init // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
protected void initViews(final View rootView, final Bundle savedInstanceState) { } protected void initViews(final View rootView, final Bundle savedInstanceState) {
}
protected void initListeners() { } protected void initListeners() {
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Utils // Utils

View File

@ -165,7 +165,7 @@ public class MainActivity extends AppCompatActivity {
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator .add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator
.getTranslatedKioskName(ks, this)) .getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this)); .setIcon(KioskTranslator.getKioskIcon(ks, this));
kioskId++; kioskId++;
} }
@ -175,24 +175,24 @@ public class MainActivity extends AppCompatActivity {
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title) .add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.rss)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_rss));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks) .add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads) .add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.download)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_file_download));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history) .add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.history)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_history));
//Settings and About //Settings and About
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings) .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.settings)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_settings));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about) .add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.info)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_info_outline));
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open,
R.string.drawer_close); R.string.drawer_close);
@ -420,7 +420,7 @@ public class MainActivity extends AppCompatActivity {
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, kioskId, ORDER, .add(R.id.menu_tabs_group, kioskId, ORDER,
KioskTranslator.getTranslatedKioskName(ks, this)) KioskTranslator.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this)); .setIcon(KioskTranslator.getKioskIcon(ks, this));
kioskId++; kioskId++;
} }
@ -429,24 +429,24 @@ public class MainActivity extends AppCompatActivity {
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title) .add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.rss)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_rss));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks) .add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_bookmark));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads) .add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.download)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_file_download));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history) .add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.history)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_history));
//Settings and About //Settings and About
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings) .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.settings)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_settings));
drawerItems.getMenu() drawerItems.getMenu()
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about) .add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.info)); .setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_info_outline));
} }
@Override @Override

View File

@ -25,6 +25,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@ -313,7 +314,9 @@ public class RouterActivity extends AppCompatActivity {
final RadioButton radioButton final RadioButton radioButton
= (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null); = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
radioButton.setText(item.description); radioButton.setText(item.description);
radioButton.setCompoundDrawablesWithIntrinsicBounds(item.icon, 0, 0, 0); radioButton.setCompoundDrawablesWithIntrinsicBounds(
AppCompatResources.getDrawable(getApplicationContext(), item.icon),
null, null, null);
radioButton.setChecked(false); radioButton.setChecked(false);
radioButton.setId(id++); radioButton.setId(id++);
radioButton.setLayoutParams(new RadioGroup.LayoutParams( radioButton.setLayoutParams(new RadioGroup.LayoutParams(
@ -366,26 +369,26 @@ public class RouterActivity extends AppCompatActivity {
returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key), returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key),
getString(R.string.show_info), getString(R.string.show_info),
resolveResourceIdFromAttr(context, R.attr.info))); resolveResourceIdFromAttr(context, R.attr.ic_info_outline)));
if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) { if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) {
returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key), returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key),
getString(R.string.video_player), getString(R.string.video_player),
resolveResourceIdFromAttr(context, R.attr.play))); resolveResourceIdFromAttr(context, R.attr.ic_play_arrow)));
returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key), returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key),
getString(R.string.popup_player), getString(R.string.popup_player),
resolveResourceIdFromAttr(context, R.attr.popup))); resolveResourceIdFromAttr(context, R.attr.ic_popup)));
} }
if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) { if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) {
returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key), returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key),
getString(R.string.background_player), getString(R.string.background_player),
resolveResourceIdFromAttr(context, R.attr.audio))); resolveResourceIdFromAttr(context, R.attr.ic_headset)));
} }
returnList.add(new AdapterChoiceItem(getString(R.string.download_key), returnList.add(new AdapterChoiceItem(getString(R.string.download_key),
getString(R.string.download), getString(R.string.download),
resolveResourceIdFromAttr(context, R.attr.download))); resolveResourceIdFromAttr(context, R.attr.ic_file_download)));
return returnList; return returnList;
} }

View File

@ -2,8 +2,6 @@ package org.schabi.newpipe.about;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -17,9 +15,9 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ShareUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
/** /**
* Fragment containing the software licenses. * Fragment containing the software licenses.
@ -27,7 +25,7 @@ import java.util.Comparator;
public class LicenseFragment extends Fragment { public class LicenseFragment extends Fragment {
private static final String ARG_COMPONENTS = "components"; private static final String ARG_COMPONENTS = "components";
private SoftwareComponent[] softwareComponents; private SoftwareComponent[] softwareComponents;
private SoftwareComponent mComponentForContextMenu; private SoftwareComponent componentForContextMenu;
public static LicenseFragment newInstance(final SoftwareComponent[] softwareComponents) { public static LicenseFragment newInstance(final SoftwareComponent[] softwareComponents) {
if (softwareComponents == null) { if (softwareComponents == null) {
@ -46,7 +44,7 @@ public class LicenseFragment extends Fragment {
* @param context the context to use * @param context the context to use
* @param license the license to show * @param license the license to show
*/ */
public static void showLicense(final Context context, final License license) { private static void showLicense(final Context context, final License license) {
new LicenseFragmentHelper((Activity) context).execute(license); new LicenseFragmentHelper((Activity) context).execute(license);
} }
@ -57,45 +55,34 @@ public class LicenseFragment extends Fragment {
.getParcelableArray(ARG_COMPONENTS); .getParcelableArray(ARG_COMPONENTS);
// Sort components by name // Sort components by name
Arrays.sort(softwareComponents, new Comparator<SoftwareComponent>() { Arrays.sort(softwareComponents, (o1, o2) -> o1.getName().compareTo(o2.getName()));
@Override
public int compare(final SoftwareComponent o1, final SoftwareComponent o2) {
return o1.getName().compareTo(o2.getName());
}
});
} }
@Nullable @Nullable
@Override @Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) { @Nullable final Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_licenses, container, false); final View rootView = inflater.inflate(R.layout.fragment_licenses, container, false);
ViewGroup softwareComponentsView = rootView.findViewById(R.id.software_components); final ViewGroup softwareComponentsView = rootView.findViewById(R.id.software_components);
View licenseLink = rootView.findViewById(R.id.app_read_license); final View licenseLink = rootView.findViewById(R.id.app_read_license);
licenseLink.setOnClickListener(new OnReadFullLicenseClickListener()); licenseLink.setOnClickListener(v ->
showLicense(getActivity(), StandardLicenses.GPL3));
for (final SoftwareComponent component : softwareComponents) { for (final SoftwareComponent component : softwareComponents) {
View componentView = inflater final View componentView = inflater
.inflate(R.layout.item_software_component, container, false); .inflate(R.layout.item_software_component, container, false);
TextView softwareName = componentView.findViewById(R.id.name); final TextView softwareName = componentView.findViewById(R.id.name);
TextView copyright = componentView.findViewById(R.id.copyright); final TextView copyright = componentView.findViewById(R.id.copyright);
softwareName.setText(component.getName()); softwareName.setText(component.getName());
copyright.setText(getContext().getString(R.string.copyright, copyright.setText(getString(R.string.copyright,
component.getYears(), component.getYears(),
component.getCopyrightOwner(), component.getCopyrightOwner(),
component.getLicense().getAbbreviation())); component.getLicense().getAbbreviation()));
componentView.setTag(component); componentView.setTag(component);
componentView.setOnClickListener(new View.OnClickListener() { componentView.setOnClickListener(v ->
@Override showLicense(getActivity(), component.getLicense()));
public void onClick(final View v) {
Context context = v.getContext();
if (context != null) {
showLicense(context, component.getLicense());
}
}
});
softwareComponentsView.addView(componentView); softwareComponentsView.addView(componentView);
registerForContextMenu(componentView); registerForContextMenu(componentView);
} }
@ -105,40 +92,28 @@ public class LicenseFragment extends Fragment {
@Override @Override
public void onCreateContextMenu(final ContextMenu menu, final View v, public void onCreateContextMenu(final ContextMenu menu, final View v,
final ContextMenu.ContextMenuInfo menuInfo) { final ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflater = getActivity().getMenuInflater(); final MenuInflater inflater = getActivity().getMenuInflater();
SoftwareComponent component = (SoftwareComponent) v.getTag(); final SoftwareComponent component = (SoftwareComponent) v.getTag();
menu.setHeaderTitle(component.getName()); menu.setHeaderTitle(component.getName());
inflater.inflate(R.menu.software_component, menu); inflater.inflate(R.menu.software_component, menu);
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
mComponentForContextMenu = (SoftwareComponent) v.getTag(); componentForContextMenu = (SoftwareComponent) v.getTag();
} }
@Override @Override
public boolean onContextItemSelected(final MenuItem item) { public boolean onContextItemSelected(final MenuItem item) {
// item.getMenuInfo() is null so we use the tag of the view // item.getMenuInfo() is null so we use the tag of the view
final SoftwareComponent component = mComponentForContextMenu; final SoftwareComponent component = componentForContextMenu;
if (component == null) { if (component == null) {
return false; return false;
} }
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_website: case R.id.action_website:
openWebsite(component.getLink()); ShareUtils.openUrlInBrowser(getActivity(), component.getLink());
return true; return true;
case R.id.action_show_license: case R.id.action_show_license:
showLicense(getContext(), component.getLicense()); showLicense(getActivity(), component.getLicense());
} }
return false; return false;
} }
private void openWebsite(final String componentLink) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(componentLink));
startActivity(browserIntent);
}
private static class OnReadFullLicenseClickListener implements View.OnClickListener {
@Override
public void onClick(final View v) {
LicenseFragment.showLicense(v.getContext(), StandardLicenses.GPL3);
}
}
} }

View File

@ -3,8 +3,10 @@ package org.schabi.newpipe.about;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Base64;
import android.webkit.WebView; import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@ -12,6 +14,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -26,28 +29,18 @@ public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
weakReference = new WeakReference<>(activity); weakReference = new WeakReference<>(activity);
} }
private static String getFinishString(final Activity activity) {
return activity.getApplicationContext().getResources().getString(R.string.finish);
}
/** /**
* @param context the context to use * @param context the context to use
* @param license the license * @param license the license
* @return String which contains a HTML formatted license page * @return String which contains a HTML formatted license page
* styled according to the context's theme * styled according to the context's theme
*/ */
public static String getFormattedLicense(final Context context, final License license) { private static String getFormattedLicense(@NonNull final Context context,
if (context == null) { @NonNull final License license) {
throw new NullPointerException("context is null"); final StringBuilder licenseContent = new StringBuilder();
} final String webViewData;
if (license == null) {
throw new NullPointerException("license is null");
}
StringBuilder licenseContent = new StringBuilder();
String webViewData;
try { try {
BufferedReader in = new BufferedReader(new InputStreamReader( final BufferedReader in = new BufferedReader(new InputStreamReader(
context.getAssets().open(license.getFilename()), StandardCharsets.UTF_8)); context.getAssets().open(license.getFilename()), StandardCharsets.UTF_8));
String str; String str;
while ((str = in.readLine()) != null) { while ((str = in.readLine()) != null) {
@ -56,13 +49,11 @@ public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
in.close(); in.close();
// split the HTML file and insert the stylesheet into the HEAD of the file // split the HTML file and insert the stylesheet into the HEAD of the file
String[] insert = licenseContent.toString().split("</head>"); webViewData = licenseContent.toString().replace("</head>",
webViewData = insert[0] + "<style type=\"text/css\">" "<style>" + getLicenseStylesheet(context) + "</style></head>");
+ getLicenseStylesheet(context) + "</style></head>" } catch (IOException e) {
+ insert[1]; throw new IllegalArgumentException(
} catch (Exception e) { "Could not get license file: " + license.getFilename(), e);
throw new NullPointerException("could not get license file:"
+ getLicenseStylesheet(context));
} }
return webViewData; return webViewData;
} }
@ -71,21 +62,19 @@ public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
* @param context * @param context
* @return String which is a CSS stylesheet according to the context's theme * @return String which is a CSS stylesheet according to the context's theme
*/ */
public static String getLicenseStylesheet(final Context context) { private static String getLicenseStylesheet(final Context context) {
boolean isLightTheme = ThemeHelper.isLightThemeSelected(context); final boolean isLightTheme = ThemeHelper.isLightThemeSelected(context);
return "body{padding:12px 15px;margin:0;background:#" return "body{padding:12px 15px;margin:0;"
+ getHexRGBColor(context, isLightTheme + "background:#" + getHexRGBColor(context, isLightTheme
? R.color.light_license_background_color ? R.color.light_license_background_color
: R.color.dark_license_background_color) : R.color.dark_license_background_color) + ";"
+ ";color:#" + "color:#" + getHexRGBColor(context, isLightTheme
+ getHexRGBColor(context, isLightTheme
? R.color.light_license_text_color ? R.color.light_license_text_color
: R.color.dark_license_text_color) + ";}" : R.color.dark_license_text_color) + "}"
+ "a[href]{color:#" + "a[href]{color:#" + getHexRGBColor(context, isLightTheme
+ getHexRGBColor(context, isLightTheme
? R.color.light_youtube_primary_color ? R.color.light_youtube_primary_color
: R.color.dark_youtube_primary_color) + ";}" : R.color.dark_youtube_primary_color) + "}"
+ "pre{white-space: pre-wrap;}"; + "pre{white-space:pre-wrap}";
} }
/** /**
@ -95,13 +84,13 @@ public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
* @param color the color number from R.color * @param color the color number from R.color
* @return a six characters long String with hexadecimal RGB values * @return a six characters long String with hexadecimal RGB values
*/ */
public static String getHexRGBColor(final Context context, final int color) { private static String getHexRGBColor(final Context context, final int color) {
return context.getResources().getString(color).substring(3); return context.getResources().getString(color).substring(3);
} }
@Nullable @Nullable
private Activity getActivity() { private Activity getActivity() {
Activity activity = weakReference.get(); final Activity activity = weakReference.get();
if (activity != null && activity.isFinishing()) { if (activity != null && activity.isFinishing()) {
return null; return null;
@ -118,22 +107,22 @@ public class LicenseFragmentHelper extends AsyncTask<Object, Void, Integer> {
@Override @Override
protected void onPostExecute(final Integer result) { protected void onPostExecute(final Integer result) {
Activity activity = getActivity(); final Activity activity = getActivity();
if (activity == null) { if (activity == null) {
return; return;
} }
String webViewData = getFormattedLicense(activity, license); final String webViewData = Base64.encodeToString(getFormattedLicense(activity, license)
AlertDialog.Builder alert = new AlertDialog.Builder(activity); .getBytes(StandardCharsets.UTF_8), Base64.NO_PADDING);
final WebView webView = new WebView(activity);
webView.loadData(webViewData, "text/html; charset=UTF-8", "base64");
final AlertDialog.Builder alert = new AlertDialog.Builder(activity);
alert.setTitle(license.getName()); alert.setTitle(license.getName());
alert.setView(webView);
WebView wv = new WebView(activity); assureCorrectAppLanguage(activity);
wv.loadData(webViewData, "text/html; charset=UTF-8", null); alert.setNegativeButton(activity.getString(R.string.finish),
(dialog, which) -> dialog.dismiss());
alert.setView(wv);
assureCorrectAppLanguage(activity.getApplicationContext());
alert.setNegativeButton(getFinishString(activity), (dialog, which) -> dialog.dismiss());
alert.show(); alert.show();
} }
} }

View File

@ -396,13 +396,11 @@ public class DownloadDialog extends DialogFragment
Log.d(TAG, "initToolbar() called with: toolbar = [" + toolbar + "]"); Log.d(TAG, "initToolbar() called with: toolbar = [" + toolbar + "]");
} }
boolean isLight = ThemeHelper.isLightThemeSelected(getActivity());
toolbar.setTitle(R.string.download_dialog_title); toolbar.setTitle(R.string.download_dialog_title);
toolbar.setNavigationIcon(isLight ? R.drawable.ic_arrow_back_black_24dp toolbar.setNavigationIcon(
: R.drawable.ic_arrow_back_white_24dp); ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_arrow_back));
toolbar.inflateMenu(R.menu.dialog_url); toolbar.inflateMenu(R.menu.dialog_url);
toolbar.setNavigationOnClickListener(v -> getDialog().dismiss()); toolbar.setNavigationOnClickListener(v -> requireDialog().dismiss());
toolbar.setNavigationContentDescription(R.string.cancel); toolbar.setNavigationContentDescription(R.string.cancel);
okButton = toolbar.findViewById(R.id.okay); okButton = toolbar.findViewById(R.id.okay);

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe.fragments; package org.schabi.newpipe.fragments;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
@ -31,6 +32,7 @@ import org.schabi.newpipe.settings.tabs.Tab;
import org.schabi.newpipe.settings.tabs.TabsManager; import org.schabi.newpipe.settings.tabs.TabsManager;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.ScrollableTabLayout; import org.schabi.newpipe.views.ScrollableTabLayout;
import java.util.ArrayList; import java.util.ArrayList;
@ -90,6 +92,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
tabLayout = rootView.findViewById(R.id.main_tab_layout); tabLayout = rootView.findViewById(R.id.main_tab_layout);
viewPager = rootView.findViewById(R.id.pager); viewPager = rootView.findViewById(R.id.pager);
tabLayout.setTabIconTint(ColorStateList.valueOf(
ThemeHelper.resolveColorFromAttr(requireContext(), R.attr.colorAccent)));
tabLayout.setupWithViewPager(viewPager); tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this); tabLayout.addOnTabSelectedListener(this);

View File

@ -4,6 +4,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -35,6 +36,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
@ -85,6 +87,7 @@ import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.StreamItemAdapter; import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.AnimatedProgressBar; import org.schabi.newpipe.views.AnimatedProgressBar;
import org.schabi.newpipe.views.LargeTextMovementMethod; import org.schabi.newpipe.views.LargeTextMovementMethod;
@ -495,13 +498,15 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
videoTitleTextView.setMaxLines(1); videoTitleTextView.setMaxLines(1);
videoDescriptionRootLayout.setVisibility(View.GONE); videoDescriptionRootLayout.setVisibility(View.GONE);
videoDescriptionView.setFocusable(false); videoDescriptionView.setFocusable(false);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down); videoTitleToggleArrow.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_expand_more));
} else { } else {
videoTitleTextView.setMaxLines(10); videoTitleTextView.setMaxLines(10);
videoDescriptionRootLayout.setVisibility(View.VISIBLE); videoDescriptionRootLayout.setVisibility(View.VISIBLE);
videoDescriptionView.setFocusable(true); videoDescriptionView.setFocusable(true);
videoDescriptionView.setMovementMethod(new LargeTextMovementMethod()); videoDescriptionView.setMovementMethod(new LargeTextMovementMethod());
videoTitleToggleArrow.setImageResource(R.drawable.arrow_up); videoTitleToggleArrow.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_expand_less));
} }
} }
@ -1073,7 +1078,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
return; return;
} }
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, imageResource)); thumbnailImageView.setImageDrawable(
AppCompatResources.getDrawable(requireContext(), imageResource));
animateView(thumbnailImageView, false, 0, 0, animateView(thumbnailImageView, false, 0, 0,
() -> animateView(thumbnailImageView, true, 500)); () -> animateView(thumbnailImageView, true, 500));
} }
@ -1114,7 +1120,6 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
animateView(videoTitleTextView, true, 0); animateView(videoTitleTextView, true, 0);
videoDescriptionRootLayout.setVisibility(View.GONE); videoDescriptionRootLayout.setVisibility(View.GONE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoTitleToggleArrow.setVisibility(View.GONE); videoTitleToggleArrow.setVisibility(View.GONE);
videoTitleRoot.setClickable(false); videoTitleRoot.setClickable(false);
@ -1166,8 +1171,9 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
uploaderThumb.setVisibility(View.GONE); uploaderThumb.setVisibility(View.GONE);
} }
subChannelThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy)); Drawable buddyDrawable = AppCompatResources.getDrawable(activity, R.drawable.buddy);
uploaderThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy)); subChannelThumb.setImageDrawable(buddyDrawable);
uploaderThumb.setImageDrawable(buddyDrawable);
if (info.getViewCount() >= 0) { if (info.getViewCount() >= 0) {
if (info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) { if (info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) {
@ -1229,8 +1235,9 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
videoDescriptionView.setVisibility(View.GONE); videoDescriptionView.setVisibility(View.GONE);
videoTitleRoot.setClickable(true); videoTitleRoot.setClickable(true);
videoTitleToggleArrow.setImageResource(
ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_expand_more));
videoTitleToggleArrow.setVisibility(View.VISIBLE); videoTitleToggleArrow.setVisibility(View.VISIBLE);
videoTitleToggleArrow.setImageResource(R.drawable.arrow_down);
videoDescriptionRootLayout.setVisibility(View.GONE); videoDescriptionRootLayout.setVisibility(View.GONE);
if (info.getUploadDate() != null) { if (info.getUploadDate() != null) {
@ -1276,7 +1283,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
detailControlsPopup.setVisibility(View.GONE); detailControlsPopup.setVisibility(View.GONE);
spinnerToolbar.setVisibility(View.GONE); spinnerToolbar.setVisibility(View.GONE);
thumbnailPlayButton.setImageResource(R.drawable.ic_headset_white_24dp); thumbnailPlayButton.setImageResource(R.drawable.ic_headset_shadow);
break; break;
} }

View File

@ -47,6 +47,7 @@ import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ShareUtils; import org.schabi.newpipe.util.ShareUtils;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
@ -374,8 +375,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
int backgroundDuration = isButtonVisible ? 300 : 0; int backgroundDuration = isButtonVisible ? 300 : 0;
int textDuration = isButtonVisible ? 200 : 0; int textDuration = isButtonVisible ? 200 : 0;
int subscribeBackground = ContextCompat int subscribeBackground = ThemeHelper
.getColor(activity, R.color.subscribe_background_color); .resolveColorFromAttr(activity, R.attr.colorPrimary);
int subscribeText = ContextCompat.getColor(activity, R.color.subscribe_text_color); int subscribeText = ContextCompat.getColor(activity, R.color.subscribe_text_color);
int subscribedBackground = ContextCompat int subscribedBackground = ContextCompat
.getColor(activity, R.color.subscribed_background_color); .getColor(activity, R.color.subscribed_background_color);

View File

@ -117,8 +117,8 @@ public class SuggestionListAdapter
queryView = rootView.findViewById(R.id.suggestion_search); queryView = rootView.findViewById(R.id.suggestion_search);
insertView = rootView.findViewById(R.id.suggestion_insert); insertView = rootView.findViewById(R.id.suggestion_insert);
historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.history); historyResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.ic_history);
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search); searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.ic_search);
} }
private static int resolveResourceIdFromAttr(final Context context, private static int resolveResourceIdFromAttr(final Context context,

View File

@ -3,6 +3,7 @@ package org.schabi.newpipe.info_list.holder;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.text.style.URLSpan; import android.text.style.URLSpan;
import android.text.util.Linkify; import android.text.util.Linkify;
@ -12,7 +13,6 @@ import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import org.jsoup.helper.StringUtil;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
@ -143,7 +143,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
} }
private void openCommentAuthor(final CommentsInfoItem item) { private void openCommentAuthor(final CommentsInfoItem item) {
if (StringUtil.isBlank(item.getUploaderUrl())) { if (TextUtils.isEmpty(item.getUploaderUrl())) {
return; return;
} }
try { try {

View File

@ -362,14 +362,14 @@ public class StatisticsPlaylistFragment
if (sortMode == StatisticSortMode.LAST_PLAYED) { if (sortMode == StatisticSortMode.LAST_PLAYED) {
sortMode = StatisticSortMode.MOST_PLAYED; sortMode = StatisticSortMode.MOST_PLAYED;
setTitle(getString(R.string.title_most_played)); setTitle(getString(R.string.title_most_played));
sortButtonIcon sortButtonIcon.setImageResource(
.setImageResource(ThemeHelper.getIconByAttr(R.attr.history, getContext())); ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_history));
sortButtonText.setText(R.string.title_last_played); sortButtonText.setText(R.string.title_last_played);
} else { } else {
sortMode = StatisticSortMode.LAST_PLAYED; sortMode = StatisticSortMode.LAST_PLAYED;
setTitle(getString(R.string.title_last_played)); setTitle(getString(R.string.title_last_played));
sortButtonIcon sortButtonIcon.setImageResource(
.setImageResource(ThemeHelper.getIconByAttr(R.attr.filter, getContext())); ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_filter_list));
sortButtonText.setText(R.string.title_most_played); sortButtonText.setText(R.string.title_most_played);
} }
startLoading(true); startLoading(true);

View File

@ -20,10 +20,10 @@ enum class FeedGroupIcon(
ALL(0, R.attr.ic_asterisk), ALL(0, R.attr.ic_asterisk),
MUSIC(1, R.attr.ic_music_note), MUSIC(1, R.attr.ic_music_note),
EDUCATION(2, R.attr.ic_school), EDUCATION(2, R.attr.ic_school),
FITNESS(3, R.attr.ic_fitness), FITNESS(3, R.attr.ic_fitness_center),
SPACE(4, R.attr.ic_telescope), SPACE(4, R.attr.ic_telescope),
COMPUTER(5, R.attr.ic_computer), COMPUTER(5, R.attr.ic_computer),
GAMING(6, R.attr.ic_videogame), GAMING(6, R.attr.ic_videogame_asset),
SPORTS(7, R.attr.ic_sports), SPORTS(7, R.attr.ic_sports),
NEWS(8, R.attr.ic_megaphone), NEWS(8, R.attr.ic_megaphone),
FAVORITES(9, R.attr.ic_heart), FAVORITES(9, R.attr.ic_heart),
@ -32,29 +32,29 @@ enum class FeedGroupIcon(
TREND(12, R.attr.ic_trending_up), TREND(12, R.attr.ic_trending_up),
MOVIE(13, R.attr.ic_movie), MOVIE(13, R.attr.ic_movie),
BACKUP(14, R.attr.ic_backup), BACKUP(14, R.attr.ic_backup),
ART(15, R.attr.palette), ART(15, R.attr.ic_palette),
PERSON(16, R.attr.ic_person), PERSON(16, R.attr.ic_person),
PEOPLE(17, R.attr.ic_people), PEOPLE(17, R.attr.ic_people),
MONEY(18, R.attr.ic_money), MONEY(18, R.attr.ic_money),
KIDS(19, R.attr.ic_kids), KIDS(19, R.attr.ic_child_care),
FOOD(20, R.attr.ic_fastfood), FOOD(20, R.attr.ic_fastfood),
SMILE(21, R.attr.ic_smile), SMILE(21, R.attr.ic_smile),
EXPLORE(22, R.attr.ic_explore), EXPLORE(22, R.attr.ic_explore),
RESTAURANT(23, R.attr.ic_restaurant), RESTAURANT(23, R.attr.ic_restaurant),
MIC(24, R.attr.ic_mic), MIC(24, R.attr.ic_mic),
HEADSET(25, R.attr.audio), HEADSET(25, R.attr.ic_headset),
RADIO(26, R.attr.ic_radio), RADIO(26, R.attr.ic_radio),
SHOPPING_CART(27, R.attr.ic_shopping_cart), SHOPPING_CART(27, R.attr.ic_shopping_cart),
WATCH_LATER(28, R.attr.ic_watch_later), WATCH_LATER(28, R.attr.ic_watch_later),
WORK(29, R.attr.ic_work), WORK(29, R.attr.ic_work),
HOT(30, R.attr.ic_hot), HOT(30, R.attr.ic_kiosk_hot),
CHANNEL(31, R.attr.ic_channel), CHANNEL(31, R.attr.ic_channel),
BOOKMARK(32, R.attr.ic_bookmark), BOOKMARK(32, R.attr.ic_bookmark),
PETS(33, R.attr.ic_pets), PETS(33, R.attr.ic_pets),
WORLD(34, R.attr.ic_world), WORLD(34, R.attr.ic_world),
STAR(35, R.attr.ic_stars), STAR(35, R.attr.ic_stars),
SUN(36, R.attr.ic_sunny), SUN(36, R.attr.ic_sunny),
RSS(37, R.attr.rss); RSS(37, R.attr.ic_rss);
@DrawableRes @DrawableRes
fun getDrawableRes(context: Context): Int { fun getDrawableRes(context: Context): Int {

View File

@ -49,7 +49,6 @@ import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver; import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.MediaSourceTag;
@ -91,7 +90,6 @@ public final class BackgroundPlayer extends Service {
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Service-Activity Binder // Service-Activity Binder
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private LockManager lockManager;
private SharedPreferences sharedPreferences; private SharedPreferences sharedPreferences;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -116,7 +114,6 @@ public final class BackgroundPlayer extends Service {
Log.d(TAG, "onCreate() called"); Log.d(TAG, "onCreate() called");
} }
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
lockManager = new LockManager(this);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
assureCorrectAppLanguage(this); assureCorrectAppLanguage(this);
ThemeHelper.setTheme(this); ThemeHelper.setTheme(this);
@ -166,9 +163,6 @@ public final class BackgroundPlayer extends Service {
Log.d(TAG, "onClose() called"); Log.d(TAG, "onClose() called");
} }
if (lockManager != null) {
lockManager.releaseWifiAndCpu();
}
if (basePlayerImpl != null) { if (basePlayerImpl != null) {
basePlayerImpl.savePlaybackState(); basePlayerImpl.savePlaybackState();
basePlayerImpl.stopActivityBinding(); basePlayerImpl.stopActivityBinding();
@ -179,7 +173,6 @@ public final class BackgroundPlayer extends Service {
} }
mBinder = null; mBinder = null;
basePlayerImpl = null; basePlayerImpl = null;
lockManager = null;
stopForeground(true); stopForeground(true);
stopSelf(); stopSelf();
@ -208,9 +201,10 @@ public final class BackgroundPlayer extends Service {
} }
private NotificationCompat.Builder createNotification() { private NotificationCompat.Builder createNotification() {
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification); notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
R.layout.player_background_notification);
bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID,
R.layout.player_notification_expanded); R.layout.player_background_notification_expanded);
setupNotification(notRemoteView); setupNotification(notRemoteView);
setupNotification(bigNotRemoteView); setupNotification(bigNotRemoteView);
@ -662,8 +656,7 @@ public final class BackgroundPlayer extends Service {
super.onPlaying(); super.onPlaying();
resetNotification(); resetNotification();
updateNotificationThumbnail(); updateNotificationThumbnail();
updateNotification(R.drawable.ic_pause_white); updateNotification(R.drawable.exo_controls_pause);
lockManager.acquireWifiAndCpu();
} }
@Override @Override
@ -671,8 +664,7 @@ public final class BackgroundPlayer extends Service {
super.onPaused(); super.onPaused();
resetNotification(); resetNotification();
updateNotificationThumbnail(); updateNotificationThumbnail();
updateNotification(R.drawable.ic_play_arrow_white); updateNotification(R.drawable.exo_controls_play);
lockManager.releaseWifiAndCpu();
} }
@Override @Override
@ -686,8 +678,7 @@ public final class BackgroundPlayer extends Service {
notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false); notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
} }
updateNotificationThumbnail(); updateNotificationThumbnail();
updateNotification(R.drawable.ic_replay_white); updateNotification(R.drawable.ic_replay_white_24dp);
lockManager.releaseWifiAndCpu();
} }
} }
} }

View File

@ -35,9 +35,9 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.LoadControl; import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
@ -232,14 +232,18 @@ public abstract class BasePlayer implements
public void initPlayer(final boolean playOnReady) { public void initPlayer(final boolean playOnReady) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "initPlayer() called with: context = [" + context + "]"); Log.d(TAG, "initPlayer() called with: playOnReady = [" + playOnReady + "]");
} }
simpleExoPlayer = ExoPlayerFactory simpleExoPlayer = new SimpleExoPlayer.Builder(context, renderFactory)
.newSimpleInstance(context, renderFactory, trackSelector, loadControl); .setTrackSelector(trackSelector)
.setLoadControl(loadControl)
.build();
simpleExoPlayer.addListener(this); simpleExoPlayer.addListener(this);
simpleExoPlayer.setPlayWhenReady(playOnReady); simpleExoPlayer.setPlayWhenReady(playOnReady);
simpleExoPlayer.setSeekParameters(PlayerHelper.getSeekParameters(context)); simpleExoPlayer.setSeekParameters(PlayerHelper.getSeekParameters(context));
simpleExoPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
simpleExoPlayer.setHandleAudioBecomingNoisy(true);
audioReactor = new AudioReactor(context, simpleExoPlayer); audioReactor = new AudioReactor(context, simpleExoPlayer);
mediaSessionManager = new MediaSessionManager(context, simpleExoPlayer, mediaSessionManager = new MediaSessionManager(context, simpleExoPlayer,
@ -666,11 +670,9 @@ public abstract class BasePlayer implements
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override @Override
public void onTimelineChanged(final Timeline timeline, final Object manifest, public void onTimelineChanged(final Timeline timeline, final int reason) {
@Player.TimelineChangeReason final int reason) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "ExoPlayer - onTimelineChanged() called with " Log.d(TAG, "ExoPlayer - onTimelineChanged() called with "
+ (manifest == null ? "no manifest" : "available manifest") + ", "
+ "timeline size = [" + timeline.getWindowCount() + "], " + "timeline size = [" + timeline.getWindowCount() + "], "
+ "reason = [" + reason + "]"); + "reason = [" + reason + "]");
} }

View File

@ -491,7 +491,7 @@ public final class MainVideoPlayer extends AppCompatActivity
protected void setMuteButton(final ImageButton muteButton, final boolean isMuted) { protected void setMuteButton(final ImageButton muteButton, final boolean isMuted) {
muteButton.setImageDrawable(AppCompatResources.getDrawable(getApplicationContext(), isMuted muteButton.setImageDrawable(AppCompatResources.getDrawable(getApplicationContext(), isMuted
? R.drawable.ic_volume_off_white_72dp : R.drawable.ic_volume_up_white_72dp)); ? R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp));
} }
@ -989,7 +989,7 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override @Override
public void onBlocked() { public void onBlocked() {
super.onBlocked(); super.onBlocked();
playPauseButton.setImageResource(R.drawable.ic_pause_white); playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp);
animatePlayButtons(false, 100); animatePlayButtons(false, 100);
animateView(closeButton, false, DEFAULT_CONTROLS_DURATION); animateView(closeButton, false, DEFAULT_CONTROLS_DURATION);
getRootView().setKeepScreenOn(true); getRootView().setKeepScreenOn(true);
@ -1005,7 +1005,7 @@ public final class MainVideoPlayer extends AppCompatActivity
public void onPlaying() { public void onPlaying() {
super.onPlaying(); super.onPlaying();
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> { animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
playPauseButton.setImageResource(R.drawable.ic_pause_white); playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp);
animatePlayButtons(true, 200); animatePlayButtons(true, 200);
playPauseButton.requestFocus(); playPauseButton.requestFocus();
animateView(closeButton, false, DEFAULT_CONTROLS_DURATION); animateView(closeButton, false, DEFAULT_CONTROLS_DURATION);
@ -1018,7 +1018,7 @@ public final class MainVideoPlayer extends AppCompatActivity
public void onPaused() { public void onPaused() {
super.onPaused(); super.onPaused();
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> { animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 80, 0, () -> {
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white); playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp);
animatePlayButtons(true, 200); animatePlayButtons(true, 200);
playPauseButton.requestFocus(); playPauseButton.requestFocus();
animateView(closeButton, false, DEFAULT_CONTROLS_DURATION); animateView(closeButton, false, DEFAULT_CONTROLS_DURATION);
@ -1039,7 +1039,7 @@ public final class MainVideoPlayer extends AppCompatActivity
@Override @Override
public void onCompleted() { public void onCompleted() {
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, () -> { animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 0, 0, () -> {
playPauseButton.setImageResource(R.drawable.ic_replay_white); playPauseButton.setImageResource(R.drawable.ic_replay_white_24dp);
animatePlayButtons(true, DEFAULT_CONTROLS_DURATION); animatePlayButtons(true, DEFAULT_CONTROLS_DURATION);
animateView(closeButton, true, DEFAULT_CONTROLS_DURATION); animateView(closeButton, true, DEFAULT_CONTROLS_DURATION);
}); });
@ -1368,12 +1368,12 @@ public final class MainVideoPlayer extends AppCompatActivity
} }
final int resId = currentProgressPercent <= 0 final int resId = currentProgressPercent <= 0
? R.drawable.ic_volume_off_white_72dp ? R.drawable.ic_volume_off_white_24dp
: currentProgressPercent < 0.25 : currentProgressPercent < 0.25
? R.drawable.ic_volume_mute_white_72dp ? R.drawable.ic_volume_mute_white_24dp
: currentProgressPercent < 0.75 : currentProgressPercent < 0.75
? R.drawable.ic_volume_down_white_72dp ? R.drawable.ic_volume_down_white_24dp
: R.drawable.ic_volume_up_white_72dp; : R.drawable.ic_volume_up_white_24dp;
playerImpl.getVolumeImageView().setImageDrawable( playerImpl.getVolumeImageView().setImageDrawable(
AppCompatResources.getDrawable(getApplicationContext(), resId) AppCompatResources.getDrawable(getApplicationContext(), resId)
@ -1400,10 +1400,10 @@ public final class MainVideoPlayer extends AppCompatActivity
} }
final int resId = currentProgressPercent < 0.25 final int resId = currentProgressPercent < 0.25
? R.drawable.ic_brightness_low_white_72dp ? R.drawable.ic_brightness_low_white_24dp
: currentProgressPercent < 0.75 : currentProgressPercent < 0.75
? R.drawable.ic_brightness_medium_white_72dp ? R.drawable.ic_brightness_medium_white_24dp
: R.drawable.ic_brightness_high_white_72dp; : R.drawable.ic_brightness_high_white_24dp;
playerImpl.getBrightnessImageView().setImageDrawable( playerImpl.getBrightnessImageView().setImageDrawable(
AppCompatResources.getDrawable(getApplicationContext(), resId) AppCompatResources.getDrawable(getApplicationContext(), resId)

View File

@ -68,7 +68,6 @@ import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.helper.LockManager;
import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.player.resolver.MediaSourceTag; import org.schabi.newpipe.player.resolver.MediaSourceTag;
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
@ -132,7 +131,6 @@ public final class PopupVideoPlayer extends Service {
private RemoteViews notRemoteView; private RemoteViews notRemoteView;
private VideoPlayerImpl playerImpl; private VideoPlayerImpl playerImpl;
private LockManager lockManager;
private boolean isPopupClosing = false; private boolean isPopupClosing = false;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -152,7 +150,6 @@ public final class PopupVideoPlayer extends Service {
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
lockManager = new LockManager(this);
playerImpl = new VideoPlayerImpl(this); playerImpl = new VideoPlayerImpl(this);
ThemeHelper.setTheme(this); ThemeHelper.setTheme(this);
@ -378,9 +375,6 @@ public final class PopupVideoPlayer extends Service {
} }
mBinder = null; mBinder = null;
if (lockManager != null) {
lockManager.releaseWifiAndCpu();
}
if (notificationManager != null) { if (notificationManager != null) {
notificationManager.cancel(NOTIFICATION_ID); notificationManager.cancel(NOTIFICATION_ID);
} }
@ -898,7 +892,7 @@ public final class PopupVideoPlayer extends Service {
public void onBlocked() { public void onBlocked() {
super.onBlocked(); super.onBlocked();
resetNotification(); resetNotification();
updateNotification(R.drawable.ic_play_arrow_white); updateNotification(R.drawable.exo_controls_play);
} }
@Override @Override
@ -908,20 +902,19 @@ public final class PopupVideoPlayer extends Service {
updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS); updateWindowFlags(ONGOING_PLAYBACK_WINDOW_FLAGS);
resetNotification(); resetNotification();
updateNotification(R.drawable.ic_pause_white); updateNotification(R.drawable.exo_controls_pause);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white); videoPlayPause.setBackgroundResource(R.drawable.exo_controls_pause);
hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
startForeground(NOTIFICATION_ID, notBuilder.build()); startForeground(NOTIFICATION_ID, notBuilder.build());
lockManager.acquireWifiAndCpu();
} }
@Override @Override
public void onBuffering() { public void onBuffering() {
super.onBuffering(); super.onBuffering();
resetNotification(); resetNotification();
updateNotification(R.drawable.ic_play_arrow_white); updateNotification(R.drawable.exo_controls_play);
} }
@Override @Override
@ -931,10 +924,8 @@ public final class PopupVideoPlayer extends Service {
updateWindowFlags(IDLE_WINDOW_FLAGS); updateWindowFlags(IDLE_WINDOW_FLAGS);
resetNotification(); resetNotification();
updateNotification(R.drawable.ic_play_arrow_white); updateNotification(R.drawable.exo_controls_play);
videoPlayPause.setBackgroundResource(R.drawable.exo_controls_play);
videoPlayPause.setBackgroundResource(R.drawable.ic_play_arrow_white);
lockManager.releaseWifiAndCpu();
stopForeground(false); stopForeground(false);
} }
@ -943,9 +934,9 @@ public final class PopupVideoPlayer extends Service {
public void onPausedSeek() { public void onPausedSeek() {
super.onPausedSeek(); super.onPausedSeek();
resetNotification(); resetNotification();
updateNotification(R.drawable.ic_play_arrow_white); updateNotification(R.drawable.exo_controls_play);
videoPlayPause.setBackgroundResource(R.drawable.ic_pause_white); videoPlayPause.setBackgroundResource(R.drawable.exo_controls_play);
} }
@Override @Override
@ -955,10 +946,8 @@ public final class PopupVideoPlayer extends Service {
updateWindowFlags(IDLE_WINDOW_FLAGS); updateWindowFlags(IDLE_WINDOW_FLAGS);
resetNotification(); resetNotification();
updateNotification(R.drawable.ic_replay_white); updateNotification(R.drawable.ic_replay_white_24dp);
videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white_24dp);
videoPlayPause.setBackgroundResource(R.drawable.ic_replay_white);
lockManager.releaseWifiAndCpu();
stopForeground(false); stopForeground(false);
} }

View File

@ -643,13 +643,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
private void onStateChanged(final int state) { private void onStateChanged(final int state) {
switch (state) { switch (state) {
case BasePlayer.STATE_PAUSED: case BasePlayer.STATE_PAUSED:
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white); playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp);
break; break;
case BasePlayer.STATE_PLAYING: case BasePlayer.STATE_PLAYING:
playPauseButton.setImageResource(R.drawable.ic_pause_white); playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp);
break; break;
case BasePlayer.STATE_COMPLETED: case BasePlayer.STATE_COMPLETED:
playPauseButton.setImageResource(R.drawable.ic_replay_white); playPauseButton.setImageResource(R.drawable.ic_replay_white_24dp);
break; break;
default: default:
break; break;
@ -717,9 +717,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
// using rootView.getContext() because getApplicationContext() didn't work // using rootView.getContext() because getApplicationContext() didn't work
item.setIcon(player.isMuted() item.setIcon(player.isMuted()
? ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), ? ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(),
R.attr.volume_off) R.attr.ic_volume_off)
: ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(), : ThemeHelper.resolveResourceIdFromAttr(rootView.getContext(),
R.attr.volume_on)); R.attr.ic_volume_up));
} }
} }
} }

View File

@ -46,7 +46,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.appcompat.content.res.AppCompatResources;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
@ -682,13 +682,13 @@ public abstract class VideoPlayer extends BasePlayer
@Override @Override
public void onFastRewind() { public void onFastRewind() {
super.onFastRewind(); super.onFastRewind();
showAndAnimateControl(R.drawable.ic_action_av_fast_rewind, true); showAndAnimateControl(R.drawable.ic_fast_rewind_white_24dp, true);
} }
@Override @Override
public void onFastForward() { public void onFastForward() {
super.onFastForward(); super.onFastForward();
showAndAnimateControl(R.drawable.ic_action_av_fast_forward, true); showAndAnimateControl(R.drawable.ic_fast_forward_white_24dp, true);
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -956,7 +956,7 @@ public abstract class VideoPlayer extends BasePlayer
controlAnimationView.setVisibility(View.VISIBLE); controlAnimationView.setVisibility(View.VISIBLE);
controlAnimationView.setImageDrawable(ContextCompat.getDrawable(context, drawableId)); controlAnimationView.setImageDrawable(AppCompatResources.getDrawable(context, drawableId));
controlViewAnimator.start(); controlViewAnimator.start();
} }

View File

@ -36,9 +36,9 @@ public class LoadedMediaSource implements ManagedMediaSource {
} }
@Override @Override
public void prepareSource(final SourceInfoRefreshListener listener, public void prepareSource(final MediaSourceCaller mediaSourceCaller,
@Nullable final TransferListener mediaTransferListener) { @Nullable final TransferListener mediaTransferListener) {
source.prepareSource(listener, mediaTransferListener); source.prepareSource(mediaSourceCaller, mediaTransferListener);
} }
@Override @Override
@ -46,6 +46,11 @@ public class LoadedMediaSource implements ManagedMediaSource {
source.maybeThrowSourceInfoRefreshError(); source.maybeThrowSourceInfoRefreshError();
} }
@Override
public void enable(final MediaSourceCaller caller) {
source.enable(caller);
}
@Override @Override
public MediaPeriod createPeriod(final MediaPeriodId id, final Allocator allocator, public MediaPeriod createPeriod(final MediaPeriodId id, final Allocator allocator,
final long startPositionUs) { final long startPositionUs) {
@ -58,8 +63,13 @@ public class LoadedMediaSource implements ManagedMediaSource {
} }
@Override @Override
public void releaseSource(final SourceInfoRefreshListener listener) { public void disable(final MediaSourceCaller caller) {
source.releaseSource(listener); source.disable(caller);
}
@Override
public void releaseSource(final MediaSourceCaller mediaSourceCaller) {
source.releaseSource(mediaSourceCaller);
} }
@Override @Override

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe.player.mediasource; package org.schabi.newpipe.player.mediasource;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
@ -27,4 +28,10 @@ public interface ManagedMediaSource extends MediaSource {
* @return whether this source is for the specified stream * @return whether this source is for the specified stream
*/ */
boolean isStreamEqual(@NonNull PlayQueueItem stream); boolean isStreamEqual(@NonNull PlayQueueItem stream);
@Nullable
@Override
default Object getTag() {
return this;
}
} }

View File

@ -35,7 +35,7 @@ public class ManagedMediaSourcePlaylist {
@Nullable @Nullable
public ManagedMediaSource get(final int index) { public ManagedMediaSource get(final int index) {
return (index < 0 || index >= size()) return (index < 0 || index >= size())
? null : (ManagedMediaSource) internalSource.getMediaSource(index); ? null : (ManagedMediaSource) internalSource.getMediaSource(index).getTag();
} }
@NonNull @NonNull

View File

@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
@ -48,27 +49,31 @@ public class CustomTrackSelector extends DefaultTrackSelector {
@Override @Override
@Nullable @Nullable
protected Pair<TrackSelection.Definition, TextTrackScore> selectTextTrack( protected Pair<TrackSelection.Definition, TextTrackScore> selectTextTrack(
final TrackGroupArray groups, final int[][] formatSupport, final Parameters params, final TrackGroupArray groups,
@NonNull final int[][] formatSupport,
@NonNull final Parameters params,
@Nullable final String selectedAudioLanguage) { @Nullable final String selectedAudioLanguage) {
TrackGroup selectedGroup = null; TrackGroup selectedGroup = null;
int selectedTrackIndex = C.INDEX_UNSET; int selectedTrackIndex = C.INDEX_UNSET;
int newPipeTrackScore = 0;
TextTrackScore selectedTrackScore = null; TextTrackScore selectedTrackScore = null;
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup trackGroup = groups.get(groupIndex); TrackGroup trackGroup = groups.get(groupIndex);
int[] trackFormatSupport = formatSupport[groupIndex]; @Capabilities int[] trackFormatSupport = formatSupport[groupIndex];
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
if (isSupported(trackFormatSupport[trackIndex], if (isSupported(trackFormatSupport[trackIndex],
params.exceedRendererCapabilitiesIfNecessary)) { params.exceedRendererCapabilitiesIfNecessary)) {
Format format = trackGroup.getFormat(trackIndex); Format format = trackGroup.getFormat(trackIndex);
TextTrackScore trackScore = new TextTrackScore(format, params, TextTrackScore trackScore = new TextTrackScore(format, params,
trackFormatSupport[trackIndex], selectedAudioLanguage); trackFormatSupport[trackIndex], selectedAudioLanguage);
if (formatHasLanguage(format, preferredTextLanguage)) { if (formatHasLanguage(format, preferredTextLanguage)) {
selectedGroup = trackGroup; selectedGroup = trackGroup;
selectedTrackIndex = trackIndex; selectedTrackIndex = trackIndex;
selectedTrackScore = trackScore; selectedTrackScore = trackScore;
// found user selected match (perfect!) break; // found user selected match (perfect!)
break;
} else if (trackScore.isWithinConstraints && (selectedTrackScore == null } else if (trackScore.isWithinConstraints && (selectedTrackScore == null
|| trackScore.compareTo(selectedTrackScore) > 0)) { || trackScore.compareTo(selectedTrackScore) > 0)) {
selectedGroup = trackGroup; selectedGroup = trackGroup;

View File

@ -4,7 +4,7 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.acra.collector.CrashReportData; import org.acra.data.CrashReportData;
import org.acra.sender.ReportSender; import org.acra.sender.ReportSender;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;

View File

@ -4,7 +4,7 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.acra.config.ACRAConfiguration; import org.acra.config.CoreConfiguration;
import org.acra.sender.ReportSender; import org.acra.sender.ReportSender;
import org.acra.sender.ReportSenderFactory; import org.acra.sender.ReportSenderFactory;
@ -31,7 +31,7 @@ import org.acra.sender.ReportSenderFactory;
public class AcraReportSenderFactory implements ReportSenderFactory { public class AcraReportSenderFactory implements ReportSenderFactory {
@NonNull @NonNull
public ReportSender create(@NonNull final Context context, public ReportSender create(@NonNull final Context context,
@NonNull final ACRAConfiguration config) { @NonNull final CoreConfiguration config) {
return new AcraReportSender(); return new AcraReportSender();
} }
} }

View File

@ -32,7 +32,7 @@ import com.google.android.material.snackbar.Snackbar;
import com.grack.nanojson.JsonWriter; import com.grack.nanojson.JsonWriter;
import org.acra.ReportField; import org.acra.ReportField;
import org.acra.collector.CrashReportData; import org.acra.data.CrashReportData;
import org.schabi.newpipe.ActivityCommunicator; import org.schabi.newpipe.ActivityCommunicator;
import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
@ -149,14 +149,7 @@ public class ErrorActivity extends AppCompatActivity {
public static void reportError(final Context context, final CrashReportData report, public static void reportError(final Context context, final CrashReportData report,
final ErrorInfo errorInfo) { final ErrorInfo errorInfo) {
// get key first (don't ask about this solution) String[] el = new String[]{report.getString(ReportField.STACK_TRACE)};
ReportField key = null;
for (ReportField k : report.keySet()) {
if (k.toString().equals("STACK_TRACE")) {
key = k;
}
}
String[] el = new String[]{report.get(key).toString()};
Intent intent = new Intent(context, ErrorActivity.class); Intent intent = new Intent(context, ErrorActivity.class);
intent.putExtra(ERROR_INFO, errorInfo); intent.putExtra(ERROR_INFO, errorInfo);
@ -229,12 +222,15 @@ public class ErrorActivity extends AppCompatActivity {
context.startActivity(webIntent); context.startActivity(webIntent);
}) })
.setPositiveButton(R.string.accept, (dialog, which) -> { .setPositiveButton(R.string.accept, (dialog, which) -> {
Intent i = new Intent(Intent.ACTION_SENDTO); final Intent i = new Intent(Intent.ACTION_SENDTO)
i.setData(Uri.parse("mailto:" + ERROR_EMAIL_ADDRESS)) .setData(Uri.parse("mailto:")) // only email apps should handle this
.putExtra(Intent.EXTRA_EMAIL, new String[]{ERROR_EMAIL_ADDRESS})
.putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT) .putExtra(Intent.EXTRA_SUBJECT, ERROR_EMAIL_SUBJECT)
.putExtra(Intent.EXTRA_TEXT, buildJson()); .putExtra(Intent.EXTRA_TEXT, buildJson());
if (i.resolveActivity(getPackageManager()) != null) {
startActivity(i);
}
startActivity(Intent.createChooser(i, "Send Email"));
}) })
.setNegativeButton(R.string.decline, (dialog, which) -> { .setNegativeButton(R.string.decline, (dialog, which) -> {
// do nothing // do nothing

View File

@ -10,6 +10,7 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -22,6 +23,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import org.schabi.newpipe.local.subscription.SubscriptionManager; import org.schabi.newpipe.local.subscription.SubscriptionManager;
import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
@ -83,6 +85,12 @@ public class SelectChannelFragment extends DialogFragment {
// Init // Init
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NO_TITLE, ThemeHelper.getMinWidthDialogTheme(requireContext()));
}
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) { final Bundle savedInstanceState) {

View File

@ -9,7 +9,8 @@ import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.content.ContextCompat; import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -21,6 +22,7 @@ import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.ThemeHelper;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
@ -61,6 +63,16 @@ public class SelectKioskFragment extends DialogFragment {
onCancelListener = listener; onCancelListener = listener;
} }
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NO_TITLE, ThemeHelper.getMinWidthDialogTheme(requireContext()));
}
@Override @Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) { final Bundle savedInstanceState) {
@ -148,13 +160,8 @@ public class SelectKioskFragment extends DialogFragment {
final Entry entry = kioskList.get(position); final Entry entry = kioskList.get(position);
holder.titleView.setText(entry.kioskName); holder.titleView.setText(entry.kioskName);
holder.thumbnailView holder.thumbnailView
.setImageDrawable(ContextCompat.getDrawable(getContext(), entry.icon)); .setImageDrawable(AppCompatResources.getDrawable(requireContext(), entry.icon));
holder.view.setOnClickListener(new View.OnClickListener() { holder.view.setOnClickListener(view -> clickedItem(entry));
@Override
public void onClick(final View view) {
clickedItem(entry);
}
});
} }
class Entry { class Entry {

View File

@ -60,7 +60,7 @@ public final class AddTabDialog {
private DialogListAdapter(final Context context, final ChooseTabListItem[] items) { private DialogListAdapter(final Context context, final ChooseTabListItem[] items) {
this.inflater = LayoutInflater.from(context); this.inflater = LayoutInflater.from(context);
this.items = items; this.items = items;
this.fallbackIcon = ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot); this.fallbackIcon = ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_kiosk_hot);
} }
@Override @Override

View File

@ -233,7 +233,7 @@ public class ChooseTabsFragment extends Fragment {
case KIOSK: case KIOSK:
returnList.add(new ChooseTabListItem(tab.getTabId(), returnList.add(new ChooseTabListItem(tab.getTabId(),
getString(R.string.kiosk_page_summary), getString(R.string.kiosk_page_summary),
ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot))); ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_kiosk_hot)));
break; break;
case CHANNEL: case CHANNEL:
returnList.add(new ChooseTabListItem(tab.getTabId(), returnList.add(new ChooseTabListItem(tab.getTabId(),
@ -244,7 +244,8 @@ public class ChooseTabsFragment extends Fragment {
if (!tabList.contains(tab)) { if (!tabList.contains(tab)) {
returnList.add(new ChooseTabListItem(tab.getTabId(), returnList.add(new ChooseTabListItem(tab.getTabId(),
getString(R.string.default_kiosk_page_summary), getString(R.string.default_kiosk_page_summary),
ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot))); ThemeHelper.resolveResourceIdFromAttr(context,
R.attr.ic_kiosk_hot)));
} }
break; break;
default: default:

View File

@ -231,7 +231,7 @@ public abstract class Tab {
@DrawableRes @DrawableRes
@Override @Override
public int getTabIconRes(final Context context) { public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.rss); return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_rss);
} }
@Override @Override
@ -281,7 +281,7 @@ public abstract class Tab {
@DrawableRes @DrawableRes
@Override @Override
public int getTabIconRes(final Context context) { public int getTabIconRes(final Context context) {
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.history); return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_history);
} }
@Override @Override
@ -323,7 +323,7 @@ public abstract class Tab {
@DrawableRes @DrawableRes
@Override @Override
public int getTabIconRes(final Context context) { public int getTabIconRes(final Context context) {
final int kioskIcon = KioskTranslator.getKioskIcons(kioskId, context); final int kioskIcon = KioskTranslator.getKioskIcon(kioskId, context);
if (kioskIcon <= 0) { if (kioskIcon <= 0) {
throw new IllegalStateException("Kiosk ID is not valid: \"" + kioskId + "\""); throw new IllegalStateException("Kiosk ID is not valid: \"" + kioskId + "\"");
@ -459,7 +459,7 @@ public abstract class Tab {
@DrawableRes @DrawableRes
@Override @Override
public int getTabIconRes(final Context context) { public int getTabIconRes(final Context context) {
return KioskTranslator.getKioskIcons(getDefaultKioskId(context), context); return KioskTranslator.getKioskIcon(getDefaultKioskId(context), context);
} }
@Override @Override

View File

@ -1,6 +1,6 @@
package org.schabi.newpipe.util; package org.schabi.newpipe.util;
import org.jsoup.helper.StringUtil; import android.text.TextUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -16,7 +16,7 @@ public final class CookieUtils {
for (String cookies : cookieStrings) { for (String cookies : cookieStrings) {
cookieSet.addAll(splitCookies(cookies)); cookieSet.addAll(splitCookies(cookies));
} }
return StringUtil.join(cookieSet, "; ").trim(); return TextUtils.join("; ", cookieSet).trim();
} }
public static Set<String> splitCookies(final String cookies) { public static Set<String> splitCookies(final String cookies) {

View File

@ -49,19 +49,19 @@ public final class KioskTranslator {
} }
} }
public static int getKioskIcons(final String kioskId, final Context c) { public static int getKioskIcon(final String kioskId, final Context c) {
switch (kioskId) { switch (kioskId) {
case "Trending": case "Trending":
case "Top 50": case "Top 50":
case "New & hot": case "New & hot":
case "conferences": case "conferences":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_hot); return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_hot);
case "Local": case "Local":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_local); return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_local);
case "Recently added": case "Recently added":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_recent); return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_kiosk_recent);
case "Most liked": case "Most liked":
return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.thumbs_up); return ThemeHelper.resolveResourceIdFromAttr(c, R.attr.ic_thumb_up);
default: default:
return 0; return 0;
} }

View File

@ -5,6 +5,7 @@ import android.content.SharedPreferences;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@ -267,23 +268,22 @@ public final class ListHelper {
} }
/** /**
* Get the audio from the list with the highest quality. Format will be ignored if it yields * Get the audio from the list with the highest quality.
* no results. * Format will be ignored if it yields no results.
* *
* @param format the format to look for * @param format The target format type or null if it doesn't matter
* @param audioStreams list the audio streams * @param audioStreams List of audio streams
* @return index of the audio with the highest average bitrate of the default format * @return Index of audio stream that produces the most compact results or -1 if not found
*/ */
static int getHighestQualityAudioIndex(final MediaFormat format, static int getHighestQualityAudioIndex(@Nullable MediaFormat format,
final List<AudioStream> audioStreams) { final List<AudioStream> audioStreams) {
int result = -1; int result = -1;
boolean hasOneFormat = false;
if (audioStreams != null) { if (audioStreams != null) {
while (result == -1) { while (result == -1) {
AudioStream prevStream = null; AudioStream prevStream = null;
for (int idx = 0; idx < audioStreams.size(); idx++) { for (int idx = 0; idx < audioStreams.size(); idx++) {
AudioStream stream = audioStreams.get(idx); AudioStream stream = audioStreams.get(idx);
if ((format == null || stream.getFormat() == format || hasOneFormat) if ((format == null || stream.getFormat() == format)
&& (prevStream == null || compareAudioStreamBitrate(prevStream, stream, && (prevStream == null || compareAudioStreamBitrate(prevStream, stream,
AUDIO_FORMAT_QUALITY_RANKING) < 0)) { AUDIO_FORMAT_QUALITY_RANKING) < 0)) {
prevStream = stream; prevStream = stream;
@ -293,30 +293,29 @@ public final class ListHelper {
if (result == -1 && format == null) { if (result == -1 && format == null) {
break; break;
} }
hasOneFormat = true; format = null;
} }
} }
return result; return result;
} }
/** /**
* Get the audio from the list with the lowest bitrate and efficient format. Format will be * Get the audio from the list with the lowest bitrate and most efficient format.
* ignored if it yields no results. * Format will be ignored if it yields no results.
* *
* @param format The target format type or null if it doesn't matter * @param format The target format type or null if it doesn't matter
* @param audioStreams List of audio streams * @param audioStreams List of audio streams
* @return Index of audio stream that can produce the most compact results or -1 if not found * @return Index of audio stream that produces the most compact results or -1 if not found
*/ */
static int getMostCompactAudioIndex(final MediaFormat format, static int getMostCompactAudioIndex(@Nullable MediaFormat format,
final List<AudioStream> audioStreams) { final List<AudioStream> audioStreams) {
int result = -1; int result = -1;
boolean hasOneFormat = false;
if (audioStreams != null) { if (audioStreams != null) {
while (result == -1) { while (result == -1) {
AudioStream prevStream = null; AudioStream prevStream = null;
for (int idx = 0; idx < audioStreams.size(); idx++) { for (int idx = 0; idx < audioStreams.size(); idx++) {
AudioStream stream = audioStreams.get(idx); AudioStream stream = audioStreams.get(idx);
if ((format == null || stream.getFormat() == format || hasOneFormat) if ((format == null || stream.getFormat() == format)
&& (prevStream == null || compareAudioStreamBitrate(prevStream, stream, && (prevStream == null || compareAudioStreamBitrate(prevStream, stream,
AUDIO_FORMAT_EFFICIENCY_RANKING) > 0)) { AUDIO_FORMAT_EFFICIENCY_RANKING) > 0)) {
prevStream = stream; prevStream = stream;
@ -326,7 +325,7 @@ public final class ListHelper {
if (result == -1 && format == null) { if (result == -1 && format == null) {
break; break;
} }
hasOneFormat = true; format = null;
} }
} }
return result; return result;

View File

@ -2,19 +2,74 @@ package org.schabi.newpipe.util;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
public final class ShareUtils { public final class ShareUtils {
private ShareUtils() { } private ShareUtils() {
}
/**
* Open the url with the system default browser.
* <p>
* If no browser is set as default, fallbacks to
* {@link ShareUtils#openInDefaultApp(Context, String)}
*
* @param context the context to use
* @param url the url to browse
*/
public static void openUrlInBrowser(final Context context, final String url) { public static void openUrlInBrowser(final Context context, final String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); final String defaultBrowserPackageName = getDefaultBrowserPackageName(context);
if (defaultBrowserPackageName.equals("android")) {
// no browser set as default
openInDefaultApp(context, url);
} else {
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setPackage(defaultBrowserPackageName);
context.startActivity(intent);
}
}
/**
* Open the url in the default app set to open this type of link.
* <p>
* If no app is set as default, it will open a chooser
*
* @param context the context to use
* @param url the url to browse
*/
private static void openInDefaultApp(final Context context, final String url) {
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(Intent.createChooser( context.startActivity(Intent.createChooser(
intent, context.getString(R.string.share_dialog_title))); intent, context.getString(R.string.share_dialog_title)));
} }
/**
* Get the default browser package name.
* <p>
* If no browser is set as default, it will return "android"
*
* @param context the context to use
* @return the package name of the default browser, or "android" if there's no default
*/
private static String getDefaultBrowserPackageName(final Context context) {
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://"));
final ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(
intent, PackageManager.MATCH_DEFAULT_ONLY);
return resolveInfo.activityInfo.packageName;
}
/**
* Open the android share menu to share the current url.
*
* @param context the context to use
* @param subject the url subject, typically the title
* @param url the url to share
*/
public static void shareUrl(final Context context, final String subject, final String url) { public static void shareUrl(final Context context, final String subject, final String url) {
Intent intent = new Intent(Intent.ACTION_SEND); Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain"); intent.setType("text/plain");

View File

@ -231,16 +231,4 @@ public final class ThemeHelper {
return PreferenceManager.getDefaultSharedPreferences(context) return PreferenceManager.getDefaultSharedPreferences(context)
.getString(themeKey, defaultTheme); .getString(themeKey, defaultTheme);
} }
/**
* This will get the R.drawable.* resource to which attr is currently pointing to.
*
* @param attr a R.attribute.* resource value
* @param context the context to use
* @return a R.drawable.* resource value
*/
public static int getIconByAttr(final int attr, final Context context) {
return context.obtainStyledAttributes(new int[]{attr})
.getResourceId(0, -1);
}
} }

View File

@ -224,15 +224,9 @@ public class MissionsFragment extends Fragment {
mList.setAdapter(mAdapter); mList.setAdapter(mAdapter);
if (mSwitch != null) { if (mSwitch != null) {
boolean isLight = ThemeHelper.isLightThemeSelected(mContext); mSwitch.setIcon(mLinear
int icon; ? ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_grid)
: ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_list));
if (mLinear)
icon = isLight ? R.drawable.ic_grid_black_24dp : R.drawable.ic_grid_white_24dp;
else
icon = isLight ? R.drawable.ic_list_black_24dp : R.drawable.ic_list_white_24dp;
mSwitch.setIcon(icon);
mSwitch.setTitle(mLinear ? R.string.grid : R.string.list); mSwitch.setTitle(mLinear ? R.string.grid : R.string.list);
mPrefs.edit().putBoolean("linear", mLinear).apply(); mPrefs.edit().putBoolean("linear", mLinear).apply();
} }

View File

@ -191,12 +191,12 @@ public class Utility {
public static int getIconForFileType(FileType type) { public static int getIconForFileType(FileType type) {
switch (type) { switch (type) {
case MUSIC: case MUSIC:
return R.drawable.music; return R.drawable.ic_headset_white_24dp;
default: default:
case VIDEO: case VIDEO:
return R.drawable.video; return R.drawable.ic_movie_white_24dp;
case SUBTITLE: case SUBTITLE:
return R.drawable.subtitle; return R.drawable.ic_subtitles_white_24dp;
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

Some files were not shown because too many files have changed in this diff Show More